Skip to content

Commit 96e2f1a

Browse files
authored
Fetch workflow state; align data models with backend (#88)
Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>
1 parent 0362c6b commit 96e2f1a

File tree

8 files changed

+82
-28
lines changed

8 files changed

+82
-28
lines changed

common/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const PLUGIN_ID = 'flow-framework';
1111
export const FLOW_FRAMEWORK_API_ROUTE_PREFIX = '/_plugins/_flow_framework';
1212
export const FLOW_FRAMEWORK_WORKFLOW_ROUTE_PREFIX = `${FLOW_FRAMEWORK_API_ROUTE_PREFIX}/workflow`;
1313
export const FLOW_FRAMEWORK_SEARCH_WORKFLOWS_ROUTE = `${FLOW_FRAMEWORK_WORKFLOW_ROUTE_PREFIX}/_search`;
14+
export const FLOW_FRAMEWORK_SEARCH_WORKFLOW_STATE_ROUTE = `${FLOW_FRAMEWORK_WORKFLOW_ROUTE_PREFIX}/state/_search`;
1415

1516
/**
1617
* NODE APIs

common/interfaces.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,13 @@ export type UseCaseTemplate = {
7171
export type Workflow = {
7272
id: string;
7373
name: string;
74+
useCase: string;
7475
description?: string;
7576
// ReactFlow state may not exist if a workflow is created via API/backend-only.
7677
workspaceFlowState?: WorkspaceFlowState;
7778
template: UseCaseTemplate;
7879
lastUpdated: number;
80+
lastLaunched: number;
7981
state: WORKFLOW_STATE;
8082
};
8183

@@ -95,12 +97,12 @@ export type WorkflowLaunch = {
9597
lastUpdated: number;
9698
};
9799

98-
// TODO: finalize list of possible workflow states from backend
100+
// Based off of https://github.com/opensearch-project/flow-framework/blob/main/src/main/java/org/opensearch/flowframework/model/State.java
99101
export enum WORKFLOW_STATE {
100-
SUCCEEDED = 'Succeeded',
101-
FAILED = 'Failed',
102-
IN_PROGRESS = 'In progress',
103102
NOT_STARTED = 'Not started',
103+
PROVISIONING = 'Provisioning',
104+
FAILED = 'Failed',
105+
COMPLETED = 'Completed',
104106
}
105107

106108
export type WorkflowDict = {

public/pages/workflow_detail/launches/launch_list.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export function LaunchList(props: LaunchListProps) {
3030
const workflowLaunches = [
3131
{
3232
id: 'Launch_1',
33-
state: WORKFLOW_STATE.IN_PROGRESS,
33+
state: WORKFLOW_STATE.PROVISIONING,
3434
lastUpdated: 12345678,
3535
},
3636
{

public/pages/workflows/workflow_list/columns.tsx

+12-7
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,23 @@ export const columns = [
1717
),
1818
},
1919
{
20-
field: 'id',
21-
name: 'ID',
20+
field: 'state',
21+
name: 'Status',
2222
sortable: true,
2323
},
2424
{
25-
field: 'description',
26-
name: 'Description',
27-
sortable: false,
25+
field: 'useCase',
26+
name: 'Type',
27+
sortable: true,
2828
},
2929
{
30-
field: 'state',
31-
name: 'Status',
30+
field: 'lastUpdated',
31+
name: 'Last updated',
32+
sortable: true,
33+
},
34+
{
35+
field: 'lastLaunched',
36+
name: 'Last launched',
3237
sortable: true,
3338
},
3439
];

public/utils/utils.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -131,22 +131,22 @@ export function getStateOptions(): EuiFilterSelectItem[] {
131131
return [
132132
// @ts-ignore
133133
{
134-
name: WORKFLOW_STATE.SUCCEEDED,
134+
name: WORKFLOW_STATE.NOT_STARTED,
135135
checked: 'on',
136136
} as EuiFilterSelectItem,
137137
// @ts-ignore
138138
{
139-
name: WORKFLOW_STATE.NOT_STARTED,
139+
name: WORKFLOW_STATE.PROVISIONING,
140140
checked: 'on',
141141
} as EuiFilterSelectItem,
142142
// @ts-ignore
143143
{
144-
name: WORKFLOW_STATE.IN_PROGRESS,
144+
name: WORKFLOW_STATE.FAILED,
145145
checked: 'on',
146146
} as EuiFilterSelectItem,
147147
// @ts-ignore
148148
{
149-
name: WORKFLOW_STATE.FAILED,
149+
name: WORKFLOW_STATE.COMPLETED,
150150
checked: 'on',
151151
} as EuiFilterSelectItem,
152152
];

server/cluster/flow_framework_plugin.ts

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import {
77
FLOW_FRAMEWORK_SEARCH_WORKFLOWS_ROUTE,
8+
FLOW_FRAMEWORK_SEARCH_WORKFLOW_STATE_ROUTE,
89
FLOW_FRAMEWORK_WORKFLOW_ROUTE_PREFIX,
910
} from '../../common';
1011

@@ -55,6 +56,15 @@ export function flowFrameworkPlugin(Client: any, config: any, components: any) {
5556
method: 'GET',
5657
});
5758

59+
flowFramework.searchWorkflowState = ca({
60+
url: {
61+
fmt: FLOW_FRAMEWORK_SEARCH_WORKFLOW_STATE_ROUTE,
62+
},
63+
needBody: true,
64+
// Exposed client rejects making GET requests with a body. So, we use POST
65+
method: 'POST',
66+
});
67+
5868
flowFramework.createWorkflow = ca({
5969
url: {
6070
fmt: FLOW_FRAMEWORK_WORKFLOW_ROUTE_PREFIX,

server/routes/flow_framework_routes_service.ts

+15-8
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ import {
1717
GET_WORKFLOW_NODE_API_PATH,
1818
GET_WORKFLOW_STATE_NODE_API_PATH,
1919
SEARCH_WORKFLOWS_NODE_API_PATH,
20-
WorkflowDict,
2120
} from '../../common';
22-
import { generateCustomError, toWorkflowObj } from './helpers';
21+
import { generateCustomError, getWorkflowsFromResponses } from './helpers';
2322

2423
/**
2524
* Server-side routes to process flow-framework-related node API calls and execute the
@@ -112,22 +111,30 @@ export class FlowFrameworkRoutesService {
112111
}
113112
};
114113

114+
// TODO: can remove or simplify if we can fetch all data from a single API call. Tracking issue:
115+
// https://github.com/opensearch-project/flow-framework/issues/171
116+
// Current implementation is making two calls and combining results via helper fn
115117
searchWorkflows = async (
116118
context: RequestHandlerContext,
117119
req: OpenSearchDashboardsRequest,
118120
res: OpenSearchDashboardsResponseFactory
119121
): Promise<IOpenSearchDashboardsResponse<any>> => {
120122
const body = req.body;
121123
try {
122-
const response = await this.client
124+
const workflowsResponse = await this.client
123125
.asScoped(req)
124126
.callAsCurrentUser('flowFramework.searchWorkflows', { body });
125-
const workflowHits = response.hits.hits as any[];
126-
const workflowDict = {} as WorkflowDict;
127-
workflowHits.forEach((workflowHit: any) => {
128-
workflowDict[workflowHit._id] = toWorkflowObj(workflowHit);
129-
});
127+
const workflowHits = workflowsResponse.hits.hits as any[];
128+
129+
const workflowStatesResponse = await this.client
130+
.asScoped(req)
131+
.callAsCurrentUser('flowFramework.searchWorkflowState', { body });
132+
const workflowStateHits = workflowStatesResponse.hits.hits as any[];
130133

134+
const workflowDict = getWorkflowsFromResponses(
135+
workflowHits,
136+
workflowStateHits
137+
);
131138
return res.ok({ body: { workflows: workflowDict } });
132139
} catch (err: any) {
133140
return generateCustomError(res, err);

server/routes/helpers.ts

+33-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { WORKFLOW_STATE, Workflow } from '../../common';
6+
import { WORKFLOW_STATE, Workflow, WorkflowDict } from '../../common';
77

88
// OSD does not provide an interface for this response, but this is following the suggested
99
// implementations. To prevent typescript complaining, leaving as loosely-typed 'any'
@@ -19,18 +19,47 @@ export function generateCustomError(res: any, err: any) {
1919
});
2020
}
2121

22-
export function toWorkflowObj(workflowHit: any): Workflow {
22+
function toWorkflowObj(workflowHit: any): Workflow {
2323
// TODO: update schema parsing after hit schema has been updated.
2424
// https://github.com/opensearch-project/flow-framework/issues/546
2525
const hitSource = workflowHit.fields.filter[0];
26-
// const hitSource = workflowHit._source;
2726
return {
2827
id: workflowHit._id,
2928
name: hitSource.name,
29+
useCase: hitSource.use_case,
3030
description: hitSource.description || '',
3131
// TODO: update below values after frontend Workflow interface is finalized
3232
template: {},
33+
// TODO: this needs to be persisted by backend. Tracking issue:
34+
// https://github.com/opensearch-project/flow-framework/issues/548
3335
lastUpdated: 1234,
34-
state: WORKFLOW_STATE.SUCCEEDED,
3536
} as Workflow;
3637
}
38+
39+
// TODO: can remove or simplify if we can fetch all data from a single API call. Tracking issue:
40+
// https://github.com/opensearch-project/flow-framework/issues/171
41+
// Current implementation combines 2 search responses to create a single set of workflows with
42+
// static information + state information
43+
export function getWorkflowsFromResponses(
44+
workflowHits: any[],
45+
workflowStateHits: any[]
46+
): WorkflowDict {
47+
const workflowDict = {} as WorkflowDict;
48+
workflowHits.forEach((workflowHit: any) => {
49+
workflowDict[workflowHit._id] = toWorkflowObj(workflowHit);
50+
const workflowStateHit = workflowStateHits.find(
51+
(workflowStateHit) => workflowStateHit._id === workflowHit._id
52+
);
53+
const workflowState = workflowStateHit._source
54+
.state as typeof WORKFLOW_STATE;
55+
workflowDict[workflowHit._id] = {
56+
...workflowDict[workflowHit._id],
57+
// @ts-ignore
58+
state: WORKFLOW_STATE[workflowState],
59+
// TODO: this needs to be persisted by backend. Tracking issue:
60+
// https://github.com/opensearch-project/flow-framework/issues/548
61+
lastLaunched: 1234,
62+
};
63+
});
64+
return workflowDict;
65+
}

0 commit comments

Comments
 (0)