diff --git a/common/interfaces.ts b/common/interfaces.ts
index 4025adbc..d9c6bc43 100644
--- a/common/interfaces.ts
+++ b/common/interfaces.ts
@@ -119,6 +119,8 @@ export type Workflow = WorkflowTemplate & {
lastLaunched?: number;
// won't exist until launched/provisioned in backend
state?: WORKFLOW_STATE;
+ // won't exist until launched/provisioned in backend
+ resourcesCreated?: WorkflowResource[];
};
export enum USE_CASE {
@@ -152,6 +154,20 @@ export enum WORKFLOW_STATE {
COMPLETED = 'Completed',
}
+export type WorkflowResource = {
+ id: string;
+ type: WORKFLOW_RESOURCE_TYPE;
+};
+
+// Based off of https://github.com/opensearch-project/flow-framework/blob/main/src/main/java/org/opensearch/flowframework/common/WorkflowResources.java
+export enum WORKFLOW_RESOURCE_TYPE {
+ PIPELINE_ID = 'Ingest pipeline',
+ INDEX_NAME = 'Index',
+ MODEL_ID = 'Model',
+ MODEL_GROUP_ID = 'Model group',
+ CONNECTOR_ID = 'Connector',
+}
+
export type WorkflowDict = {
[workflowId: string]: Workflow;
};
diff --git a/public/app.tsx b/public/app.tsx
index a95902b3..2e14044d 100644
--- a/public/app.tsx
+++ b/public/app.tsx
@@ -8,7 +8,6 @@ import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import { EuiPageSideBar, EuiSideNav, EuiPageTemplate } from '@elastic/eui';
import { Navigation, APP_PATH } from './utils';
import {
- Overview,
Workflows,
WorkflowDetail,
WorkflowDetailRouterProps,
@@ -27,15 +26,9 @@ export const FlowFrameworkDashboardsApp = (props: Props) => {
name: Navigation.FlowFramework,
id: 0,
items: [
- {
- name: Navigation.Overview,
- id: 1,
- href: `#${APP_PATH.OVERVIEW}`,
- isSelected: props.location.pathname === APP_PATH.OVERVIEW,
- },
{
name: Navigation.Workflows,
- id: 2,
+ id: 1,
href: `#${APP_PATH.WORKFLOWS}`,
isSelected: props.location.pathname === APP_PATH.WORKFLOWS,
},
@@ -69,10 +62,12 @@ export const FlowFrameworkDashboardsApp = (props: Props) => {
)}
/>
- {/* Defaulting to Overview page */}
+ {/* Defaulting to Workflows page */}
}
+ render={(routeProps: RouteComponentProps) => (
+
+ )}
/>
diff --git a/public/pages/index.ts b/public/pages/index.ts
index ad722b21..1db9a64a 100644
--- a/public/pages/index.ts
+++ b/public/pages/index.ts
@@ -4,5 +4,4 @@
*/
export * from './workflows';
-export * from './overview';
export * from './workflow_detail';
diff --git a/public/pages/overview/overview.tsx b/public/pages/overview/overview.tsx
deleted file mode 100644
index aa4c2ba0..00000000
--- a/public/pages/overview/overview.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React, { useEffect } from 'react';
-import {
- EuiPage,
- EuiPageBody,
- EuiPageHeader,
- EuiPageContent,
- EuiText,
-} from '@elastic/eui';
-import { BREADCRUMBS } from '../../utils';
-import { getCore } from '../../services';
-
-/**
- * The overview page. This contains a detailed description on what
- * this plugin offers, and links to different resources (blogs, demos,
- * documentation, etc.)
- *
- * This may be hidden for the initial release until we have sufficient content
- * such that this page adds enough utility & user value.
- */
-export function Overview() {
- useEffect(() => {
- getCore().chrome.setBreadcrumbs([
- BREADCRUMBS.FLOW_FRAMEWORK,
- BREADCRUMBS.OVERVIEW,
- ]);
- });
-
- return (
-
-
-
-
-
- TODO: Put overview details here...
-
-
-
- );
-}
diff --git a/public/pages/workflow_detail/prototype/index.ts b/public/pages/workflow_detail/prototype/index.ts
deleted file mode 100644
index 6b2a3ec5..00000000
--- a/public/pages/workflow_detail/prototype/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-export { Prototype } from './prototype';
diff --git a/public/pages/workflow_detail/resources/columns.tsx b/public/pages/workflow_detail/resources/columns.tsx
new file mode 100644
index 00000000..7e13b49c
--- /dev/null
+++ b/public/pages/workflow_detail/resources/columns.tsx
@@ -0,0 +1,17 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export const columns = [
+ {
+ field: 'id',
+ name: 'ID',
+ sortable: true,
+ },
+ {
+ field: 'type',
+ name: 'Type',
+ sortable: true,
+ },
+];
diff --git a/public/pages/overview/index.ts b/public/pages/workflow_detail/resources/index.ts
similarity index 67%
rename from public/pages/overview/index.ts
rename to public/pages/workflow_detail/resources/index.ts
index 5c460c3d..2132669a 100644
--- a/public/pages/overview/index.ts
+++ b/public/pages/workflow_detail/resources/index.ts
@@ -3,4 +3,4 @@
* SPDX-License-Identifier: Apache-2.0
*/
-export { Overview } from './overview';
+export { Resources } from './resources';
diff --git a/public/pages/workflow_detail/resources/resource_list.tsx b/public/pages/workflow_detail/resources/resource_list.tsx
new file mode 100644
index 00000000..0acb0f9f
--- /dev/null
+++ b/public/pages/workflow_detail/resources/resource_list.tsx
@@ -0,0 +1,54 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useState, useEffect } from 'react';
+import {
+ EuiInMemoryTable,
+ Direction,
+ EuiFlexGroup,
+ EuiFlexItem,
+} from '@elastic/eui';
+import { Workflow, WorkflowResource } from '../../../../common';
+import { columns } from './columns';
+
+interface ResourceListProps {
+ workflow?: Workflow;
+}
+
+/**
+ * The searchable list of resources for a particular workflow.
+ */
+export function ResourceList(props: ResourceListProps) {
+ const [allResources, setAllResources] = useState([]);
+
+ // Hook to initialize all resources
+ useEffect(() => {
+ if (props.workflow?.resourcesCreated) {
+ setAllResources(props.workflow.resourcesCreated);
+ }
+ }, [props.workflow?.resourcesCreated]);
+
+ const sorting = {
+ sort: {
+ field: 'id',
+ direction: 'asc' as Direction,
+ },
+ };
+
+ return (
+
+
+
+ items={allResources}
+ rowHeader="id"
+ columns={columns}
+ sorting={sorting}
+ pagination={true}
+ message={'No existing resources found'}
+ />
+
+
+ );
+}
diff --git a/public/pages/workflow_detail/prototype/prototype.tsx b/public/pages/workflow_detail/resources/resources.tsx
similarity index 50%
rename from public/pages/workflow_detail/prototype/prototype.tsx
rename to public/pages/workflow_detail/resources/resources.tsx
index ede48acd..cee54acd 100644
--- a/public/pages/workflow_detail/prototype/prototype.tsx
+++ b/public/pages/workflow_detail/resources/resources.tsx
@@ -5,33 +5,34 @@
import React from 'react';
import {
+ EuiFlexGroup,
EuiFlexItem,
EuiPageContent,
EuiSpacer,
- EuiText,
EuiTitle,
} from '@elastic/eui';
import { Workflow } from '../../../../common';
+import { ResourceList } from './resource_list';
-interface PrototypeProps {
+interface ResourcesProps {
workflow?: Workflow;
}
/**
- * The prototype page. Dedicated for testing out a launched workflow.
- * Will have default simple interfaces for common application types, such as
- * conversational chatbots.
+ * A simple resources page to browse created resources for a given Workflow.
*/
-export function Prototype(props: PrototypeProps) {
+export function Resources(props: ResourcesProps) {
return (
- Prototype
+ Resources
-
- TODO: add prototype page
-
+
+
+
+
+
);
}
diff --git a/public/pages/workflow_detail/workflow_detail.tsx b/public/pages/workflow_detail/workflow_detail.tsx
index ff32777c..8c397b22 100644
--- a/public/pages/workflow_detail/workflow_detail.tsx
+++ b/public/pages/workflow_detail/workflow_detail.tsx
@@ -19,13 +19,12 @@ import {
useAppDispatch,
} from '../../store';
import { ResizableWorkspace } from './workspace';
-import { Launches } from './launches';
-import { Prototype } from './prototype';
import {
DEFAULT_NEW_WORKFLOW_NAME,
FETCH_ALL_QUERY_BODY,
NEW_WORKFLOW_ID_URL,
} from '../../../common';
+import { Resources } from './resources';
// styling
import './workflow-detail-styles.scss';
@@ -39,8 +38,10 @@ interface WorkflowDetailProps
enum WORKFLOW_DETAILS_TAB {
EDITOR = 'editor',
- LAUNCHES = 'launches',
- PROTOTYPE = 'prototype',
+ // TODO: temporarily adding a resources tab until UX is finalized.
+ // This gives clarity into what has been done on the cluster on behalf
+ // of the frontend provisioning workflows.
+ RESOURCES = 'resources',
}
const ACTIVE_TAB_PARAM = 'tab';
@@ -133,21 +134,12 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
},
},
{
- id: WORKFLOW_DETAILS_TAB.LAUNCHES,
- label: 'Launches',
- isSelected: selectedTabId === WORKFLOW_DETAILS_TAB.LAUNCHES,
+ id: WORKFLOW_DETAILS_TAB.RESOURCES,
+ label: 'Resources',
+ isSelected: selectedTabId === WORKFLOW_DETAILS_TAB.RESOURCES,
onClick: () => {
- setSelectedTabId(WORKFLOW_DETAILS_TAB.LAUNCHES);
- replaceActiveTab(WORKFLOW_DETAILS_TAB.LAUNCHES, props);
- },
- },
- {
- id: WORKFLOW_DETAILS_TAB.PROTOTYPE,
- label: 'Prototype',
- isSelected: selectedTabId === WORKFLOW_DETAILS_TAB.PROTOTYPE,
- onClick: () => {
- setSelectedTabId(WORKFLOW_DETAILS_TAB.PROTOTYPE);
- replaceActiveTab(WORKFLOW_DETAILS_TAB.PROTOTYPE, props);
+ setSelectedTabId(WORKFLOW_DETAILS_TAB.RESOURCES);
+ replaceActiveTab(WORKFLOW_DETAILS_TAB.RESOURCES, props);
},
},
];
@@ -169,9 +161,8 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
/>
)}
- {selectedTabId === WORKFLOW_DETAILS_TAB.LAUNCHES && }
- {selectedTabId === WORKFLOW_DETAILS_TAB.PROTOTYPE && (
-
+ {selectedTabId === WORKFLOW_DETAILS_TAB.RESOURCES && (
+
)}
diff --git a/public/store/reducers/workflows_reducer.ts b/public/store/reducers/workflows_reducer.ts
index 31640fd3..2a04cb81 100644
--- a/public/store/reducers/workflows_reducer.ts
+++ b/public/store/reducers/workflows_reducer.ts
@@ -235,12 +235,13 @@ const workflowsSlice = createSlice({
state.errorMessage = '';
})
.addCase(getWorkflowState.fulfilled, (state, action) => {
- const { workflowId, workflowState } = action.payload;
+ const { workflowId, workflowState, resourcesCreated } = action.payload;
state.workflows = {
...state.workflows,
[workflowId]: {
...state.workflows[workflowId],
state: workflowState,
+ resourcesCreated,
},
};
state.loading = false;
diff --git a/public/utils/constants.ts b/public/utils/constants.ts
index 1c0c4617..0f3b75f6 100644
--- a/public/utils/constants.ts
+++ b/public/utils/constants.ts
@@ -5,20 +5,17 @@
export enum Navigation {
FlowFramework = 'Flow Framework',
- Overview = 'Overview',
Workflows = 'Workflows',
}
export enum APP_PATH {
HOME = '/',
- OVERVIEW = '/overview',
WORKFLOWS = '/workflows',
WORKFLOW_DETAIL = '/workflows/:workflowId',
}
export const BREADCRUMBS = Object.freeze({
FLOW_FRAMEWORK: { text: 'Flow Framework', href: '#/' },
- OVERVIEW: { text: 'Overview', href: `#${APP_PATH.OVERVIEW}` },
WORKFLOWS: { text: 'Workflows', href: `#${APP_PATH.WORKFLOWS}` },
});
diff --git a/server/routes/flow_framework_routes_service.ts b/server/routes/flow_framework_routes_service.ts
index 0c7a3af0..7c60e631 100644
--- a/server/routes/flow_framework_routes_service.ts
+++ b/server/routes/flow_framework_routes_service.ts
@@ -26,10 +26,12 @@ import {
WORKFLOW_STATE,
Workflow,
WorkflowDict,
+ WorkflowResource,
WorkflowTemplate,
} from '../../common';
import {
generateCustomError,
+ getResourcesCreatedFromResponse,
getWorkflowStateFromResponse,
getWorkflowsFromResponses,
isIgnorableError,
@@ -174,10 +176,14 @@ export class FlowFrameworkRoutesService {
const state = getWorkflowStateFromResponse(
stateResponse.state as typeof WORKFLOW_STATE
);
+ const resourcesCreated = getResourcesCreatedFromResponse(
+ stateResponse.resources_created as WorkflowResource[]
+ );
const workflowWithState = {
...workflow,
state,
- };
+ resourcesCreated,
+ } as Workflow;
return res.ok({ body: { workflow: workflowWithState } });
} catch (err: any) {
return generateCustomError(res, err);
@@ -226,12 +232,21 @@ export class FlowFrameworkRoutesService {
try {
const response = await this.client
.asScoped(req)
- .callAsCurrentUser('flowFramework.getWorkflowState', { workflow_id });
+ .callAsCurrentUser('flowFramework.getWorkflowState', {
+ workflow_id,
+ });
const state = getWorkflowStateFromResponse(
- response.state as typeof WORKFLOW_STATE
+ response.state as typeof WORKFLOW_STATE | undefined
+ );
+ const resourcesCreated = getResourcesCreatedFromResponse(
+ response.resources_created as WorkflowResource[] | undefined
);
return res.ok({
- body: { workflowId: workflow_id, workflowState: state },
+ body: {
+ workflowId: workflow_id,
+ workflowState: state,
+ resourcesCreated,
+ },
});
} catch (err: any) {
return generateCustomError(res, err);
diff --git a/server/routes/helpers.ts b/server/routes/helpers.ts
index 72385b86..b4d39991 100644
--- a/server/routes/helpers.ts
+++ b/server/routes/helpers.ts
@@ -8,9 +8,11 @@ import {
INDEX_NOT_FOUND_EXCEPTION,
Model,
ModelDict,
+ WORKFLOW_RESOURCE_TYPE,
WORKFLOW_STATE,
Workflow,
WorkflowDict,
+ WorkflowResource,
} from '../../common';
// OSD does not provide an interface for this response, but this is following the suggested
@@ -64,12 +66,17 @@ export function getWorkflowsFromResponses(
const workflowStateHit = workflowStateHits.find(
(workflowStateHit) => workflowStateHit._id === workflowHit._id
);
- const workflowState = (workflowStateHit?._source?.state ||
- DEFAULT_NEW_WORKFLOW_STATE_TYPE) as typeof WORKFLOW_STATE;
+ const workflowState = getWorkflowStateFromResponse(
+ workflowStateHit?._source?.state
+ );
+ const workflowResourcesCreated = getResourcesCreatedFromResponse(
+ workflowStateHit?._source?.resources_created
+ );
workflowDict[workflowHit._id] = {
...workflowDict[workflowHit._id],
// @ts-ignore
- state: WORKFLOW_STATE[workflowState],
+ state: workflowState,
+ resourcesCreated: workflowResourcesCreated,
};
});
return workflowDict;
@@ -89,6 +96,7 @@ export function getModelsFromResponses(modelHits: any[]): ModelDict {
return modelDict;
}
+// Convert the workflow state into a readable/presentable state on frontend
export function getWorkflowStateFromResponse(
state: typeof WORKFLOW_STATE | undefined
): WORKFLOW_STATE {
@@ -96,3 +104,24 @@ export function getWorkflowStateFromResponse(
// @ts-ignore
return WORKFLOW_STATE[finalState];
}
+
+// Convert the workflow resources into a readable/presentable state on frontend
+export function getResourcesCreatedFromResponse(
+ resourcesCreated: any[] | undefined
+): WorkflowResource[] {
+ const finalResources = [] as WorkflowResource[];
+ if (resourcesCreated) {
+ resourcesCreated.forEach((backendResource) => {
+ finalResources.push({
+ id: backendResource.resource_id,
+ type:
+ // @ts-ignore
+ WORKFLOW_RESOURCE_TYPE[
+ // the backend persists the types in lowercase. e.g., "pipeline_id"
+ (backendResource.resource_type as string).toUpperCase()
+ ],
+ } as WorkflowResource);
+ });
+ }
+ return finalResources;
+}