Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b122738

Browse files
ohltylergithub-actions[bot]
authored andcommittedApr 16, 2024·
Onboard get workflow API (#133)
Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> (cherry picked from commit 28ce545)
1 parent 98ed8fc commit b122738

File tree

10 files changed

+55
-37
lines changed

10 files changed

+55
-37
lines changed
 

‎common/interfaces.ts

-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ export type Index = {
1616
*/
1717

1818
export type ReactFlowComponent = Node<IComponentData>;
19-
20-
// TODO: we may not need this re-defined type here at all, if we don't add
21-
// any special fields/configuration for an edge. Currently this
22-
// is the same as the default Edge type.
2319
export type ReactFlowEdge = Edge<{}> & {};
2420

2521
type ReactFlowViewport = {

‎public/component_types/indexer/indexer.ts

-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ export class Indexer extends BaseComponent {
2222
{
2323
id: 'transformer',
2424
label: 'Transformer',
25-
// TODO: may need to change to be looser. it should be able to take
26-
// in other component types
2725
baseClass: COMPONENT_CLASS.TRANSFORMER,
2826
acceptMultiple: false,
2927
},

‎public/component_types/interfaces.ts

-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import { COMPONENT_CATEGORY, COMPONENT_CLASS } from '../utils';
1212
*/
1313
export type FieldType = 'string' | 'json' | 'select';
1414
export type SelectType = 'model';
15-
// TODO: this may expand to more types in the future. Formik supports 'any' so we can too.
16-
// For now, limiting scope to expected types.
1715
export type FieldValue = string | {};
1816
export type ComponentFormValues = FormikValues;
1917
export type WorkspaceFormValues = {

‎public/pages/workflow_detail/workflow_detail.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { getCore } from '../../services';
1414
import { WorkflowDetailHeader } from './components';
1515
import {
1616
AppState,
17+
getWorkflow,
1718
searchModels,
18-
searchWorkflows,
1919
useAppDispatch,
2020
} from '../../store';
2121
import { ResizableWorkspace } from './workspace';
@@ -109,8 +109,7 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
109109
// - fetch available models as their IDs may be used when building flows
110110
useEffect(() => {
111111
if (!isNewWorkflow) {
112-
// TODO: can optimize to only fetch a single workflow
113-
dispatch(searchWorkflows(FETCH_ALL_QUERY_BODY));
112+
dispatch(getWorkflow(workflowId));
114113
}
115114
dispatch(searchModels(FETCH_ALL_QUERY_BODY));
116115
}, []);

‎public/pages/workflow_detail/workspace/resizable_workspace.tsx

+16-6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
WORKFLOW_STATE,
3333
processNodes,
3434
reduceToTemplate,
35+
ReactFlowEdge,
3536
} from '../../../../common';
3637
import { validateWorkspaceFlow, toTemplateFlows } from '../utils';
3738
import {
@@ -133,8 +134,13 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
133134
* - open the panel if a node is selected and the panel is closed
134135
* - it is assumed that only one node can be selected at once
135136
*/
136-
// TODO: make more typesafe
137-
function onSelectionChange({ nodes, edges }) {
137+
function onSelectionChange({
138+
nodes,
139+
edges,
140+
}: {
141+
nodes: ReactFlowComponent[];
142+
edges: ReactFlowEdge[];
143+
}) {
138144
if (nodes && nodes.length > 0) {
139145
setSelectedComponent(nodes[0]);
140146
if (!isDetailsPanelOpen) {
@@ -276,7 +282,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
276282
} as Workflow;
277283
processWorkflowFn(updatedWorkflow);
278284
} else {
279-
// TODO: bubble up flow error?
280285
setFlowValidOnSubmit(false);
281286
setIsSaving(false);
282287
}
@@ -336,7 +341,10 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
336341
setIsDeprovisioning(false);
337342
});
338343
} else {
339-
// TODO: this case should not happen
344+
// This case should not happen
345+
console.debug(
346+
'Deprovisioning triggered on an invalid workflow. Ignoring.'
347+
);
340348
}
341349
}}
342350
>
@@ -360,7 +368,10 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
360368
setIsProvisioning(false);
361369
});
362370
} else {
363-
// TODO: this case should not happen
371+
// This case should not happen
372+
console.debug(
373+
'Provisioning triggered on an invalid workflow. Ignoring.'
374+
);
364375
}
365376
}}
366377
>
@@ -370,7 +381,6 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
370381
fill={false}
371382
disabled={!isSaveable || isLoadingGlobal || isDeprovisionable}
372383
isLoading={isSaving}
373-
// TODO: if props.isNewWorkflow is true, clear the workflow cache if saving is successful.
374384
onClick={() => {
375385
setIsSaving(true);
376386
dispatch(removeDirty());

‎public/pages/workflow_detail/workspace/workspace.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { setDirty, useAppDispatch } from '../../../store';
2121
import {
2222
IComponentData,
2323
ReactFlowComponent,
24+
ReactFlowEdge,
2425
Workflow,
2526
} from '../../../../common';
2627
import {
@@ -41,8 +42,13 @@ interface WorkspaceProps {
4142
readonly: boolean;
4243
onNodesChange: (nodes: ReactFlowComponent[]) => void;
4344
id: string;
44-
// TODO: make more typesafe
45-
onSelectionChange: ({ nodes, edges }) => void;
45+
onSelectionChange: ({
46+
nodes,
47+
edges,
48+
}: {
49+
nodes: ReactFlowComponent[];
50+
edges: ReactFlowEdge[];
51+
}) => void;
4652
}
4753

4854
const nodeTypes = {

‎public/store/reducers/workflows_reducer.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,11 @@ const workflowsSlice = createSlice({
220220
// Fulfilled states: mutate state depending on the action type
221221
// and payloads
222222
.addCase(getWorkflow.fulfilled, (state, action) => {
223-
// TODO: add logic to mutate state
224-
// const workflow = action.payload;
225-
// state.workflows = {
226-
// ...state.workflows,
227-
// [workflow.id]: workflow,
228-
// };
223+
const { workflow } = action.payload;
224+
state.workflows = {
225+
...state.workflows,
226+
[workflow.id]: workflow,
227+
};
229228
state.loading = false;
230229
state.errorMessage = '';
231230
})

‎public/utils/utils.ts

-2
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,6 @@ export function getComponentSchema(data: IComponentData): ObjectSchema<any> {
154154
return yup.object(schemaObj);
155155
}
156156

157-
// TODO: finalize validations for different field types. May need
158-
// to refer to some backend implementations or OpenSearch documentation
159157
function getFieldSchema(field: IComponentField): Schema {
160158
let baseSchema: Schema;
161159
switch (field.type) {

‎server/routes/flow_framework_routes_service.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
getWorkflowStateFromResponse,
3434
getWorkflowsFromResponses,
3535
isIgnorableError,
36+
toWorkflowObj,
3637
} from './helpers';
3738

3839
/**
@@ -152,7 +153,9 @@ export class FlowFrameworkRoutesService {
152153
this.client = client;
153154
}
154155

155-
// TODO: test e2e
156+
// TODO: can remove or simplify if we can fetch all data from a single API call. Tracking issue:
157+
// https://github.com/opensearch-project/flow-framework/issues/171
158+
// Current implementation is making two calls and combining results via helper fn
156159
getWorkflow = async (
157160
context: RequestHandlerContext,
158161
req: OpenSearchDashboardsRequest,
@@ -163,9 +166,19 @@ export class FlowFrameworkRoutesService {
163166
const response = await this.client
164167
.asScoped(req)
165168
.callAsCurrentUser('flowFramework.getWorkflow', { workflow_id });
166-
console.log('response from get workflow: ', response);
167-
// TODO: format response
168-
return res.ok({ body: response });
169+
const workflow = toWorkflowObj(response, workflow_id);
170+
171+
const stateResponse = await this.client
172+
.asScoped(req)
173+
.callAsCurrentUser('flowFramework.getWorkflowState', { workflow_id });
174+
const state = getWorkflowStateFromResponse(
175+
stateResponse.state as typeof WORKFLOW_STATE
176+
);
177+
const workflowWithState = {
178+
...workflow,
179+
state,
180+
};
181+
return res.ok({ body: { workflow: workflowWithState } });
169182
} catch (err: any) {
170183
return generateCustomError(res, err);
171184
}

‎server/routes/helpers.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ export function isIgnorableError(error: any): boolean {
3232
return error.body?.error?.type === INDEX_NOT_FOUND_EXCEPTION;
3333
}
3434

35-
function toWorkflowObj(workflowHit: any): Workflow {
36-
// TODO: update schema parsing after hit schema has been updated.
37-
// https://github.com/opensearch-project/flow-framework/issues/546
38-
const hitSource = workflowHit.fields.filter[0];
35+
// Convert backend workflow into frontend workflow obj
36+
export function toWorkflowObj(hitSource: any, id: string): Workflow {
3937
return {
40-
id: workflowHit._id,
38+
id,
4139
name: hitSource.name,
4240
use_case: hitSource.use_case,
4341
description: hitSource.description || '',
@@ -59,7 +57,10 @@ export function getWorkflowsFromResponses(
5957
): WorkflowDict {
6058
const workflowDict = {} as WorkflowDict;
6159
workflowHits.forEach((workflowHit: any) => {
62-
workflowDict[workflowHit._id] = toWorkflowObj(workflowHit);
60+
// TODO: update schema parsing after hit schema has been updated.
61+
// https://github.com/opensearch-project/flow-framework/issues/546
62+
const hitSource = workflowHit.fields.filter[0];
63+
workflowDict[workflowHit._id] = toWorkflowObj(hitSource, workflowHit._id);
6364
const workflowStateHit = workflowStateHits.find(
6465
(workflowStateHit) => workflowStateHit._id === workflowHit._id
6566
);

0 commit comments

Comments
 (0)
Please sign in to comment.