Skip to content

Commit

Permalink
Parse and persist UI metadata on editor page (#125) (#126)
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Ohlsen <[email protected]>
(cherry picked from commit ce28197)

Co-authored-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
opensearch-trigger-bot[bot] and ohltyler committed Apr 5, 2024
1 parent f0195f1 commit c2bcd95
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 231 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ jobs:
su `id -un 1000` -c "source $NVM_DIR/nvm.sh && nvm use && node -v && yarn -v &&
cd ./plugins/dashboards-flow-framework &&
whoami && yarn osd bootstrap && yarn build && yarn run test:jest --coverage"
- name: Uploads coverage
uses: codecov/codecov-action@v1
# TODO: once github actions supports windows and macos docker containers, we can
# merge these in to the above step's matrix, including adding windows support
Expand Down Expand Up @@ -98,6 +96,4 @@ jobs:
run: |
cd OpenSearch-Dashboards/plugins/dashboards-flow-framework
yarn run test:jest --coverage
- name: Uploads coverage
uses: codecov/codecov-action@v1
17 changes: 17 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# disable tracking status entirely until UT is added.
# tracking issue: https://github.com/opensearch-project/dashboards-flow-framework/issues/95
coverage:
# displays different colors depending on below, between, or above the range
range: 50..90
status:
project:
enabled: no
default:
target: auto
# allows 5% coverage reduction without failing
threshold: 5%
patch: no
changes: no

# disable comments in PRs
comment: no
8 changes: 1 addition & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,13 @@
"osd": "../../scripts/use_node ../../scripts/osd",
"opensearch": "../../scripts/use_node ../../scripts/opensearch",
"lint:es": "../../scripts/use_node ../../scripts/eslint -c eslintrc.json",
"lint:es:precommit": "yarn lint:es common/* public/* server/*",
"test:jest": "../../node_modules/.bin/jest --config ./test/jest.config.js",
"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"
},
"repository": {
"type": "git",
"url": "https://github.com/opensearch-project/dashboards-flow-framework.git"
},
"pre-commit": [
"lint:es:precommit"
],
"lint-staged": {
"*.{ts,tsx,js,jsx,json,css,md}": [
"prettier --write",
Expand All @@ -35,8 +31,6 @@
"reactflow": "^11.8.3",
"yup": "^1.3.2"
},
"devDependencies": {
"pre-commit": "^1.2.2"
},
"devDependencies": {},
"resolutions": {}
}
6 changes: 3 additions & 3 deletions public/component_types/indexer/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,19 @@ export class Indexer extends BaseComponent {
this.fields = [
{
label: 'Index Name',
name: 'indexName',
id: 'indexName',
type: 'select',
},
];
this.createFields = [
{
label: 'Index Name',
name: 'indexName',
id: 'indexName',
type: 'string',
},
// {
// label: 'Mappings',
// name: 'indexMappings',
// id: 'indexMappings',
// type: 'json',
// placeholder: 'Enter an index mappings JSON blob...',
// },
Expand Down
2 changes: 1 addition & 1 deletion public/component_types/indexer/knn_indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class KnnIndexer extends Indexer {
// TODO: finalize what to expose / what to have for defaults here
// {
// label: 'K-NN Settings',
// name: 'knnSettings',
// id: 'knnSettings',
// type: 'json',
// placeholder: 'Enter K-NN settings JSON blob...',
// },
Expand Down
2 changes: 1 addition & 1 deletion public/component_types/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export interface IComponentInput {
export interface IComponentField {
label: string;
type: FieldType;
name: string;
id: string;
value?: FieldValue;
placeholder?: string;
helpText?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class TextEmbeddingTransformer extends MLTransformer {
this.createFields = [
{
label: 'Model ID',
name: 'modelId',
id: 'modelId',
type: 'select',
selectType: 'model',
helpText: 'The deployed text embedding model to use for embedding.',
Expand All @@ -26,7 +26,7 @@ export class TextEmbeddingTransformer extends MLTransformer {
},
{
label: 'Input Field',
name: 'inputField',
id: 'inputField',
type: 'string',
helpText:
'The name of the field from which to obtain text for generating text embeddings.',
Expand All @@ -36,7 +36,7 @@ export class TextEmbeddingTransformer extends MLTransformer {

{
label: 'Vector Field',
name: 'vectorField',
id: 'vectorField',
type: 'string',
helpText:
' The name of the vector field in which to store the generated text embeddings.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function SelectField(props: SelectFieldProps) {
}
}, [models]);

const formField = `${props.componentId}.${props.field.name}`;
const formField = `${props.componentId}.${props.field.id}`;
const { errors, touched } = useFormikContext<WorkspaceFormValues>();

return (
Expand Down Expand Up @@ -84,7 +84,7 @@ export function SelectField(props: SelectFieldProps) {
}}
isInvalid={isFieldInvalid(
props.componentId,
props.field.name,
props.field.id,
errors,
touched
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface TextFieldProps {
* An input field for a component where users input plaintext
*/
export function TextField(props: TextFieldProps) {
const formField = `${props.componentId}.${props.field.name}`;
const formField = `${props.componentId}.${props.field.id}`;
const { errors, touched } = useFormikContext<WorkspaceFormValues>();

return (
Expand All @@ -44,10 +44,10 @@ export function TextField(props: TextFieldProps) {
) : undefined
}
helpText={props.field.helpText || undefined}
error={getFieldError(props.componentId, props.field.name, errors)}
error={getFieldError(props.componentId, props.field.id, errors)}
isInvalid={isFieldInvalid(
props.componentId,
props.field.name,
props.field.id,
errors,
touched
)}
Expand Down
6 changes: 0 additions & 6 deletions public/pages/workflow_detail/utils/index.ts

This file was deleted.

27 changes: 0 additions & 27 deletions public/pages/workflow_detail/utils/utils.ts

This file was deleted.

54 changes: 30 additions & 24 deletions public/pages/workflow_detail/workspace/resizable_workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
DEFAULT_NEW_WORKFLOW_DESCRIPTION,
USE_CASE,
WORKFLOW_STATE,
processNodes,
} from '../../../../common';
import {
AppState,
Expand All @@ -47,7 +48,6 @@ import {
} from '../../../store';
import { Workspace } from './workspace';
import { ComponentDetails } from '../component_details';
import { processNodes } from '../utils';

// styling
import './workspace-styles.scss';
Expand Down Expand Up @@ -147,30 +147,36 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
// Metadata fields (name/description/use_case/etc.) may not exist if the user
// cold reloads the page on a new, unsaved workflow.
useEffect(() => {
let workflowCopy = { ...props.workflow } as Workflow;
if (!workflowCopy.ui_metadata || !workflowCopy.ui_metadata.workspaceFlow) {
workflowCopy.ui_metadata = {
...(workflowCopy.ui_metadata || {}),
workspaceFlow: toWorkspaceFlow(workflowCopy.workflows),
};
console.debug(
`There is no saved UI flow for workflow: ${workflowCopy.name}. Generating a default one.`
);
}
if (props.workflow) {
let workflowCopy = { ...props.workflow } as Workflow;
if (
!workflowCopy.ui_metadata ||
!workflowCopy.ui_metadata.workspaceFlow
) {
workflowCopy.ui_metadata = {
...(workflowCopy.ui_metadata || {}),
workspaceFlow: toWorkspaceFlow(workflowCopy.workflows),
};
console.debug(
`There is no saved UI flow for workflow: ${workflowCopy.name}. Generating a default one.`
);
}

// TODO: tune some of the defaults, like use_case and version as these will change
workflowCopy = {
...workflowCopy,
name: workflowCopy.name || DEFAULT_NEW_WORKFLOW_NAME,
description: workflowCopy.description || DEFAULT_NEW_WORKFLOW_DESCRIPTION,
use_case: workflowCopy.use_case || USE_CASE.PROVISION,
version: workflowCopy.version || {
template: '1.0.0',
compatibility: ['2.12.0', '3.0.0'],
},
};
// TODO: tune some of the defaults, like use_case and version as these will change
workflowCopy = {
...workflowCopy,
name: workflowCopy.name || DEFAULT_NEW_WORKFLOW_NAME,
description:
workflowCopy.description || DEFAULT_NEW_WORKFLOW_DESCRIPTION,
use_case: workflowCopy.use_case || USE_CASE.PROVISION,
version: workflowCopy.version || {
template: '1.0.0',
compatibility: ['2.12.0', '3.0.0'],
},
};

setWorkflow(workflowCopy);
setWorkflow(workflowCopy);
}
}, [props.workflow]);

// Hook to updated the selected ReactFlow component
Expand Down Expand Up @@ -260,7 +266,7 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
let curFlowState = reactFlowInstance.toObject() as WorkspaceFlowState;
curFlowState = {
...curFlowState,
nodes: processNodes(curFlowState.nodes),
nodes: processNodes(curFlowState.nodes, formikProps.values),
};
if (validateWorkspaceFlow(curFlowState)) {
setFlowValidOnSubmit(true);
Expand Down
42 changes: 35 additions & 7 deletions public/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
IComponentField,
WorkspaceFormValues,
WORKFLOW_STATE,
ReactFlowComponent,
} from '../../common';

// Append 16 random characters
Expand Down Expand Up @@ -48,18 +49,27 @@ export function initComponentData(
export function componentDataToFormik(data: IComponentData): FormikValues {
const formikValues = {} as FormikValues;
data.createFields?.forEach((field) => {
formikValues[field.name] = field.value || getInitialValue(field.type);
formikValues[field.id] = field.value || getInitialValue(field.type);
});
return formikValues;
}

// TODO: below, we are hardcoding to only persisting and validating create fields.
// If we support both, we will need to dynamically update.
// Injecting the current form values into the component data
export function formikToComponentData(
data: IComponentData,
values: FormikValues
origData: IComponentData,
formValues: FormikValues
): IComponentData {
// TODO: populate data.fields with updated values based on the formik values
// We will need this when submitting to the backend.
return data;
return {
...origData,
createFields: origData.createFields?.map(
(createField: IComponentField) => ({
...createField,
value: formValues[createField.id],
})
),
} as IComponentData;
}

// Helper fn to get an initial value based on the field type
Expand Down Expand Up @@ -97,6 +107,24 @@ export function getFieldError(
return errors[componentId]?.[fieldName] as string | undefined;
}

// Process the raw ReactFlow nodes.
// De-select them all, and propagate the form data to the internal node data
export function processNodes(
nodes: ReactFlowComponent[],
formValues: WorkspaceFormValues
): ReactFlowComponent[] {
return nodes.map((node: ReactFlowComponent) => {
return {
...node,
selected: false,
data: formikToComponentData(
{ ...node.data, selected: false },
formValues[node.id]
),
};
});
}

/*
**************** Yup (validation) utils **********************
*/
Expand All @@ -106,7 +134,7 @@ export function getFieldError(
export function getComponentSchema(data: IComponentData): ObjectSchema<any> {
const schemaObj = {} as { [key: string]: Schema };
data.createFields?.forEach((field) => {
schemaObj[field.name] = getFieldSchema(field);
schemaObj[field.id] = getFieldSchema(field);
});
return yup.object(schemaObj);
}
Expand Down
1 change: 1 addition & 0 deletions server/routes/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function toWorkflowObj(workflowHit: any): Workflow {
description: hitSource.description || '',
version: hitSource.version,
workflows: hitSource.workflows,
ui_metadata: hitSource.ui_metadata,
lastUpdated: hitSource.last_updated_time,
lastLaunched: hitSource.last_provisioned_time,
} as Workflow;
Expand Down
Loading

0 comments on commit c2bcd95

Please sign in to comment.