diff --git a/public/general_components/processors_title.tsx b/public/general_components/processors_title.tsx index fd718e5a..3e97ad61 100644 --- a/public/general_components/processors_title.tsx +++ b/public/general_components/processors_title.tsx @@ -9,6 +9,7 @@ import { EuiFlexItem, EuiText } from '@elastic/eui'; interface ProcessorsTitleProps { title: string; processorCount: number; + optional: boolean; } /** @@ -24,11 +25,13 @@ export function ProcessorsTitle(props: ProcessorsTitleProps) { <>

{`${props.title} (${props.processorCount}) -`}

+ >{`${props.title} (${props.processorCount})`}   -

- optional -

+ {props.optional && ( +

+ - optional +

+ )} diff --git a/public/pages/workflow_detail/workflow_inputs/ingest_inputs/enrich_data.tsx b/public/pages/workflow_detail/workflow_inputs/ingest_inputs/enrich_data.tsx index 4cb03de7..ae184a9e 100644 --- a/public/pages/workflow_detail/workflow_inputs/ingest_inputs/enrich_data.tsx +++ b/public/pages/workflow_detail/workflow_inputs/ingest_inputs/enrich_data.tsx @@ -23,6 +23,7 @@ export function EnrichData(props: EnrichDataProps) { (false); + // query params state, if applicable. Users cannot run preview if there are query parameters + // and the user is configuring something in a search context (search request/response) + const [queryParams, setQueryParams] = useState([]); + useEffect(() => { + if (props.context !== PROCESSOR_CONTEXT.INGEST && query !== undefined) { + const placeholders = getPlaceholdersFromQuery(query); + if ( + !containsSameValues( + placeholders, + queryParams.map((queryParam) => queryParam.name) + ) + ) { + setQueryParams( + placeholders.map((placeholder) => ({ + name: placeholder, + type: 'Text', + value: '', + })) + ); + } + } + }, [query]); + // hook to re-generate the transform when any inputs to the transform are updated useEffect(() => { const tempExpressionAsInputMap = [ @@ -330,19 +359,21 @@ export function ConfigureExpressionModal(props: ConfigureExpressionModalProps) { - + Preview - + { setIsFetching(true); @@ -426,7 +457,9 @@ export function ConfigureExpressionModal(props: ConfigureExpressionModalProps) { // this if check as an extra layer of checking, and if mechanism for gating // this is changed in the future. if (curSearchPipeline === undefined) { - setSourceInput(values.search.request); + setSourceInput( + injectParameters(queryParams, query) + ); } setIsFetching(false); break; @@ -448,7 +481,7 @@ export function ConfigureExpressionModal(props: ConfigureExpressionModalProps) { index: values.search.index.name, body: JSON.stringify({ ...JSON.parse( - values.search.request as string + injectParameters(queryParams, query) ), search_pipeline: curSearchPipeline || {}, @@ -490,6 +523,15 @@ export function ConfigureExpressionModal(props: ConfigureExpressionModalProps) { + {props.context !== PROCESSOR_CONTEXT.INGEST && + !isEmpty(queryParams) && ( + + + + )} Source data diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_multi_expression_modal.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_multi_expression_modal.tsx index c35c86df..a640a1ed 100644 --- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_multi_expression_modal.tsx +++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_multi_expression_modal.tsx @@ -33,6 +33,7 @@ import { MultiExpressionFormValues, OutputMapEntry, PROCESSOR_CONTEXT, + QueryParam, SearchHit, SearchPipelineConfig, SimulateIngestPipelineResponse, @@ -41,9 +42,13 @@ import { WorkflowFormValues, } from '../../../../../../../common'; import { + containsEmptyValues, + containsSameValues, formikToPartialPipeline, generateTransform, getDataSourceId, + getPlaceholdersFromQuery, + injectParameters, prepareDocsForSimulate, unwrapTransformedDocs, } from '../../../../../../utils'; @@ -54,6 +59,7 @@ import { useAppDispatch, } from '../../../../../../store'; import { getCore } from '../../../../../../services'; +import { QueryParamsList } from '../../../../../../general_components'; interface ConfigureMultiExpressionModalProps { uiConfig: WorkflowConfig; @@ -144,6 +150,29 @@ export function ConfigureMultiExpressionModal( // fetching input data state const [isFetching, setIsFetching] = useState(false); + // query params state, if applicable. Users cannot run preview if there are query parameters + // and the user is configuring something in a search context (search request/response) + const [queryParams, setQueryParams] = useState([]); + useEffect(() => { + if (props.context !== PROCESSOR_CONTEXT.INGEST && query !== undefined) { + const placeholders = getPlaceholdersFromQuery(query); + if ( + !containsSameValues( + placeholders, + queryParams.map((queryParam) => queryParam.name) + ) + ) { + setQueryParams( + placeholders.map((placeholder) => ({ + name: placeholder, + type: 'Text', + value: '', + })) + ); + } + } + }, [query]); + // hook to re-generate the transform when any inputs to the transform are updated useEffect(() => { const tempExpressionsAsOutputMap = tempExpressions.map( @@ -356,19 +385,21 @@ export function ConfigureMultiExpressionModal( - + Preview - + { setIsFetching(true); @@ -482,7 +513,7 @@ export function ConfigureMultiExpressionModal( index: values.search.index.name, body: JSON.stringify({ ...JSON.parse( - values.search.request as string + injectParameters(queryParams, query) ), search_pipeline: curSearchPipeline || {}, @@ -522,6 +553,15 @@ export function ConfigureMultiExpressionModal( + {props.context !== PROCESSOR_CONTEXT.INGEST && + !isEmpty(queryParams) && ( + + + + )} Source data diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_template_modal.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_template_modal.tsx index ce3bb2cc..ffe7910c 100644 --- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_template_modal.tsx +++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/modals/configure_template_modal.tsx @@ -43,13 +43,18 @@ import { TRANSFORM_CONTEXT, WorkflowConfig, WorkflowFormValues, + QueryParam, } from '../../../../../../../common'; import { + containsEmptyValues, + containsSameValues, formikToPartialPipeline, generateArrayTransform, generateTransform, getDataSourceId, getInitialValue, + getPlaceholdersFromQuery, + injectParameters, prepareDocsForSimulate, unwrapTransformedDocs, } from '../../../../../../utils'; @@ -60,6 +65,7 @@ import { useAppDispatch, } from '../../../../../../store'; import { getCore } from '../../../../../../services'; +import { QueryParamsList } from '../../../../../../general_components'; interface ConfigureTemplateModalProps { uiConfig: WorkflowConfig; @@ -168,6 +174,29 @@ export function ConfigureTemplateModal(props: ConfigureTemplateModalProps) { // fetching input data state const [isFetching, setIsFetching] = useState(false); + // query params state, if applicable. Users cannot run preview if there are query parameters + // and the user is configuring something in a search context (search request/response) + const [queryParams, setQueryParams] = useState([]); + useEffect(() => { + if (props.context !== PROCESSOR_CONTEXT.INGEST && query !== undefined) { + const placeholders = getPlaceholdersFromQuery(query); + if ( + !containsSameValues( + placeholders, + queryParams.map((queryParam) => queryParam.name) + ) + ) { + setQueryParams( + placeholders.map((placeholder) => ({ + name: placeholder, + type: 'Text', + value: '', + })) + ); + } + } + }, [query]); + // hook to re-generate the transform when any inputs to the transform are updated useEffect(() => { const nestedVarsAsInputMap = tempNestedVars?.map((expressionVar) => { @@ -546,7 +575,9 @@ export function ConfigureTemplateModal(props: ConfigureTemplateModalProps) { disabled={ onIngestAndNoDocs || onSearchAndNoQuery || - !props.isDataFetchingAvailable + !props.isDataFetchingAvailable || + (props.context !== PROCESSOR_CONTEXT.INGEST && + containsEmptyValues(queryParams)) } onClick={async () => { setIsFetching(true); @@ -630,7 +661,9 @@ export function ConfigureTemplateModal(props: ConfigureTemplateModalProps) { // this if check as an extra layer of checking, and if mechanism for gating // this is changed in the future. if (curSearchPipeline === undefined) { - setSourceInput(values.search.request); + setSourceInput( + injectParameters(queryParams, query) + ); } setIsFetching(false); break; @@ -652,7 +685,7 @@ export function ConfigureTemplateModal(props: ConfigureTemplateModalProps) { index: values.search.index.name, body: JSON.stringify({ ...JSON.parse( - values.search.request as string + injectParameters(queryParams, query) ), search_pipeline: curSearchPipeline || {}, @@ -694,6 +727,15 @@ export function ConfigureTemplateModal(props: ConfigureTemplateModalProps) { + {props.context !== PROCESSOR_CONTEXT.INGEST && + !isEmpty(queryParams) && ( + + + + )} Source data diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_inputs.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_inputs.tsx index 5fbdce24..6ec1da14 100644 --- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_inputs.tsx +++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_inputs.tsx @@ -32,6 +32,7 @@ import { EMPTY_INPUT_MAP_ENTRY, WorkflowConfig, getCharacterLimitedString, + ModelInputFormField, } from '../../../../../../common'; import { TextField, SelectWithCustomOptions } from '../../input_fields'; import { AppState, getMappings, useAppDispatch } from '../../../../../store'; @@ -214,8 +215,31 @@ export function ModelInputs(props: ModelInputsProps) { setFieldTouched(inputMapFieldPath, true); } - // Defining constants for the key/value text vars, typically dependent on the different processor contexts. - const keyOptions = parseModelInputs(modelInterface); + // The options for keys can change. We update what options are available, based + // on if there is a model interface found, and additionally filter out any + // options that are already being used in the input map, to discourage duplicate keys. + const [keyOptions, setKeyOptions] = useState([]); + useEffect(() => { + setKeyOptions(parseModelInputs(modelInterface)); + }, [modelInterface]); + useEffect(() => { + if (modelInterface !== undefined) { + const modelInputs = parseModelInputs(modelInterface); + if (getIn(values, inputMapFieldPath) !== undefined) { + const existingKeys = getIn(values, inputMapFieldPath).map( + (inputMapEntry: InputMapEntry) => inputMapEntry.key + ) as string[]; + setKeyOptions( + modelInputs.filter( + (modelInput) => !existingKeys.includes(modelInput.label) + ) + ); + } else { + setKeyOptions(modelInputs); + } + } + }, [getIn(values, inputMapFieldPath), modelInterface]); + const valueOptions = props.context === PROCESSOR_CONTEXT.INGEST ? docFields diff --git a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_outputs.tsx b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_outputs.tsx index 384f371f..0ca94d8d 100644 --- a/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_outputs.tsx +++ b/public/pages/workflow_detail/workflow_inputs/processor_inputs/ml_processor_inputs/model_outputs.tsx @@ -21,6 +21,7 @@ import { OutputMapFormValue, EMPTY_OUTPUT_MAP_ENTRY, ExpressionVar, + ModelOutputFormField, } from '../../../../../../common'; import { SelectWithCustomOptions, TextField } from '../../input_fields'; @@ -136,9 +137,30 @@ export function ModelOutputs(props: ModelOutputsProps) { setFieldTouched(outputMapFieldPath, true); } - const keyOptions = fullResponsePath - ? undefined - : parseModelOutputs(modelInterface, false); + // The options for keys can change. We update what options are available, based + // on if there is a model interface found, what full_response_path is, and additionally filter out any + // options that are already being used in the output map, to discourage duplicate keys. + const [keyOptions, setKeyOptions] = useState([]); + useEffect(() => { + setKeyOptions(parseModelOutputs(modelInterface)); + }, [modelInterface]); + useEffect(() => { + if (modelInterface !== undefined && fullResponsePath === false) { + const modelOutputs = parseModelOutputs(modelInterface); + if (getIn(values, outputMapFieldPath) !== undefined) { + const existingKeys = getIn(values, outputMapFieldPath).map( + (outputMapEntry: OutputMapEntry) => outputMapEntry.key + ) as string[]; + setKeyOptions( + modelOutputs.filter( + (modelOutput) => !existingKeys.includes(modelOutput.label) + ) + ); + } else { + setKeyOptions(modelOutputs); + } + } + }, [getIn(values, outputMapFieldPath), modelInterface, fullResponsePath]); return ( diff --git a/public/pages/workflow_detail/workflow_inputs/search_inputs/enrich_search_request.tsx b/public/pages/workflow_detail/workflow_inputs/search_inputs/enrich_search_request.tsx index 763eb6cf..d2fd3488 100644 --- a/public/pages/workflow_detail/workflow_inputs/search_inputs/enrich_search_request.tsx +++ b/public/pages/workflow_detail/workflow_inputs/search_inputs/enrich_search_request.tsx @@ -25,6 +25,7 @@ export function EnrichSearchRequest(props: EnrichSearchRequestProps) { processorCount={ props.uiConfig.search.enrichRequest.processors?.length || 0 } + optional={false} />