From b911754324e69c6e2dfa04c7926c3b90bfd8f11d Mon Sep 17 00:00:00 2001 From: Tyler Ohlsen Date: Tue, 3 Oct 2023 12:07:33 -0700 Subject: [PATCH] Clean up obj serialization; use setDirty(); Signed-off-by: Tyler Ohlsen --- public/component_types/base_component.tsx | 15 +++++++++++++++ public/component_types/indices/knn_index.ts | 4 +++- .../processors/text_embedding_processor.ts | 6 +++++- .../workflow_detail/components/header.tsx | 19 +++++++++++++++++-- public/pages/workflow_detail/utils/index.ts | 6 ++++++ public/pages/workflow_detail/utils/utils.ts | 16 ++++++++++++++++ .../workflow_detail/workspace/workspace.tsx | 8 ++++---- public/store/reducers/workflows_reducer.ts | 6 +++--- public/store/reducers/workspace_reducer.ts | 17 +---------------- 9 files changed, 70 insertions(+), 27 deletions(-) create mode 100644 public/component_types/base_component.tsx create mode 100644 public/pages/workflow_detail/utils/index.ts create mode 100644 public/pages/workflow_detail/utils/utils.ts diff --git a/public/component_types/base_component.tsx b/public/component_types/base_component.tsx new file mode 100644 index 00000000..1e6dcd4f --- /dev/null +++ b/public/component_types/base_component.tsx @@ -0,0 +1,15 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * A base component class. + */ +export abstract class BaseComponent { + // Persist a standard toObj() fn that all component classes can use. This is necessary + // so we have standard JS Object when serializing comoponent state in redux. + toObj() { + return Object.assign({}, this); + } +} diff --git a/public/component_types/indices/knn_index.ts b/public/component_types/indices/knn_index.ts index 9255b47d..b66e17c0 100644 --- a/public/component_types/indices/knn_index.ts +++ b/public/component_types/indices/knn_index.ts @@ -4,6 +4,7 @@ */ import { COMPONENT_CATEGORY, COMPONENT_CLASS } from '../../utils'; +import { BaseComponent } from '../base_component'; import { IComponent, IComponentField, @@ -15,7 +16,7 @@ import { /** * A k-NN index UI component */ -export class KnnIndex implements IComponent { +export class KnnIndex extends BaseComponent implements IComponent { type: COMPONENT_CLASS; label: string; description: string; @@ -30,6 +31,7 @@ export class KnnIndex implements IComponent { outputs: IComponentOutput[]; constructor() { + super(); this.type = COMPONENT_CLASS.KNN_INDEX; this.label = 'k-NN Index'; this.description = 'A k-NN Index to be used as a vector store'; diff --git a/public/component_types/processors/text_embedding_processor.ts b/public/component_types/processors/text_embedding_processor.ts index 2c3fa078..c6f3961a 100644 --- a/public/component_types/processors/text_embedding_processor.ts +++ b/public/component_types/processors/text_embedding_processor.ts @@ -4,6 +4,7 @@ */ import { COMPONENT_CATEGORY, COMPONENT_CLASS } from '../../utils'; +import { BaseComponent } from '../base_component'; import { IComponent, IComponentField, @@ -15,7 +16,9 @@ import { /** * A text embedding processor UI component */ -export class TextEmbeddingProcessor implements IComponent { +export class TextEmbeddingProcessor + extends BaseComponent + implements IComponent { type: COMPONENT_CLASS; label: string; description: string; @@ -29,6 +32,7 @@ export class TextEmbeddingProcessor implements IComponent { outputs: IComponentOutput[]; constructor() { + super(); this.type = COMPONENT_CLASS.TEXT_EMBEDDING_PROCESSOR; this.label = 'Text Embedding Processor'; this.description = diff --git a/public/pages/workflow_detail/components/header.tsx b/public/pages/workflow_detail/components/header.tsx index 4c70ef20..1ee8c1db 100644 --- a/public/pages/workflow_detail/components/header.tsx +++ b/public/pages/workflow_detail/components/header.tsx @@ -3,15 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React from 'react'; +import React, { useContext } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { EuiPageHeader, EuiButton } from '@elastic/eui'; import { Workflow } from '../../../../common'; +import { generateUseCaseTemplate } from '../utils'; +import { rfContext, AppState, removeDirty } from '../../../store'; interface WorkflowDetailHeaderProps { workflow?: Workflow; } export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) { + const dispatch = useDispatch(); + const { reactFlowInstance } = useContext(rfContext); + const isDirty = useSelector((state: AppState) => state.workspace.isDirty); + return (
{}}> Prototype , - {}}> + { + // @ts-ignore + generateUseCaseTemplate(props.workflow, reactFlowInstance); + dispatch(removeDirty()); + }} + > Save , ]} diff --git a/public/pages/workflow_detail/utils/index.ts b/public/pages/workflow_detail/utils/index.ts new file mode 100644 index 00000000..079132ce --- /dev/null +++ b/public/pages/workflow_detail/utils/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './utils'; diff --git a/public/pages/workflow_detail/utils/utils.ts b/public/pages/workflow_detail/utils/utils.ts new file mode 100644 index 00000000..d8a6f107 --- /dev/null +++ b/public/pages/workflow_detail/utils/utils.ts @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { UseCaseTemplate, Workflow } from '../../../../common'; + +export function generateUseCaseTemplate( + workflow: Workflow, + rfInstance: any +): UseCaseTemplate { + let useCaseTemplate = {} as UseCaseTemplate; + + return useCaseTemplate; +} diff --git a/public/pages/workflow_detail/workspace/workspace.tsx b/public/pages/workflow_detail/workspace/workspace.tsx index 1acd61a2..dceb0a06 100644 --- a/public/pages/workflow_detail/workspace/workspace.tsx +++ b/public/pages/workflow_detail/workspace/workspace.tsx @@ -4,6 +4,7 @@ */ import React, { useRef, useContext, useCallback, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; import ReactFlow, { Controls, Background, @@ -12,7 +13,7 @@ import ReactFlow, { addEdge, } from 'reactflow'; import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; -import { rfContext } from '../../../store'; +import { rfContext, setDirty } from '../../../store'; import { IComponent, Workflow } from '../../../../common'; import { generateId } from '../../../utils'; import { getCore } from '../../../services'; @@ -30,6 +31,7 @@ const nodeTypes = { customComponent: WorkspaceComponent }; // TODO: probably have custom edge types here too export function Workspace(props: WorkspaceProps) { + const dispatch = useDispatch(); const reactFlowWrapper = useRef(null); const { reactFlowInstance, setReactFlowInstance } = useContext(rfContext); @@ -39,10 +41,8 @@ export function Workspace(props: WorkspaceProps) { const onConnect = useCallback( (params) => { setEdges((eds) => addEdge(params, eds)); + dispatch(setDirty()); }, - // TODO: add customized logic to prevent connections based on the node's - // allowed inputs. If allowed, update that node state as well with the added - // connection details. [setEdges] ); diff --git a/public/store/reducers/workflows_reducer.ts b/public/store/reducers/workflows_reducer.ts index 77d43b94..49bf56d3 100644 --- a/public/store/reducers/workflows_reducer.ts +++ b/public/store/reducers/workflows_reducer.ts @@ -18,19 +18,19 @@ const dummyNodes = [ { id: generateId('text_embedding_processor'), position: { x: 0, y: 500 }, - data: new TextEmbeddingProcessor(), + data: new TextEmbeddingProcessor().toObj(), type: 'customComponent', }, { id: generateId('text_embedding_processor'), position: { x: 0, y: 200 }, - data: new TextEmbeddingProcessor(), + data: new TextEmbeddingProcessor().toObj(), type: 'customComponent', }, { id: generateId('knn_index'), position: { x: 500, y: 500 }, - data: new KnnIndex(), + data: new KnnIndex().toObj(), type: 'customComponent', }, ] as ReactFlowComponent[]; diff --git a/public/store/reducers/workspace_reducer.ts b/public/store/reducers/workspace_reducer.ts index 2eb6748e..7bfbd6dc 100644 --- a/public/store/reducers/workspace_reducer.ts +++ b/public/store/reducers/workspace_reducer.ts @@ -4,20 +4,9 @@ */ import { createSlice } from '@reduxjs/toolkit'; -import { IComponent } from '../../../common'; -import { KnnIndex, TextEmbeddingProcessor } from '../../component_types'; - -// TODO: should be fetched from server-side. This will be the list of all -// available components that the framework offers. This will be used in the component -// library to populate the available components to drag-and-drop into the workspace. -const dummyComponents = [ - new TextEmbeddingProcessor(), - new KnnIndex(), -] as IComponent[]; const initialState = { isDirty: false, - components: dummyComponents, }; const workspaceSlice = createSlice({ @@ -30,12 +19,8 @@ const workspaceSlice = createSlice({ removeDirty(state) { state.isDirty = false; }, - setComponents(state, action) { - state.components = action.payload; - state.isDirty = true; - }, }, }); export const workspaceReducer = workspaceSlice.reducer; -export const { setDirty, removeDirty, setComponents } = workspaceSlice.actions; +export const { setDirty, removeDirty } = workspaceSlice.actions;