Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add interfaces & types; fetch reactflow state from workflow redux store #45

Merged
merged 4 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 0 additions & 74 deletions common/helpers.ts

This file was deleted.

1 change: 0 additions & 1 deletion common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@

export * from './constants';
export * from './interfaces';
export * from './helpers';
export { IComponent } from '../public/component_types';
78 changes: 68 additions & 10 deletions common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,77 @@
* SPDX-License-Identifier: Apache-2.0
*/

/**
* Interfaces here are primarily used for standardizing the data across
* server & client side
*/
import { Node, Edge } from 'reactflow';
import { IComponent as IComponentData } from '../public/component_types';

export interface IIndex {
export type Index = {
name: string;
health: 'green' | 'yellow' | 'red';
}
};

// TODO: this will grow as more fields are defined and what frontend reqts there will be
export interface IWorkflow {
name: string;
/**
********** REACTFLOW TYPES/INTERFACES **********
*/

export type ReactFlowComponent = Node<IComponentData>;

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

type ReactFlowViewport = {
x: number;
y: number;
zoom: number;
};

export type ReactFlowState = {
nodes: ReactFlowComponent[];
edges: ReactFlowEdge[];
viewport?: ReactFlowViewport;
};

/**
********** USE CASE TEMPLATE TYPES/INTERFACES **********
*/

type TemplateNode = {
id: string;
inputs: {};
};

type TemplateEdge = {
source: string;
target: string;
};

type TemplateFlow = {
userParams: {};
nodes: TemplateNode[];
edges: TemplateEdge[];
};

type TemplateFlows = {
provision: TemplateFlow;
ingest: TemplateFlow;
query: TemplateFlow;
};

export type UseCaseTemplate = {
type: string;
name: string;
description: string;
}
userInputs: {};
workflows: TemplateFlows;
};

export type Workflow = {
id: string;
name: string;
description?: string;
// ReactFlow state may not exist if a workflow is created via API/backend-only.
reactFlowState?: ReactFlowState;
template: UseCaseTemplate;
lastUpdated: number;
};
7 changes: 7 additions & 0 deletions eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"rules": {
"@typescript-eslint/consistent-type-definitions": "off",
"@typescript-eslint/no-empty-interface": "off",
"react-hooks/exhaustive-deps": "off"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"plugin-helpers": "../../scripts/use_node ../../scripts/plugin_helpers",
"osd": "../../scripts/use_node ../../scripts/osd",
"opensearch": "../../scripts/use_node ../../scripts/opensearch",
"lint:es": "../../scripts/use_node ../../scripts/eslint",
"lint:es": "../../scripts/use_node ../../scripts/eslint -c eslintrc.json",
"lint:es:precommit": "yarn lint:es common/* public/* server/*",
"build": "yarn plugin-helpers build && echo Renaming artifact to $npm_package_config_plugin_zip_name-$npm_package_config_plugin_version.zip && mv ./build/$npm_package_config_plugin_name*.zip ./build/$npm_package_config_plugin_zip_name-$npm_package_config_plugin_version.zip"
},
Expand Down
1 change: 0 additions & 1 deletion public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
WorkflowDetailRouterProps,
} from './pages';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Props extends RouteComponentProps {}

export const AiFlowDashboardsApp = (props: Props) => {
Expand Down
2 changes: 1 addition & 1 deletion public/component_types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/

export * from './base_interfaces';
export * from './interfaces';
export * from './processors';
export * from './indices';
2 changes: 1 addition & 1 deletion public/component_types/indices/knn_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
IComponentOutput,
UIFlow,
BaseClass,
} from '../base_interfaces';
} from '../interfaces';

/**
* A k-NN index UI component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
IComponentOutput,
UIFlow,
BaseClass,
} from '../base_interfaces';
} from '../interfaces';

/**
* A text embedding processor UI component
Expand Down
4 changes: 2 additions & 2 deletions public/pages/workflow_detail/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

import React from 'react';
import { EuiPageHeader, EuiButton } from '@elastic/eui';
import { IWorkflow } from '../../../../common';
import { Workflow } from '../../../../common';

interface WorkflowDetailHeaderProps {
workflow: IWorkflow | undefined;
workflow?: Workflow;
}

export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {
Expand Down
3 changes: 1 addition & 2 deletions public/pages/workflow_detail/workflow_detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export interface WorkflowDetailRouterProps {
workflowId: string;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface WorkflowDetailProps
extends RouteComponentProps<WorkflowDetailRouterProps> {}

Expand All @@ -41,7 +40,7 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
<div>
<WorkflowDetailHeader workflow={workflow} />
<EuiSpacer size="l" />
<Workspace />
<Workspace workflow={workflow} />
</div>
);
}
42 changes: 22 additions & 20 deletions public/pages/workflow_detail/workspace/workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,25 @@ import ReactFlow, {
useEdgesState,
addEdge,
} from 'reactflow';
import { useSelector } from 'react-redux';
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { AppState, rfContext } from '../../../store';
import { convertToReactFlowData } from '../../../../common';
import { rfContext } from '../../../store';
import { Workflow } from '../../../../common';
import { getCore } from '../../../services';

// styling
import 'reactflow/dist/style.css';
import './reactflow-styles.scss';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface WorkspaceProps {}
interface WorkspaceProps {
workflow?: Workflow;
}

export function Workspace(props: WorkspaceProps) {
const reactFlowWrapper = useRef(null);
const { reactFlowInstance, setReactFlowInstance } = useContext(rfContext);

// Fetching workspace state to populate the initial nodes/edges.
// Where/how the low-level ReactFlow JSON will be persisted is TBD.
// TODO: update when that design is finalized
const storedComponents = useSelector(
(state: AppState) => state.workspace.components
);
const { rfNodes, rfEdges } = convertToReactFlowData(storedComponents);
const [nodes, setNodes, onNodesChange] = useNodesState(rfNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(rfEdges);
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);

const onConnect = useCallback(
(params) => {
Expand Down Expand Up @@ -88,16 +82,24 @@ export function Workspace(props: WorkspaceProps) {

setNodes((nds) => nds.concat(newNode));
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[reactFlowInstance]
);

// Initialization hook
// Initialization. Set the nodes and edges to an existing workflow,
// if applicable.
useEffect(() => {
// TODO: fetch the nodes/edges dynamically (loading existing flow,
// creating fresh from template, creating blank template, etc.)
// Will involve populating and/or fetching from redux store
}, []);
const workflow = props.workflow;
if (workflow) {
if (workflow.reactFlowState) {
setNodes(workflow.reactFlowState.nodes);
setEdges(workflow.reactFlowState.edges);
} else {
getCore().notifications.toasts.addWarning(
`There is no configured UI flow for workflow: ${workflow.name}`
);
}
}
}, [props.workflow]);

return (
<EuiFlexItem grow={true}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ interface WorkspaceComponentProps {
}

/**
* TODO: This will be the ReactFlow node in the drag-and-drop workspace. It will take in a component
* from the global workflow state and render it appropriately (inputs / params / outputs / etc.)
* TODO: This will be the ReactFlow node in the drag-and-drop workspace. It will take in the data passed
* to it from the workspace and render it appropriately (inputs / params / outputs / etc.)
* Similar to Flowise's CanvasNode - see
* https://github.com/FlowiseAI/Flowise/blob/main/packages/ui/src/views/canvas/CanvasNode.js
*/
Expand Down
4 changes: 2 additions & 2 deletions public/pages/workflows/components/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

import React from 'react';
import { EuiLink } from '@elastic/eui';
import { PLUGIN_ID, IWorkflow } from '../../../../common';
import { PLUGIN_ID, Workflow } from '../../../../common';

export const columns = [
{
field: 'name',
name: 'Name',
sortable: true,
render: (name: string, workflow: IWorkflow) => (
render: (name: string, workflow: Workflow) => (
<EuiLink href={`${PLUGIN_ID}#/workflows/${workflow.id}`}>{name}</EuiLink>
),
},
Expand Down
5 changes: 2 additions & 3 deletions public/pages/workflows/components/workflow_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import React from 'react';
import { useSelector } from 'react-redux';
import { EuiInMemoryTable, Direction } from '@elastic/eui';
import { AppState } from '../../../store';
import { IWorkflow } from '../../../../common';
import { Workflow } from '../../../../common';
import { columns } from './columns';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface WorkflowListProps {}

export function WorkflowList(props: WorkflowListProps) {
Expand All @@ -24,7 +23,7 @@ export function WorkflowList(props: WorkflowListProps) {
};

return (
<EuiInMemoryTable<IWorkflow>
<EuiInMemoryTable<Workflow>
items={workflows}
rowHeader="name"
columns={columns}
Expand Down
8 changes: 4 additions & 4 deletions public/store/reducers/opensearch_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { getRouteService } from '../../services';
import { IIndex } from '../../../common';
import { Index } from '../../../common';

const initialState = {
loading: false,
errorMessage: '',
indices: {} as { [key: string]: IIndex },
indices: {} as { [key: string]: Index },
};

const OPENSEARCH_PREFIX = 'opensearch';
Expand All @@ -36,8 +36,8 @@ const opensearchSlice = createSlice({
state.loading = true;
})
.addCase(fetchIndices.fulfilled, (state, action) => {
const indicesMap = new Map<string, IIndex>();
action.payload.forEach((index: IIndex) => {
const indicesMap = new Map<string, Index>();
action.payload.forEach((index: Index) => {
indicesMap.set(index.name, index);
});
state.indices = Object.fromEntries(indicesMap.entries());
Expand Down
Loading
Loading