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

Support MDS on workflows and workflow_detail pages #253

Closed
wants to merge 15 commits into from
3 changes: 3 additions & 0 deletions common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export type Index = {
/**
********** WORKFLOW TYPES/INTERFACES **********
*/
export type MDSQueryParams = {
dataSourceId?: string;
};

export type ConfigFieldType =
| 'string'
Expand Down
15 changes: 15 additions & 0 deletions common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,22 @@

import moment from 'moment';
import { DATE_FORMAT_PATTERN } from './';
import { isEmpty } from 'lodash';

export function toFormattedDate(timestampMillis: number): String {
return moment(new Date(timestampMillis)).format(DATE_FORMAT_PATTERN);
}

const PERMISSIONS_ERROR_PATTERN = /no permissions for \[(.+)\] and User \[name=(.+), backend_roles/;

export const prettifyErrorMessage = (rawErrorMessage: string) => {
if (isEmpty(rawErrorMessage) || rawErrorMessage === 'undefined') {
return 'Unknown error is returned.';
}
const match = rawErrorMessage.match(PERMISSIONS_ERROR_PATTERN);
if (isEmpty(match)) {
return rawErrorMessage;
} else {
return `User ${match[2]} has no permissions to [${match[1]}].`;
}
};
2 changes: 1 addition & 1 deletion opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"navigation",
"opensearchDashboardsUtils"
],
"optionalPlugins": []
"optionalPlugins": ["dataSource","dataSourceManagement"]
}
41 changes: 35 additions & 6 deletions public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
*/

import React from 'react';
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
import {
Route,
RouteComponentProps,
Switch,
useLocation,
} from 'react-router-dom';
import {
EuiPageSideBar,
EuiSideNav,
Expand All @@ -18,13 +23,24 @@ import {
WorkflowDetailRouterProps,
WorkflowsRouterProps,
} from './pages';
import { MountPoint } from '../../../src/core/public';
import {
constructHrefWithDataSourceId,
getDataSourceFromURL,
} from './utils/utils';

// styling
import './global-styles.scss';

interface Props extends RouteComponentProps {}
interface Props extends RouteComponentProps {
setHeaderActionMenu: (menuMount?: MountPoint) => void;
}

export const FlowFrameworkDashboardsApp = (props: Props) => {
const { setHeaderActionMenu } = props;
const location = useLocation();
const queryParams = getDataSourceFromURL(location);
const dataSourceId = queryParams.dataSourceId;
const sidebar = (
<EuiPageSideBar style={{ minWidth: 190 }} hidden={false} paddingSize="l">
<EuiSideNav
Expand All @@ -37,7 +53,10 @@ export const FlowFrameworkDashboardsApp = (props: Props) => {
{
name: Navigation.Workflows,
id: 1,
href: `#${APP_PATH.WORKFLOWS}`,
href: constructHrefWithDataSourceId(
APP_PATH.WORKFLOWS,
dataSourceId
),
isSelected: props.location.pathname === APP_PATH.WORKFLOWS,
},
],
Expand All @@ -61,12 +80,17 @@ export const FlowFrameworkDashboardsApp = (props: Props) => {
path={APP_PATH.WORKFLOW_DETAIL}
render={(
routeProps: RouteComponentProps<WorkflowDetailRouterProps>
) => <WorkflowDetail {...routeProps} />}
) => (
<WorkflowDetail
setActionMenu={setHeaderActionMenu}
{...routeProps}
/>
)}
/>
<Route
path={APP_PATH.WORKFLOWS}
render={(routeProps: RouteComponentProps<WorkflowsRouterProps>) => (
<Workflows {...routeProps} />
<Workflows setActionMenu={setHeaderActionMenu} {...routeProps} />
)}
/>
{/*
Expand All @@ -82,7 +106,12 @@ export const FlowFrameworkDashboardsApp = (props: Props) => {
pathname: APP_PATH.WORKFLOWS,
});
}
return <Workflows {...routeProps} />;
return (
<Workflows
setActionMenu={setHeaderActionMenu}
{...routeProps}
/>
);
}}
/>
</Switch>
Expand Down
63 changes: 53 additions & 10 deletions public/pages/workflow_detail/workflow_detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useEffect } from 'react';
import React, { useEffect, ReactElement } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { ReactFlowProvider } from 'reactflow';
Expand All @@ -22,17 +22,30 @@ import {
DEFAULT_NEW_WORKFLOW_NAME,
FETCH_ALL_QUERY_BODY,
} from '../../../common';
import { MountPoint } from '../../../../../src/core/public';

// styling
import './workflow-detail-styles.scss';
import '../../global-styles.scss';

import { getDataSourceId } from '../../utils/utils';

import {
getDataSourceManagementPlugin,
getDataSourceEnabled,
getNotifications,
getSavedObjectsClient,
} from '../../services';
import { DataSourceViewConfig } from '../../../../../src/plugins/data_source_management/public';

export interface WorkflowDetailRouterProps {
workflowId: string;
}

interface WorkflowDetailProps
extends RouteComponentProps<WorkflowDetailRouterProps> {}
extends RouteComponentProps<WorkflowDetailRouterProps> {
setActionMenu: (menuMount?: MountPoint) => void;
}

/**
* The workflow details page. This is where users will configure, create, and
Expand All @@ -42,6 +55,8 @@ interface WorkflowDetailProps

export function WorkflowDetail(props: WorkflowDetailProps) {
const dispatch = useAppDispatch();
const dataSourceEnabled = getDataSourceEnabled().enabled;
const dataSourceId = getDataSourceId();
const { workflows } = useSelector((state: AppState) => state.workflows);

// selected workflow state
Expand All @@ -50,23 +65,51 @@ export function WorkflowDetail(props: WorkflowDetailProps) {
const workflowName = workflow ? workflow.name : DEFAULT_NEW_WORKFLOW_NAME;

useEffect(() => {
getCore().chrome.setBreadcrumbs([
BREADCRUMBS.FLOW_FRAMEWORK,
BREADCRUMBS.WORKFLOWS,
{ text: workflowName },
]);
});
if (dataSourceEnabled) {
getCore().chrome.setBreadcrumbs([
BREADCRUMBS.FLOW_FRAMEWORK,
BREADCRUMBS.WORKFLOWS(dataSourceId),
{ text: workflowName },
]);
} else {
getCore().chrome.setBreadcrumbs([
BREADCRUMBS.FLOW_FRAMEWORK,
BREADCRUMBS.WORKFLOWS(),
{ text: workflowName },
]);
}
}, []);

// On initial load:
// - fetch workflow
// - fetch available models as their IDs may be used when building flows
useEffect(() => {
dispatch(getWorkflow(workflowId));
dispatch(searchModels(FETCH_ALL_QUERY_BODY));
dispatch(getWorkflow({ workflowId, dataSourceId }));
dispatch(searchModels({ apiBody: FETCH_ALL_QUERY_BODY, dataSourceId }));
}, []);

let renderDataSourceComponent: ReactElement | null = null;
if (dataSourceEnabled && getDataSourceManagementPlugin()) {
const DataSourceMenu = getDataSourceManagementPlugin().ui.getDataSourceMenu<
DataSourceViewConfig
>();
renderDataSourceComponent = (
<DataSourceMenu
setMenuMountPoint={props.setActionMenu}
componentType={'DataSourceView'}
componentConfig={{
activeOption: [{ id: dataSourceId }],
fullWidth: false,
savedObjects: getSavedObjectsClient(),
notifications: getNotifications(),
}}
/>
);
}

return (
<ReactFlowProvider>
{dataSourceEnabled && renderDataSourceComponent}
<EuiPage>
<EuiPageBody className="workflow-detail stretch-relative">
<WorkflowDetailHeader workflow={workflow} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
useAppDispatch,
} from '../../../../store';
import { getCore } from '../../../../services';
import { getDataSourceId } from '../../../../utils/utils';
import { MapArrayField } from '../input_fields';

interface InputTransformModalProps {
Expand All @@ -68,6 +69,7 @@ interface InputTransformModalProps {
*/
export function InputTransformModal(props: InputTransformModalProps) {
const dispatch = useAppDispatch();
const dataSourceId = getDataSourceId();
const { values } = useFormikContext<WorkflowFormValues>();

// source input / transformed output state
Expand Down Expand Up @@ -120,8 +122,11 @@ export function InputTransformModal(props: InputTransformModalProps) {
);
await dispatch(
simulatePipeline({
pipeline: curIngestPipeline as IngestPipelineConfig,
docs: curDocs,
apiBody: {
pipeline: curIngestPipeline as IngestPipelineConfig,
docs: curDocs,
},
dataSourceId,
})
)
.unwrap()
Expand Down Expand Up @@ -169,11 +174,14 @@ export function InputTransformModal(props: InputTransformModalProps) {
// the partial search pipeline (inline) to get the latest transformed version of the response.
dispatch(
searchIndex({
index: values.ingest.index.name,
body: JSON.stringify({
...JSON.parse(values.search.request as string),
search_pipeline: curSearchPipeline,
}),
apiBody: {
index: values.ingest.index.name,
body: JSON.stringify({
...JSON.parse(values.search.request as string),
search_pipeline: curSearchPipeline,
}),
},
dataSourceId,
})
)
.unwrap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
} from '../../../../store';
import { getCore } from '../../../../services';
import { MapArrayField } from '../input_fields';
import { getDataSourceId } from '../../../../utils/utils';

interface OutputTransformModalProps {
uiConfig: WorkflowConfig;
Expand All @@ -65,6 +66,7 @@ interface OutputTransformModalProps {
*/
export function OutputTransformModal(props: OutputTransformModalProps) {
const dispatch = useAppDispatch();
const dataSourceId = getDataSourceId();
const { values } = useFormikContext<WorkflowFormValues>();

// source input / transformed output state
Expand Down Expand Up @@ -124,8 +126,11 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
);
await dispatch(
simulatePipeline({
pipeline: curIngestPipeline,
docs: curDocs,
apiBody: {
pipeline: curIngestPipeline,
docs: curDocs,
},
dataSourceId,
})
)
.unwrap()
Expand Down Expand Up @@ -162,11 +167,14 @@ export function OutputTransformModal(props: OutputTransformModalProps) {
// version of the request.
dispatch(
searchIndex({
index: values.ingest.index.name,
body: JSON.stringify({
...JSON.parse(values.search.request as string),
search_pipeline: curSearchPipeline,
}),
apiBody: {
index: values.ingest.index.name,
body: JSON.stringify({
...JSON.parse(values.search.request as string),
search_pipeline: curSearchPipeline,
}),
},
dataSourceId,
})
)
.unwrap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
searchIndex,
useAppDispatch,
} from '../../../../store';
import { getDataSourceId } from '../../../../utils/utils';

interface ConfigureSearchRequestProps {
setQuery: (query: string) => void;
Expand All @@ -42,6 +43,7 @@ interface ConfigureSearchRequestProps {
*/
export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) {
const dispatch = useAppDispatch();
const dataSourceId = getDataSourceId();

// Form state
const { values, setFieldValue, setFieldTouched } = useFormikContext<
Expand Down Expand Up @@ -80,7 +82,7 @@ export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) {
useEffect(() => {
if (!ingestEnabled) {
// Fetch all indices besides system indices
dispatch(catIndices('*,-.*'));
dispatch(catIndices({ pattern: '*,-.*', dataSourceId }));
}
}, []);

Expand Down Expand Up @@ -177,9 +179,12 @@ export function ConfigureSearchRequest(props: ConfigureSearchRequestProps) {
// see https://opensearch.org/docs/latest/search-plugins/search-pipelines/using-search-pipeline/#disabling-the-default-pipeline-for-a-request
dispatch(
searchIndex({
index: values.search.index.name,
body: values.search.request,
searchPipeline: '_none',
apiBody: {
index: indexName,
body: values.search.request,
searchPipeline: '_none',
},
dataSourceId,
})
)
.unwrap()
Expand Down
Loading
Loading