Skip to content

Commit

Permalink
Support query execution; support index settings/mappings; support cre…
Browse files Browse the repository at this point in the history
…ate/edit states for ingest (#186)

Signed-off-by: Tyler Ohlsen <[email protected]>
  • Loading branch information
ohltyler committed Jun 19, 2024
1 parent 43b89da commit d6a1805
Show file tree
Hide file tree
Showing 17 changed files with 426 additions and 101 deletions.
5 changes: 4 additions & 1 deletion common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,13 @@ export type ProcessorsConfig = {

export type IndexConfig = {
name: IConfigField;
mappings: IConfigField;
settings: IConfigField;
};

export type IngestConfig = {
source: IConfig;
enabled: boolean;
source: {};
enrich: ProcessorsConfig;
index: IndexConfig;
};
Expand Down
14 changes: 12 additions & 2 deletions public/pages/workflow_detail/resizable_workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import { APP_PATH, uiConfigToFormik, uiConfigToSchema } from '../../utils';
import { AppState, setDirty, useAppDispatch } from '../../store';
import { WorkflowInputs } from './workflow_inputs';
import { Workspace } from './workspace';
import { Tools } from './tools';

// styling
import './workspace/workspace-styles.scss';
import '../../global-styles.scss';
import { Tools } from './tools';

interface ResizableWorkspaceProps {
workflow?: Workflow;
Expand Down Expand Up @@ -55,6 +55,9 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
const [formValues, setFormValues] = useState<WorkflowFormValues>({});
const [formSchema, setFormSchema] = useState<WorkflowSchema>(yup.object({}));

// ingest state
const [ingestDocs, setIngestDocs] = useState<string>('');

// Temp UI config state. For persisting changes to the UI config that may
// not be saved in the backend (e.g., adding / removing an ingest processor)
const [uiConfig, setUiConfig] = useState<WorkflowConfig | undefined>(
Expand All @@ -78,6 +81,9 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
// ingest state
const [ingestResponse, setIngestResponse] = useState<string>('');

// query state
const [queryResponse, setQueryResponse] = useState<string>('');

// Tools side panel state
const [isToolsPanelOpen, setIsToolsPanelOpen] = useState<boolean>(true);
const collapseFnVertical = useRef(
Expand Down Expand Up @@ -117,7 +123,7 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
// Initialize the form state based on the current UI config
useEffect(() => {
if (uiConfig) {
const initFormValues = uiConfigToFormik(uiConfig);
const initFormValues = uiConfigToFormik(uiConfig, ingestDocs);
const initFormSchema = uiConfigToSchema(uiConfig);
setFormValues(initFormValues);
setFormSchema(initFormSchema);
Expand Down Expand Up @@ -178,6 +184,9 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
uiConfig={uiConfig}
setUiConfig={setUiConfig}
setIngestResponse={setIngestResponse}
setQueryResponse={setQueryResponse}
ingestDocs={ingestDocs}
setIngestDocs={setIngestDocs}
/>
</EuiResizablePanel>
<EuiResizableButton />
Expand Down Expand Up @@ -250,6 +259,7 @@ export function ResizableWorkspace(props: ResizableWorkspaceProps) {
<Tools
workflow={workflow}
ingestResponse={ingestResponse}
queryResponse={queryResponse}
/>
</EuiResizablePanel>
</>
Expand Down
36 changes: 32 additions & 4 deletions public/pages/workflow_detail/tools/tools.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { isEmpty } from 'lodash';
import {
EuiCodeEditor,
EuiFlexGroup,
Expand All @@ -20,6 +21,7 @@ import { Resources } from './resources';
interface ToolsProps {
workflow?: Workflow;
ingestResponse: string;
queryResponse: string;
}

enum TAB_ID {
Expand Down Expand Up @@ -58,6 +60,20 @@ const inputTabs = [
export function Tools(props: ToolsProps) {
const [selectedTabId, setSelectedTabId] = useState<string>(TAB_ID.INGEST);

// auto-navigate to ingest response if a populated value has been set, indicating ingest has been ran
useEffect(() => {
if (!isEmpty(props.ingestResponse)) {
setSelectedTabId(TAB_ID.INGEST);
}
}, [props.ingestResponse]);

// auto-navigate to query response if a populated value has been set, indicating search has been ran
useEffect(() => {
if (!isEmpty(props.queryResponse)) {
setSelectedTabId(TAB_ID.QUERY);
}
}, [props.queryResponse]);

return (
<EuiPanel paddingSize="m" grow={true} style={{ height: '100%' }}>
<EuiFlexGroup
Expand Down Expand Up @@ -94,14 +110,14 @@ export function Tools(props: ToolsProps) {
{selectedTabId === TAB_ID.INGEST && (
// TODO: known issue with the editor where resizing the resizablecontainer does not
// trigger vertical scroll updates. Updating the window, or reloading the component
// by switching tabs etc. will refresh it correctly.
// by switching tabs etc. will refresh it correctly. This applies to the code editor
// components in both ingest and query below.
<EuiCodeEditor
mode="json"
theme="textmate"
width="100%"
height="100%"
value={props.ingestResponse}
onChange={(input) => {}}
readOnly={true}
setOptions={{
fontSize: '12px',
Expand All @@ -111,7 +127,19 @@ export function Tools(props: ToolsProps) {
/>
)}
{selectedTabId === TAB_ID.QUERY && (
<EuiText>TODO: Run queries placeholder</EuiText>
<EuiCodeEditor
mode="json"
theme="textmate"
width="100%"
height="100%"
value={props.queryResponse}
readOnly={true}
setOptions={{
fontSize: '12px',
autoScrollEditorIntoView: true,
}}
tabSize={2}
/>
)}
{selectedTabId === TAB_ID.ERRORS && (
<EuiText>TODO: View errors placeholder</EuiText>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import {
EuiAccordion,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
} from '@elastic/eui';
import { IConfigField, WorkflowConfig } from '../../../../../common';
import { JsonField } from '../input_fields';

interface AdvancedSettingsProps {
uiConfig: WorkflowConfig;
onFormChange: () => void;
}

/**
* Input component for configuring ingest-side advanced settings
*/
export function AdvancedSettings(props: AdvancedSettingsProps) {
return (
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<EuiAccordion id="advancedSettings" buttonContent="Advanced settings">
<EuiSpacer size="s" />
<EuiFlexGroup direction="column">
<EuiFlexItem>
<JsonField
field={props.uiConfig.ingest.index?.mappings as IConfigField}
fieldPath={'ingest.index.mappings'}
onFormChange={props.onFormChange}
/>
</EuiFlexItem>
<EuiFlexItem>
<JsonField
field={props.uiConfig.ingest.index?.settings as IConfigField}
fieldPath={'ingest.index.settings'}
onFormChange={props.onFormChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiAccordion>
</EuiFlexItem>
</EuiFlexGroup>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import { IConfigField, WorkflowConfig } from '../../../../../common';
import { TextField } from '../input_fields';
import { AdvancedSettings } from './advanced_settings';

interface IngestDataProps {
uiConfig: WorkflowConfig;
Expand All @@ -31,6 +32,12 @@ export function IngestData(props: IngestDataProps) {
onFormChange={props.onFormChange}
/>
</EuiFlexItem>
<EuiFlexItem>
<AdvancedSettings
uiConfig={props.uiConfig}
onFormChange={props.onFormChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import { WorkflowConfig } from '../../../../../common';

interface IngestInputsProps {
onFormChange: () => void;
ingestDocs: {}[];
setIngestDocs: (docs: {}[]) => void;
setIngestDocs: (docs: string) => void;
uiConfig: WorkflowConfig;
setUiConfig: (uiConfig: WorkflowConfig) => void;
}
Expand All @@ -22,12 +21,14 @@ 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}>
<SourceData
ingestDocs={props.ingestDocs}
setIngestDocs={props.setIngestDocs}
onFormChange={props.onFormChange}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,30 @@
* SPDX-License-Identifier: Apache-2.0
*/

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

interface SourceDataProps {
ingestDocs: {}[];
setIngestDocs: (docs: {}[]) => void;
setIngestDocs: (docs: string) => void;
onFormChange: () => void;
}

/**
* Input component for configuring the source data for ingest.
*/
export function SourceData(props: SourceDataProps) {
const [jsonStr, setJsonStr] = useState<string>('{}');
const { values } = useFormikContext<WorkspaceFormValues>();

// Hook to listen when the docs form value changes.
// Try to set the ingestDocs if possible
useEffect(() => {
try {
const json = JSON.parse(jsonStr);
props.setIngestDocs([json]);
} catch (e) {
props.setIngestDocs([]);
if (values?.ingest?.docs) {
props.setIngestDocs(values.ingest.docs);
}
}, [jsonStr]);
}, [values?.ingest?.docs]);

return (
<EuiFlexGroup direction="column">
Expand All @@ -39,21 +36,18 @@ export function SourceData(props: SourceDataProps) {
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiCodeEditor
mode="json"
theme="textmate"
width="100%"
height="25vh"
value={jsonStr}
onChange={(input) => {
setJsonStr(input);
}}
readOnly={false}
setOptions={{
fontSize: '14px',
}}
aria-label="Code Editor"
tabSize={2}
<JsonField
// We want to integrate docs into the form, but not persist in the config.
// So, we create the ConfigField explicitly inline, instead of pulling
// from the config.
field={
{
label: 'Upload JSON documents',
} as IConfigField
}
fieldPath={'ingest.docs'}
onFormChange={props.onFormChange}
editorHeight="25vh"
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Loading

0 comments on commit d6a1805

Please sign in to comment.