From 45240ad253d497f6d5597bfcf0b2edff456ade21 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:40:43 -0700 Subject: [PATCH] Simplify export flow; improve form state (#284) (#288) Signed-off-by: Tyler Ohlsen (cherry picked from commit ad86b005c397d1a9c74b0d624558bea684c646ca) Co-authored-by: Tyler Ohlsen --- common/constants.ts | 2 + common/interfaces.ts | 10 +- .../component_types/indexer/base_indexer.ts | 6 +- public/component_types/indexer/index.ts | 1 + .../components/export_modal.tsx | 114 +++++++++++ .../workflow_detail/components/header.tsx | 73 ++++--- .../export_options/export_options.tsx | 83 -------- .../workflow_inputs/export_options/index.ts | 6 - .../configure_search_request.tsx | 13 +- .../workflow_inputs/workflow_inputs.tsx | 181 ++++++++++++------ public/utils/config_to_template_utils.ts | 19 +- public/utils/config_to_workspace_utils.ts | 6 +- public/utils/form_to_pipeline_utils.ts | 111 +++++------ 13 files changed, 372 insertions(+), 253 deletions(-) create mode 100644 public/pages/workflow_detail/components/export_modal.tsx delete mode 100644 public/pages/workflow_detail/workflow_inputs/export_options/export_options.tsx delete mode 100644 public/pages/workflow_detail/workflow_inputs/export_options/index.ts diff --git a/common/constants.ts b/common/constants.ts index fea993aa..3ea554bb 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -123,6 +123,8 @@ export const ML_CHOOSE_MODEL_LINK = 'https://opensearch.org/docs/latest/ml-commons-plugin/integrating-ml-models/#choosing-a-model'; export const TEXT_CHUNKING_PROCESSOR_LINK = 'https://opensearch.org/docs/latest/ingest-pipelines/processors/text-chunking/'; +export const CREATE_WORKFLOW_LINK = + 'https://opensearch.org/docs/latest/automating-configurations/api/create-workflow/'; /** * Text chunking algorithm constants diff --git a/common/interfaces.ts b/common/interfaces.ts index 9f705c8f..39c53f01 100644 --- a/common/interfaces.ts +++ b/common/interfaces.ts @@ -260,14 +260,14 @@ export type CreateIngestPipelineNode = TemplateNode & { model_id?: string; input_field?: string; output_field?: string; - configurations: IngestPipelineConfig; + configurations: string; }; }; export type CreateSearchPipelineNode = TemplateNode & { user_inputs: { pipeline_id: string; - configurations: SearchPipelineConfig; + configurations: string; }; }; @@ -277,10 +277,7 @@ export type CreateIndexNode = TemplateNode & { }; user_inputs: { index_name: string; - configurations: { - settings: {}; - mappings: {}; - }; + configurations: string; }; }; @@ -292,6 +289,7 @@ export type TemplateEdge = { export type TemplateFlow = { nodes: TemplateNode[]; edges?: TemplateEdge[]; + user_params?: {}; }; export type TemplateFlows = { diff --git a/public/component_types/indexer/base_indexer.ts b/public/component_types/indexer/base_indexer.ts index f526121b..cfcc8b94 100644 --- a/public/component_types/indexer/base_indexer.ts +++ b/public/component_types/indexer/base_indexer.ts @@ -9,12 +9,12 @@ import { BaseComponent } from '../base_component'; /** * A base indexer UI component */ -export abstract class BaseIndexer extends BaseComponent { +export class BaseIndexer extends BaseComponent { constructor() { super(); this.type = COMPONENT_CLASS.INDEXER; - this.label = 'Indexer'; - this.description = 'A general indexer'; + this.label = 'Index'; + this.description = 'An OpenSearch index'; this.inputs = [ { id: 'input', diff --git a/public/component_types/indexer/index.ts b/public/component_types/indexer/index.ts index 264add90..a4e85f53 100644 --- a/public/component_types/indexer/index.ts +++ b/public/component_types/indexer/index.ts @@ -3,4 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ +export * from './base_indexer'; export * from './knn_indexer'; diff --git a/public/pages/workflow_detail/components/export_modal.tsx b/public/pages/workflow_detail/components/export_modal.tsx new file mode 100644 index 00000000..ce31b3da --- /dev/null +++ b/public/pages/workflow_detail/components/export_modal.tsx @@ -0,0 +1,114 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useEffect, useState } from 'react'; +import yaml from 'js-yaml'; +import { + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiCompressedRadioGroup, + EuiText, + EuiLink, + EuiModal, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalBody, + EuiModalFooter, + EuiSmallButtonEmpty, +} from '@elastic/eui'; +import { CREATE_WORKFLOW_LINK, Workflow } from '../../../../common'; +import { reduceToTemplate } from '../../../utils'; + +interface ExportModalProps { + workflow?: Workflow; + setIsExportModalOpen(isOpen: boolean): void; +} + +enum EXPORT_OPTION { + JSON = 'json', + YAML = 'yaml', +} + +const exportOptions = [ + { + id: EXPORT_OPTION.JSON, + label: 'JSON', + }, + { + id: EXPORT_OPTION.YAML, + label: 'YAML', + }, +]; + +/** + * Modal containing all of the export options + */ +export function ExportModal(props: ExportModalProps) { + // format type state + const [selectedOption, setSelectedOption] = useState( + EXPORT_OPTION.JSON + ); + + // formatted string state + const [formattedConfig, setFormattedConfig] = useState(''); + useEffect(() => { + if (props.workflow) { + const workflowTemplate = reduceToTemplate(props.workflow); + if (selectedOption === EXPORT_OPTION.JSON) { + setFormattedConfig(JSON.stringify(workflowTemplate, undefined, 2)); + } else if (selectedOption === EXPORT_OPTION.YAML) { + setFormattedConfig(yaml.dump(workflowTemplate)); + } + } + }, [props.workflow, selectedOption]); + + return ( + props.setIsExportModalOpen(false)}> + + +

{`Export ${props.workflow?.name}`}

+
+
+ + + + + Copy the below workflow templates to use in other clusters. + + + Learn more + + + + { + setSelectedOption(option as EXPORT_OPTION); + }} + /> + + {props.workflow !== undefined && ( + + + {formattedConfig} + + + )} + + + + props.setIsExportModalOpen(false)}> + Close + + +
+ ); +} diff --git a/public/pages/workflow_detail/components/header.tsx b/public/pages/workflow_detail/components/header.tsx index bc5c129d..a69afb22 100644 --- a/public/pages/workflow_detail/components/header.tsx +++ b/public/pages/workflow_detail/components/header.tsx @@ -11,6 +11,7 @@ import { EuiFlexItem, EuiText, EuiSmallButtonEmpty, + EuiSmallButton, } from '@elastic/eui'; import { DEFAULT_NEW_WORKFLOW_STATE, @@ -19,6 +20,7 @@ import { toFormattedDate, } from '../../../../common'; import { APP_PATH } from '../../../utils'; +import { ExportModal } from './export_modal'; interface WorkflowDetailHeaderProps { workflow?: Workflow; @@ -31,6 +33,9 @@ export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) { const [workflowState, setWorkflowState] = useState(''); const [workflowLastUpdated, setWorkflowLastUpdated] = useState(''); + // export modal state + const [isExportModalOpen, setIsExportModalOpen] = useState(false); + useEffect(() => { if (props.workflow) { setWorkflowName(props.workflow.name); @@ -48,31 +53,47 @@ export function WorkflowDetailHeader(props: WorkflowDetailHeaderProps) { }, [props.workflow]); return ( - - {workflowName} - - {workflowState} - - - } - rightSideItems={[ - { - // TODO: add lightweight save here when available - history.replace(APP_PATH.WORKFLOWS); - }} - > - Close - , - - {`Last updated: ${workflowLastUpdated}`} - , - ]} - bottomBorder={false} - /> + <> + {isExportModalOpen && ( + + )} + + {workflowName} + + {workflowState} + + + } + rightSideItems={[ + { + setIsExportModalOpen(true); + }} + > + Export + , + { + history.replace(APP_PATH.WORKFLOWS); + }} + > + Close + , + + {`Last updated: ${workflowLastUpdated}`} + , + ]} + bottomBorder={false} + /> + ); } diff --git a/public/pages/workflow_detail/workflow_inputs/export_options/export_options.tsx b/public/pages/workflow_detail/workflow_inputs/export_options/export_options.tsx deleted file mode 100644 index f239a5c0..00000000 --- a/public/pages/workflow_detail/workflow_inputs/export_options/export_options.tsx +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { useEffect, useState } from 'react'; -import yaml from 'js-yaml'; -import { - EuiCodeBlock, - EuiFlexGroup, - EuiFlexItem, - EuiCompressedRadioGroup, -} from '@elastic/eui'; -import { Workflow } from '../../../../../common'; -import { reduceToTemplate } from '../../../../utils'; - -interface SearchInputsProps { - workflow?: Workflow; -} - -enum EXPORT_OPTION { - JSON = 'json', - YAML = 'yaml', -} - -const exportOptions = [ - { - id: EXPORT_OPTION.JSON, - label: 'JSON', - }, - { - id: EXPORT_OPTION.YAML, - label: 'YAML', - }, -]; - -/** - * The base component containing all of the export options - */ -export function ExportOptions(props: SearchInputsProps) { - // format type state - const [selectedOption, setSelectedOption] = useState( - EXPORT_OPTION.JSON - ); - - // formatted string state - const [formattedConfig, setFormattedConfig] = useState(''); - useEffect(() => { - if (props.workflow) { - const workflowTemplate = reduceToTemplate(props.workflow); - if (selectedOption === EXPORT_OPTION.JSON) { - setFormattedConfig(JSON.stringify(workflowTemplate, undefined, 2)); - } else if (selectedOption === EXPORT_OPTION.YAML) { - setFormattedConfig(yaml.dump(workflowTemplate)); - } - } - }, [props.workflow, selectedOption]); - - return ( - - - { - setSelectedOption(option as EXPORT_OPTION); - }} - /> - - {props.workflow !== undefined && ( - - - {formattedConfig} - - - )} - - ); -} diff --git a/public/pages/workflow_detail/workflow_inputs/export_options/index.ts b/public/pages/workflow_detail/workflow_inputs/export_options/index.ts deleted file mode 100644 index 6048a6dd..00000000 --- a/public/pages/workflow_detail/workflow_inputs/export_options/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export * from './export_options'; diff --git a/public/pages/workflow_detail/workflow_inputs/search_inputs/configure_search_request.tsx b/public/pages/workflow_detail/workflow_inputs/search_inputs/configure_search_request.tsx index 998385de..430ef7c6 100644 --- a/public/pages/workflow_detail/workflow_inputs/search_inputs/configure_search_request.tsx +++ b/public/pages/workflow_detail/workflow_inputs/search_inputs/configure_search_request.tsx @@ -49,7 +49,7 @@ export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) { const { values, setFieldValue, setFieldTouched } = useFormikContext< WorkflowFormValues >(); - const ingestEnabled = values.ingest.enabled; + const ingestEnabled = values?.ingest?.enabled || true; const searchIndexNameFormPath = 'search.index.name'; // All indices state @@ -57,13 +57,13 @@ export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) { // Selected index state const [selectedIndex, setSelectedIndex] = useState( - values.search.index.name + values?.search?.index?.name ); // initial load: set the search index value, if not already set useEffect(() => { - if (values.ingest.enabled) { - setFieldValue(searchIndexNameFormPath, values.ingest.index.name); + if (values?.ingest?.enabled) { + setFieldValue(searchIndexNameFormPath, values?.ingest?.index?.name); } }, []); @@ -127,7 +127,10 @@ export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) { {ingestEnabled ? ( - + ) : ( (false); - const [searchProvisioned, setSearchProvisioned] = useState(false); // confirm modal state const [isModalOpen, setIsModalOpen] = useState(false); @@ -121,7 +120,6 @@ export function WorkflowInputs(props: WorkflowInputsProps) { // maintain global states const onIngest = selectedStep === STEP.INGEST; const onSearch = selectedStep === STEP.SEARCH; - const onExport = selectedStep === STEP.EXPORT; const ingestEnabled = values?.ingest?.enabled || false; const onIngestAndProvisioned = onIngest && ingestProvisioned; const onIngestAndUnprovisioned = onIngest && !ingestProvisioned; @@ -130,6 +128,112 @@ export function WorkflowInputs(props: WorkflowInputsProps) { isEmpty(getIn(values, 'search.enrichRequest')) && isEmpty(getIn(values, 'search.enrichResponse')); + // maintaining any fine-grained differences between the generated templates produced by the form, + // and the one persisted in the workflow itself. We enable/disable buttons + // based on any discrepancies found. + const [persistedTemplateNodes, setPersistedTemplateNodes] = useState< + TemplateNode[] + >([]); + const [ + persistedIngestTemplateNodes, + setPersistedIngestTemplateNodes, + ] = useState([]); + const [ + persistedSearchTemplateNodes, + setPersistedSearchTemplateNodes, + ] = useState([]); + const [formGeneratedTemplateNodes, setFormGeneratedTemplateNodes] = useState< + TemplateNode[] + >([]); + const [ + formGeneratedIngestTemplateNodes, + setFormGeneratedIngestTemplateNodes, + ] = useState([]); + const [ + formGeneratedSearchTemplateNodes, + setFormGeneratedSearchTemplateNodes, + ] = useState([]); + const [ingestTemplatesDifferent, setIngestTemplatesDifferent] = useState< + boolean + >(false); + const [searchTemplatesDifferent, setSearchTemplatesDifferent] = useState< + boolean + >(false); + + // fetch the total template nodes + useEffect(() => { + setPersistedTemplateNodes( + props.workflow?.workflows?.provision?.nodes || [] + ); + setFormGeneratedTemplateNodes( + (values?.ingest && + values?.search && + props.uiConfig && + props.workflow && + configToTemplateFlows( + formikToUiConfig(values, props.uiConfig as WorkflowConfig) + ).provision.nodes) || + [] + ); + }, [values, props.uiConfig, props.workflow]); + + // fetch the persisted template nodes for ingest & search + useEffect(() => { + const tmpIngestNodes = [] as TemplateNode[]; + const tmpSearchNodes = [] as TemplateNode[]; + persistedTemplateNodes.forEach((templateNode) => { + if ( + templateNode.type === + WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE + ) { + tmpSearchNodes.push(templateNode); + } else { + tmpIngestNodes.push(templateNode); + } + }); + setPersistedIngestTemplateNodes(tmpIngestNodes); + setPersistedSearchTemplateNodes(tmpSearchNodes); + }, [persistedTemplateNodes]); + + // fetch the form-generated template nodes for ingest & search + useEffect(() => { + const tmpIngestNodes = [] as TemplateNode[]; + const tmpSearchNodes = [] as TemplateNode[]; + formGeneratedTemplateNodes.forEach((templateNode) => { + if ( + templateNode.type === + WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE + ) { + tmpSearchNodes.push(templateNode); + } else { + tmpIngestNodes.push(templateNode); + } + }); + setFormGeneratedIngestTemplateNodes(tmpIngestNodes); + setFormGeneratedSearchTemplateNodes(tmpSearchNodes); + }, [formGeneratedTemplateNodes]); + + // determine any discrepancies between the form-generated and persisted templates + useEffect(() => { + setIngestTemplatesDifferent( + !isEqual( + persistedIngestTemplateNodes, + formGeneratedIngestTemplateNodes + ) || false + ); + setSearchTemplatesDifferent( + !isEqual( + persistedSearchTemplateNodes, + formGeneratedSearchTemplateNodes + ) || false + ); + }, [ + persistedIngestTemplateNodes, + persistedSearchTemplateNodes, + formGeneratedIngestTemplateNodes, + formGeneratedSearchTemplateNodes, + ]); + // Auto-save the UI metadata when users update form values. // Only update the underlying workflow template (deprovision/provision) when // users explicitly run ingest/search and need to have updated resources @@ -198,7 +302,6 @@ export function WorkflowInputs(props: WorkflowInputsProps) { useEffect(() => { setIngestProvisioned(hasProvisionedIngestResources(props.workflow)); - setSearchProvisioned(hasProvisionedSearchResources(props.workflow)); }, [props.workflow]); // Utility fn to update the workflow, including any updated/new resources. @@ -468,20 +571,14 @@ export function WorkflowInputs(props: WorkflowInputsProps) { steps={[ { title: STEP.INGEST, - isComplete: onSearch || onExport, + isComplete: onSearch, isSelected: onIngest, onClick: () => {}, }, { title: STEP.SEARCH, - isComplete: onExport, - isSelected: onSearch, - onClick: () => {}, - }, - { - title: STEP.EXPORT, isComplete: false, - isSelected: onExport, + isSelected: onSearch, onClick: () => {}, }, ]} @@ -523,7 +620,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) { // @ts-ignore await dispatch( getWorkflow({ - workflowId: props.workflow.id, + workflowId: props.workflow?.id as string, dataSourceId, }) ); @@ -612,10 +709,8 @@ export function WorkflowInputs(props: WorkflowInputsProps) { - ) : onSearch ? ( - 'Define search pipeline' ) : ( - 'Export project as' + 'Define search pipeline' )} @@ -634,7 +729,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) { uiConfig={props.uiConfig} setUiConfig={props.setUiConfig} /> - ) : onSearch ? ( + ) : ( - ) : ( - )} @@ -675,8 +768,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) { onClick={() => { validateAndRunIngestion(); }} - // TODO: only enable if ingest is dirty - disabled={ingestProvisioned && !isDirty} + disabled={!ingestTemplatesDifferent} > Run ingestion @@ -687,18 +779,21 @@ export function WorkflowInputs(props: WorkflowInputsProps) { onClick={() => { setSelectedStep(STEP.SEARCH); }} - // TODO: only disable if ingest is dirty - disabled={!ingestProvisioned || isDirty} + disabled={ingestTemplatesDifferent} > {`Search pipeline >`} - ) : onSearch ? ( + ) : ( <> setSelectedStep(STEP.INGEST)} > Back @@ -715,38 +810,6 @@ export function WorkflowInputs(props: WorkflowInputsProps) { Run query - - { - setSelectedStep(STEP.EXPORT); - }} - > - {`Export >`} - - - - ) : ( - <> - - setSelectedStep(STEP.SEARCH)} - > - Back - - - - { - // TODO: final UX for export flow is TBD. - }} - > - Export - - )} diff --git a/public/utils/config_to_template_utils.ts b/public/utils/config_to_template_utils.ts index f8d85707..732809dd 100644 --- a/public/utils/config_to_template_utils.ts +++ b/public/utils/config_to_template_utils.ts @@ -12,14 +12,12 @@ import { CreateIndexNode, TemplateFlow, TemplateEdge, - ModelFormValue, WORKFLOW_STEP_TYPE, WorkflowConfig, PROCESSOR_TYPE, IndexConfig, IProcessorConfig, MLInferenceProcessor, - MapArrayFormValue, IngestProcessor, Workflow, WorkflowTemplate, @@ -33,6 +31,8 @@ import { SHARED_OPTIONAL_FIELDS, FIXED_TOKEN_LENGTH_OPTIONAL_FIELDS, DELIMITER_OPTIONAL_FIELDS, + IngestPipelineConfig, + SearchPipelineConfig, } from '../../common'; import { processorConfigToFormik } from './config_to_form_utils'; @@ -77,6 +77,7 @@ function configToProvisionTemplateFlow(config: WorkflowConfig): TemplateFlow { return { nodes, edges, + user_params: {}, }; } @@ -95,12 +96,13 @@ function ingestConfigToTemplateNodes( { id: ingestPipelineName, type: WORKFLOW_STEP_TYPE.CREATE_INGEST_PIPELINE_STEP_TYPE, + previous_node_inputs: {}, user_inputs: { pipeline_id: ingestPipelineName, - configurations: { + configurations: JSON.stringify({ description: 'An ingest pipeline', processors: ingestProcessors, - }, + } as IngestPipelineConfig), }, } as CreateIngestPipelineNode, ] @@ -125,12 +127,13 @@ function searchConfigToTemplateNodes( { id: searchPipelineName, type: WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE, + previous_node_inputs: {}, user_inputs: { pipeline_id: searchPipelineName, - configurations: { + configurations: JSON.stringify({ request_processors: searchRequestProcessors, response_processors: searchResponseProcessors, - }, + } as SearchPipelineConfig), }, } as CreateSearchPipelineNode, ] @@ -331,10 +334,10 @@ function indexConfigToTemplateNode( previous_node_inputs: finalPreviousNodeInputs, user_inputs: { index_name: indexConfig.name.value as string, - configurations: { + configurations: JSON.stringify({ settings: finalSettings, mappings: finalMappings, - }, + }), }, }; } diff --git a/public/utils/config_to_workspace_utils.ts b/public/utils/config_to_workspace_utils.ts index cb5aa459..1aaa6ea2 100644 --- a/public/utils/config_to_workspace_utils.ts +++ b/public/utils/config_to_workspace_utils.ts @@ -21,7 +21,7 @@ import { } from '../../common'; import { Document, - KnnIndexer, + BaseIndexer, MLTransformer, BaseTransformer, Query, @@ -118,7 +118,7 @@ function ingestConfigToWorkspaceFlow( x: parentNode.style.width - (NODE_WIDTH + NODE_SPACING), y: NODE_HEIGHT_Y, }, - data: initComponentData(new KnnIndexer().toObj(), indexNodeId), + data: initComponentData(new BaseIndexer().toObj(), indexNodeId), type: NODE_CATEGORY.CUSTOM, parentNode: parentNode.id, extent: 'parent', @@ -233,7 +233,7 @@ function searchConfigToWorkspaceFlow( (enrichResponseWorkspaceFlow.nodes.length + 2), y: NODE_HEIGHT_Y, }, - data: initComponentData(new KnnIndexer().toObj(), indexNodeId), + data: initComponentData(new BaseIndexer().toObj(), indexNodeId), type: NODE_CATEGORY.CUSTOM, parentNode: parentNode.id, extent: 'parent', diff --git a/public/utils/form_to_pipeline_utils.ts b/public/utils/form_to_pipeline_utils.ts index 89af59c0..36ff5981 100644 --- a/public/utils/form_to_pipeline_utils.ts +++ b/public/utils/form_to_pipeline_utils.ts @@ -30,62 +30,65 @@ export function formikToPartialPipeline( includeCurProcessor: boolean, context: PROCESSOR_CONTEXT ): IngestPipelineConfig | SearchPipelineConfig | undefined { - const uiConfig = formikToUiConfig(values, existingConfig); - switch (context) { - // Generating ingest pipeline: just fetch existing ingest processors and - // check if there are preceding ones - case PROCESSOR_CONTEXT.INGEST: { - const precedingProcessors = getPrecedingProcessors( - uiConfig.ingest.enrich.processors, - curProcessorId, - includeCurProcessor - ); - return !isEmpty(precedingProcessors) - ? ({ - processors: processorConfigsToTemplateProcessors( - precedingProcessors - ), - } as IngestPipelineConfig) - : undefined; - } - // Generating search pipeline (request): just fetch existing search request - // processors and check if there are preceding ones - case PROCESSOR_CONTEXT.SEARCH_REQUEST: { - const precedingProcessors = getPrecedingProcessors( - uiConfig.search.enrichRequest.processors, - curProcessorId, - includeCurProcessor - ); - return !isEmpty(precedingProcessors) - ? ({ - request_processors: processorConfigsToTemplateProcessors( - precedingProcessors - ), - } as SearchPipelineConfig) - : undefined; - } - // Generating search pipeline (response): fetch existing search response - // processors and check if there are preceding ones. Also add on any - // existing search request processors - case PROCESSOR_CONTEXT.SEARCH_RESPONSE: { - const requestProcessors = uiConfig.search.enrichRequest.processors; - const precedingProcessors = getPrecedingProcessors( - uiConfig.search.enrichResponse.processors, - curProcessorId, - includeCurProcessor - ); - return !isEmpty(precedingProcessors) || !isEmpty(requestProcessors) - ? ({ - request_processors: processorConfigsToTemplateProcessors( - requestProcessors - ), - response_processors: processorConfigsToTemplateProcessors( - precedingProcessors - ), - } as SearchPipelineConfig) - : undefined; + if (values?.ingest && values?.search) { + const uiConfig = formikToUiConfig(values, existingConfig); + switch (context) { + // Generating ingest pipeline: just fetch existing ingest processors and + // check if there are preceding ones + case PROCESSOR_CONTEXT.INGEST: { + const precedingProcessors = getPrecedingProcessors( + uiConfig.ingest.enrich.processors, + curProcessorId, + includeCurProcessor + ); + return !isEmpty(precedingProcessors) + ? ({ + processors: processorConfigsToTemplateProcessors( + precedingProcessors + ), + } as IngestPipelineConfig) + : undefined; + } + // Generating search pipeline (request): just fetch existing search request + // processors and check if there are preceding ones + case PROCESSOR_CONTEXT.SEARCH_REQUEST: { + const precedingProcessors = getPrecedingProcessors( + uiConfig.search.enrichRequest.processors, + curProcessorId, + includeCurProcessor + ); + return !isEmpty(precedingProcessors) + ? ({ + request_processors: processorConfigsToTemplateProcessors( + precedingProcessors + ), + } as SearchPipelineConfig) + : undefined; + } + // Generating search pipeline (response): fetch existing search response + // processors and check if there are preceding ones. Also add on any + // existing search request processors + case PROCESSOR_CONTEXT.SEARCH_RESPONSE: { + const requestProcessors = uiConfig.search.enrichRequest.processors; + const precedingProcessors = getPrecedingProcessors( + uiConfig.search.enrichResponse.processors, + curProcessorId, + includeCurProcessor + ); + return !isEmpty(precedingProcessors) || !isEmpty(requestProcessors) + ? ({ + request_processors: processorConfigsToTemplateProcessors( + requestProcessors + ), + response_processors: processorConfigsToTemplateProcessors( + precedingProcessors + ), + } as SearchPipelineConfig) + : undefined; + } } } + return undefined; } function getPrecedingProcessors(