diff --git a/public/pages/workflow_detail/workflow_inputs/input_fields/boolean_field.tsx b/public/pages/workflow_detail/workflow_inputs/input_fields/boolean_field.tsx new file mode 100644 index 00000000..660f2b38 --- /dev/null +++ b/public/pages/workflow_detail/workflow_inputs/input_fields/boolean_field.tsx @@ -0,0 +1,41 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { Field, FieldProps } from 'formik'; +import { EuiRadioGroup, EuiRadioGroupOption } from '@elastic/eui'; + +interface BooleanFieldProps { + fieldPath: string; // the full path in string-form to the field (e.g., 'ingest.enrich.processors.text_embedding_processor.inputField') + onFormChange: () => void; + enabledOption: EuiRadioGroupOption; + disabledOption: EuiRadioGroupOption; +} + +/** + * An input field for a boolean value. Implemented as an EuiRadioGroup with 2 mutually exclusive options. + */ +export function BooleanField(props: BooleanFieldProps) { + return ( + + {({ field, form }: FieldProps) => { + return ( + { + form.setFieldValue(field.name, !field.value); + props.onFormChange(); + }} + /> + ); + }} + + ); +} diff --git a/public/pages/workflow_detail/workflow_inputs/input_fields/index.ts b/public/pages/workflow_detail/workflow_inputs/input_fields/index.ts index b211fd3d..ca741ba6 100644 --- a/public/pages/workflow_detail/workflow_inputs/input_fields/index.ts +++ b/public/pages/workflow_detail/workflow_inputs/input_fields/index.ts @@ -8,3 +8,4 @@ export { JsonField } from './json_field'; export { SelectField } from './select_field'; export { ModelField } from './model_field'; export { MapField } from './map_field'; +export { BooleanField } from './boolean_field'; diff --git a/public/pages/workflow_detail/workflow_inputs/workflow_inputs.tsx b/public/pages/workflow_detail/workflow_inputs/workflow_inputs.tsx index e849f783..a5f3d60d 100644 --- a/public/pages/workflow_detail/workflow_inputs/workflow_inputs.tsx +++ b/public/pages/workflow_detail/workflow_inputs/workflow_inputs.tsx @@ -14,7 +14,9 @@ import { EuiHorizontalRule, EuiLoadingSpinner, EuiPanel, + EuiSpacer, EuiStepsHorizontal, + EuiText, EuiTitle, } from '@elastic/eui'; import { @@ -40,6 +42,7 @@ import { configToTemplateFlows, hasProvisionedIngestResources, } from '../../../utils'; +import { BooleanField } from './input_fields'; // styling import '../workspace/workspace-styles.scss'; @@ -57,11 +60,16 @@ interface WorkflowInputsProps { setQuery: (query: string) => void; } -export enum STEP { +enum STEP { INGEST = 'Ingestion pipeline', SEARCH = 'Search pipeline', } +enum INGEST_OPTION { + CREATE = 'create', + SKIP = 'skip', +} + /** * The workflow inputs component containing the multi-step flow to create ingest * and search flows for a particular workflow. @@ -81,8 +89,10 @@ export function WorkflowInputs(props: WorkflowInputsProps) { // maintain global states const onIngest = selectedStep === STEP.INGEST; + const ingestEnabled = values?.ingest?.enabled || false; const onIngestAndProvisioned = onIngest && ingestProvisioned; const onIngestAndUnprovisioned = onIngest && !ingestProvisioned; + const onIngestAndDisabled = onIngest && !ingestEnabled; useEffect(() => { setIngestProvisioned(hasProvisionedIngestResources(props.workflow)); @@ -265,42 +275,81 @@ export function WorkflowInputs(props: WorkflowInputsProps) { onClick: () => {}, }, ]} - > - - - -

- {onIngestAndUnprovisioned - ? 'Define ingest pipeline' - : onIngestAndProvisioned - ? 'Edit ingest pipeline' - : 'Define search pipeline'} -

-
-
- - {onIngest ? ( - - ) : ( - + /> + {onIngest && ( + <> + + + + Create an ingest pipeline + + + Configure and ingest data into an index. + + + ), + }} + disabledOption={{ + id: INGEST_OPTION.SKIP, + label: ( + + + Skip ingestion pipeline + + + Use an existing index with data ingested. + + + ), + }} + /> + )} + {!onIngestAndDisabled && ( + <> + + +

+ {onIngestAndUnprovisioned + ? 'Define ingest pipeline' + : onIngestAndProvisioned + ? 'Edit ingest pipeline' + : 'Define search pipeline'} +

+
+
+ + {onIngest ? ( + + ) : ( + + )} + + + )} @@ -308,7 +357,16 @@ export function WorkflowInputs(props: WorkflowInputsProps) { - {onIngestAndUnprovisioned ? ( + {onIngest && !ingestEnabled ? ( + + setSelectedStep(STEP.SEARCH)} + > + {`Search pipeline >`} + + + ) : onIngestAndUnprovisioned ? ( <> setSelectedStep(STEP.SEARCH)} > - {`Next >`} + {`Search pipeline >`} diff --git a/public/utils/config_to_form_utils.ts b/public/utils/config_to_form_utils.ts index c5dbc4da..96f7489f 100644 --- a/public/utils/config_to_form_utils.ts +++ b/public/utils/config_to_form_utils.ts @@ -41,6 +41,7 @@ function ingestConfigToFormik( ): FormikValues { let ingestFormikValues = {} as FormikValues; if (ingestConfig) { + ingestFormikValues['enabled'] = ingestConfig.enabled; ingestFormikValues['docs'] = ingestDocs || getInitialValue('json'); ingestFormikValues['enrich'] = processorsConfigToFormik( ingestConfig.enrich diff --git a/public/utils/config_to_template_utils.ts b/public/utils/config_to_template_utils.ts index 1cf4d80a..206441fc 100644 --- a/public/utils/config_to_template_utils.ts +++ b/public/utils/config_to_template_utils.ts @@ -11,7 +11,6 @@ import { TemplateFlow, TemplateEdge, ModelFormValue, - IndexMappings, WORKFLOW_STEP_TYPE, WorkflowConfig, PROCESSOR_TYPE, @@ -58,13 +57,15 @@ function configToProvisionTemplateFlow(config: WorkflowConfig): TemplateFlow { (node) => node.type === WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE ) as CreateSearchPipelineNode; - nodes.push( - indexConfigToTemplateNode( - config.ingest.index, - createIngestPipelineNode, - createSearchPipelineNode - ) - ); + if (config.ingest.enabled) { + nodes.push( + indexConfigToTemplateNode( + config.ingest.index, + createIngestPipelineNode, + createSearchPipelineNode + ) + ); + } return { nodes, @@ -81,7 +82,7 @@ function ingestConfigToTemplateNodes( ); const hasProcessors = ingestProcessors.length > 0; - return hasProcessors + return hasProcessors && ingestConfig.enabled ? [ { id: ingestPipelineName, @@ -179,7 +180,17 @@ function indexConfigToTemplateNode( ingestPipelineNode?: CreateIngestPipelineNode, searchPipelineNode?: CreateSearchPipelineNode ): CreateIndexNode { - let finalSettings = indexConfig.settings.value as {}; + let finalSettings = {}; + let finalMappings = {}; + try { + // @ts-ignore + finalSettings = JSON.parse(indexConfig.settings?.value); + } catch (e) {} + try { + // @ts-ignore + finalMappings = JSON.parse(indexConfig.mappings?.value); + } catch (e) {} + let finalPreviousNodeInputs = {}; function updateFinalInputsAndSettings( @@ -218,7 +229,7 @@ function indexConfigToTemplateNode( index_name: indexConfig.name.value as string, configurations: { settings: finalSettings, - mappings: indexConfig.mappings.value as IndexMappings, + mappings: finalMappings, }, }, }; diff --git a/public/utils/form_to_config_utils.ts b/public/utils/form_to_config_utils.ts index d124c122..6ab866f1 100644 --- a/public/utils/form_to_config_utils.ts +++ b/public/utils/form_to_config_utils.ts @@ -44,6 +44,7 @@ function formikToIngestUiConfig( ): IngestConfig { return { ...existingConfig, + enabled: ingestFormValues['enabled'], enrich: formikToProcessorsUiConfig( ingestFormValues['enrich'], existingConfig.enrich