Skip to content

Commit

Permalink
Add error field to workflow; propagate all runtime errors in tools tab (
Browse files Browse the repository at this point in the history
#210) (#211)

Signed-off-by: Tyler Ohlsen <[email protected]>
(cherry picked from commit 9f7e6eb)

Co-authored-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
opensearch-trigger-bot[bot] and ohltyler committed Jul 11, 2024
1 parent d6f3b94 commit 822ef08
Show file tree
Hide file tree
Showing 11 changed files with 76 additions and 32 deletions.
2 changes: 2 additions & 0 deletions common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ export type Workflow = WorkflowTemplate & {
// won't exist until launched/provisioned in backend
state?: WORKFLOW_STATE;
// won't exist until launched/provisioned in backend
error?: string;
// won't exist until launched/provisioned in backend
resourcesCreated?: WorkflowResource[];
};

Expand Down
19 changes: 16 additions & 3 deletions public/pages/workflow_detail/tools/errors/errors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,27 @@
*/

import React from 'react';
import { EuiText } from '@elastic/eui';
import { EuiCodeBlock, EuiText } from '@elastic/eui';
import { isEmpty } from 'lodash';

interface ErrorsProps {}
interface ErrorsProps {
errorMessage: string;
}

/**
* The basic errors component for the Tools panel.
* Displays any errors found while users configure and test their workflow.
*/
export function Errors(props: ErrorsProps) {
return <EuiText>TODO: add errors details here</EuiText>;
return (
<>
{isEmpty(props.errorMessage) ? (
<EuiText>There are no errors.</EuiText>
) : (
<EuiCodeBlock fontSize="m" isCopyable={false}>
{props.errorMessage}
</EuiCodeBlock>
)}
</>
);
}
41 changes: 38 additions & 3 deletions public/pages/workflow_detail/tools/tools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/

import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from '../../../store';
import { isEmpty } from 'lodash';
import {
EuiFlexGroup,
Expand Down Expand Up @@ -59,16 +61,47 @@ const inputTabs = [
* The base Tools component for performing ingest and search, viewing resources, and debugging.
*/
export function Tools(props: ToolsProps) {
// error message state
const { opensearch, workflows } = useSelector((state: AppState) => state);
const opensearchError = opensearch.errorMessage;
const workflowsError = workflows.errorMessage;
const [curErrorMessage, setCurErrorMessage] = useState<string>('');

// selected tab state
const [selectedTabId, setSelectedTabId] = useState<string>(TAB_ID.INGEST);

// auto-navigate to ingest response if a populated value has been set, indicating ingest has been ran
// auto-navigate to errors tab if a new error has been set as a result of
// executing OpenSearch or Flow Framework workflow APIs, or from the workflow state
// (note that if provision/deprovision fails, there is no concrete exception returned at the API level -
// it is just set in the workflow's error field when fetching workflow state)
useEffect(() => {
setCurErrorMessage(opensearchError);
if (!isEmpty(opensearchError)) {
setSelectedTabId(TAB_ID.ERRORS);
}
}, [opensearchError]);

useEffect(() => {
setCurErrorMessage(workflowsError);
if (!isEmpty(workflowsError)) {
setSelectedTabId(TAB_ID.ERRORS);
}
}, [workflowsError]);
useEffect(() => {
setCurErrorMessage(props.workflow?.error || '');
if (!isEmpty(props.workflow?.error)) {
setSelectedTabId(TAB_ID.ERRORS);
}
}, [props.workflow?.error]);

// auto-navigate to ingest tab if a populated value has been set, indicating ingest has been ran
useEffect(() => {
if (!isEmpty(props.ingestResponse)) {
setSelectedTabId(TAB_ID.INGEST);
}
}, [props.ingestResponse]);

// auto-navigate to query response if a populated value has been set, indicating search has been ran
// auto-navigate to query tab if a populated value has been set, indicating search has been ran
useEffect(() => {
if (!isEmpty(props.queryResponse)) {
setSelectedTabId(TAB_ID.QUERY);
Expand Down Expand Up @@ -114,7 +147,9 @@ export function Tools(props: ToolsProps) {
{selectedTabId === TAB_ID.QUERY && (
<Query queryResponse={props.queryResponse} />
)}
{selectedTabId === TAB_ID.ERRORS && <Errors />}
{selectedTabId === TAB_ID.ERRORS && (
<Errors errorMessage={curErrorMessage} />
)}
{selectedTabId === TAB_ID.RESOURCES && (
<Resources workflow={props.workflow} />
)}
Expand Down
12 changes: 1 addition & 11 deletions public/pages/workflow_detail/workflow_detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ interface WorkflowDetailProps

export function WorkflowDetail(props: WorkflowDetailProps) {
const dispatch = useAppDispatch();
const { workflows, errorMessage } = useSelector(
(state: AppState) => state.workflows
);
const { workflows } = useSelector((state: AppState) => state.workflows);

// selected workflow state
const workflowId = props.match?.params?.workflowId;
Expand All @@ -67,14 +65,6 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
dispatch(searchModels(FETCH_ALL_QUERY_BODY));
}, []);

// Show a toast if an error message exists in state
useEffect(() => {
if (errorMessage) {
console.error(errorMessage);
getCore().notifications.toasts.addDanger(errorMessage);
}
}, [errorMessage]);

return (
<ReactFlowProvider>
<EuiPage>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
dispatch(removeDirty());
})
.catch((error: any) => {
getCore().notifications.toasts.addDanger(error);
props.setIngestResponse('');
throw error;
});
Expand Down Expand Up @@ -257,7 +256,6 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
dispatch(removeDirty());
})
.catch((error: any) => {
getCore().notifications.toasts.addDanger(error);
props.setQueryResponse('');
throw error;
});
Expand Down Expand Up @@ -334,9 +332,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
// @ts-ignore
await dispatch(getWorkflow(props.workflow.id));
})
.catch((error: any) => {
getCore().notifications.toasts.addDanger(error);
})
.catch((error: any) => {})
.finally(() => {
setIsModalOpen(false);
});
Expand Down
10 changes: 1 addition & 9 deletions public/pages/workflows/workflows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function replaceActiveTab(activeTab: string, props: WorkflowsProps) {
*/
export function Workflows(props: WorkflowsProps) {
const dispatch = useAppDispatch();
const { workflows, loading, errorMessage } = useSelector(
const { workflows, loading } = useSelector(
(state: AppState) => state.workflows
);

Expand Down Expand Up @@ -92,14 +92,6 @@ export function Workflows(props: WorkflowsProps) {
]);
});

// Show a toast if an error message exists in state
useEffect(() => {
if (errorMessage) {
console.error(errorMessage);
getCore().notifications.toasts.addDanger(errorMessage);
}
}, [errorMessage]);

// On initial render: fetch all workflows
useEffect(() => {
dispatch(searchWorkflows(FETCH_ALL_QUERY_BODY));
Expand Down
12 changes: 12 additions & 0 deletions public/store/reducers/opensearch_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ const opensearchSlice = createSlice({
state.loading = true;
state.errorMessage = '';
})
.addCase(ingest.pending, (state, action) => {
state.loading = true;
state.errorMessage = '';
})
.addCase(catIndices.fulfilled, (state, action) => {
const indicesMap = new Map<string, Index>();
action.payload.forEach((index: Index) => {
Expand All @@ -102,13 +106,21 @@ const opensearchSlice = createSlice({
state.loading = false;
state.errorMessage = '';
})
.addCase(ingest.fulfilled, (state, action) => {
state.loading = false;
state.errorMessage = '';
})
.addCase(catIndices.rejected, (state, action) => {
state.errorMessage = action.payload as string;
state.loading = false;
})
.addCase(searchIndex.rejected, (state, action) => {
state.errorMessage = action.payload as string;
state.loading = false;
})
.addCase(ingest.rejected, (state, action) => {
state.errorMessage = action.payload as string;
state.loading = false;
});
},
});
Expand Down
2 changes: 1 addition & 1 deletion public/store/reducers/workflows_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { Workflow, WorkflowDict, WorkflowTemplate } from '../../../common';
import { WorkflowDict, WorkflowTemplate } from '../../../common';
import { HttpFetchError } from '../../../../../src/core/public';
import { getRouteService } from '../../services';

Expand Down
1 change: 1 addition & 0 deletions public/utils/config_to_template_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export function reduceToTemplate(workflow: Workflow): WorkflowTemplate {
lastUpdated,
lastLaunched,
state,
error,
resourcesCreated,
...workflowTemplate
} = workflow;
Expand Down
1 change: 1 addition & 0 deletions server/routes/flow_framework_routes_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export class FlowFrameworkRoutesService {
const workflowWithState = {
...workflow,
state,
error: stateResponse.error,
resourcesCreated,
} as Workflow;
return res.ok({ body: { workflow: workflowWithState } });
Expand Down
2 changes: 2 additions & 0 deletions server/routes/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ export function getWorkflowsFromResponses(
const workflowState = getWorkflowStateFromResponse(
workflowStateHit?._source?.state
);
const workflowError = workflowStateHit?._source?.error;
const workflowResourcesCreated = getResourcesCreatedFromResponse(
workflowStateHit?._source?.resources_created
);
workflowDict[workflowHit._id] = {
...workflowDict[workflowHit._id],
// @ts-ignore
state: workflowState,
error: workflowError,
resourcesCreated: workflowResourcesCreated,
};
});
Expand Down

0 comments on commit 822ef08

Please sign in to comment.