Skip to content

Commit

Permalink
Finalize edit flow for ingest inputs (#202)
Browse files Browse the repository at this point in the history
Signed-off-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
ohltyler authored Jul 9, 2024
1 parent 2ef7e05 commit 73a2edd
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,22 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useEffect } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import {
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiSuperSelect,
EuiSuperSelectOption,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { useFormikContext } from 'formik';
import { IConfigField, WorkspaceFormValues } from '../../../../../common';
import { JsonField } from '../input_fields';
import { AppState, catIndices, useAppDispatch } from '../../../../store';

interface ConfigureSearchRequestProps {
setQuery: (query: string) => void;
Expand All @@ -18,7 +29,20 @@ interface ConfigureSearchRequestProps {
* Input component for configuring a search request
*/
export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) {
const dispatch = useAppDispatch();

// Form state
const { values } = useFormikContext<WorkspaceFormValues>();
const indexName = values.ingest.index.name;
const ingestEnabled = values.ingest.enabled;

// All indices state
const indices = useSelector((state: AppState) => state.opensearch.indices);

// Selected index state
const [selectedIndex, setSelectedIndex] = useState<string | undefined>(
undefined
);

// Hook to listen when the query form value changes.
// Try to set the query request if possible
Expand All @@ -28,13 +52,44 @@ export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) {
}
}, [values?.search?.request]);

// Initialization hook to fetch available indices (if applicable)
useEffect(() => {
if (!ingestEnabled) {
// Fetch all indices besides system indices
dispatch(catIndices('*,-.*'));
}
}, []);

return (
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h2>Configure query</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow label="Retrieval index">
{ingestEnabled ? (
<EuiFieldText value={indexName} readOnly={true} />
) : (
<EuiSuperSelect
options={Object.values(indices).map(
(option) =>
({
value: option.name,
inputDisplay: <EuiText>{option.name}</EuiText>,
disabled: false,
} as EuiSuperSelectOption<string>)
)}
valueOfSelected={selectedIndex}
onChange={(option) => {
setSelectedIndex(option);
}}
isInvalid={selectedIndex !== undefined}
/>
)}
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<JsonField
// We want to integrate query into the form, but not persist in the config.
Expand Down
129 changes: 96 additions & 33 deletions public/pages/workflow_detail/workflow_inputs/workflow_inputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useFormikContext } from 'formik';
import { isEmpty } from 'lodash';
import {
Expand All @@ -12,7 +13,13 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
EuiIcon,
EuiLoadingSpinner,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiPanel,
EuiSpacer,
EuiStepsHorizontal,
Expand All @@ -27,10 +34,12 @@ import {
import { IngestInputs } from './ingest_inputs';
import { SearchInputs } from './search_inputs';
import {
AppState,
deprovisionWorkflow,
getWorkflow,
ingest,
provisionWorkflow,
removeDirty,
searchIndex,
updateWorkflow,
useAppDispatch,
Expand All @@ -41,6 +50,7 @@ import {
reduceToTemplate,
configToTemplateFlows,
hasProvisionedIngestResources,
hasProvisionedSearchResources,
} from '../../../utils';
import { BooleanField } from './input_fields';
import { ExportOptions } from './export_options';
Expand Down Expand Up @@ -78,16 +88,23 @@ enum INGEST_OPTION {
*/

export function WorkflowInputs(props: WorkflowInputsProps) {
const { submitForm, validateForm, values } = useFormikContext<
const { submitForm, validateForm, setFieldValue, values } = useFormikContext<
WorkflowFormValues
>();
const dispatch = useAppDispatch();

// Overall workspace state
const { isDirty } = useSelector((state: AppState) => state.workspace);

// selected step state
const [selectedStep, setSelectedStep] = useState<STEP>(STEP.INGEST);

// ingest state
// provisioned resources states
const [ingestProvisioned, setIngestProvisioned] = useState<boolean>(false);
const [searchProvisioned, setSearchProvisioned] = useState<boolean>(false);

// confirm modal state
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);

// maintain global states
const onIngest = selectedStep === STEP.INGEST;
Expand All @@ -100,6 +117,7 @@ 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
Expand Down Expand Up @@ -203,6 +221,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
.unwrap()
.then(async (resp) => {
props.setIngestResponse(JSON.stringify(resp, undefined, 2));
dispatch(removeDirty());
})
.catch((error: any) => {
getCore().notifications.toasts.addDanger(error);
Expand Down Expand Up @@ -235,6 +254,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
.then(async (resp) => {
const hits = resp.hits.hits;
props.setQueryResponse(JSON.stringify(hits, undefined, 2));
dispatch(removeDirty());
})
.catch((error: any) => {
getCore().notifications.toasts.addDanger(error);
Expand Down Expand Up @@ -286,7 +306,50 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
},
]}
/>
{onIngest && (
{isModalOpen && (
<EuiModal onClose={() => setIsModalOpen(false)}>
<EuiModalHeader>
<EuiModalHeaderTitle>
<p>{`Delete resources for workflow ${props.workflow.name}?`}</p>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiText>
The resources for this workflow will be permanently deleted.
This action cannot be undone.
</EuiText>
</EuiModalBody>
<EuiModalFooter>
<EuiButtonEmpty onClick={() => setIsModalOpen(false)}>
{' '}
Cancel
</EuiButtonEmpty>
<EuiButton
onClick={async () => {
// @ts-ignore
await dispatch(deprovisionWorkflow(props.workflow.id))
.unwrap()
.then(async (result) => {
setFieldValue('ingest.enabled', false);
// @ts-ignore
await dispatch(getWorkflow(props.workflow.id));
})
.catch((error: any) => {
getCore().notifications.toasts.addDanger(error);
})
.finally(() => {
setIsModalOpen(false);
});
}}
fill={true}
color="danger"
>
Delete resources
</EuiButton>
</EuiModalFooter>
</EuiModal>
)}
{onIngestAndUnprovisioned && (
<>
<EuiSpacer size="m" />
<BooleanField
Expand Down Expand Up @@ -327,13 +390,31 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
<EuiFlexItem grow={false}>
<EuiTitle>
<h2>
{onIngestAndUnprovisioned
? 'Define ingest pipeline'
: onIngestAndProvisioned
? 'Edit ingest pipeline'
: onSearch
? 'Define search pipeline'
: 'Export project as'}
{onIngestAndUnprovisioned ? (
'Define ingest pipeline'
) : onIngestAndProvisioned ? (
<EuiFlexGroup
direction="row"
justifyContent="spaceBetween"
>
<EuiFlexItem grow={false}>
Edit ingest pipeline
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
color="danger"
onClick={() => setIsModalOpen(true)}
>
<EuiIcon type="trash" />
{` `}Delete resources
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
) : onSearch ? (
'Define search pipeline'
) : (
'Export project as'
)}
</h2>
</EuiTitle>
</EuiFlexItem>
Expand Down Expand Up @@ -380,34 +461,15 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
{`Search pipeline >`}
</EuiButton>
</EuiFlexItem>
) : onIngestAndUnprovisioned ? (
<>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
onClick={() => setSelectedStep(STEP.SEARCH)}
>
Skip
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
fill={true}
onClick={() => {
validateAndRunIngestion();
}}
>
Run ingestion
</EuiButton>
</EuiFlexItem>
</>
) : onIngestAndProvisioned ? (
) : onIngest ? (
<>
<EuiFlexItem grow={false}>
<EuiButton
fill={false}
onClick={() => {
validateAndRunIngestion();
}}
disabled={ingestProvisioned && !isDirty}
>
Run ingestion
</EuiButton>
Expand All @@ -416,6 +478,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
<EuiButton
fill={true}
onClick={() => setSelectedStep(STEP.SEARCH)}
disabled={!ingestProvisioned || isDirty}
>
{`Search pipeline >`}
</EuiButton>
Expand All @@ -432,7 +495,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
disabled={false}
disabled={searchProvisioned && !isDirty}
fill={false}
onClick={() => {
validateAndRunQuery();
Expand All @@ -443,7 +506,7 @@ export function WorkflowInputs(props: WorkflowInputsProps) {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
disabled={false}
disabled={!searchProvisioned || isDirty}
fill={false}
onClick={() => {
setSelectedStep(STEP.EXPORT);
Expand Down
14 changes: 14 additions & 0 deletions public/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,17 @@ export function hasProvisionedIngestResources(
});
return result;
}

export function hasProvisionedSearchResources(
workflow: Workflow | undefined
): boolean {
let result = false;
workflow?.resourcesCreated?.some((resource) => {
if (
resource.stepType === WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE
) {
result = true;
}
});
return result;
}

0 comments on commit 73a2edd

Please sign in to comment.