Skip to content

Commit

Permalink
Refactor and organize public/utils (#184)
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Ohlsen <[email protected]>
(cherry picked from commit 43b89da)
  • Loading branch information
ohltyler authored and github-actions[bot] committed Jun 18, 2024
1 parent 147df5b commit 0b51252
Show file tree
Hide file tree
Showing 10 changed files with 843 additions and 783 deletions.
23 changes: 13 additions & 10 deletions public/pages/workflow_detail/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import React, { useEffect, useState } from 'react';
import {
EuiPageHeader,
EuiButton,
Expand All @@ -12,8 +12,8 @@ import {
EuiText,
} from '@elastic/eui';
import {
DEFAULT_NEW_WORKFLOW_NAME,
DEFAULT_NEW_WORKFLOW_STATE,
WORKFLOW_STATE,
Workflow,
} from '../../../../common';

Expand All @@ -22,22 +22,25 @@ interface WorkflowDetailHeaderProps {
}

export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) {
function getTitle() {
return props.workflow ? props.workflow.name : DEFAULT_NEW_WORKFLOW_NAME;
}
// workflow state
const [workflowName, setWorkflowName] = useState<string>('');
const [workflowState, setWorkflowState] = useState<WORKFLOW_STATE>('');

function getState() {
return props.workflow ? props.workflow.state : DEFAULT_NEW_WORKFLOW_STATE;
}
useEffect(() => {
if (props.workflow) {
setWorkflowName(props.workflow.name);
setWorkflowState(props.workflow.state || DEFAULT_NEW_WORKFLOW_STATE);
}
}, [props.workflow]);

return (
<EuiPageHeader
style={{ marginTop: '-8px' }}
pageTitle={
<EuiFlexGroup direction="row" alignItems="flexEnd" gutterSize="m">
<EuiFlexItem grow={false}>{getTitle()}</EuiFlexItem>
<EuiFlexItem grow={false}>{workflowName}</EuiFlexItem>
<EuiFlexItem grow={false} style={{ marginBottom: '10px' }}>
<EuiText size="m">{getState()}</EuiText>
<EuiText size="m">{workflowState}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
}
Expand Down
1 change: 0 additions & 1 deletion public/pages/workflow_detail/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
* SPDX-License-Identifier: Apache-2.0
*/

export * from './workflow_to_template_utils';
export * from './data_extractor_utils';
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ import {
useAppDispatch,
} from '../../../store';
import { getCore } from '../../../services';
import { formikToUiConfig, reduceToTemplate } from '../../../utils';
import { configToTemplateFlows } from '../utils';
import {
formikToUiConfig,
reduceToTemplate,
configToTemplateFlows,
} from '../../../utils';

// styling
import '../workspace/workspace-styles.scss';
Expand Down
111 changes: 111 additions & 0 deletions public/utils/config_to_form_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { FormikValues } from 'formik';
import {
WorkflowConfig,
WorkflowFormValues,
IngestConfig,
SearchConfig,
ProcessorsConfig,
IndexConfig,
IProcessorConfig,
ConfigFieldType,
ConfigFieldValue,
ModelFormValue,
} from '../../common';

/*
**************** Config -> formik utils **********************
*/

export function uiConfigToFormik(config: WorkflowConfig): WorkflowFormValues {
const formikValues = {} as WorkflowFormValues;
formikValues['ingest'] = ingestConfigToFormik(config.ingest);
formikValues['search'] = searchConfigToFormik(config.search);
return formikValues;
}

function ingestConfigToFormik(
ingestConfig: IngestConfig | undefined
): FormikValues {
let ingestFormikValues = {} as FormikValues;
if (ingestConfig) {
ingestFormikValues['enrich'] = processorsConfigToFormik(
ingestConfig.enrich
);
ingestFormikValues['index'] = indexConfigToFormik(ingestConfig.index);
}
return ingestFormikValues;
}

function processorsConfigToFormik(
processorsConfig: ProcessorsConfig
): FormikValues {
let formValues = {} as FormikValues;
processorsConfig.processors.forEach((processorConfig) => {
formValues[processorConfig.id] = processorConfigToFormik(processorConfig);
});
return formValues;
}

export function processorConfigToFormik(
processorConfig: IProcessorConfig
): FormikValues {
const fieldValues = {} as FormikValues;
processorConfig.fields.forEach((field) => {
fieldValues[field.id] = field.value || getInitialValue(field.type);
});
return fieldValues;
}

function indexConfigToFormik(indexConfig: IndexConfig): FormikValues {
let formValues = {} as FormikValues;
formValues['name'] =
indexConfig.name.value || getInitialValue(indexConfig.name.type);
return formValues;
}

function searchConfigToFormik(
searchConfig: SearchConfig | undefined
): FormikValues {
let searchFormikValues = {} as FormikValues;
if (searchConfig) {
// TODO: implement for request
searchFormikValues['request'] = {};
searchFormikValues['enrichRequest'] = processorsConfigToFormik(
searchConfig.enrichRequest
);
searchFormikValues['enrichResponse'] = processorsConfigToFormik(
searchConfig.enrichResponse
);
}
return searchFormikValues;
}

// Helper fn to get an initial value based on the field type
export function getInitialValue(fieldType: ConfigFieldType): ConfigFieldValue {
switch (fieldType) {
case 'string': {
return '';
}
case 'select': {
return '';
}
case 'model': {
return {
id: '',
category: undefined,
algorithm: undefined,
} as ModelFormValue;
}
case 'map': {
return [];
}
case 'json': {
return {};
}
}
}
114 changes: 114 additions & 0 deletions public/utils/config_to_schema_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { Schema, ObjectSchema } from 'yup';
import * as yup from 'yup';
import {
WorkflowConfig,
WorkflowSchema,
IngestConfig,
SearchConfig,
ProcessorsConfig,
WorkflowSchemaObj,
IConfigField,
IndexConfig,
} from '../../common';

/*
**************** Schema / validation utils **********************
*/

export function uiConfigToSchema(config: WorkflowConfig): WorkflowSchema {
const schemaObj = {} as WorkflowSchemaObj;
schemaObj['ingest'] = ingestConfigToSchema(config.ingest);
schemaObj['search'] = searchConfigToSchema(config.search);
return yup.object(schemaObj) as WorkflowSchema;
}

function ingestConfigToSchema(
ingestConfig: IngestConfig | undefined
): ObjectSchema<any> {
const ingestSchemaObj = {} as { [key: string]: Schema };
if (ingestConfig) {
// TODO: implement for the other sub-categories
ingestSchemaObj['enrich'] = processorsConfigToSchema(ingestConfig.enrich);
ingestSchemaObj['index'] = indexConfigToSchema(ingestConfig.index);
}
return yup.object(ingestSchemaObj);
}

function processorsConfigToSchema(processorsConfig: ProcessorsConfig): Schema {
const processorsSchemaObj = {} as { [key: string]: Schema };
processorsConfig.processors.forEach((processorConfig) => {
const processorSchemaObj = {} as { [key: string]: Schema };
processorConfig.fields.forEach((field) => {
processorSchemaObj[field.id] = getFieldSchema(field);
});
processorsSchemaObj[processorConfig.id] = yup.object(processorSchemaObj);
});

return yup.object(processorsSchemaObj);
}

function indexConfigToSchema(indexConfig: IndexConfig): Schema {
const indexSchemaObj = {} as { [key: string]: Schema };
indexSchemaObj['name'] = getFieldSchema(indexConfig.name);
return yup.object(indexSchemaObj);
}

// TODO: implement this
function searchConfigToSchema(
searchConfig: SearchConfig | undefined
): ObjectSchema<any> {
const searchSchemaObj = {} as { [key: string]: Schema };

return yup.object(searchSchemaObj);
}

/*
**************** Yup (validation) utils **********************
*/

function getFieldSchema(field: IConfigField): Schema {
let baseSchema: Schema;
switch (field.type) {
case 'string':
case 'select': {
baseSchema = yup.string().min(1, 'Too short').max(70, 'Too long');
break;
}
case 'model': {
baseSchema = yup.object().shape({
id: yup.string().min(1, 'Too short').max(70, 'Too long').required(),
category: yup.string().required(),
});
break;
}
case 'map': {
baseSchema = yup.array().of(
yup.object().shape({
key: yup.string().min(1, 'Too short').max(70, 'Too long').required(),
value: yup
.string()
.min(1, 'Too short')
.max(70, 'Too long')
.required(),
})
);
break;
}
case 'json': {
baseSchema = yup.object().json();
break;
}
}

// TODO: make optional schema if we support optional fields in the future
// return field.optional
// ? baseSchema.optional()
// : baseSchema.required('Required');

return baseSchema.required('Required');
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ import {
MLInferenceProcessor,
MapFormValue,
IngestProcessor,
Workflow,
WorkflowTemplate,
CreateSearchPipelineNode,
SearchProcessor,
IngestConfig,
SearchConfig,
CreateSearchPipelineNode,
} from '../../../../common';
import { generateId, processorConfigToFormik } from '../../../utils';
} from '../../common';
import { processorConfigToFormik } from './config_to_form_utils';
import { generateId } from './utils';

/**
* Given a WorkflowConfig with fully populated input values,
* generate a backend-compatible set of sub-workflows.
/*
**************** Config -> template utils **********************
*/

export function configToTemplateFlows(config: WorkflowConfig): TemplateFlows {
Expand Down Expand Up @@ -228,3 +230,17 @@ function indexConfigToTemplateNode(
},
};
}

// Helper fn to remove state-related fields from a workflow and have a stateless template
// to export and/or pass around, use when updating, etc.
export function reduceToTemplate(workflow: Workflow): WorkflowTemplate {
const {
id,
lastUpdated,
lastLaunched,
state,
resourcesCreated,
...workflowTemplate
} = workflow;
return workflowTemplate;
}
Loading

0 comments on commit 0b51252

Please sign in to comment.