Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.x] Add invalid workflow state; support JSON doc uploads #201

Merged
merged 1 commit into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 42 additions & 16 deletions public/pages/workflow_detail/resizable_workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,28 @@

import React, { useRef, useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { Form, Formik } from 'formik';
import * as yup from 'yup';
import { EuiFlexGroup, EuiFlexItem, EuiResizableContainer } from '@elastic/eui';
import { getCore } from '../../services';
import {
EuiCodeBlock,
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiResizableContainer,
EuiText,
} from '@elastic/eui';

import {
Workflow,
WorkflowConfig,
WorkflowFormValues,
WorkflowSchema,
} from '../../../common';
import { APP_PATH, uiConfigToFormik, uiConfigToSchema } from '../../utils';
import {
reduceToTemplate,
uiConfigToFormik,
uiConfigToSchema,
} from '../../utils';
import { AppState, setDirty, useAppDispatch } from '../../store';
import { WorkflowInputs } from './workflow_inputs';
import { Workspace } from './workspace';
Expand All @@ -40,7 +49,6 @@ const TOOLS_PANEL_ID = 'tools_panel_id';
*/
export function ResizableWorkspace(props: ResizableWorkspaceProps) {
const dispatch = useAppDispatch();
const history = useHistory();

// Overall workspace state
const { isDirty } = useSelector((state: AppState) => state.workspace);
Expand Down Expand Up @@ -97,20 +105,15 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
setIsToolsPanelOpen(!isToolsPanelOpen);
};

// Hook to update some default values for the workflow, if applicable.
// We need to handle different scenarios:
// 1. Rendering backend-only-created workflow / an existing workflow with no ui_metadata.
// In this case, we revert to the home page with a warn toast that we don't support it, for now.
// 2. Rendering a created workflow with ui_metadata.
// In these cases, just render what is persisted, no action needed.
// workflow state
const [isValidWorkflow, setIsValidWorkflow] = useState<boolean>(true);

// Hook to check if the workflow is valid or not
useEffect(() => {
const missingUiFlow =
props.workflow && !props.workflow?.ui_metadata?.config;
if (missingUiFlow) {
history.replace(APP_PATH.WORKFLOWS);
getCore().notifications.toasts.addWarning(
`There is no ui_metadata for workflow: ${props.workflow?.name}`
);
setIsValidWorkflow(false);
} else {
setWorkflow(props.workflow);
}
Expand Down Expand Up @@ -143,7 +146,7 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
}
}

return (
return isValidWorkflow ? (
<Formik
enableReinitialize={true}
initialValues={formValues}
Expand Down Expand Up @@ -282,5 +285,28 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
</Form>
)}
</Formik>
) : (
<>
<EuiEmptyPrompt
iconType={'cross'}
title={<h2>Unable to view workflow details</h2>}
titleSize="s"
body={
<>
<EuiText>
Only valid workflows created from this OpenSearch Dashboards
application are editable and viewable.
</EuiText>
</>
}
/>
<EuiCodeBlock language="json" fontSize="m" isCopyable={false}>
{JSON.stringify(
reduceToTemplate(props.workflow as Workflow),
undefined,
2
)}
</EuiCodeBlock>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ interface IngestInputsProps {
* The base component containing all of the ingest-related inputs
*/
export function IngestInputs(props: IngestInputsProps) {
// TODO: add some toggle to enable/disable ingest altogether.
// UX not finalized on where that will live currently
return (
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@

import React, { useEffect } from 'react';
import { useFormikContext } from 'formik';
import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import {
EuiFilePicker,
EuiFlexGroup,
EuiFlexItem,
EuiTitle,
} from '@elastic/eui';
import { JsonField } from '../input_fields';
import { IConfigField, WorkspaceFormValues } from '../../../../../common';

Expand All @@ -18,7 +23,15 @@ interface SourceDataProps {
* Input component for configuring the source data for ingest.
*/
export function SourceData(props: SourceDataProps) {
const { values } = useFormikContext<WorkspaceFormValues>();
const { values, setFieldValue } = useFormikContext<WorkspaceFormValues>();

// files state. when a file is read, update the form value.
const fileReader = new FileReader();
fileReader.onload = (e) => {
if (e.target) {
setFieldValue('ingest.docs', e.target.result);
}
};

// Hook to listen when the docs form value changes.
// Try to set the ingestDocs if possible
Expand All @@ -35,6 +48,19 @@ export function SourceData(props: SourceDataProps) {
<h2>Source data</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFilePicker
accept="application/json"
multiple={false}
initialPromptText="Select a JSON file containing documents"
onChange={(files) => {
if (files && files.length > 0) {
fileReader.readAsText(files[0]);
}
}}
display="default"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<JsonField
// We want to integrate docs into the form, but not persist in the config.
Expand Down
Loading