From bc2228d50ba5d26c16160291f5e3d5bb3ef25e43 Mon Sep 17 00:00:00 2001 From: gaobinlong Date: Tue, 3 Sep 2024 22:06:44 +0800 Subject: [PATCH] Move entry point to query editor Signed-off-by: gaobinlong --- opensearch_dashboards.json | 2 +- .../SuggestAnomalyDetector.test.tsx | 301 ++++++++---------- .../DiscoverAction/SuggestAnomalyDetector.tsx | 77 ++--- public/plugin.ts | 19 +- public/utils/contextMenu/getActions.tsx | 67 ++-- public/utils/discoverAction.tsx | 38 --- server/routes/assistant.ts | 6 +- server/utils/constants.ts | 2 +- 8 files changed, 230 insertions(+), 282 deletions(-) delete mode 100644 public/utils/discoverAction.tsx diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 551bd678..5aa5d906 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -8,7 +8,7 @@ "optionalPlugins": [ "dataSource", "dataSourceManagement", - "dataExplorer" + "assistantDashboards" ], "requiredPlugins": [ "opensearchDashboardsUtils", diff --git a/public/components/DiscoverAction/SuggestAnomalyDetector.test.tsx b/public/components/DiscoverAction/SuggestAnomalyDetector.test.tsx index cca54108..63e753c7 100644 --- a/public/components/DiscoverAction/SuggestAnomalyDetector.test.tsx +++ b/public/components/DiscoverAction/SuggestAnomalyDetector.test.tsx @@ -16,50 +16,13 @@ import { import { Provider } from 'react-redux'; import configureStore from '../../redux/configureStore'; -import GenerateAnomalyDetector from './SuggestAnomalyDetector'; -import { DiscoverActionContext } from '../../../../../src/plugins/data_explorer/public'; +import SuggestAnomalyDetector from './SuggestAnomalyDetector'; import { fieldFormatsMock } from '../../../../../src/plugins/data/common/field_formats/mocks'; import { IndexPattern } from '../../../../../src/plugins/data/common'; import userEvent from '@testing-library/user-event'; import { HttpFetchOptionsWithPath } from '../../../../../src/core/public'; import { BASE_NODE_API_PATH } from '../../../utils/constants'; - -const notifications = { - toasts: { - addDanger: jest.fn().mockName('addDanger'), - addSuccess: jest.fn().mockName('addSuccess'), - } -}; - -const getNotifications = () => { - return notifications; -} - -jest.mock('../../services', () => ({ - ...jest.requireActual('../../services'), - getNotifications: getNotifications, -})); - -const renderWithRouter = (context: DiscoverActionContext) => ({ - ...render( - - - - ( - - - - )} - /> - - - - ), -}); +import { getQueryService } from '../../services'; export function shouldReadFieldFromDocValues(aggregatable: boolean, opensearchType: string) { return ( @@ -69,7 +32,6 @@ export function shouldReadFieldFromDocValues(aggregatable: boolean, opensearchTy ); } - function stubbedSampleFields() { return [ ['bytes', 'long', true, true, { count: 10 }], @@ -141,108 +103,123 @@ function createIndexPattern(id: string): IndexPattern { }; } -const expectedAnomalyDetector = { - name: "test-pattern_anomaly_detector", - description: "Created based on the OpenSearch Assistant", - indices: ["test-pattern"], - filterQuery: { - match_all: {} - }, - uiMetadata: { - features: { - feature_responseLatency: { - featureType: "simple_aggs", - aggregationBy: "avg", - aggregationOf: "responseLatency" - }, - feature_response: { - featureType: "simple_aggs", - aggregationBy: "sum", - aggregationOf: "response" - } - }, - filters: [] - }, - featureAttributes: [ - { - featureName: "feature_responseLatency", - featureEnabled: true, - importance: 1, - aggregationQuery: { - feature_response_latency: { - avg: { - field: "responseLatency" - } - } - } - }, - { - featureName: "feature_response", - featureEnabled: true, - importance: 1, - aggregationQuery: { - feature_response: { - sum: { - field: "response" - } - } - } - } - ], - timeField: "timestamp", - detectionInterval: { - period: { - interval: 10, - unit: "Minutes" - } - }, - windowDelay: { - period: { - interval: 1, - unit: "Minutes" - } - }, - shingleSize: 8, - categoryField: ["ip"] +const mockedIndexPattern = createIndexPattern('test-pattern'); + +const notifications = { + toasts: { + addDanger: jest.fn().mockName('addDanger'), + addSuccess: jest.fn().mockName('addSuccess'), + } }; +const getNotifications = () => { + return notifications; +} + +jest.mock('../../services', () => ({ + ...jest.requireActual('../../services'), + getNotifications: getNotifications, + getQueryService: jest.fn().mockReturnValue({ + queryString: { + getQuery: jest.fn(), + }, + }), + getIndexPatternService: () => ({ + get: () => (mockedIndexPattern) + }) +})); + +const renderWithRouter = () => ({ + ...render( + + + + ( + + + + )} + /> + + + + ), +}); + describe('GenerateAnomalyDetector spec', () => { - describe('Renders loading component', () => { + describe('Renders failed', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders with invalid dataset type', async () => { + const queryService = getQueryService(); + queryService.queryString.getQuery.mockReturnValue({ + dataset: { + id: undefined, + title: undefined, + type: 'INDEX' + }, + }); + + + const { queryByText } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).toBeNull(); + + await waitFor(() => { + expect(getNotifications().toasts.addDanger).toHaveBeenCalledTimes(1); + expect(getNotifications().toasts.addDanger).toHaveBeenCalledWith( + 'Unsupported dataset type' + ); + }); + }); + it('renders empty component', async () => { - httpClientMock.post = jest.fn().mockResolvedValue({ - ok: true, - generatedParameters: { - categoryField: '', - aggregationField: '', - aggregationMethod: '', - dateFields: '', + const queryService = getQueryService(); + queryService.queryString.getQuery.mockReturnValue({ + dataset: { + id: undefined, + title: undefined, + type: 'INDEX_PATTERN' }, }); - const context = { - indexPattern: createIndexPattern(''), - }; - const { queryByText } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).toBeNull(); + const { queryByText } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).toBeNull(); await waitFor(() => { expect(getNotifications().toasts.addDanger).toHaveBeenCalledTimes(1); expect(getNotifications().toasts.addDanger).toHaveBeenCalledWith( - 'Cannot extract index pattern from the context' + 'Cannot extract complete index info from the context' ); }); }); + }); + + describe('Renders loading component', () => { + beforeEach(() => { + jest.clearAllMocks(); + const queryService = getQueryService(); + queryService.queryString.getQuery.mockReturnValue({ + dataset: { + id: 'test-pattern', + title: 'test-pattern', + type: 'INDEX_PATTERN', + timeFieldName: '@timestamp', + }, + }); + }); it('renders with empty generated parameters', async () => { httpClientMock.post = jest.fn().mockResolvedValue({ ok: true, }); - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(getNotifications().toasts.addDanger).toHaveBeenCalledTimes(1); @@ -263,11 +240,8 @@ describe('GenerateAnomalyDetector spec', () => { }, }); - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(getNotifications().toasts.addDanger).toHaveBeenCalledTimes(1); @@ -288,11 +262,8 @@ describe('GenerateAnomalyDetector spec', () => { }, }); - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(getNotifications().toasts.addDanger).toHaveBeenCalledTimes(1); @@ -313,11 +284,8 @@ describe('GenerateAnomalyDetector spec', () => { }, }); - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(getNotifications().toasts.addDanger).toHaveBeenCalledTimes(1); @@ -338,11 +306,8 @@ describe('GenerateAnomalyDetector spec', () => { }, }); - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(queryByText('Create detector')).not.toBeNull(); @@ -354,8 +319,20 @@ describe('GenerateAnomalyDetector spec', () => { }); - describe('Test API calls', () => { + beforeEach(() => { + jest.clearAllMocks(); + const queryService = getQueryService(); + queryService.queryString.getQuery.mockReturnValue({ + dataset: { + id: 'test-pattern', + title: 'test-pattern', + type: 'INDEX_PATTERN', + timeFieldName: '@timestamp', + }, + }); + }); + it('All API calls execute successfully', async () => { httpClientMock.post = jest.fn((pathOrOptions: string | HttpFetchOptionsWithPath) => { const url = typeof pathOrOptions === 'string' ? pathOrOptions : pathOrOptions.path; @@ -384,11 +361,8 @@ describe('GenerateAnomalyDetector spec', () => { } }); - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText, getByTestId } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText, getByTestId } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(queryByText('Generating parameters...')).toBeNull(); expect(queryByText('Create detector')).not.toBeNull(); @@ -407,15 +381,6 @@ describe('GenerateAnomalyDetector spec', () => { body: JSON.stringify({ index: 'test-pattern' }), } ); - expect(httpClientMock.post).toHaveBeenCalledWith( - `${BASE_NODE_API_PATH}/detectors`, - { - body: JSON.stringify(expectedAnomalyDetector), - } - ); - expect(httpClientMock.post).toHaveBeenCalledWith( - `${BASE_NODE_API_PATH}/detectors/test/start` - ); expect(getNotifications().toasts.addSuccess).toHaveBeenCalledTimes(1); }); }); @@ -426,11 +391,8 @@ describe('GenerateAnomalyDetector spec', () => { error: 'Generate parameters failed' }); - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(getNotifications().toasts.addDanger).toHaveBeenCalledTimes(1); expect(getNotifications().toasts.addDanger).toHaveBeenCalledWith( @@ -472,12 +434,8 @@ describe('GenerateAnomalyDetector spec', () => { }, }); - - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText, getByTestId } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText, getByTestId } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(queryByText('Generating parameters...')).toBeNull(); @@ -539,11 +497,8 @@ describe('GenerateAnomalyDetector spec', () => { }); - const context = { - indexPattern: createIndexPattern('test-pattern'), - } - const { queryByText, getByTestId } = renderWithRouter(context); - expect(queryByText('Suggest anomaly detector')).not.toBeNull(); + const { queryByText, getByTestId } = renderWithRouter(); + expect(queryByText('Suggested anomaly detector')).not.toBeNull(); await waitFor(() => { expect(queryByText('Generating parameters...')).toBeNull(); diff --git a/public/components/DiscoverAction/SuggestAnomalyDetector.tsx b/public/components/DiscoverAction/SuggestAnomalyDetector.tsx index a5ec1a59..7831c18c 100644 --- a/public/components/DiscoverAction/SuggestAnomalyDetector.tsx +++ b/public/components/DiscoverAction/SuggestAnomalyDetector.tsx @@ -24,7 +24,7 @@ import { EuiComboBox, } from '@elastic/eui'; import '../FeatureAnywhereContextMenu/CreateAnomalyDetector/styles.scss'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { isEmpty, get } from 'lodash'; import { Field, @@ -54,6 +54,7 @@ import { } from '../../../server/utils/constants'; import { focusOnFirstWrongFeature, + getCategoryFields, initialFeatureValue, validateFeatures, } from '../../pages/ConfigureModel/utils/helpers'; @@ -61,7 +62,7 @@ import { formikToDetector } from '../../pages/ReviewAndCreate/utils/helpers'; import { FormattedFormRow } from '../FormattedFormRow/FormattedFormRow'; import { FeatureAccordion } from '../../pages/ConfigureModel/components/FeatureAccordion'; import { AD_DOCS_LINK, DEFAULT_SHINGLE_SIZE, MAX_FEATURE_NUM, PLUGIN_NAME } from '../../utils/constants'; -import { getNotifications } from '../../services'; +import { getNotifications, getQueryService } from '../../services'; import { prettifyErrorMessage } from '../../../server/utils/helpers'; import EnhancedAccordion from '../FeatureAnywhereContextMenu/EnhancedAccordion'; import MinimalAccordion from '../FeatureAnywhereContextMenu/MinimalAccordion'; @@ -69,10 +70,11 @@ import { DataFilterList } from '../../pages/DefineDetector/components/DataFilter import { generateParameters } from '../../redux/reducers/assistant'; import { FEATURE_TYPE } from '../../models/interfaces'; import { FeaturesFormikValues } from '../../pages/ConfigureModel/models/interfaces'; -import { DiscoverActionContext } from '../../../../../src/plugins/data_explorer/public/types'; import { getMappings } from '../../redux/reducers/opensearch'; import { mountReactNode } from '../../../../../src/core/public/utils'; import { formikToDetectorName } from '../FeatureAnywhereContextMenu/CreateAnomalyDetector/helpers'; +import { DEFAULT_DATA } from '../../../../../src/plugins/data/common'; +import { AppState } from '../../redux/reducers'; export interface GeneratedParameters { categoryField: string; @@ -80,41 +82,35 @@ export interface GeneratedParameters { dateFields: string[]; } -function GenerateAnomalyDetector({ +function SuggestAnomalyDetector({ closeFlyout, - context, }: { closeFlyout: any; - context: DiscoverActionContext; }) { const dispatch = useDispatch(); const notifications = getNotifications(); - const indexPatternId = context.indexPattern?.id; - const indexPatternName = context.indexPattern?.title; - if (!indexPatternId || !indexPatternName) { + const queryString = getQueryService().queryString; + const dataset = queryString.getQuery().dataset || queryString.getDefaultQuery().dataset; + const datasetType = dataset.type; + if (datasetType != DEFAULT_DATA.SET_TYPES.INDEX_PATTERN && datasetType != DEFAULT_DATA.SET_TYPES.INDEX) { notifications.toasts.addDanger( - 'Cannot extract index pattern from the context' + 'Unsupported dataset type' ); return <>; } - const dataSourceId = context.indexPattern?.dataSourceRef?.id; - const timeFieldFromIndexPattern = context.indexPattern?.timeFieldName; - const fieldsFromContext = context.indexPattern?.fields || []; - const [categoricalFields, dateFields] = fieldsFromContext.reduce( - ([cFields, dFields], indexPatternField) => { - const esType = indexPatternField.spec.esTypes?.[0]; - const name = indexPatternField.spec.name; - if (esType === 'keyword' || esType === 'ip') { - cFields.push(name); - } else if (esType === 'date') { - dFields.push(name); - } - return [cFields, dFields]; - }, - [[], []] as [string[], string[]] - ) || [[], []]; + const indexPatternId = dataset.id; + // indexName could be a index pattern or a concrete index + const indexName = dataset.title; + const timeFieldName = dataset.timeFieldName; + if (!indexPatternId || !indexName || !timeFieldName) { + notifications.toasts.addDanger( + 'Cannot extract complete index info from the context' + ); + return <>; + } + const dataSourceId = dataset.dataSource?.id; const [isLoading, setIsLoading] = useState(true); const [buttonName, setButtonName] = useState( 'Generating parameters...' @@ -127,14 +123,22 @@ function GenerateAnomalyDetector({ const [delayValue, setDelayValue] = useState(1); const [enabled, setEnabled] = useState(false); const [detectorName, setDetectorName] = useState( - formikToDetectorName(indexPatternName.substring(0, 40)) + formikToDetectorName(indexName.substring(0, 40)) + ); + const indexDataTypes = useSelector( + (state: AppState) => state.opensearch.dataTypes ); + const categoricalFields = getCategoryFields(indexDataTypes); + + const dateFields = get(indexDataTypes, 'date', []) as string[]; + const dateNanoFields = get(indexDataTypes, 'date_nanos', []) as string[]; + const allDateFields = dateFields.concat(dateNanoFields); // let LLM to generate parameters for creating anomaly detector async function getParameters() { try { const result = await dispatch( - generateParameters(indexPatternName!, dataSourceId) + generateParameters(indexName!, dataSourceId) ); const rawGeneratedParameters = get(result, 'generatedParameters'); if (!rawGeneratedParameters) { @@ -207,11 +211,8 @@ function GenerateAnomalyDetector({ useEffect(() => { async function fetchData() { + await dispatch(getMappings(indexName, dataSourceId)); await getParameters(); - const getMappingDispatchCall = dispatch( - getMappings(indexPatternName, dataSourceId) - ); - await Promise.all([getMappingDispatchCall]); } fetchData(); }, []); @@ -353,8 +354,8 @@ function GenerateAnomalyDetector({ let initialDetectorValue = { name: detectorName, - index: [{ label: indexPatternName }], - timeField: timeFieldFromIndexPattern, + index: [{ label: indexName }], + timeField: timeFieldName, interval: intervalValue, windowDelay: delayValue, shingleSize: DEFAULT_SHINGLE_SIZE, @@ -382,7 +383,7 @@ function GenerateAnomalyDetector({

- Suggest anomaly detector + Suggested anomaly detector

@@ -729,7 +730,7 @@ function GenerateAnomalyDetector({ data-test-subj="timestampFilter" id="timeField" placeholder="Find timestamp" - options={dateFields.map((field) => { + options={allDateFields.map((field) => { return { label: field, }; @@ -750,7 +751,7 @@ function GenerateAnomalyDetector({ label: field.value, }, ] - : [{ label: timeFieldFromIndexPattern }] + : [{ label: timeFieldName }] } singleSelection={{ asPlainText: true }} isClearable={false} @@ -857,4 +858,4 @@ function GenerateAnomalyDetector({ ); } -export default GenerateAnomalyDetector; +export default SuggestAnomalyDetector; diff --git a/public/plugin.ts b/public/plugin.ts index 68eedfcf..a340e6be 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -27,7 +27,7 @@ import { } from '../../../src/plugins/embeddable/public'; import { ACTION_AD } from './action/ad_dashboard_action'; import { APP_PATH, DASHBOARD_PAGE_NAV_ID, DETECTORS_PAGE_NAV_ID, OVERVIEW_PAGE_NAV_ID, PLUGIN_NAME } from './utils/constants'; -import { getActions } from './utils/contextMenu/getActions'; +import { ACTION_SUGGEST_AD, getActions, getSuggestAnomalyDetectorAction } from './utils/contextMenu/getActions'; import { overlayAnomaliesFunction } from './expressions/overlay_anomalies'; import { setClient, @@ -42,7 +42,8 @@ import { setDataSourceManagementPlugin, setDataSourceEnabled, setNavigationUI, - setApplication + setApplication, + setIndexPatternService } from './services'; import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; import { @@ -50,16 +51,16 @@ import { VisAugmenterStart, } from '../../../src/plugins/vis_augmenter/public'; import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; -import { DataPublicPluginStart } from '../../../src/plugins/data/public'; +import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../src/plugins/data/public'; import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public'; import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; -import { getDiscoverAction } from './utils/discoverAction'; -import { DataExplorerPluginSetup } from '../../../src/plugins/data_explorer/public'; +import { AssistantSetup } from '../../../plugins/dashboards-assistant/public'; declare module '../../../src/plugins/ui_actions/public' { export interface ActionContextMapping { [ACTION_AD]: {}; + [ACTION_SUGGEST_AD]: {} } } @@ -70,7 +71,7 @@ export interface AnomalyDetectionSetupDeps { visAugmenter: VisAugmenterSetup; dataSourceManagement: DataSourceManagementPluginSetup; dataSource: DataSourcePluginSetup; - dataExplorer: DataExplorerPluginSetup; + data: DataPublicPluginSetup; } export interface AnomalyDetectionStartDeps { @@ -192,9 +193,9 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, action); }); - // Add action to Discover - const discoverAction = getDiscoverAction(); - plugins.dataExplorer.registerDiscoverAction(discoverAction); + // Add suggest anomaly detector action to the uiActions in Discover + const suggestAnomalyDetectorAction = getSuggestAnomalyDetectorAction(); + plugins.uiActions.addTriggerAction(plugins.assistantDashboards.assistantTriggers.AI_ASSISTANT_TRIGGER, suggestAnomalyDetectorAction); // registers the expression function used to render anomalies on an Augmented Visualization plugins.expressions.registerFunction(overlayAnomaliesFunction); diff --git a/public/utils/contextMenu/getActions.tsx b/public/utils/contextMenu/getActions.tsx index f58a7a9e..30ae041e 100644 --- a/public/utils/contextMenu/getActions.tsx +++ b/public/utils/contextMenu/getActions.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { i18n } from '@osd/i18n'; import { EuiIconType } from '@elastic/eui'; import { toMountPoint } from '../../../../../src/plugins/opensearch_dashboards_react/public'; -import { Action } from '../../../../../src/plugins/ui_actions/public'; +import { Action, createAction } from '../../../../../src/plugins/ui_actions/public'; import { createADAction } from '../../action/ad_dashboard_action'; import AnywhereParentFlyout from '../../components/FeatureAnywhereContextMenu/AnywhereParentFlyout'; import { Provider } from 'react-redux'; @@ -16,6 +16,9 @@ import DocumentationTitle from '../../components/FeatureAnywhereContextMenu/Docu import { AD_FEATURE_ANYWHERE_LINK, ANOMALY_DETECTION_ICON } from '../constants'; import { getClient, getOverlays } from '../../../public/services'; import { FLYOUT_MODES } from '../../../public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/constants'; +import SuggestAnomalyDetector from '../../../public/components/DiscoverAction/SuggestAnomalyDetector'; + +export const ACTION_SUGGEST_AD = 'suggestAnomalyDetector'; // This is used to create all actions in the same context menu const grouping: Action['grouping'] = [ @@ -31,23 +34,23 @@ const grouping: Action['grouping'] = [ export const getActions = () => { const getOnClick = (startingFlyout) => - async ({ embeddable }) => { - const overlayService = getOverlays(); - const openFlyout = overlayService.openFlyout; - const store = configureStore(getClient()); - const overlay = openFlyout( - toMountPoint( - - overlay.close()} - /> - - ), - { size: 'm', className: 'context-menu__flyout' } - ); - }; + async ({ embeddable }) => { + const overlayService = getOverlays(); + const openFlyout = overlayService.openFlyout; + const store = configureStore(getClient()); + const overlay = openFlyout( + toMountPoint( + + overlay.close()} + /> + + ), + { size: 'm', className: 'context-menu__flyout' } + ); + }; return [ { @@ -87,3 +90,31 @@ export const getActions = () => { }, ].map((options) => createADAction({ ...options, grouping })); }; + +export const getSuggestAnomalyDetectorAction = () => { + const onClick = async function () { + const overlayService = getOverlays(); + const openFlyout = overlayService.openFlyout; + const store = configureStore(getClient()); + const overlay = openFlyout( + toMountPoint( + + overlay.close()} + /> + + ) + ); + } + + return createAction({ + id: 'suggestAnomalyDetector', + order: 100, + type: ACTION_SUGGEST_AD, + getDisplayName: () => 'Suggest anomaly detector', + getIconType: () => ANOMALY_DETECTION_ICON, + execute: async () => { + onClick(); + }, + }); +} diff --git a/public/utils/discoverAction.tsx b/public/utils/discoverAction.tsx deleted file mode 100644 index 9b345ee8..00000000 --- a/public/utils/discoverAction.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react' -import { ANOMALY_DETECTION_ICON } from "./constants"; -import GenerateAnomalyDetector from "../components/DiscoverAction/SuggestAnomalyDetector"; -import { getClient, getOverlays } from '../../public/services'; -import { toMountPoint } from "../../../../src/plugins/opensearch_dashboards_react/public"; -import { Provider } from "react-redux"; -import configureStore from '../redux/configureStore'; -import { DiscoverAction, DiscoverActionContext } from "../../../../src/plugins/data_explorer/public/types"; - -export const getDiscoverAction = (): DiscoverAction => { - const onClick = function (context: DiscoverActionContext) { - const overlayService = getOverlays(); - const openFlyout = overlayService.openFlyout; - const store = configureStore(getClient()); - const overlay = openFlyout( - toMountPoint( - - overlay.close()} - context={context} - /> - - ) - ); - } - - return { - order: 0, - name: 'Suggest anomaly detector', - iconType: ANOMALY_DETECTION_ICON, - onClick: onClick, - } -}; diff --git a/server/routes/assistant.ts b/server/routes/assistant.ts index 6669b9c3..5c1a0cd0 100644 --- a/server/routes/assistant.ts +++ b/server/routes/assistant.ts @@ -56,16 +56,14 @@ export default class AssistantService { }); if ( - !getAgentResponse || - !getAgentResponse['configuration'] || - !getAgentResponse['configuration']['agent_id'] + !getAgentResponse || !(getAgentResponse.ml_configuration?.agent_id || getAgentResponse.configuration?.agent_id) ) { throw new Error( 'Cannot get flow agent id for generating anomaly detector' ); } - const agentId = getAgentResponse['configuration']['agent_id']; + const agentId = getAgentResponse.ml_configuration?.agent_id || getAgentResponse.configuration?.agent_id; const executeAgentResponse = await callWithRequest('ml.executeAgent', { agentId: agentId, diff --git a/server/utils/constants.ts b/server/utils/constants.ts index 8c071556..1a756187 100644 --- a/server/utils/constants.ts +++ b/server/utils/constants.ts @@ -133,4 +133,4 @@ export const HISTORICAL_TASK_TYPES = [ export const CUSTOM_AD_RESULT_INDEX_PREFIX = 'opensearch-ad-plugin-result-'; -export const SUGGEST_ANOMALY_DETECTOR_CONFIG_ID = 'suggest_anomaly_detector'; +export const SUGGEST_ANOMALY_DETECTOR_CONFIG_ID = 'os_suggest_ad';