(0);
22 | const hasLabel =
23 | props.input.label !== undefined && !isEmpty(props.input.label);
24 |
25 | useEffect(() => {
26 | setPosition(calculateHandlePosition(ref));
27 | }, [ref]);
28 |
29 | return (
30 |
31 | <>
32 | {hasLabel && (
33 |
34 |
35 | {props.input.label}
36 |
37 |
38 | )}
39 |
44 | // @ts-ignore
45 | isValidConnection(connection, reactFlowInstance)
46 | }
47 | style={
48 | hasLabel
49 | ? {
50 | top: position,
51 | }
52 | : {}
53 | }
54 | />
55 | >
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/public/pages/workflow_detail/workspace/workspace_components/output_handle.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React, { useState, useRef, useEffect } from 'react';
7 | import { Connection, Handle, Position, useReactFlow } from 'reactflow';
8 | import { isEmpty } from 'lodash';
9 | import { EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
10 | import { IComponent, IComponentOutput } from '../../../../../common';
11 | import { calculateHandlePosition, isValidConnection } from './utils';
12 |
13 | interface OutputHandleProps {
14 | data: IComponent;
15 | output: IComponentOutput;
16 | }
17 |
18 | export function OutputHandle(props: OutputHandleProps) {
19 | const ref = useRef(null);
20 | const reactFlowInstance = useReactFlow();
21 | const [position, setPosition] = useState(0);
22 | const hasLabel =
23 | props.output.label !== undefined && !isEmpty(props.output.label);
24 |
25 | useEffect(() => {
26 | setPosition(calculateHandlePosition(ref));
27 | }, [ref]);
28 |
29 | return (
30 |
31 | <>
32 | {hasLabel && (
33 |
34 |
35 | {props.output.label}
36 |
37 |
38 | )}
39 |
44 | // @ts-ignore
45 | isValidConnection(connection, reactFlowInstance)
46 | }
47 | style={
48 | hasLabel
49 | ? {
50 | top: position,
51 | }
52 | : {}
53 | }
54 | />
55 | >
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/public/pages/workflow_detail/workspace/workspace_components/search_group_component.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import { GroupComponent } from './group_component';
8 |
9 | interface SearchGroupComponentProps {
10 | data: { label: string };
11 | }
12 | const SEARCH_COLOR = '#6092C0'; // euiColorVis1: see https://oui.opensearch.org/1.6/#/guidelines/colors
13 |
14 | /**
15 | * A lightweight wrapper on the group component.
16 | * Any specific additions to search can be specified here.
17 | */
18 | export function SearchGroupComponent(props: SearchGroupComponentProps) {
19 | return ;
20 | }
21 |
--------------------------------------------------------------------------------
/public/pages/workflow_detail/workspace/workspace_components/utils.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { Connection, ReactFlowInstance } from 'reactflow';
7 | import { IComponentInput } from '../../../../../common';
8 |
9 | /**
10 | * Collection of utility functions for the workspace component
11 | */
12 |
13 | // Uses DOM elements to calculate where the handle should be placed
14 | // vertically on the ReactFlow component. offsetTop is the offset relative to the
15 | // parent element, and clientHeight is the element height including padding.
16 | // We can combine them to get the exact amount, in pixels.
17 | export function calculateHandlePosition(ref: any): number {
18 | if (ref.current && ref.current.offsetTop && ref.current.clientHeight) {
19 | return ref.current.offsetTop + ref.current.clientHeight / 2;
20 | } else {
21 | return 0;
22 | }
23 | }
24 |
25 | // Validates that connections can only be made when the source and target classes align, and
26 | // that multiple connections to the same target handle are not allowed unless the input configuration
27 | // for that particular component allows for it.
28 | export function isValidConnection(
29 | connection: Connection,
30 | rfInstance: ReactFlowInstance
31 | ): boolean {
32 | const sourceHandle = connection.sourceHandle;
33 | const targetHandle = connection.targetHandle;
34 | const targetNodeId = connection.target;
35 |
36 | // We store the output classes in a pipe-delimited string. Converting back to a list.
37 | const sourceClasses = sourceHandle?.split('|') || [];
38 | const targetClass = targetHandle || '';
39 |
40 | if (sourceClasses?.includes(targetClass)) {
41 | const targetNode = rfInstance.getNode(targetNodeId || '');
42 | if (targetNode) {
43 | const inputConfig = targetNode.data.inputs.find(
44 | (input: IComponentInput) => sourceClasses.includes(input.baseClass)
45 | ) as IComponentInput;
46 | const existingEdge = rfInstance
47 | .getEdges()
48 | .find(
49 | (edge) =>
50 | edge.target === targetNodeId && edge.targetHandle === targetHandle
51 | );
52 | if (existingEdge && inputConfig.acceptMultiple === false) {
53 | return false;
54 | }
55 | }
56 | return true;
57 | } else {
58 | return false;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/public/pages/workflow_detail/workspace/workspace_components/workspace_component.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import { isEmpty } from 'lodash';
8 | import {
9 | EuiFlexGroup,
10 | EuiFlexItem,
11 | EuiCard,
12 | EuiText,
13 | EuiSpacer,
14 | EuiIcon,
15 | IconType,
16 | } from '@elastic/eui';
17 | import {
18 | IComponentData,
19 | IComponentInput,
20 | IComponentOutput,
21 | } from '../../../../../common';
22 | import { InputHandle } from './input_handle';
23 | import { OutputHandle } from './output_handle';
24 |
25 | // styling
26 | import '../../workspace/reactflow-styles.scss';
27 |
28 | interface WorkspaceComponentProps {
29 | data: IComponentData;
30 | }
31 |
32 | /**
33 | * The React component in the drag-and-drop workspace. It will take in the component data passed
34 | * to it from the workspace and render it appropriately (inputs / params / outputs / etc.).
35 | * As users interact with it (input data, add connections), the stored IComponent data will update.
36 | */
37 | export function WorkspaceComponent(props: WorkspaceComponentProps) {
38 | const component = props.data;
39 | // we don't render any component body if no metadata, such as no description, or no defined inputs/outputs.
40 | const hasDescription =
41 | component.description !== undefined && !isEmpty(component.description);
42 | const hasIcon =
43 | component.iconType !== undefined && !isEmpty(component.iconType);
44 | const hasMetadata =
45 | hasDescription ||
46 | hasLabels(component.inputs) ||
47 | hasLabels(component.outputs);
48 |
49 | return (
50 |
55 | {hasIcon && (
56 |
57 |
58 |
59 | )}
60 |
61 |
62 | {component.label}
63 |
64 |
65 |
66 |
67 | }
68 | >
69 |
74 | {hasDescription && (
75 |
76 |
77 | {component.description}
78 |
79 |
80 |
81 | )}
82 | {component.inputs?.map((input, index) => {
83 | return (
84 |
85 |
86 |
87 | );
88 | })}
89 | {component.outputs?.map((output, index) => {
90 | return (
91 |
92 |
93 |
94 | );
95 | })}
96 |
97 |
98 | );
99 | }
100 |
101 | // small utility fn to check if inputs or outputs have labels. Component is dynamically
102 | // rendered based on these being populated or not.
103 | function hasLabels(
104 | inputsOrOutputs: (IComponentInput | IComponentOutput)[] | undefined
105 | ): boolean {
106 | return !isEmpty(
107 | inputsOrOutputs
108 | ?.map((inputOrOutput) => inputOrOutput.label)
109 | .filter((label) => !isEmpty(label))
110 | );
111 | }
112 |
--------------------------------------------------------------------------------
/public/pages/workflow_detail/workspace/workspace_edge/deletable-edge-styles.scss:
--------------------------------------------------------------------------------
1 | .delete-edge-button {
2 | width: 20px;
3 | height: 20px;
4 | background: #000000;
5 | border: 1px solid #000000;
6 | cursor: pointer;
7 | border-radius: 50%;
8 | font-size: 12px;
9 | line-height: 1;
10 | }
11 |
12 | .delete-edge-button:hover {
13 | box-shadow: 0 0 6px 2px rgba(0, 0, 0, 0.5);
14 | }
15 |
--------------------------------------------------------------------------------
/public/pages/workflow_detail/workspace/workspace_edge/deletable_edge.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import {
8 | BaseEdge,
9 | Edge,
10 | EdgeLabelRenderer,
11 | EdgeProps,
12 | MarkerType,
13 | getBezierPath,
14 | useReactFlow,
15 | } from 'reactflow';
16 |
17 | // styling
18 | import './deletable-edge-styles.scss';
19 |
20 | type DeletableEdgeProps = EdgeProps;
21 |
22 | /**
23 | * A custom deletable edge. Renders a delete button in the center of the edge once connected.
24 | * Using bezier path by default. For all edge types,
25 | * see https://reactflow.dev/docs/examples/edges/edge-types/
26 | */
27 | export function DeletableEdge(props: DeletableEdgeProps) {
28 | const [edgePath, labelX, labelY] = getBezierPath({
29 | sourceX: props.sourceX,
30 | sourceY: props.sourceY,
31 | sourcePosition: props.sourcePosition,
32 | targetX: props.targetX,
33 | targetY: props.targetY,
34 | targetPosition: props.targetPosition,
35 | });
36 |
37 | const reactFlowInstance = useReactFlow();
38 |
39 | const deleteEdge = (edgeId: string) => {
40 | reactFlowInstance.setEdges(
41 | reactFlowInstance.getEdges().filter((edge: Edge) => edge.id !== edgeId)
42 | );
43 | };
44 |
45 | const onEdgeClick = (event: any, edgeId: string) => {
46 | // Prevent this event from bubbling up and putting reactflow into an unexpected state.
47 | // This implementation follows the doc example: https://reactflow.dev/docs/examples/edges/custom-edge/
48 | event.stopPropagation();
49 | deleteEdge(edgeId);
50 | };
51 |
52 | return (
53 | <>
54 |
55 |
56 | {/** Using in-line styling since scss can't support dynamic values*/}
57 |
67 | onEdgeClick(event, props.id)}
73 | >
74 | ×
75 |
76 |
77 |
78 | >
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/public/pages/workflow_detail/workspace/workspace_edge/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export { DeletableEdge } from './deletable_edge';
7 |
--------------------------------------------------------------------------------
/public/pages/workflows/empty_list_message.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import {
8 | EuiSmallButton,
9 | EuiFlexGroup,
10 | EuiFlexItem,
11 | EuiSpacer,
12 | EuiText,
13 | } from '@elastic/eui';
14 |
15 | interface EmptyListMessageProps {
16 | onClickNewWorkflow: () => void;
17 | }
18 |
19 | export function EmptyListMessage(props: EmptyListMessageProps) {
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 | No workflows found
28 |
29 |
30 |
31 |
32 | Create a workflow to start building and testing your application.
33 |
34 |
35 |
36 |
37 | New workflow
38 |
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/public/pages/workflows/get_started_accordion.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import {
8 | EuiFlexGroup,
9 | EuiFlexItem,
10 | EuiSpacer,
11 | EuiText,
12 | EuiCard,
13 | EuiLink,
14 | EuiTitle,
15 | EuiAccordion,
16 | } from '@elastic/eui';
17 | import { CREATE_WORKFLOW_LINK, ML_REMOTE_MODEL_LINK } from '../../../common';
18 |
19 | interface GetStartedAccordionProps {
20 | initialIsOpen: boolean;
21 | }
22 |
23 | export function GetStartedAccordion(props: GetStartedAccordionProps) {
24 | return (
25 |
32 |
33 | Get started
34 |
35 |
36 | }
37 | >
38 |
39 |
40 |
41 |
42 |
46 | 1. Set up models
47 |
48 | }
49 | >
50 |
51 | Connect to an externally hosted model and make it available in
52 | your OpenSearch cluster.{' '}
53 |
54 | Learn more
55 |
56 |
57 |
58 |
59 |
60 |
64 | 2. Ingest data
65 |
66 | }
67 | >
68 |
69 | Import sample data to get started; add processors to customize
70 | your ingest pipeline.
71 |
72 |
73 |
74 |
75 |
79 | 3. Build a search pipeline
80 |
81 | }
82 | >
83 |
84 | Set up a query and configure your search pipeline.
85 |
86 |
87 |
88 |
89 |
93 | 4. Export the workflow
94 |
95 | }
96 | >
97 |
98 | Export your workflow template to create and deploy the workflow
99 | on other OpenSearch clusters.{' '}
100 |
101 | Learn more
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | );
110 | }
111 |
--------------------------------------------------------------------------------
/public/pages/workflows/import_workflow/import_workflow_modal.test.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import { render } from '@testing-library/react';
8 | import { Provider } from 'react-redux';
9 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
10 | import { store } from '../../../store';
11 | import { ImportWorkflowModal } from './import_workflow_modal';
12 |
13 | jest.mock('../../../services', () => {
14 | const { mockCoreServices } = require('../../../../test');
15 | return {
16 | ...jest.requireActual('../../../services'),
17 | ...mockCoreServices,
18 | };
19 | });
20 |
21 | const renderWithRouter = () =>
22 | render(
23 |
24 |
25 |
26 | (
28 |
33 | )}
34 | />
35 |
36 |
37 |
38 | );
39 |
40 | describe('ImportWorkflowModal', () => {
41 | beforeEach(() => {
42 | jest.clearAllMocks();
43 | });
44 | test('renders the page', () => {
45 | const { getAllByText } = renderWithRouter();
46 | expect(
47 | getAllByText('Import a workflow (JSON/YAML)').length
48 | ).toBeGreaterThan(0);
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/public/pages/workflows/import_workflow/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export { ImportWorkflowModal } from './import_workflow_modal';
7 |
--------------------------------------------------------------------------------
/public/pages/workflows/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export * from './workflows';
7 |
--------------------------------------------------------------------------------
/public/pages/workflows/new_workflow/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export { NewWorkflow } from './new_workflow';
7 | export { fetchEmptyMetadata, fetchEmptyUIConfig } from './utils';
8 |
--------------------------------------------------------------------------------
/public/pages/workflows/new_workflow/use_case.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React, { useState } from 'react';
7 | import {
8 | EuiText,
9 | EuiFlexGroup,
10 | EuiFlexItem,
11 | EuiCard,
12 | EuiSmallButton,
13 | } from '@elastic/eui';
14 | import { Workflow } from '../../../../common';
15 | import { QuickConfigureModal } from './quick_configure_modal';
16 |
17 | interface UseCaseProps {
18 | workflow: Workflow;
19 | }
20 |
21 | export function UseCase(props: UseCaseProps) {
22 | // name modal state
23 | const [isNameModalOpen, setIsNameModalOpen] = useState(false);
24 |
25 | return (
26 | <>
27 | {isNameModalOpen && (
28 | setIsNameModalOpen(false)}
31 | />
32 | )}
33 |
36 | {props.workflow.name}
37 |
38 | }
39 | titleSize="s"
40 | paddingSize="l"
41 | textAlign="left"
42 | description={props.workflow?.description || ''}
43 | footer={
44 |
45 |
46 | {
50 | setIsNameModalOpen(true);
51 | }}
52 | data-testid="goButton"
53 | >
54 | Create
55 |
56 |
57 |
58 | }
59 | >
60 | >
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/public/pages/workflows/workflow_list/columns.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import { EuiLink } from '@elastic/eui';
8 | import {
9 | EMPTY_FIELD_STRING,
10 | MAX_WORKFLOW_NAME_TO_DISPLAY,
11 | Workflow,
12 | getCharacterLimitedString,
13 | toFormattedDate,
14 | } from '../../../../common';
15 | import {
16 | constructHrefWithDataSourceId,
17 | getDataSourceId,
18 | } from '../../../utils/utils';
19 |
20 | export const columns = (actions: any[]) => {
21 | const dataSourceId = getDataSourceId();
22 |
23 | return [
24 | {
25 | field: 'name',
26 | name: 'Name',
27 | width: '25%',
28 | sortable: true,
29 | render: (name: string, workflow: Workflow) => (
30 |
36 | {getCharacterLimitedString(name, MAX_WORKFLOW_NAME_TO_DISPLAY)}
37 |
38 | ),
39 | },
40 | {
41 | field: 'ui_metadata.type',
42 | name: 'Type',
43 | width: '25%',
44 | sortable: true,
45 | },
46 | {
47 | field: 'description',
48 | name: 'Description',
49 | width: '35%',
50 | sortable: false,
51 | },
52 | {
53 | field: 'lastUpdated',
54 | name: 'Last saved',
55 | width: '15%',
56 | sortable: true,
57 | render: (lastUpdated: number) =>
58 | lastUpdated !== undefined
59 | ? toFormattedDate(lastUpdated)
60 | : EMPTY_FIELD_STRING,
61 | },
62 | {
63 | name: 'Actions',
64 | width: '10%',
65 | actions,
66 | },
67 | ];
68 | };
69 |
--------------------------------------------------------------------------------
/public/pages/workflows/workflow_list/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export { WorkflowList } from './workflow_list';
7 |
--------------------------------------------------------------------------------
/public/pages/workflows/workflows.test.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import { render, waitFor } from '@testing-library/react';
8 | import '@testing-library/jest-dom';
9 | import userEvent from '@testing-library/user-event';
10 | import { Provider } from 'react-redux';
11 |
12 | import {
13 | BrowserRouter as Router,
14 | RouteComponentProps,
15 | Route,
16 | Switch,
17 | } from 'react-router-dom';
18 | import { store } from '../../store';
19 | import { Workflows } from './workflows';
20 |
21 | jest.mock('../../services', () => {
22 | const { mockCoreServices } = require('../../../test');
23 | return {
24 | ...jest.requireActual('../../services'),
25 | ...mockCoreServices,
26 | };
27 | });
28 |
29 | const renderWithRouter = () => ({
30 | ...render(
31 |
32 |
33 |
34 | (
36 |
37 | )}
38 | />
39 |
40 |
41 |
42 | ),
43 | });
44 |
45 | describe('Workflows', () => {
46 | beforeEach(() => {
47 | jest.clearAllMocks();
48 | });
49 | test('renders the page', async () => {
50 | const { getAllByText, getByTestId, queryByText } = renderWithRouter();
51 |
52 | // The "Manage Workflows" tab is displayed by default
53 |
54 | // Import Workflow Testing
55 | expect(getAllByText('Workflows').length).toBeGreaterThan(0);
56 | const importWorkflowButton = getByTestId('importWorkflowButton');
57 | userEvent.click(importWorkflowButton);
58 | await waitFor(() => {
59 | expect(
60 | getAllByText('Select or drag and drop a file').length
61 | ).toBeGreaterThan(0);
62 | });
63 |
64 | // Closing or canceling the import
65 | const cancelImportButton = getByTestId('cancelImportButton');
66 | userEvent.click(cancelImportButton);
67 | await waitFor(() => {
68 | expect(
69 | queryByText('Select or drag and drop a file')
70 | ).not.toBeInTheDocument();
71 | });
72 |
73 | // When the "Create Workflow" button is clicked, the "New workflow" tab opens
74 | // Create Workflow Testing
75 | const createWorkflowButton = getByTestId('createWorkflowButton');
76 | expect(createWorkflowButton).toBeInTheDocument();
77 | userEvent.click(createWorkflowButton);
78 | await waitFor(() => {
79 | expect(
80 | getAllByText('Create a workflow using a template').length
81 | ).toBeGreaterThan(0);
82 | });
83 | });
84 | });
85 |
--------------------------------------------------------------------------------
/public/plugin.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import {
7 | AppMountParameters,
8 | CoreSetup,
9 | DEFAULT_NAV_GROUPS,
10 | DEFAULT_APP_CATEGORIES,
11 | CoreStart,
12 | Plugin,
13 | } from '../../../src/core/public';
14 | import {
15 | FlowFrameworkDashboardsPluginStart,
16 | FlowFrameworkDashboardsPluginSetup,
17 | AppPluginStartDependencies,
18 | } from './types';
19 | import { registerPluginCard } from './general_components';
20 | import { PLUGIN_ID, PLUGIN_NAME } from '../common';
21 | import {
22 | setCore,
23 | setRouteService,
24 | setSavedObjectsClient,
25 | setDataSourceManagementPlugin,
26 | setDataSourceEnabled,
27 | setNotifications,
28 | setNavigationUI,
29 | setApplication,
30 | setUISettings,
31 | setHeaderActionMenu,
32 | } from './services';
33 | import { configureRoutes } from './route_service';
34 |
35 | export class FlowFrameworkDashboardsPlugin
36 | implements
37 | Plugin<
38 | FlowFrameworkDashboardsPluginSetup,
39 | FlowFrameworkDashboardsPluginStart
40 | > {
41 | public setup(
42 | core: CoreSetup,
43 | plugins: any
44 | ): FlowFrameworkDashboardsPluginSetup {
45 | // Register the plugin in the side navigation
46 | core.application.register({
47 | id: PLUGIN_ID,
48 | title: PLUGIN_NAME,
49 | category: {
50 | id: 'opensearch',
51 | label: 'OpenSearch plugins',
52 | // TODO: this may change after plugin position is finalized
53 | order: 2000,
54 | },
55 | // TODO: can i remove this below order
56 | order: 5000,
57 | async mount(params: AppMountParameters) {
58 | const { renderApp } = await import('./render_app');
59 | const [coreStart] = await core.getStartServices();
60 | const routeServices = configureRoutes(coreStart);
61 | setCore(coreStart);
62 | setHeaderActionMenu(params.setHeaderActionMenu);
63 | setRouteService(routeServices);
64 | return renderApp(coreStart, params);
65 | },
66 | });
67 | core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.search, [
68 | {
69 | id: PLUGIN_ID,
70 | title: PLUGIN_NAME,
71 | category: DEFAULT_APP_CATEGORIES.configure,
72 | showInAllNavGroup: true,
73 | },
74 | ]);
75 | setUISettings(core.uiSettings);
76 | setDataSourceManagementPlugin(plugins.dataSourceManagement);
77 | const enabled = !!plugins.dataSource;
78 | setDataSourceEnabled({ enabled });
79 | return {
80 | dataSourceManagement: plugins.dataSourceManagement,
81 | dataSource: plugins.dataSource,
82 | };
83 | }
84 |
85 | public start(
86 | core: CoreStart,
87 | { navigation, contentManagement }: AppPluginStartDependencies
88 | ): FlowFrameworkDashboardsPluginStart {
89 | setNotifications(core.notifications);
90 | setSavedObjectsClient(core.savedObjects.client);
91 | setNavigationUI(navigation.ui);
92 | setApplication(core.application);
93 | if (contentManagement) {
94 | registerPluginCard(contentManagement, core);
95 | }
96 | return {};
97 | }
98 |
99 | public stop() {}
100 | }
101 |
--------------------------------------------------------------------------------
/public/render_app.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import React from 'react';
7 | import ReactDOM from 'react-dom';
8 | import { HashRouter as Router, Route } from 'react-router-dom';
9 | import { Provider } from 'react-redux';
10 | import { AppMountParameters, CoreStart } from '../../../src/core/public';
11 | import { FlowFrameworkDashboardsApp } from './app';
12 | import { store } from './store';
13 |
14 | // styling
15 | import './global-styles.scss';
16 |
17 | export const renderApp = (coreStart: CoreStart, params: AppMountParameters) => {
18 | // This is so our base element stretches to fit the entire webpage
19 | params.element.className = 'stretch-absolute';
20 | ReactDOM.render(
21 |
22 |
23 | (
25 |
29 | )}
30 | />
31 |
32 | ,
33 | params.element
34 | );
35 |
36 | const unlistenParentHistory = params.history.listen(() => {
37 | window.dispatchEvent(new HashChangeEvent('hashchange'));
38 | });
39 |
40 | return () => {
41 | ReactDOM.unmountComponentAtNode(params.element);
42 | unlistenParentHistory();
43 | };
44 | };
45 |
--------------------------------------------------------------------------------
/public/services.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/public';
7 | import {
8 | CoreStart,
9 | NotificationsStart,
10 | IUiSettingsClient,
11 | AppMountParameters,
12 | } from '../../../src/core/public';
13 | import { RouteService } from './route_service';
14 | import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public';
15 | import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public';
16 |
17 | export interface DataSourceEnabled {
18 | enabled: boolean;
19 | }
20 |
21 | export const [getCore, setCore] = createGetterSetter('Core');
22 |
23 | export const [getRouteService, setRouteService] = createGetterSetter<
24 | RouteService
25 | >('');
26 |
27 | export const [
28 | getSavedObjectsClient,
29 | setSavedObjectsClient,
30 | ] = createGetterSetter(
31 | 'SavedObjectsClient'
32 | );
33 |
34 | export const [
35 | getDataSourceManagementPlugin,
36 | setDataSourceManagementPlugin,
37 | ] = createGetterSetter('DataSourceManagement');
38 |
39 | export const [getDataSourceEnabled, setDataSourceEnabled] = createGetterSetter<
40 | DataSourceEnabled
41 | >('DataSourceEnabled');
42 |
43 | export const [getNotifications, setNotifications] = createGetterSetter<
44 | NotificationsStart
45 | >('Notifications');
46 |
47 | export const [getUISettings, setUISettings] = createGetterSetter<
48 | IUiSettingsClient
49 | >('UISettings');
50 |
51 | export const [getApplication, setApplication] = createGetterSetter<
52 | CoreStart['application']
53 | >('Application');
54 |
55 | export const [getNavigationUI, setNavigationUI] = createGetterSetter<
56 | NavigationPublicPluginStart['ui']
57 | >('Navigation');
58 |
59 | export const [getHeaderActionMenu, setHeaderActionMenu] = createGetterSetter<
60 | AppMountParameters['setHeaderActionMenu']
61 | >('SetHeaderActionMenu');
62 |
--------------------------------------------------------------------------------
/public/store/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export * from './store';
7 | export * from './reducers';
8 |
--------------------------------------------------------------------------------
/public/store/reducers/errors_reducer.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
7 | import { IngestPipelineErrors, SearchPipelineErrors } from '../../../common';
8 |
9 | export const INITIAL_ERRORS_STATE = {
10 | ingestPipeline: {} as IngestPipelineErrors,
11 | searchPipeline: {} as SearchPipelineErrors,
12 | };
13 |
14 | const ERRORS_PREFIX = 'errors';
15 | const SET_INGEST_PIPELINE_ERRORS = `${ERRORS_PREFIX}/setIngestPipelineErrors`;
16 | const SET_SEARCH_PIPELINE_ERRORS = `${ERRORS_PREFIX}/setSearchPipelineErrors`;
17 |
18 | export const setIngestPipelineErrors = createAsyncThunk(
19 | SET_INGEST_PIPELINE_ERRORS,
20 | async ({ errors }: { errors: IngestPipelineErrors }, { rejectWithValue }) => {
21 | return errors;
22 | }
23 | );
24 |
25 | export const setSearchPipelineErrors = createAsyncThunk(
26 | SET_SEARCH_PIPELINE_ERRORS,
27 | async ({ errors }: { errors: SearchPipelineErrors }, { rejectWithValue }) => {
28 | return errors;
29 | }
30 | );
31 |
32 | const errorsSlice = createSlice({
33 | name: ERRORS_PREFIX,
34 | initialState: INITIAL_ERRORS_STATE,
35 | reducers: {},
36 | extraReducers: (builder) => {
37 | builder
38 | .addCase(setIngestPipelineErrors.fulfilled, (state, action) => {
39 | state.ingestPipeline = action.payload;
40 | })
41 | .addCase(setSearchPipelineErrors.fulfilled, (state, action) => {
42 | state.searchPipeline = action.payload;
43 | });
44 | },
45 | });
46 |
47 | export const errorsReducer = errorsSlice.reducer;
48 |
--------------------------------------------------------------------------------
/public/store/reducers/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export * from './opensearch_reducer';
7 | export * from './workflows_reducer';
8 | export * from './presets_reducer';
9 | export * from './ml_reducer';
10 | export * from './errors_reducer';
11 |
--------------------------------------------------------------------------------
/public/store/reducers/ml_reducer.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
7 | import { ConnectorDict, ModelDict } from '../../../common';
8 | import { HttpFetchError } from '../../../../../src/core/public';
9 | import { getRouteService } from '../../services';
10 |
11 | export const INITIAL_ML_STATE = {
12 | loading: false,
13 | errorMessage: '',
14 | models: {} as ModelDict,
15 | connectors: {} as ConnectorDict,
16 | };
17 |
18 | const MODELS_ACTION_PREFIX = 'models';
19 | const CONNECTORS_ACTION_PREFIX = 'connectors';
20 | const SEARCH_MODELS_ACTION = `${MODELS_ACTION_PREFIX}/search`;
21 | const SEARCH_CONNECTORS_ACTION = `${CONNECTORS_ACTION_PREFIX}/search`;
22 |
23 | export const searchModels = createAsyncThunk(
24 | SEARCH_MODELS_ACTION,
25 | async (
26 | { apiBody, dataSourceId }: { apiBody: {}; dataSourceId?: string },
27 | { rejectWithValue }
28 | ) => {
29 | const response: any | HttpFetchError = await getRouteService().searchModels(
30 | apiBody,
31 | dataSourceId
32 | );
33 | if (response instanceof HttpFetchError) {
34 | return rejectWithValue(
35 | 'Error searching models: ' + response.body.message
36 | );
37 | } else {
38 | return response;
39 | }
40 | }
41 | );
42 |
43 | export const searchConnectors = createAsyncThunk(
44 | SEARCH_CONNECTORS_ACTION,
45 | async (
46 | { apiBody, dataSourceId }: { apiBody: {}; dataSourceId?: string },
47 | { rejectWithValue }
48 | ) => {
49 | const response:
50 | | any
51 | | HttpFetchError = await getRouteService().searchConnectors(
52 | apiBody,
53 | dataSourceId
54 | );
55 | if (response instanceof HttpFetchError) {
56 | return rejectWithValue(
57 | 'Error searching connectors: ' + response.body.message
58 | );
59 | } else {
60 | return response;
61 | }
62 | }
63 | );
64 |
65 | const mlSlice = createSlice({
66 | name: 'ml',
67 | initialState: INITIAL_ML_STATE,
68 | reducers: {},
69 | extraReducers: (builder) => {
70 | builder
71 | // Pending states
72 | .addCase(searchModels.pending, (state, action) => {
73 | state.loading = true;
74 | state.errorMessage = '';
75 | })
76 | .addCase(searchConnectors.pending, (state, action) => {
77 | state.loading = true;
78 | state.errorMessage = '';
79 | })
80 | // Fulfilled states
81 | .addCase(searchModels.fulfilled, (state, action) => {
82 | const { models } = action.payload as { models: ModelDict };
83 | state.models = models;
84 | state.loading = false;
85 | state.errorMessage = '';
86 | })
87 | .addCase(searchConnectors.fulfilled, (state, action) => {
88 | const { connectors } = action.payload as { connectors: ConnectorDict };
89 | state.connectors = connectors;
90 | state.loading = false;
91 | state.errorMessage = '';
92 | })
93 | // Rejected states
94 | .addCase(searchModels.rejected, (state, action) => {
95 | state.errorMessage = action.payload as string;
96 | state.loading = false;
97 | })
98 | .addCase(searchConnectors.rejected, (state, action) => {
99 | state.errorMessage = action.payload as string;
100 | state.loading = false;
101 | });
102 | },
103 | });
104 |
105 | export const mlReducer = mlSlice.reducer;
106 |
--------------------------------------------------------------------------------
/public/store/reducers/presets_reducer.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
7 | import { WorkflowTemplate } from '../../../common';
8 | import { HttpFetchError } from '../../../../../src/core/public';
9 | import { getRouteService } from '../../services';
10 |
11 | export const INITIAL_PRESETS_STATE = {
12 | loading: false,
13 | errorMessage: '',
14 | presetWorkflows: [] as Partial[],
15 | };
16 |
17 | const PRESET_ACTION_PREFIX = 'presets';
18 | const GET_WORKFLOW_PRESETS_ACTION = `${PRESET_ACTION_PREFIX}/getPresets`;
19 |
20 | export const getWorkflowPresets = createAsyncThunk(
21 | GET_WORKFLOW_PRESETS_ACTION,
22 | async (_, { rejectWithValue }) => {
23 | const response:
24 | | any
25 | | HttpFetchError = await getRouteService().getWorkflowPresets();
26 | if (response instanceof HttpFetchError) {
27 | return rejectWithValue(
28 | 'Error getting workflow presets: ' + response.body.message
29 | );
30 | } else {
31 | return response;
32 | }
33 | }
34 | );
35 |
36 | const presetsSlice = createSlice({
37 | name: 'presets',
38 | initialState: INITIAL_PRESETS_STATE,
39 | reducers: {},
40 | extraReducers: (builder) => {
41 | builder
42 | .addCase(getWorkflowPresets.pending, (state, action) => {
43 | state.loading = true;
44 | state.errorMessage = '';
45 | })
46 | .addCase(getWorkflowPresets.fulfilled, (state, action) => {
47 | state.presetWorkflows = action.payload.workflowTemplates as Partial<
48 | WorkflowTemplate
49 | >[];
50 | state.loading = false;
51 | state.errorMessage = '';
52 | })
53 | .addCase(getWorkflowPresets.rejected, (state, action) => {
54 | state.loading = false;
55 | state.errorMessage = action.payload as string;
56 | });
57 | },
58 | });
59 |
60 | export const presetsReducer = presetsSlice.reducer;
61 |
--------------------------------------------------------------------------------
/public/store/store.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { ThunkDispatch, configureStore } from '@reduxjs/toolkit';
7 | import { AnyAction, combineReducers } from 'redux';
8 | import { useDispatch } from 'react-redux';
9 | import {
10 | opensearchReducer,
11 | workflowsReducer,
12 | presetsReducer,
13 | mlReducer,
14 | errorsReducer,
15 | } from './reducers';
16 |
17 | const rootReducer = combineReducers({
18 | workflows: workflowsReducer,
19 | presets: presetsReducer,
20 | ml: mlReducer,
21 | opensearch: opensearchReducer,
22 | errors: errorsReducer,
23 | });
24 |
25 | export const store = configureStore({
26 | reducer: rootReducer,
27 | });
28 |
29 | export type AppState = ReturnType;
30 | export type AppThunkDispatch = ThunkDispatch;
31 | export const useAppDispatch = () => useDispatch();
32 |
--------------------------------------------------------------------------------
/public/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public';
7 | import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public';
8 | import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public';
9 | import { ContentManagementPluginStart } from '../../../src/plugins/content_management/public';
10 |
11 | export interface FlowFrameworkDashboardsPluginSetup {
12 | dataSourceManagement: DataSourceManagementPluginSetup;
13 | dataSource: DataSourcePluginSetup;
14 | }
15 |
16 | export interface FlowFrameworkDashboardsPluginStart {
17 | }
18 |
19 | export interface AppPluginStartDependencies {
20 | navigation: NavigationPublicPluginStart;
21 | contentManagement?: ContentManagementPluginStart;
22 | }
23 |
--------------------------------------------------------------------------------
/public/utils/constants.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { constructHrefWithDataSourceId } from './utils';
7 | import { getUISettings } from '../../public/services';
8 | import { PLUGIN_NAME } from '../../common/constants';
9 |
10 | export enum Navigation {
11 | PluginName = PLUGIN_NAME,
12 | Workflows = 'Workflows',
13 | }
14 |
15 | export enum APP_PATH {
16 | HOME = '/',
17 | WORKFLOWS = '/workflows',
18 | WORKFLOW_DETAIL = '/workflows/:workflowId',
19 | }
20 |
21 | export const BREADCRUMBS = Object.freeze({
22 | PLUGIN_NAME: { text: PLUGIN_NAME },
23 | WORKFLOWS: (dataSourceId?: string) => ({
24 | text: 'Workflows',
25 | href: constructHrefWithDataSourceId(APP_PATH.WORKFLOWS, dataSourceId),
26 | }),
27 | TITLE: { text: PLUGIN_NAME },
28 | TITLE_WITH_REF: (dataSourceId?: string) => ({
29 | text: PLUGIN_NAME,
30 | href: constructHrefWithDataSourceId(APP_PATH.WORKFLOWS, dataSourceId),
31 | }),
32 | WORKFLOW_NAME: (workflowName: string) => ({
33 | text: workflowName,
34 | }),
35 | });
36 |
37 | export const USE_NEW_HOME_PAGE = getUISettings().get('home:useNewHomePage');
38 |
39 | export const getAppBasePath = () => {
40 | const currentPath = window.location.pathname;
41 | return currentPath.substring(0, currentPath.indexOf('/app/'));
42 | };
--------------------------------------------------------------------------------
/public/utils/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export * from './constants';
7 | export * from './utils';
8 | export * from './config_to_template_utils';
9 | export * from './config_to_form_utils';
10 | export * from './config_to_workspace_utils';
11 | export * from './config_to_schema_utils';
12 | export * from './form_to_config_utils';
13 | export * from './form_to_pipeline_utils';
14 |
--------------------------------------------------------------------------------
/release-notes/opensearch-flow-framework-dashboards.release-notes-2.19.0.0.md:
--------------------------------------------------------------------------------
1 | ## Version 2.19.0.0 Release Notes
2 |
3 | The initial release of the Flow Framework OpenSearch Dashboards Plugin. Compatible with OpenSearch Dashboards 2.19.0
4 |
5 | This plugin provides a UI for building and testing AI/ML use cases. For further information, examples, and documentation, see [here](https://github.com/opensearch-project/dashboards-flow-framework/blob/2.x/documentation/tutorial.md).
--------------------------------------------------------------------------------
/release-notes/opensearch-flow-framework-dashboards.release-notes-3.0.0.0-alpha1.md:
--------------------------------------------------------------------------------
1 | ## Version 3.0.0.0-alpha1 Release Notes
2 |
3 | Compatible with OpenSearch 3.0.0.0-alpha1
4 | ### Features
5 | - Add fine-grained error handling ([#598](https://github.com/opensearch-project/dashboards-flow-framework/pull/598))
6 | - Change ingestion input to JSON lines format ([#639](https://github.com/opensearch-project/dashboards-flow-framework/pull/639))
7 |
8 | ### Enhancements
9 | - Integrate legacy presets with quick-configure fields ([#602](https://github.com/opensearch-project/dashboards-flow-framework/pull/602))
10 | - Simplify RAG presets, add bulk API details ([#610](https://github.com/opensearch-project/dashboards-flow-framework/pull/610))
11 | - Improve RAG preset experience ([#617](https://github.com/opensearch-project/dashboards-flow-framework/pull/617))
12 | - Update model options and callout ([#622](https://github.com/opensearch-project/dashboards-flow-framework/pull/622))
13 | - Added popover to display links to suggested models ([#625](https://github.com/opensearch-project/dashboards-flow-framework/pull/625))
14 | - Implicitly update input maps defined on non-expanded queries (common cases) ([#632](https://github.com/opensearch-project/dashboards-flow-framework/pull/632))
15 | - Show interim JSON provision flow even if provisioned ([#633](https://github.com/opensearch-project/dashboards-flow-framework/pull/633))
16 | - Add functional buttons in form headers, fix query parse bug ([#649](https://github.com/opensearch-project/dashboards-flow-framework/pull/649))
17 | - Block simulate API calls if datasource version is missing ([#657](https://github.com/opensearch-project/dashboards-flow-framework/pull/657))
18 | - Update default queries, update quick config fields, misc updates ([#660](https://github.com/opensearch-project/dashboards-flow-framework/pull/660))
19 | - Update visible plugin name to 'AI Search Flows' ([#662](https://github.com/opensearch-project/dashboards-flow-framework/pull/662))
20 | - Update plugin name and rearrange Try AI Search Flows card ([#664](https://github.com/opensearch-project/dashboards-flow-framework/pull/664))
21 |
22 | ### Bug Fixes
23 | - Fix error that local cluster cannot get version ([#606](https://github.com/opensearch-project/dashboards-flow-framework/pull/606))
24 | - UX fit-n-finish updates XI ([#613](https://github.com/opensearch-project/dashboards-flow-framework/pull/613))
25 | - UX fit-n-finish updates XII ([#618](https://github.com/opensearch-project/dashboards-flow-framework/pull/618))
26 | - Bug fixes XIII ([#630](https://github.com/opensearch-project/dashboards-flow-framework/pull/630))
27 | - Various bug fixes & improvements ([#644](https://github.com/opensearch-project/dashboards-flow-framework/pull/644))
28 | - Fixed bug related to Search Index in Local Cluster scenario ([#654](https://github.com/opensearch-project/dashboards-flow-framework/pull/654))
29 |
30 | ### Maintenance
31 | - Support 2.17 BWC with latest backend integrations ([#612](https://github.com/opensearch-project/dashboards-flow-framework/pull/612))
32 |
33 | ### Refactoring
34 | - Refactor quick configure components, improve processor error handling ([#604](https://github.com/opensearch-project/dashboards-flow-framework/pull/604))
35 | - Hide search query section when version is less than 2.19 ([#605](https://github.com/opensearch-project/dashboards-flow-framework/pull/605))
36 |
--------------------------------------------------------------------------------
/release-notes/opensearch-flow-framework-dashboards.release-notes-3.0.0.0-beta1.md:
--------------------------------------------------------------------------------
1 | ## Version 3.0.0.0-beta1 Release Notes
2 |
3 | Compatible with OpenSearch 3.0.0.0-beta1
4 |
5 | ### Enhancements
6 | - Add new RAG + hybrid search preset ([#665](https://github.com/opensearch-project/dashboards-flow-framework/pull/665))
7 | - Update new index mappings if selecting from existing index ([#670](https://github.com/opensearch-project/dashboards-flow-framework/pull/670))
8 | - Persist state across Inspector tab switches; add presets dropdown ([#671](https://github.com/opensearch-project/dashboards-flow-framework/pull/671))
9 | - Simplify ML processor form when interface is defined ([#676](https://github.com/opensearch-project/dashboards-flow-framework/pull/676))
10 | - Cache form across ML transform types ([#678](https://github.com/opensearch-project/dashboards-flow-framework/pull/678))
11 |
12 | ### Bug Fixes
13 | - Fix missed UI autofilling after JSON Lines change ([#672](https://github.com/opensearch-project/dashboards-flow-framework/pull/672))
--------------------------------------------------------------------------------
/server/cluster/core_plugin.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { SEARCH_PIPELINE_ROUTE } from '../../common';
7 |
8 | export function corePlugin(Client: any, config: any, components: any) {
9 | const ca = components.clientAction.factory;
10 |
11 | Client.prototype.coreClient = components.clientAction.namespaceFactory();
12 | const coreClient = Client.prototype.coreClient.prototype;
13 |
14 | coreClient.getSearchPipeline = ca({
15 | url: {
16 | fmt: `${SEARCH_PIPELINE_ROUTE}/<%=pipeline_id%>`,
17 | req: {
18 | pipeline_id: {
19 | type: 'string',
20 | required: true,
21 | },
22 | },
23 | },
24 | method: 'GET',
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/server/cluster/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export * from './flow_framework_plugin';
7 | export * from './ml_plugin';
8 | export * from './core_plugin';
9 |
--------------------------------------------------------------------------------
/server/cluster/ml_plugin.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import {
7 | ML_SEARCH_CONNECTORS_ROUTE,
8 | ML_SEARCH_MODELS_ROUTE,
9 | } from '../../common';
10 |
11 | /**
12 | * Used during the plugin's setup() lifecycle phase to register various client actions
13 | * representing ML plugin APIs. These are then exposed and used on the
14 | * server-side when processing node APIs - see server/routes/ml_routes_service
15 | * for examples.
16 | */
17 | export function mlPlugin(Client: any, config: any, components: any) {
18 | const ca = components.clientAction.factory;
19 |
20 | Client.prototype.mlClient = components.clientAction.namespaceFactory();
21 | const mlClient = Client.prototype.mlClient.prototype;
22 |
23 | mlClient.searchModels = ca({
24 | url: {
25 | fmt: ML_SEARCH_MODELS_ROUTE,
26 | },
27 | needBody: true,
28 | method: 'POST',
29 | });
30 |
31 | mlClient.searchConnectors = ca({
32 | url: {
33 | fmt: ML_SEARCH_CONNECTORS_ROUTE,
34 | },
35 | needBody: true,
36 | method: 'POST',
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/server/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import {
7 | PluginConfigDescriptor,
8 | PluginInitializerContext,
9 | } from '../../../src/core/server';
10 | import { FlowFrameworkDashboardsPlugin } from './plugin';
11 | import { configSchema, ConfigSchema } from '../server/types';
12 |
13 | export const config: PluginConfigDescriptor = {
14 | schema: configSchema,
15 | };
16 |
17 | // This exports static code and TypeScript types,
18 | // as well as, OpenSearch Dashboards Platform `plugin()` initializer.
19 |
20 | export function plugin(initializerContext: PluginInitializerContext) {
21 | return new FlowFrameworkDashboardsPlugin(initializerContext);
22 | }
23 |
24 | export {
25 | FlowFrameworkDashboardsPluginSetup,
26 | FlowFrameworkDashboardsPluginStart,
27 | } from './types';
28 |
--------------------------------------------------------------------------------
/server/plugin.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import {
7 | PluginInitializerContext,
8 | CoreSetup,
9 | CoreStart,
10 | Plugin,
11 | Logger,
12 | } from '../../../src/core/server';
13 | import { first } from 'rxjs/operators';
14 | import { flowFrameworkPlugin, mlPlugin, corePlugin } from './cluster';
15 | import {
16 | FlowFrameworkDashboardsPluginSetup,
17 | FlowFrameworkDashboardsPluginStart,
18 | } from './types';
19 | import {
20 | registerOpenSearchRoutes,
21 | registerFlowFrameworkRoutes,
22 | OpenSearchRoutesService,
23 | FlowFrameworkRoutesService,
24 | registerMLRoutes,
25 | MLRoutesService,
26 | } from './routes';
27 | import { DataSourcePluginSetup } from '../../../src/plugins/data_source/server/types';
28 | import { DataSourceManagementPlugin } from '../../../src/plugins/data_source_management/public';
29 |
30 | import { ILegacyClusterClient } from '../../../src/core/server/';
31 |
32 | export interface FlowFrameworkPluginSetupDependencies {
33 | dataSourceManagement?: ReturnType;
34 | dataSource?: DataSourcePluginSetup;
35 | }
36 |
37 | export class FlowFrameworkDashboardsPlugin
38 | implements
39 | Plugin<
40 | FlowFrameworkDashboardsPluginSetup,
41 | FlowFrameworkDashboardsPluginStart
42 | > {
43 | private readonly logger: Logger;
44 | private readonly globalConfig$: any;
45 |
46 | constructor(initializerContext: PluginInitializerContext) {
47 | this.logger = initializerContext.logger.get();
48 | this.globalConfig$ = initializerContext.config.legacy.globalConfig$;
49 | }
50 |
51 | public async setup(
52 | core: CoreSetup,
53 | { dataSource }: FlowFrameworkPluginSetupDependencies
54 | ) {
55 | this.logger.debug('flow-framework-dashboards: Setup');
56 | const router = core.http.createRouter();
57 |
58 | // Get global config
59 | const globalConfig = await this.globalConfig$.pipe(first()).toPromise();
60 |
61 | // Create OpenSearch client, including flow framework plugin APIs
62 | const client: ILegacyClusterClient = core.opensearch.legacy.createClient(
63 | 'flow_framework',
64 | {
65 | plugins: [flowFrameworkPlugin, mlPlugin, corePlugin],
66 | ...globalConfig.opensearch,
67 | }
68 | );
69 |
70 | const dataSourceEnabled = !!dataSource;
71 | if (dataSourceEnabled) {
72 | dataSource.registerCustomApiSchema(flowFrameworkPlugin);
73 | dataSource.registerCustomApiSchema(mlPlugin);
74 | dataSource.registerCustomApiSchema(corePlugin);
75 | }
76 | const opensearchRoutesService = new OpenSearchRoutesService(
77 | client,
78 | dataSourceEnabled
79 | );
80 | const flowFrameworkRoutesService = new FlowFrameworkRoutesService(
81 | client,
82 | dataSourceEnabled
83 | );
84 | const mlRoutesService = new MLRoutesService(client, dataSourceEnabled);
85 |
86 | // Register server side APIs with the corresponding service functions
87 | registerOpenSearchRoutes(router, opensearchRoutesService);
88 | registerFlowFrameworkRoutes(router, flowFrameworkRoutesService);
89 | registerMLRoutes(router, mlRoutesService);
90 |
91 | return {};
92 | }
93 |
94 | public start(core: CoreStart) {
95 | this.logger.debug('flow-framework-dashboards: Started');
96 | return {};
97 | }
98 |
99 | public stop() {}
100 | }
101 |
--------------------------------------------------------------------------------
/server/resources/templates/custom.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Custom Search",
3 | "description": "Build a custom workflow tailored to your specific use case without using a template.",
4 | "use_case": "CUSTOM",
5 | "version": {
6 | "template": "1.0.0",
7 | "compatibility": [
8 | "2.19.0",
9 | "3.0.0"
10 | ]
11 | }
12 | }
--------------------------------------------------------------------------------
/server/resources/templates/hybrid_search.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Hybrid Search",
3 | "description": "Build an application that searches using a combination of vector and lexical search.",
4 | "version": {
5 | "template": "1.0.0",
6 | "compatibility": [
7 | "2.19.0",
8 | "3.0.0"
9 | ]
10 | },
11 | "ui_metadata": {
12 | "type": "Hybrid Search"
13 | }
14 | }
--------------------------------------------------------------------------------
/server/resources/templates/hybrid_search_with_rag.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RAG with Hybrid Search",
3 | "description": "Build a search application that uses retrieval-augmented generation (RAG) to retrieve relevant documents using hybrid search, pass them to large language models, and synthesize answers.",
4 | "version": {
5 | "template": "1.0.0",
6 | "compatibility": [
7 | "2.19.0",
8 | "3.0.0"
9 | ]
10 | },
11 | "ui_metadata": {
12 | "type": "RAG with Hybrid Search"
13 | }
14 | }
--------------------------------------------------------------------------------
/server/resources/templates/multimodal_search.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Multimodal Search",
3 | "description": "Build an application that searches both text and image data using multimodal embedding models.",
4 | "version": {
5 | "template": "1.0.0",
6 | "compatibility": [
7 | "2.19.0",
8 | "3.0.0"
9 | ]
10 | },
11 | "ui_metadata": {
12 | "type": "Multimodal Search"
13 | }
14 | }
--------------------------------------------------------------------------------
/server/resources/templates/semantic_search.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Semantic Search",
3 | "description": "Build an application that interprets the meaning and context of user queries to deliver more relevant and accurate search results.",
4 | "version": {
5 | "template": "1.0.0",
6 | "compatibility": [
7 | "2.19.0",
8 | "3.0.0"
9 | ]
10 | },
11 | "ui_metadata": {
12 | "type": "Semantic Search"
13 | }
14 | }
--------------------------------------------------------------------------------
/server/resources/templates/vector_search_with_rag.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RAG with Vector Retrieval",
3 | "description": "Build a search application that uses retrieval-augmented generation (RAG) to retrieve semantically similar documents using vector search, pass them to large language models, and synthesize answers.",
4 | "version": {
5 | "template": "1.0.0",
6 | "compatibility": [
7 | "2.19.0",
8 | "3.0.0"
9 | ]
10 | },
11 | "ui_metadata": {
12 | "type": "RAG with Vector Retrieval"
13 | }
14 | }
--------------------------------------------------------------------------------
/server/routes/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export * from './opensearch_routes_service';
7 | export * from './flow_framework_routes_service';
8 | export * from './ml_routes_service';
9 |
--------------------------------------------------------------------------------
/server/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { schema, TypeOf } from '@osd/config-schema';
7 |
8 | export const configSchema = schema.object({
9 | enabled: schema.maybe(schema.boolean()),
10 | });
11 |
12 | export type ConfigSchema = TypeOf;
13 |
14 | export interface FlowFrameworkDashboardsPluginSetup {}
15 | export interface FlowFrameworkDashboardsPluginStart {}
16 |
--------------------------------------------------------------------------------
/server/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import {
7 | ILegacyClusterClient,
8 | OpenSearchDashboardsRequest,
9 | RequestHandlerContext,
10 | } from '../../../../src/core/server';
11 |
12 | export function getClientBasedOnDataSource(
13 | context: RequestHandlerContext,
14 | dataSourceEnabled: boolean,
15 | request: OpenSearchDashboardsRequest,
16 | dataSourceId: string,
17 | client: ILegacyClusterClient
18 | ): (endpoint: string, clientParams?: Record) => any {
19 | if (dataSourceEnabled && dataSourceId && dataSourceId.trim().length != 0) {
20 | // client for remote cluster
21 | return context.dataSource.opensearch.legacy.getClient(dataSourceId).callAPI;
22 | } else {
23 | // fall back to default local cluster
24 | return client.asScoped(request).callAsCurrentUser;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export * from './mocks';
7 |
--------------------------------------------------------------------------------
/test/interfaces.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import { WORKFLOW_TYPE } from '../common/constants';
7 |
8 | export type WorkflowInput = {
9 | id: string;
10 | name: string;
11 | type: WORKFLOW_TYPE;
12 | version?: string;
13 | };
14 |
--------------------------------------------------------------------------------
/test/jest.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | module.exports = {
7 | rootDir: '../',
8 | roots: [''],
9 | coverageDirectory: './coverage',
10 | // we mock any non-js-related files and return an empty module. This is needed due to errors
11 | // when jest tries to interpret these types of files.
12 | moduleNameMapper: {
13 | '\\.(css|less|scss|sass|svg)$': '/test/mocks/empty_mock.ts',
14 | '@elastic/eui/lib/services/accessibility/html_id_generator':
15 | '/test/mocks/html_id_generator.ts',
16 |
17 | '^uuid$': '/test/mocks/uuid_mock.ts',
18 | '^uuid/.*$': '/test/mocks/uuid_mock.ts',
19 | },
20 | testEnvironment: 'jest-environment-jsdom',
21 | coverageReporters: ['lcov', 'text', 'cobertura'],
22 | testMatch: ['**/*.test.js', '**/*.test.jsx', '**/*.test.ts', '**/*.test.tsx'],
23 | collectCoverageFrom: [
24 | '**/*.ts',
25 | '**/*.tsx',
26 | '**/*.js',
27 | '**/*.jsx',
28 | '!**/models/**',
29 | '!**/node_modules/**',
30 | '!**/index.js',
31 | '!/public/app.js',
32 | '!/index.js',
33 | '!/babel.config.js',
34 | '!/test/**',
35 | '!/server/**',
36 | '!/coverage/**',
37 | '!/scripts/**',
38 | '!/build/**',
39 | '!**/vendor/**',
40 | ],
41 | clearMocks: true,
42 | modulePathIgnorePatterns: ['/offline-module-cache/'],
43 | testPathIgnorePatterns: ['/build/', '/node_modules/'],
44 | transformIgnorePatterns: ['/node_modules'],
45 | };
46 |
--------------------------------------------------------------------------------
/test/mocks/empty_mock.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export default {};
7 |
--------------------------------------------------------------------------------
/test/mocks/html_id_generator.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Mock implementation of EUI's htmlIdGenerator
3 | * This avoids the dependency on UUID and crypto.getRandomValues()
4 | */
5 |
6 | export const htmlIdGenerator = (prefix: string = ''): (() => string) => {
7 | let counter = 0;
8 | return (): string => {
9 | counter += 1;
10 | return `${prefix}${counter}`;
11 | };
12 | };
13 |
14 | export default htmlIdGenerator;
15 |
--------------------------------------------------------------------------------
/test/mocks/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export { mockCoreServices } from './mock_core_services';
7 |
--------------------------------------------------------------------------------
/test/mocks/mock_core_services.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | export const mockCoreServices = {
7 | getCore: () => {
8 | return {
9 | chrome: {
10 | setBreadcrumbs: jest.fn(),
11 | },
12 | };
13 | },
14 | getNotifications: () => {
15 | return {
16 | toasts: {
17 | addDanger: jest.fn().mockName('addDanger'),
18 | addSuccess: jest.fn().mockName('addSuccess'),
19 | },
20 | };
21 | },
22 | getDataSourceEnabled: () => {
23 | return {
24 | enabled: false,
25 | };
26 | },
27 | getUISettings: () => ({
28 | get: jest.fn((key) => {
29 | if (key === 'home:useNewHomePage') {
30 | return false;
31 | }
32 | }),
33 | }),
34 | getNavigationUI: () => ({
35 | TopNavMenu: jest.fn(),
36 | HeaderControl: jest.fn(),
37 | }),
38 |
39 | getApplication: () => ({
40 | setAppRightControls: jest.fn(),
41 | }),
42 |
43 | getHeaderActionMenu: () => jest.fn(),
44 | };
45 |
--------------------------------------------------------------------------------
/test/mocks/uuid_mock.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Mock implementation for UUID package
3 | * This avoids the need for crypto.getRandomValues() in tests
4 | */
5 |
6 | export function rng(): Uint8Array {
7 | const rnds8 = new Uint8Array(16);
8 | for (let i = 0; i < 16; i++) {
9 | rnds8[i] = Math.floor(Math.random() * 256);
10 | }
11 | return rnds8;
12 | }
13 |
14 | export function v1(): string {
15 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
16 | const r = (Math.random() * 16) | 0;
17 | const v = c === 'x' ? r : (r & 0x3) | 0x8;
18 | return v.toString(16);
19 | });
20 | }
21 |
22 | export function v4(): string {
23 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
24 | const r = (Math.random() * 16) | 0;
25 | const v = c === 'x' ? r : (r & 0x3) | 0x8;
26 | return v.toString(16);
27 | });
28 | }
29 |
30 | export default {
31 | v1,
32 | v4,
33 | rng,
34 | };
35 |
--------------------------------------------------------------------------------
/test/utils.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright OpenSearch Contributors
3 | * SPDX-License-Identifier: Apache-2.0
4 | */
5 |
6 | import {
7 | INITIAL_ERRORS_STATE,
8 | INITIAL_ML_STATE,
9 | INITIAL_OPENSEARCH_STATE,
10 | INITIAL_PRESETS_STATE,
11 | INITIAL_WORKFLOWS_STATE,
12 | } from '../public/store';
13 | import { WorkflowInput } from '../test/interfaces';
14 | import {
15 | MINIMUM_FULL_SUPPORTED_VERSION,
16 | WORKFLOW_TYPE,
17 | } from '../common/constants';
18 | import { UIState, Workflow, WorkflowDict } from '../common/interfaces';
19 | import {
20 | fetchEmptyMetadata,
21 | fetchHybridSearchMetadata,
22 | fetchMultimodalSearchMetadata,
23 | fetchSemanticSearchMetadata,
24 | } from '../public/pages/workflows/new_workflow/utils';
25 | import fs from 'fs';
26 | import path from 'path';
27 |
28 | export function mockStore(...workflowSets: WorkflowInput[]) {
29 | let workflowDict = {} as WorkflowDict;
30 | workflowSets?.forEach((workflowInput) => {
31 | workflowDict[workflowInput.id] = generateWorkflow(workflowInput);
32 | });
33 | return {
34 | getState: () => ({
35 | opensearch: INITIAL_OPENSEARCH_STATE,
36 | ml: INITIAL_ML_STATE,
37 | workflows: {
38 | ...INITIAL_WORKFLOWS_STATE,
39 | workflows: workflowDict,
40 | },
41 | presets: INITIAL_PRESETS_STATE,
42 | errors: INITIAL_ERRORS_STATE,
43 | }),
44 | dispatch: jest.fn(),
45 | subscribe: jest.fn(),
46 | replaceReducer: jest.fn(),
47 | [Symbol.observable]: jest.fn(),
48 | };
49 | }
50 |
51 | function generateWorkflow({ id, name, type }: WorkflowInput): Workflow {
52 | const isSearchWorkflow = [
53 | WORKFLOW_TYPE.SEMANTIC_SEARCH,
54 | WORKFLOW_TYPE.MULTIMODAL_SEARCH,
55 | WORKFLOW_TYPE.HYBRID_SEARCH,
56 | ].includes(type);
57 |
58 | const version = {
59 | template: '1.0.0',
60 | compatibility: isSearchWorkflow
61 | ? [MINIMUM_FULL_SUPPORTED_VERSION]
62 | : ['2.19.0', '3.0.0'],
63 | };
64 |
65 | return {
66 | id,
67 | name,
68 | version,
69 | ui_metadata: getConfig(type, version.compatibility[0]),
70 | };
71 | }
72 |
73 | function getConfig(workflowType: WORKFLOW_TYPE, version?: string) {
74 | let uiMetadata = {} as UIState;
75 | const searchVersion = version || MINIMUM_FULL_SUPPORTED_VERSION;
76 |
77 | switch (workflowType) {
78 | case WORKFLOW_TYPE.SEMANTIC_SEARCH: {
79 | uiMetadata = fetchSemanticSearchMetadata(searchVersion);
80 | break;
81 | }
82 | case WORKFLOW_TYPE.MULTIMODAL_SEARCH: {
83 | uiMetadata = fetchMultimodalSearchMetadata(searchVersion);
84 | break;
85 | }
86 | case WORKFLOW_TYPE.HYBRID_SEARCH: {
87 | uiMetadata = fetchHybridSearchMetadata(searchVersion);
88 | break;
89 | }
90 | default: {
91 | uiMetadata = fetchEmptyMetadata();
92 | break;
93 | }
94 | }
95 | return uiMetadata;
96 | }
97 |
98 | const templatesDir = path.resolve(
99 | __dirname,
100 | '..',
101 | 'server',
102 | 'resources',
103 | 'templates'
104 | );
105 |
106 | export const loadPresetWorkflowTemplates = () =>
107 | fs
108 | .readdirSync(templatesDir)
109 | .filter((file) => file.endsWith('.json'))
110 | .map((file) =>
111 | JSON.parse(fs.readFileSync(path.join(templatesDir, file), 'utf8'))
112 | );
113 |
114 | export const resizeObserverMock = jest.fn().mockImplementation(() => ({
115 | observe: jest.fn(),
116 | unobserve: jest.fn(),
117 | disconnect: jest.fn(),
118 | }));
119 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "skipLibCheck": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | // Allows for importing from `opensearch-dashboards` package for the exported types.
8 | "opensearch-dashboards": ["./opensearch_dashboards"],
9 | "ui/*": ["src/legacy/ui/public/*"],
10 | "test_utils/*": ["src/test_utils/public/*"]
11 | },
12 | // Support .tsx files and transform JSX into calls to React.createElement
13 | "jsx": "react",
14 | // Enables all strict type checking options.
15 | "strict": true,
16 | // enables "core language features"
17 | "lib": [
18 | // ESNext auto includes previous versions all the way back to es5
19 | "esnext",
20 | // includes support for browser APIs
21 | "dom"
22 | ],
23 | // Node 8 should support everything output by esnext, we override this
24 | // in webpack with loader-level compiler options
25 | "target": "esnext",
26 | // Use commonjs for node, overridden in webpack to keep import statements
27 | // to maintain support for things like `await import()`
28 | "module": "commonjs",
29 | // Allows default imports from modules with no default export. This does not affect code emit, just type checking.
30 | // We have to enable this option explicitly since `esModuleInterop` doesn't enable it automatically when ES2015 or
31 | // ESNext module format is used.
32 | "allowSyntheticDefaultImports": true,
33 | // Emits __importStar and __importDefault helpers for runtime babel ecosystem compatibility.
34 | "esModuleInterop": true,
35 | // Resolve modules in the same way as Node.js. Aka make `require` works the
36 | // same in TypeScript as it does in Node.js.
37 | "moduleResolution": "node",
38 | // Disallow inconsistently-cased references to the same file.
39 | "forceConsistentCasingInFileNames": true,
40 | // Disable the breaking keyof behaviour introduced in TS 2.9.2 until EUI is updated to support that too
41 | "keyofStringsOnly": true,
42 | // Forbid unused local variables as the rule was deprecated by ts-lint
43 | "noUnusedLocals": true,
44 | // Provide full support for iterables in for..of, spread and destructuring when targeting ES5 or ES3.
45 | "downlevelIteration": true,
46 | // import tslib helpers rather than inlining helpers for iteration or spreading, for instance
47 | "importHelpers": true,
48 | // adding global typings
49 | "types": ["node", "jest", "react"]
50 | },
51 | "include": [
52 | "index.ts",
53 | "common/**/*.ts",
54 | "public/**/*.ts",
55 | "public/**/*.tsx",
56 | "server/**/*.ts",
57 | "test/**/*",
58 | "../../typings/**/*"
59 | ],
60 | "exclude": ["node_modules", "*/node_modules/"]
61 | }
62 |
--------------------------------------------------------------------------------