diff --git a/common/constants.ts b/common/constants.ts index fc186934..51cfc328 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -64,3 +64,4 @@ export const FETCH_ALL_QUERY_BODY = { }, size: 1000, }; +export const INDEX_NOT_FOUND_EXCEPTION = 'index_not_found_exception'; diff --git a/public/pages/workflow_detail/component_details/input_fields/text_field.tsx b/public/pages/workflow_detail/component_details/input_fields/text_field.tsx index 2bcb51a7..4c3e5dec 100644 --- a/public/pages/workflow_detail/component_details/input_fields/text_field.tsx +++ b/public/pages/workflow_detail/component_details/input_fields/text_field.tsx @@ -57,12 +57,10 @@ export function TextField(props: TextFieldProps) { placeholder={props.field.placeholder || ''} compressed={false} value={field.value || getInitialValue(props.field.type)} - onChange={(e) => form.setFieldValue(formField, e.target.value)} - // This is a design decision to only trigger form updates onBlur() instead - // of onChange(). This is to rate limit the number of updates & re-renders made, as users - // typically rapidly type things into a text box, which would consequently trigger - // onChange() much more often. - onBlur={() => props.onFormChange()} + onChange={(e) => { + form.setFieldValue(formField, e.target.value); + props.onFormChange(); + }} /> ); diff --git a/public/pages/workflow_detail/components/header.tsx b/public/pages/workflow_detail/components/header.tsx index b31dab11..3d1e4035 100644 --- a/public/pages/workflow_detail/components/header.tsx +++ b/public/pages/workflow_detail/components/header.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { EuiPageHeader, EuiButton, - EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem, EuiText, @@ -26,13 +25,11 @@ interface WorkflowDetailHeaderProps { export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) { function getTitle() { - return props.workflow ? ( - props.workflow.name - ) : props.isNewWorkflow && !props.workflow ? ( - DEFAULT_NEW_WORKFLOW_NAME - ) : ( - - ); + return props.workflow + ? props.workflow.name + : props.isNewWorkflow && !props.workflow + ? DEFAULT_NEW_WORKFLOW_NAME + : ''; } function getState() { diff --git a/public/pages/workflow_detail/workflow_detail.tsx b/public/pages/workflow_detail/workflow_detail.tsx index a2f15dd8..eeceab69 100644 --- a/public/pages/workflow_detail/workflow_detail.tsx +++ b/public/pages/workflow_detail/workflow_detail.tsx @@ -63,7 +63,7 @@ function replaceActiveTab(activeTab: string, props: WorkflowDetailProps) { export function WorkflowDetail(props: WorkflowDetailProps) { const dispatch = useAppDispatch(); - const { workflows, cachedWorkflow } = useSelector( + const { workflows, cachedWorkflow, errorMessage } = useSelector( (state: AppState) => state.workflows ); @@ -115,6 +115,14 @@ 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]); + const tabs = [ { id: WORKFLOW_DETAILS_TAB.EDITOR, diff --git a/public/pages/workflow_detail/workspace/resizable_workspace.tsx b/public/pages/workflow_detail/workspace/resizable_workspace.tsx index d0d1e7bb..e5843d8c 100644 --- a/public/pages/workflow_detail/workspace/resizable_workspace.tsx +++ b/public/pages/workflow_detail/workspace/resizable_workspace.tsx @@ -343,8 +343,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) { setIsDeprovisioning(false); }) .catch((error: any) => { - // TODO: process error (toast msg?) - console.log('error: ', error); setIsDeprovisioning(false); }); } else { @@ -369,8 +367,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) { setIsProvisioning(false); }) .catch((error: any) => { - // TODO: process error (toast msg?) - console.log('error: ', error); setIsProvisioning(false); }); } else { @@ -407,8 +403,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) { setIsSaving(false); }) .catch((error: any) => { - // TODO: process error (toast msg?) - console.log('error: ', error); setIsSaving(false); }); } else { @@ -420,8 +414,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) { history.go(0); }) .catch((error: any) => { - // TODO: process error (toast msg?) - console.log('error: ', error); setIsSaving(false); }); } diff --git a/public/pages/workflows/workflows.tsx b/public/pages/workflows/workflows.tsx index fdc5acdb..90ae6d92 100644 --- a/public/pages/workflows/workflows.tsx +++ b/public/pages/workflows/workflows.tsx @@ -50,7 +50,7 @@ function replaceActiveTab(activeTab: string, props: WorkflowsProps) { */ export function Workflows(props: WorkflowsProps) { const dispatch = useAppDispatch(); - const { workflows, loading } = useSelector( + const { workflows, loading, errorMessage } = useSelector( (state: AppState) => state.workflows ); @@ -84,6 +84,14 @@ 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)); diff --git a/server/routes/flow_framework_routes_service.ts b/server/routes/flow_framework_routes_service.ts index d9ee6190..29f9f42d 100644 --- a/server/routes/flow_framework_routes_service.ts +++ b/server/routes/flow_framework_routes_service.ts @@ -25,6 +25,7 @@ import { UPDATE_WORKFLOW_NODE_API_PATH, WORKFLOW_STATE, Workflow, + WorkflowDict, WorkflowTemplate, validateWorkflowTemplate, } from '../../common'; @@ -32,6 +33,7 @@ import { generateCustomError, getWorkflowStateFromResponse, getWorkflowsFromResponses, + isIgnorableError, } from './helpers'; /** @@ -196,6 +198,9 @@ export class FlowFrameworkRoutesService { ); return res.ok({ body: { workflows: workflowDict } }); } catch (err: any) { + if (isIgnorableError(err)) { + return res.ok({ body: { workflows: {} as WorkflowDict } }); + } return generateCustomError(res, err); } }; diff --git a/server/routes/helpers.ts b/server/routes/helpers.ts index 531d1bd1..4186347b 100644 --- a/server/routes/helpers.ts +++ b/server/routes/helpers.ts @@ -5,6 +5,7 @@ import { DEFAULT_NEW_WORKFLOW_STATE_TYPE, + INDEX_NOT_FOUND_EXCEPTION, Model, ModelDict, WORKFLOW_STATE, @@ -26,6 +27,11 @@ export function generateCustomError(res: any, err: any) { }); } +// Helper fn to filter out backend errors that we don't want to propagate on the frontend. +export function isIgnorableError(error: any): boolean { + return error.body?.error?.type === INDEX_NOT_FOUND_EXCEPTION; +} + function toWorkflowObj(workflowHit: any): Workflow { // TODO: update schema parsing after hit schema has been updated. // https://github.com/opensearch-project/flow-framework/issues/546