From d5d955156e1c1a06dcf908bb041080be100f90cb Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 6 Mar 2023 12:35:09 -0800 Subject: [PATCH 01/17] working js manage detectors Signed-off-by: Amit Galitzky --- opensearch_dashboards.json | 1 + .../AssociatedDetectors/helpers.js | 53 ++++++++ .../AssociatedDetectors/index.js | 114 ++++++++++++++++++ .../AssociatedDetectors/styles.scss | 16 +++ public/utils/contextMenu/action.tsx | 85 +++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/helpers.js create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.js create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss create mode 100644 public/utils/contextMenu/action.tsx diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 21cd2fbb..3789dcb9 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -16,6 +16,7 @@ "visAugmenter", "opensearchDashboardsUtils" ], + "optionalPlugins": [], "server": true, "ui": true } diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/helpers.js b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/helpers.js new file mode 100644 index 00000000..05a361fe --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/helpers.js @@ -0,0 +1,53 @@ +import React from 'react'; +import { EuiHealth } from '@elastic/eui'; + + +export const stateToLabel = { + running: { label: 'Running', color: 'success' }, + initializing: { label: 'Initializing', color: 'active' }, +}; + +export const getColumns = ({ onUnlink, onView }) => [ + { + field: 'name', + name: 'Detector', + sortable: true, + truncateText: true, + width: '50%', + }, + { + field: 'state', + name: 'Real-time state', + sortable: true, + width: '105px', + render: (state) => ( + {stateToLabel[state].label} + ), + }, + { + field: 'occurance', + name: 'Anomalies/24hr', + sortable: true, + truncateText: true, + width: '50%', + }, + { + name: 'Actions', + actions: [ + { + type: 'icon', + name: 'Unlink Detector', + description: 'Unlink Detector', + icon: 'unlink', + onClick: onUnlink, + } + ], + }, +]; + +export const search = { + box: { + incremental: true, + schema: true, + }, +}; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.js b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.js new file mode 100644 index 00000000..18a766f8 --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.js @@ -0,0 +1,114 @@ +import React, { useCallback, useMemo } from 'react'; +import { + EuiFlyoutHeader, + EuiTitle, + EuiText, + EuiSpacer, + EuiInMemoryTable, + EuiFlyoutBody, + EuiEmptyPrompt, + EuiButton, + EuiFlyout, +} from '@elastic/eui'; +import uuidv4 from 'uuid/v4'; +import './styles.scss'; +import { getColumns, search } from './helpers'; +const closeFlyout = () => setIsFlyoutVisible(false); + +const AssociatedDetectors = ({ embeddable, closeFlyout }) => { + const title = embeddable.getTitle(); + const onUnlink = useCallback( + (item) => { + console.log('onUnlink', item); + closeFlyout(); + }, + [closeFlyout] + ); + const onView = useCallback( + (item) => { + console.log('onView', item); + closeFlyout(); + }, + [closeFlyout] + ); + const columns = useMemo(() => getColumns({ onUnlink, onView }), [ + onUnlink, + ]); + const detectors = [ + { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, + ]; + + + const empty = ( + No anomaly detectors to display} + titleSize="s" + body={`There are no anomaly detectors associated with ${title} visualization. + You will need to add a detector to the visualization to be able to list it here`} + actions={ + setPanel('add')}> + Add anomaly detector + + } + /> + ); + const tableProps = { + items: detectors, + columns, + search: { + box: { + disabled: detectors.length === 0, + incremental: true, + schema: true, + }, + }, + hasActions: true, + pagination: true, + sorting: true, + message: empty, + }; + + + + return ( + //
+ + + +

+ Associated detectors {detectors.length > 0 ? `(${detectors.length})` : ''} +

+
+
+ + +

{title}

+
+ + +
+
+ + //
+ ); +}; + +export default AssociatedDetectors; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss new file mode 100644 index 00000000..e6520e0e --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss @@ -0,0 +1,16 @@ +@import '@elastic/eui/src/global_styling/variables/index'; + +.associated-detectors { + height: 100%; + display: flex; + flex-direction: column; + + .euiFlyoutBody__overflowContent { + height: 100%; + padding-bottom: 0; + } + + &__flex-group { + height: 100%; + } +} diff --git a/public/utils/contextMenu/action.tsx b/public/utils/contextMenu/action.tsx new file mode 100644 index 00000000..2f1b7832 --- /dev/null +++ b/public/utils/contextMenu/action.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import { i18n } from '@osd/i18n'; +import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; +import { toMountPoint } from '../../../../../src/plugins/opensearch_dashboards_react/public'; +import { Action } from '../../../../../src/plugins/ui_actions/public'; +import { createADAction } from '../../action/ad_dashboard_action'; +import CreateAnomalyDetector from '../../components/FeatureAnywhereContextMenu/CreateAnomalyDetector'; +import AssociatedDetectors from '../../components/FeatureAnywhereContextMenu/AssociatedDetectors'; + +// This is used to create all actions in the same context menu +const grouping: Action['grouping'] = [ + { + id: 'ad-dashboard-context-menu', + getDisplayName: () => 'Anomaly Detector', + getIconType: () => 'apmTrace', + order: 200, + }, +]; + +export const getActions = ({ core }) => + [ + { + grouping, + id: 'createAnomalyDetector', + title: i18n.translate( + 'dashboard.actions.adMenuItem.createAnomalyDetector.displayName', + { + defaultMessage: 'Create anomaly detector', + } + ), + icon: 'plusInCircle' as EuiIconType, + order: 100, + onClick: async ({ embeddable }) => { + const services = await core.getStartServices(); + const openFlyout = services[0].overlays.openFlyout; + openFlyout( + toMountPoint(), + { size: 'l' } + ); + }, + }, + { + grouping, + id: 'manageAnomalyDetector', + title: i18n.translate( + 'dashboard.actions.alertingMenuItem.manageAnomalyDetector.displayName', + { + defaultMessage: 'Manage anomaly detector', + } + ), + icon: 'wrench' as EuiIconType, + order: 99, + onClick: async ({ embeddable }) => { +<<<<<<< HEAD + console.log('manage ad'); +======= + const services = await core.getStartServices(); + const openFlyout = services[0].overlays.openFlyout; + const overlay = openFlyout( + toMountPoint( + overlay.close(), core, services }} /> + ), + { size: 'l' } + ); +>>>>>>> 6e93ee7 (working js manage detectors) + }, + }, + { + id: 'documentation', + title: i18n.translate( + 'dashboard.actions.adMenuItem.documentation.displayName', + { + defaultMessage: 'Documentation', + } + ), + icon: 'documentation' as EuiIconType, + order: 98, + onClick: () => { + window.open( + 'https://opensearch.org/docs/latest/monitoring-plugins/alerting/index/', + '_blank' + ); + }, + }, + ].map((options) => createADAction({ ...options, grouping })); From e4fff52f8815aaac32651d2f9c262e531a514b87 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 20 Mar 2023 10:54:51 -0700 Subject: [PATCH 02/17] adding associated detectors page Signed-off-by: Amit Galitzky --- .../components/EmptyMessage/EmptyMessage.tsx | 37 +++ .../containers/AssociatedDetectors.tsx | 246 ++++++++++++++++++ .../AssociatedDetectors/helpers.js | 53 ---- .../AssociatedDetectors/index.js | 114 -------- .../AssociatedDetectors/index.ts | 12 + .../AssociatedDetectors/utils/helpers.tsx | 82 ++++++ public/plugin.tsx | 63 +++++ public/utils/contextMenu/action.tsx | 13 +- 8 files changed, 451 insertions(+), 169 deletions(-) create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx delete mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/helpers.js delete mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.js create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.ts create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx create mode 100644 public/plugin.tsx diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx new file mode 100644 index 00000000..996de747 --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import { EuiEmptyPrompt, EuiText } from '@elastic/eui'; +import React from 'react'; + +const FILTER_TEXT = 'There are no detectors matching your search'; + +interface EmptyDetectorProps { + //isFilterApplied: boolean; + embeddableTitle: string; +} + +export const EmptyAssociatedDetectorFlyoutMessage = ( + props: EmptyDetectorProps +) => ( + No anomaly detectors to display} + titleSize="s" + data-test-subj="emptyAssociatedDetectorFlyoutMessage" + style={{ maxWidth: '45em' }} + body={ + +

{`There are no anomaly detectors associated with ${props.embeddableTitle} visualization.`}

+ {/*

{props.isFilterApplied ? FILTER_TEXT : `There are no anomaly detectors associated with ${props.embeddableTitle} visualization.`}

*/} +
+ } + /> +); diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx new file mode 100644 index 00000000..bcf26cb8 --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -0,0 +1,246 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React, { useCallback, useMemo, useEffect, useState } from 'react'; +import { + EuiFlyoutHeader, + EuiTitle, + EuiText, + EuiSpacer, + EuiInMemoryTable, + EuiFlyoutBody, + EuiButton, + EuiFlyout, + EuiFlexItem, +} from '@elastic/eui'; +import { get } from 'lodash'; +import '../styles.scss'; +import { getColumns } from '../utils/helpers'; +import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; +import { CoreStart } from '../../../../../../../src/core/public'; +import { useDispatch, useSelector } from 'react-redux'; +import { AppState } from '../../../../redux/reducers'; +import { DetectorListItem } from '../../../../models/interfaces'; +import { getSavedFeatureAnywhereLoader } from '../../../../services'; +import { + GET_ALL_DETECTORS_QUERY_PARAMS, + SINGLE_DETECTOR_NOT_FOUND_MSG, +} from '../../../../pages/utils/constants'; +import { getDetectorList } from '../../../../redux/reducers/ad'; +import { + prettifyErrorMessage, + NO_PERMISSIONS_KEY_WORD, +} from '../../../../../server/utils/helpers'; +import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public'; +import { EmptyAssociatedDetectorFlyoutMessage } from '../components/EmptyMessage/EmptyMessage'; +import { + createAugmentVisSavedObject, + ISavedAugmentVis, + VisLayerExpressionFn, +} from '../../../../../../../src/plugins/vis_augmenter/public'; + +export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { + const core = React.useContext(CoreServicesContext) as CoreStart; + const dispatch = useDispatch(); + const allDetectors = useSelector((state: AppState) => state.ad.detectorList); + const isRequestingFromES = useSelector( + (state: AppState) => state.ad.requesting + ); + const [isLoadingFinalDetectors, setIsLoadingFinalDetectors] = + useState(true); + const isLoading = isRequestingFromES || isLoadingFinalDetectors; + const errorGettingDetectors = useSelector( + (state: AppState) => state.ad.errorMessage + ); + const embeddableTitle = embeddable.getTitle(); + const [selectedDetectors, setSelectedDetectors] = useState( + [] as DetectorListItem[] + ); + + useEffect(() => { + if ( + errorGettingDetectors && + !errorGettingDetectors.includes(SINGLE_DETECTOR_NOT_FOUND_MSG) + ) { + console.error(errorGettingDetectors); + core.notifications.toasts.addDanger( + typeof errorGettingDetectors === 'string' && + errorGettingDetectors.includes(NO_PERMISSIONS_KEY_WORD) + ? prettifyErrorMessage(errorGettingDetectors) + : 'Unable to get all detectors' + ); + setIsLoadingFinalDetectors(false); + } + }, [errorGettingDetectors]); + + useEffect(() => { + getDetectors(); + }, []); + + // Handle all filtering / sorting of detectors + useEffect(() => { + const savedObjectLoader: SavedObjectLoader = + getSavedFeatureAnywhereLoader(); + // Gets all augmented saved objects + savedObjectLoader.findAll().then((resp: any) => { + if (resp != undefined) { + const savedAugmentObjectsArr: ISavedAugmentVis[] = get( + resp, + 'hits', + [] + ); + const curSelectedDetectors = getAssociatedDetectors( + Object.values(allDetectors), + savedAugmentObjectsArr + ); + setSelectedDetectors(curSelectedDetectors); + setIsLoadingFinalDetectors(false); + } + }); + }, [allDetectors]); + + // cross checks all the detectors that exist with all the savedAugment Objects to only display ones + const getAssociatedDetectors = ( + detectors: DetectorListItem[], + savedAugmentObjects: ISavedAugmentVis[] + ) => { + const savedAugmentForThisVisualization: ISavedAugmentVis[] = + savedAugmentObjects.filter( + (savedObj) => get(savedObj, 'visId', '') === embeddable.vis.id + ); + const savedAugmentDetectorsSet = new Set( + savedAugmentForThisVisualization.map((savedObject) => + get(savedObject, 'pluginResourceId', '') + ) + ); + const detectorsToDisplay = detectors.filter((detector) => + savedAugmentDetectorsSet.has(detector.id) + ); + console.log('detectorsToDisplay: ' + JSON.stringify(detectorsToDisplay)); + return detectorsToDisplay; + }; + + const getDetectors = async () => { + dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); + }; + + // This method is only here for development/testing purposes. + const getSavedObjects = async () => { + const resp = await getSavedFeatureAnywhereLoader().findAll(); + console.log('response: ' + JSON.stringify(resp)); + }; + + // This method is only here for development/testing purposes. + const createSavedObjects = async () => { + enum VisLayerTypes { + PointInTimeEvents = 'PointInTimeEvents', + } + const fn = { + type: VisLayerTypes.PointInTimeEvents, + name: 'test-fn', + args: { + testArg: selectedDetectors[0].id, + }, + } as VisLayerExpressionFn; + + const savedObjectToCreate = { + title: 'test-title', + pluginResourceId: selectedDetectors[0].id, + visId: embeddable.vis.id, + savedObjectType: 'visualization', + visLayerExpressionFn: fn, + } as ISavedAugmentVis; + + const savedObject = await createAugmentVisSavedObject(savedObjectToCreate); + console.log('savedObject: ' + JSON.stringify(savedObject)); + + const response = await savedObject.save({}); + console.log('response: ' + JSON.stringify(response)); + }; + + const onUnlink = useCallback( + (item) => { + console.log('onUnlink', item); + closeFlyout(); + }, + [closeFlyout] + ); + const onView = useCallback( + (item) => { + console.log('onView', item); + closeFlyout(); + }, + [closeFlyout] + ); + const columns = useMemo(() => getColumns({ onUnlink, onView }), [onUnlink]); + + const tableProps = { + items: selectedDetectors, + columns, + search: { + box: { + disabled: selectedDetectors.length === 0, + incremental: true, + schema: true, + }, + }, + hasActions: true, + pagination: true, + sorting: true, + message: isLoading ? ( + 'Loading detectors...' + ) : ( + + ), + }; + + return ( + //
+ + + +

Associated anomaly detectors

+
+
+ + {/* below buttons are just here for development/testing purposes*/} + + { + createSavedObjects(); + }} + > + Create saved objects{' '} + + + + { + getSavedObjects(); + }} + > + Get Saved Objects + + + +

{embeddableTitle}

+
+ + +
+
+ + //
+ ); +}; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/helpers.js b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/helpers.js deleted file mode 100644 index 05a361fe..00000000 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/helpers.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { EuiHealth } from '@elastic/eui'; - - -export const stateToLabel = { - running: { label: 'Running', color: 'success' }, - initializing: { label: 'Initializing', color: 'active' }, -}; - -export const getColumns = ({ onUnlink, onView }) => [ - { - field: 'name', - name: 'Detector', - sortable: true, - truncateText: true, - width: '50%', - }, - { - field: 'state', - name: 'Real-time state', - sortable: true, - width: '105px', - render: (state) => ( - {stateToLabel[state].label} - ), - }, - { - field: 'occurance', - name: 'Anomalies/24hr', - sortable: true, - truncateText: true, - width: '50%', - }, - { - name: 'Actions', - actions: [ - { - type: 'icon', - name: 'Unlink Detector', - description: 'Unlink Detector', - icon: 'unlink', - onClick: onUnlink, - } - ], - }, -]; - -export const search = { - box: { - incremental: true, - schema: true, - }, -}; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.js b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.js deleted file mode 100644 index 18a766f8..00000000 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.js +++ /dev/null @@ -1,114 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { - EuiFlyoutHeader, - EuiTitle, - EuiText, - EuiSpacer, - EuiInMemoryTable, - EuiFlyoutBody, - EuiEmptyPrompt, - EuiButton, - EuiFlyout, -} from '@elastic/eui'; -import uuidv4 from 'uuid/v4'; -import './styles.scss'; -import { getColumns, search } from './helpers'; -const closeFlyout = () => setIsFlyoutVisible(false); - -const AssociatedDetectors = ({ embeddable, closeFlyout }) => { - const title = embeddable.getTitle(); - const onUnlink = useCallback( - (item) => { - console.log('onUnlink', item); - closeFlyout(); - }, - [closeFlyout] - ); - const onView = useCallback( - (item) => { - console.log('onView', item); - closeFlyout(); - }, - [closeFlyout] - ); - const columns = useMemo(() => getColumns({ onUnlink, onView }), [ - onUnlink, - ]); - const detectors = [ - { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - // { name: 'CPU_Usage_Detector_2', state: 'initializing', occurance: 3, id: uuidv4() }, - ]; - - - const empty = ( - No anomaly detectors to display} - titleSize="s" - body={`There are no anomaly detectors associated with ${title} visualization. - You will need to add a detector to the visualization to be able to list it here`} - actions={ - setPanel('add')}> - Add anomaly detector - - } - /> - ); - const tableProps = { - items: detectors, - columns, - search: { - box: { - disabled: detectors.length === 0, - incremental: true, - schema: true, - }, - }, - hasActions: true, - pagination: true, - sorting: true, - message: empty, - }; - - - - return ( - //
- - - -

- Associated detectors {detectors.length > 0 ? `(${detectors.length})` : ''} -

-
-
- - -

{title}

-
- - -
-
- - //
- ); -}; - -export default AssociatedDetectors; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.ts b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.ts new file mode 100644 index 00000000..a25a81fc --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.ts @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +export { AssociatedDetectors } from './containers/AssociatedDetectors'; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx new file mode 100644 index 00000000..f5a9dbca --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx @@ -0,0 +1,82 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React from 'react'; +import { EuiBasicTableColumn, EuiHealth, EuiLink } from '@elastic/eui'; +import { DETECTOR_STATE } from 'server/utils/constants'; +import { stateToColorMap } from '../../../../pages/utils/constants'; +import { PLUGIN_NAME } from '../../../../utils/constants'; +import { Detector } from '../../../../models/interfaces'; + +export const renderState = (state: DETECTOR_STATE) => { + console.log('detector State: ' + state); + return ( + //@ts-ignore + {state} + ); +}; + +export const getColumns = ({ onUnlink, onView }) => + [ + { + field: 'name', + name: 'Detector', + sortable: true, + truncateText: true, + width: '30%', + align: 'left', + render: (name: string, detector: Detector) => ( + + {name} + + ), + }, + { + field: 'curState', + name: 'Real-time state', + sortable: true, + align: 'left', + width: '30%', + truncateText: true, + render: renderState, + }, + { + field: 'totalAnomalies', + name: 'Anomalies/24hr', + sortable: true, + dataType: 'number', + align: 'left', + truncateText: true, + width: '30%', + }, + { + name: 'Actions', + align: 'left', + truncateText: true, + width: '10%', + actions: [ + { + type: 'icon', + name: 'Unlink Detector', + description: 'Unlink Detector', + icon: 'unlink', + onClick: onUnlink, + }, + ], + }, + ] as EuiBasicTableColumn[]; + +export const search = { + box: { + incremental: true, + schema: true, + }, +}; diff --git a/public/plugin.tsx b/public/plugin.tsx new file mode 100644 index 00000000..7e687afb --- /dev/null +++ b/public/plugin.tsx @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import { + AppMountParameters, + CoreSetup, + CoreStart, + Plugin, +} from '../../../src/core/public'; +import { CONTEXT_MENU_TRIGGER } from '../../../src/plugins/embeddable/public'; +import { ACTION_AD, createADAction } from './action/ad_dashboard_action'; +import { PLUGIN_NAME } from './utils/constants'; +import { getActions } from './utils/contextMenu/action'; +import { setSavedFeatureAnywhereLoader } from './services'; + +declare module '../../../src/plugins/ui_actions/public' { + export interface ActionContextMapping { + [ACTION_AD]: {}; + } +} + +export class AnomalyDetectionOpenSearchDashboardsPlugin implements Plugin { + public setup(core: CoreSetup, plugins) { + core.application.register({ + id: PLUGIN_NAME, + title: 'Anomaly Detection', + category: { + id: 'opensearch', + label: 'OpenSearch Plugins', + order: 2000, + }, + order: 5000, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./anomaly_detection_app'); + const [coreStart] = await core.getStartServices(); + return renderApp(coreStart, params); + }, + }); + + // Create context menu actions. Pass core, to access service for flyouts. + const actions = getActions({ core }); + + // Add actions to uiActions + actions.forEach((action) => { + plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, action); + }); + } + + public start(core: CoreStart, plugins) { + setSavedFeatureAnywhereLoader(plugins.visAugmenter.savedAugmentVisLoader) + return {}; +} + + public stop() {} +} diff --git a/public/utils/contextMenu/action.tsx b/public/utils/contextMenu/action.tsx index 2f1b7832..91932b05 100644 --- a/public/utils/contextMenu/action.tsx +++ b/public/utils/contextMenu/action.tsx @@ -5,7 +5,10 @@ import { toMountPoint } from '../../../../../src/plugins/opensearch_dashboards_r import { Action } from '../../../../../src/plugins/ui_actions/public'; import { createADAction } from '../../action/ad_dashboard_action'; import CreateAnomalyDetector from '../../components/FeatureAnywhereContextMenu/CreateAnomalyDetector'; -import AssociatedDetectors from '../../components/FeatureAnywhereContextMenu/AssociatedDetectors'; +import { AssociatedDetectors } from '../../components/FeatureAnywhereContextMenu/AssociatedDetectors'; +import { CoreServicesContext } from '../../components/CoreServices/CoreServices'; +import { Provider } from 'react-redux'; +import configureStore from '../../redux/configureStore' // This is used to create all actions in the same context menu const grouping: Action['grouping'] = [ @@ -55,10 +58,16 @@ export const getActions = ({ core }) => console.log('manage ad'); ======= const services = await core.getStartServices(); + const http = services[0].http; + const store = configureStore(http); const openFlyout = services[0].overlays.openFlyout; const overlay = openFlyout( toMountPoint( - overlay.close(), core, services }} /> + + + overlay.close(), core, services }} /> + + ), { size: 'l' } ); From c244b578964d9853df445cf36bc6cbe16f487b30 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 20 Mar 2023 12:46:56 -0700 Subject: [PATCH 03/17] adding unlink modal confirmation Signed-off-by: Amit Galitzky --- .../ConfirmUnlinkDetectorModal.tsx | 80 ++++++++++++ .../containers/AssociatedDetectors.tsx | 117 +++++++++++++++--- .../AssociatedDetectors/utils/constants.tsx | 15 +++ .../AssociatedDetectors/utils/helpers.tsx | 4 +- 4 files changed, 197 insertions(+), 19 deletions(-) create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx new file mode 100644 index 00000000..31568076 --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import React, { useState } from 'react'; +import { + EuiText, + EuiOverlayMask, + EuiButton, + EuiButtonEmpty, + EuiModal, + EuiModalHeader, + EuiModalFooter, + EuiModalBody, + EuiModalHeaderTitle, +} from '@elastic/eui'; +import { DetectorListItem } from '../../../../../models/interfaces'; +import { EuiSpacer } from '@elastic/eui'; + +interface ConfirmUnlinkDetectorModalProps { + detector: DetectorListItem; + onUnlinkDetector(): void; + onHide(): void; + onConfirm(): void; + isListLoading: boolean; +} + +export const ConfirmUnlinkDetectorModal = ( + props: ConfirmUnlinkDetectorModalProps +) => { + const [isModalLoading, setIsModalLoading] = useState(false); + const isLoading = isModalLoading || props.isListLoading; + return ( + + + + + {'Remove association?'}  + + + + Removing association unlinks {props.detector.name} detector + from the visualization but does not delete it. + The detector association can be restored. + + + + {isLoading ? null : ( + + Cancel + + )} + { + setIsModalLoading(true); + props.onUnlinkDetector(); + props.onConfirm(); + }} + > + {'Remove association'} + + + + + ); +}; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index bcf26cb8..e8271f88 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -9,7 +9,7 @@ * GitHub history for details. */ -import React, { useCallback, useMemo, useEffect, useState } from 'react'; +import React, { useMemo, useEffect, useState } from 'react'; import { EuiFlyoutHeader, EuiTitle, @@ -21,7 +21,7 @@ import { EuiFlyout, EuiFlexItem, } from '@elastic/eui'; -import { get } from 'lodash'; +import { get, isEmpty } from 'lodash'; import '../styles.scss'; import { getColumns } from '../utils/helpers'; import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; @@ -46,6 +46,16 @@ import { ISavedAugmentVis, VisLayerExpressionFn, } from '../../../../../../../src/plugins/vis_augmenter/public'; +import { ASSOCIATED_DETECTOR_ACTION } from '../utils/constants'; +import { ConfirmUnlinkDetectorModal } from '../components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal'; + +interface ConfirmModalState { + isOpen: boolean; + action: ASSOCIATED_DETECTOR_ACTION; + isListLoading: boolean; + isRequestingToClose: boolean; + affectedDetector: DetectorListItem; +} export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { const core = React.useContext(CoreServicesContext) as CoreStart; @@ -65,6 +75,20 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { [] as DetectorListItem[] ); + const [detectorToUnlink, setDetectorToUnlink] = useState( + {} as DetectorListItem + ); + const [confirmModalState, setConfirmModalState] = useState( + { + isOpen: false, + //@ts-ignore + action: null, + isListLoading: false, + isRequestingToClose: false, + affectedDetector: {} as DetectorListItem, + } + ); + useEffect(() => { if ( errorGettingDetectors && @@ -81,6 +105,25 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { } }, [errorGettingDetectors]); + // Update modal state if user decides to close + useEffect(() => { + if (confirmModalState.isRequestingToClose) { + if (isLoading) { + setConfirmModalState({ + ...confirmModalState, + isListLoading: true, + }); + } else { + setConfirmModalState({ + ...confirmModalState, + isOpen: false, + isListLoading: false, + isRequestingToClose: false, + }); + } + } + }, [confirmModalState.isRequestingToClose, isLoading]); + useEffect(() => { getDetectors(); }, []); @@ -128,6 +171,34 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { return detectorsToDisplay; }; + const getUnlinkConfirmModal = () => { + if (confirmModalState.isOpen) { + return ( + + ); + } + }; + + const handleHideModal = () => { + setConfirmModalState({ + ...confirmModalState, + isOpen: false, + }); + }; + + const handleConfirmModal = () => { + setConfirmModalState({ + ...confirmModalState, + isRequestingToClose: true, + }); + }; + const getDetectors = async () => { dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); }; @@ -166,21 +237,33 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { console.log('response: ' + JSON.stringify(response)); }; - const onUnlink = useCallback( - (item) => { - console.log('onUnlink', item); - closeFlyout(); - }, - [closeFlyout] - ); - const onView = useCallback( - (item) => { - console.log('onView', item); - closeFlyout(); - }, - [closeFlyout] + const onUnlinkDetector = async () => { + console.log('detectorToUnlink', detectorToUnlink); + }; + + const handleUnlinkDetectorAction = (detector: DetectorListItem) => { + //console.log('onUnlink: ', detector); + setDetectorToUnlink(detector); + if (!isEmpty(detector)) { + setConfirmModalState({ + isOpen: true, + action: ASSOCIATED_DETECTOR_ACTION.UNLINK, + isListLoading: false, + isRequestingToClose: false, + affectedDetector: detector, + }); + } else { + // might not need this + core.notifications.toasts.addWarning( + 'Make sure selected detector has not been deleted' + ); + } + }; + + const columns = useMemo( + () => getColumns({ handleUnlinkDetectorAction }), + [handleUnlinkDetectorAction] ); - const columns = useMemo(() => getColumns({ onUnlink, onView }), [onUnlink]); const tableProps = { items: selectedDetectors, @@ -214,6 +297,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { + {getUnlinkConfirmModal()} {/* below buttons are just here for development/testing purposes*/} { - // ); }; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx new file mode 100644 index 00000000..820782e0 --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +export enum ASSOCIATED_DETECTOR_ACTION { + UNLINK, + } + \ No newline at end of file diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx index f5a9dbca..6441fa37 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx @@ -24,7 +24,7 @@ export const renderState = (state: DETECTOR_STATE) => { ); }; -export const getColumns = ({ onUnlink, onView }) => +export const getColumns = ({ handleUnlinkDetectorAction }) => [ { field: 'name', @@ -68,7 +68,7 @@ export const getColumns = ({ onUnlink, onView }) => name: 'Unlink Detector', description: 'Unlink Detector', icon: 'unlink', - onClick: onUnlink, + onClick: handleUnlinkDetectorAction, }, ], }, From 8df6be75645af4bef6deeb5db22af90efed44006 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 20 Mar 2023 16:03:50 -0700 Subject: [PATCH 04/17] prettier formating and merge conflicts Signed-off-by: Amit Galitzky --- .../ConfirmUnlinkDetectorModal.tsx | 14 ++++++++++---- .../AssociatedDetectors/utils/constants.tsx | 5 ++--- public/plugin.tsx | 6 +++--- public/services.ts | 3 +++ public/utils/contextMenu/action.tsx | 15 +++++++++------ 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx index 31568076..607c4f8e 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx @@ -39,16 +39,22 @@ export const ConfirmUnlinkDetectorModal = ( const isLoading = isModalLoading || props.isListLoading; return ( - + {'Remove association?'}  - Removing association unlinks {props.detector.name} detector - from the visualization but does not delete it. - The detector association can be restored. + + Removing association unlinks {props.detector.name} detector from the + visualization but does not delete it. The detector association can + be restored. + diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx index 820782e0..16d43420 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx @@ -10,6 +10,5 @@ */ export enum ASSOCIATED_DETECTOR_ACTION { - UNLINK, - } - \ No newline at end of file + UNLINK, +} diff --git a/public/plugin.tsx b/public/plugin.tsx index 7e687afb..deddbd00 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -55,9 +55,9 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin implements Plugin { } public start(core: CoreStart, plugins) { - setSavedFeatureAnywhereLoader(plugins.visAugmenter.savedAugmentVisLoader) - return {}; -} + setSavedFeatureAnywhereLoader(plugins.visAugmenter.savedAugmentVisLoader); + return {}; + } public stop() {} } diff --git a/public/services.ts b/public/services.ts index 3857a95f..32561618 100644 --- a/public/services.ts +++ b/public/services.ts @@ -13,9 +13,12 @@ export const [getSavedFeatureAnywhereLoader, setSavedFeatureAnywhereLoader] = export const [getClient, setClient] = createGetterSetter('http'); +<<<<<<< HEAD export const [getEmbeddable, setEmbeddable] = createGetterSetter('Embeddable'); export const [getOverlays, setOverlays] = createGetterSetter('Overlays'); +======= +>>>>>>> a9065fb (prettier formating and merge conflicts) diff --git a/public/utils/contextMenu/action.tsx b/public/utils/contextMenu/action.tsx index 91932b05..c1ad426a 100644 --- a/public/utils/contextMenu/action.tsx +++ b/public/utils/contextMenu/action.tsx @@ -8,7 +8,7 @@ import CreateAnomalyDetector from '../../components/FeatureAnywhereContextMenu/C import { AssociatedDetectors } from '../../components/FeatureAnywhereContextMenu/AssociatedDetectors'; import { CoreServicesContext } from '../../components/CoreServices/CoreServices'; import { Provider } from 'react-redux'; -import configureStore from '../../redux/configureStore' +import configureStore from '../../redux/configureStore'; // This is used to create all actions in the same context menu const grouping: Action['grouping'] = [ @@ -54,9 +54,6 @@ export const getActions = ({ core }) => icon: 'wrench' as EuiIconType, order: 99, onClick: async ({ embeddable }) => { -<<<<<<< HEAD - console.log('manage ad'); -======= const services = await core.getStartServices(); const http = services[0].http; const store = configureStore(http); @@ -65,13 +62,19 @@ export const getActions = ({ core }) => toMountPoint( - overlay.close(), core, services }} /> + overlay.close(), + core, + services, + }} + /> ), { size: 'l' } ); ->>>>>>> 6e93ee7 (working js manage detectors) }, }, { From 9a91b75af7c369fe13fd11dc19868d350db43d5f Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Tue, 21 Mar 2023 13:03:51 -0700 Subject: [PATCH 05/17] add unlinking capability Signed-off-by: Amit Galitzky --- .../containers/AssociatedDetectors.tsx | 80 +++++++++-- .../AssociatedDetectors/styles.scss | 24 ++-- .../CreateAnomalyDetector/index.js | 131 ++++++++++++++++++ public/utils/contextMenu/action.tsx | 14 +- 4 files changed, 219 insertions(+), 30 deletions(-) create mode 100644 public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/index.js diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index e8271f88..2f9e981f 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -20,6 +20,7 @@ import { EuiButton, EuiFlyout, EuiFlexItem, + EuiFlexGroup, } from '@elastic/eui'; import { get, isEmpty } from 'lodash'; import '../styles.scss'; @@ -88,6 +89,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { affectedDetector: {} as DetectorListItem, } ); + const savedObjectLoader: SavedObjectLoader = getSavedFeatureAnywhereLoader(); useEffect(() => { if ( @@ -130,8 +132,6 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { // Handle all filtering / sorting of detectors useEffect(() => { - const savedObjectLoader: SavedObjectLoader = - getSavedFeatureAnywhereLoader(); // Gets all augmented saved objects savedObjectLoader.findAll().then((resp: any) => { if (resp != undefined) { @@ -171,6 +171,40 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { return detectorsToDisplay; }; + const onUnlinkDetector = async () => { + setIsLoadingFinalDetectors(true); + await savedObjectLoader.findAll().then(async (resp: any) => { + if (resp != undefined) { + const savedAugmentObjects: ISavedAugmentVis[] = get(resp, 'hits', []); + // gets all the saved object for this visualization + const savedAugmentForThisVisualization: ISavedAugmentVis[] = + savedAugmentObjects.filter( + (savedObj) => get(savedObj, 'visId', '') === embeddable.vis.id + ); + + const savedAugmentToUnlink = savedAugmentForThisVisualization.filter( + (savedObject) => + get(savedObject, 'pluginResourceId', '') === detectorToUnlink.id + ); + + const savedObjectToUnlinkId = get(savedAugmentToUnlink[0], 'id', ''); + await savedObjectLoader + .delete(savedObjectToUnlinkId) + .then((resp: any) => {}) + .catch((error) => { + core.notifications.toasts.addDanger( + prettifyErrorMessage( + `Error unlinking selected detector: ${error}` + ) + ); + }) + .finally(() => { + getDetectors(); + }); + } + }); + }; + const getUnlinkConfirmModal = () => { if (confirmModalState.isOpen) { return ( @@ -200,6 +234,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { }; const getDetectors = async () => { + console.log('inside getDetectors()'); dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); }; @@ -209,6 +244,9 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { console.log('response: ' + JSON.stringify(resp)); }; + const onAssociateExistingDetector = async () => { + console.log('inside create anomaly detector'); + }; // This method is only here for development/testing purposes. const createSavedObjects = async () => { enum VisLayerTypes { @@ -221,10 +259,10 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { testArg: selectedDetectors[0].id, }, } as VisLayerExpressionFn; - + console.log('all Detectors: ' + JSON.stringify(allDetectors)); const savedObjectToCreate = { title: 'test-title', - pluginResourceId: selectedDetectors[0].id, + pluginResourceId: 'bNZIp4UB3stq6UHwpWwS', visId: embeddable.vis.id, savedObjectType: 'visualization', visLayerExpressionFn: fn, @@ -235,10 +273,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { const response = await savedObject.save({}); console.log('response: ' + JSON.stringify(response)); - }; - - const onUnlinkDetector = async () => { - console.log('detectorToUnlink', detectorToUnlink); + getDetectors(); }; const handleUnlinkDetectorAction = (detector: DetectorListItem) => { @@ -290,15 +325,35 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { return ( //
- +

Associated anomaly detectors

- + {getUnlinkConfirmModal()} {/* below buttons are just here for development/testing purposes*/} + + + +

{embeddableTitle}

+
+
+ + { + onAssociateExistingDetector(); + }} + > + Associate a detector + + +
+ + { @@ -317,11 +372,6 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { Get Saved Objects - -

{embeddableTitle}

-
- -
//
diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss index e6520e0e..2243b5f9 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss @@ -1,16 +1,16 @@ @import '@elastic/eui/src/global_styling/variables/index'; -.associated-detectors { - height: 100%; - display: flex; - flex-direction: column; +// .associated-detectors { +// height: 100%; +// display: flex; +// flex-direction: column; - .euiFlyoutBody__overflowContent { - height: 100%; - padding-bottom: 0; - } +// .euiFlyoutBody__overflowContent { +// height: 100%; +// padding-bottom: 0; +// } - &__flex-group { - height: 100%; - } -} +// &__flex-group { +// height: 100%; +// } +// } diff --git a/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/index.js b/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/index.js new file mode 100644 index 00000000..c0d80471 --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/index.js @@ -0,0 +1,131 @@ +import React, { useEffect, useState } from 'react'; +import { + EuiText, + EuiHorizontalRule, + EuiFlexItem, + EuiFlexGroup, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiTitle, + EuiCheckableCard, + EuiSpacer, + EuiFlyout, +} from '@elastic/eui'; +// import { +// OuiCheckableCard +// } from '@elastic/eui'; +import './styles.scss'; +import { EmbeddablePanel } from '../../../../../../src/plugins/embeddable/public'; +import DetectorDetails from './DetectorDetails'; +import Features from './Features'; + +const accordions = [ + 'detectorDetails', + 'features', + 'shingleSize', + 'alerts', + 'triggers', +].reduce((acc, cur) => ({ ...acc, [cur]: cur }), {}); + +function CreateAnomalyDetector({ embeddable }) { + const [radio, setRadio] = useState('createRadio'); + const [accordionOpen, setAccordionOpen] = useState(accordions.triggers); + + return ( +
+ + + +

Add anomaly detector

+
+
+ + + + setRadio('createRadio')} + /> + + + setRadio('associateRadio')} + /> + + + + + This is a short description of the feature to get users exicted. + Learn more in the documentation. + + + {/* + +
+ Promise.resolve([])} + getAllEmbeddableFactories={() => []} + getEmbeddableFactory={() => null} + notifications={{}} + application={{}} + overlays={{}} + inspector={{ isAvailable: () => null }} + SavedObjectFinder={() => null} + /> +
+
+ + +
DETECTOR DETAILS
+ + } + initialIsOpen={true} + forceState={accordionOpen === accordions.detectorDetails ? 'open' : 'closed'} + onToggle={() => + setAccordionOpen( + accordionOpen !== accordions.detectorDetails && accordions.detectorDetails + ) + } + > + + +
+ + +
FEATURES
+ + } + initialIsOpen={true} + // forceState={accordionOpen === accordions.detectorDetails ? 'open' : 'closed'} + // onToggle={() => + // setAccordionOpen( + // accordionOpen !== accordions.detectorDetails && accordions.detectorDetails + // ) + // } + > + +
+ +
+
*/} +
+
+
+ ); +} +export default CreateAnomalyDetector; diff --git a/public/utils/contextMenu/action.tsx b/public/utils/contextMenu/action.tsx index c1ad426a..2a775c65 100644 --- a/public/utils/contextMenu/action.tsx +++ b/public/utils/contextMenu/action.tsx @@ -35,10 +35,18 @@ export const getActions = ({ core }) => order: 100, onClick: async ({ embeddable }) => { const services = await core.getStartServices(); + const http = services[0].http; + const store = configureStore(http); const openFlyout = services[0].overlays.openFlyout; openFlyout( - toMountPoint(), - { size: 'l' } + toMountPoint( + + + + + + ), + { size: 'm' } ); }, }, @@ -73,7 +81,7 @@ export const getActions = ({ core }) => ), - { size: 'l' } + { size: 'm' } ); }, }, From 12561bdd043c9b897694366cc2da96f65f9207ca Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Tue, 21 Mar 2023 15:56:25 -0700 Subject: [PATCH 06/17] adding message for no search results Signed-off-by: Amit Galitzky --- .../components/EmptyMessage/EmptyMessage.tsx | 5 +-- .../containers/AssociatedDetectors.tsx | 37 +++++++++++-------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx index 996de747..79eaddbc 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx @@ -15,7 +15,7 @@ import React from 'react'; const FILTER_TEXT = 'There are no detectors matching your search'; interface EmptyDetectorProps { - //isFilterApplied: boolean; + isFilterApplied: boolean; embeddableTitle: string; } @@ -29,8 +29,7 @@ export const EmptyAssociatedDetectorFlyoutMessage = ( style={{ maxWidth: '45em' }} body={ -

{`There are no anomaly detectors associated with ${props.embeddableTitle} visualization.`}

- {/*

{props.isFilterApplied ? FILTER_TEXT : `There are no anomaly detectors associated with ${props.embeddableTitle} visualization.`}

*/} +

{props.isFilterApplied ? FILTER_TEXT : `There are no anomaly detectors associated with ${props.embeddableTitle} visualization.`}

} /> diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index 2f9e981f..ba8d8c1e 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -129,7 +129,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { useEffect(() => { getDetectors(); }, []); - + // Handle all filtering / sorting of detectors useEffect(() => { // Gets all augmented saved objects @@ -167,7 +167,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { const detectorsToDisplay = detectors.filter((detector) => savedAugmentDetectorsSet.has(detector.id) ); - console.log('detectorsToDisplay: ' + JSON.stringify(detectorsToDisplay)); + //console.log('detectorsToDisplay: ' + JSON.stringify(detectorsToDisplay)); return detectorsToDisplay; }; @@ -234,18 +234,17 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { }; const getDetectors = async () => { - console.log('inside getDetectors()'); dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); }; // This method is only here for development/testing purposes. const getSavedObjects = async () => { const resp = await getSavedFeatureAnywhereLoader().findAll(); - console.log('response: ' + JSON.stringify(resp)); + //console.log('response: ' + JSON.stringify(resp)); }; const onAssociateExistingDetector = async () => { - console.log('inside create anomaly detector'); + console.log('inside create anomaly detector'); }; // This method is only here for development/testing purposes. const createSavedObjects = async () => { @@ -277,7 +276,6 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { }; const handleUnlinkDetectorAction = (detector: DetectorListItem) => { - //console.log('onUnlink: ', detector); setDetectorToUnlink(detector); if (!isEmpty(detector)) { setConfirmModalState({ @@ -300,6 +298,22 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { [handleUnlinkDetectorAction] ); + const renderEmptyMessage = () => { + if (isLoading) { + return 'Loading detectors...' + } else if (!isEmpty(selectedDetectors)) { + return (); + } else { + return (); + } + } + const tableProps = { items: selectedDetectors, columns, @@ -313,17 +327,10 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { hasActions: true, pagination: true, sorting: true, - message: isLoading ? ( - 'Loading detectors...' - ) : ( - - ), + message: renderEmptyMessage(), }; - return ( + //
From e51f0506b406d3f5fdcd9b6af21de866f9e27236 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 27 Mar 2023 10:16:44 -0700 Subject: [PATCH 07/17] clean up files Signed-off-by: Amit Galitzky --- .../components/EmptyMessage/EmptyMessage.tsx | 6 +- .../containers/AssociatedDetectors.tsx | 179 +++++++----------- .../AssociatedDetectors/styles.scss | 24 +-- .../AssociatedDetectors/utils/helpers.tsx | 1 - .../CreateAnomalyDetector/index.js | 131 ------------- 5 files changed, 84 insertions(+), 257 deletions(-) delete mode 100644 public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/index.js diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx index 79eaddbc..aae18dc6 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx @@ -29,7 +29,11 @@ export const EmptyAssociatedDetectorFlyoutMessage = ( style={{ maxWidth: '45em' }} body={ -

{props.isFilterApplied ? FILTER_TEXT : `There are no anomaly detectors associated with ${props.embeddableTitle} visualization.`}

+

+ {props.isFilterApplied + ? FILTER_TEXT + : `There are no anomaly detectors associated with ${props.embeddableTitle} visualization.`} +

} /> diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index ba8d8c1e..55edd1ad 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -13,7 +13,6 @@ import React, { useMemo, useEffect, useState } from 'react'; import { EuiFlyoutHeader, EuiTitle, - EuiText, EuiSpacer, EuiInMemoryTable, EuiFlyoutBody, @@ -42,11 +41,7 @@ import { } from '../../../../../server/utils/helpers'; import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public'; import { EmptyAssociatedDetectorFlyoutMessage } from '../components/EmptyMessage/EmptyMessage'; -import { - createAugmentVisSavedObject, - ISavedAugmentVis, - VisLayerExpressionFn, -} from '../../../../../../../src/plugins/vis_augmenter/public'; +import { ISavedAugmentVis } from '../../../../../../../src/plugins/vis_augmenter/public'; import { ASSOCIATED_DETECTOR_ACTION } from '../utils/constants'; import { ConfirmUnlinkDetectorModal } from '../components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal'; @@ -89,6 +84,8 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { affectedDetector: {} as DetectorListItem, } ); + + // Establish savedObjectLoader for all operations on vis augmented saved objects const savedObjectLoader: SavedObjectLoader = getSavedFeatureAnywhereLoader(); useEffect(() => { @@ -107,7 +104,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { } }, [errorGettingDetectors]); - // Update modal state if user decides to close + // Update modal state if user decides to close modal useEffect(() => { if (confirmModalState.isRequestingToClose) { if (isLoading) { @@ -129,8 +126,8 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { useEffect(() => { getDetectors(); }, []); - - // Handle all filtering / sorting of detectors + + // Handle all changes in the assoicated detectors such as unlinking or new detectors associated useEffect(() => { // Gets all augmented saved objects savedObjectLoader.findAll().then((resp: any) => { @@ -151,23 +148,28 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { }, [allDetectors]); // cross checks all the detectors that exist with all the savedAugment Objects to only display ones + // that are associated to the current visualization const getAssociatedDetectors = ( detectors: DetectorListItem[], savedAugmentObjects: ISavedAugmentVis[] ) => { + // Filter all savedAugmentObjects that aren't linked to the specific visualization const savedAugmentForThisVisualization: ISavedAugmentVis[] = savedAugmentObjects.filter( (savedObj) => get(savedObj, 'visId', '') === embeddable.vis.id ); + + // Map all detector IDs for all the found augmented vis objects const savedAugmentDetectorsSet = new Set( savedAugmentForThisVisualization.map((savedObject) => get(savedObject, 'pluginResourceId', '') ) ); + + // filter out any detectors that aren't on the set of detectors IDs from the augmented vis objects. const detectorsToDisplay = detectors.filter((detector) => savedAugmentDetectorsSet.has(detector.id) ); - //console.log('detectorsToDisplay: ' + JSON.stringify(detectorsToDisplay)); return detectorsToDisplay; }; @@ -182,15 +184,15 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { (savedObj) => get(savedObj, 'visId', '') === embeddable.vis.id ); - const savedAugmentToUnlink = savedAugmentForThisVisualization.filter( + // find saved Augment object matching detector we want to unlink + // There should only be one detector and vis pairing + const savedAugmentToUnlink = savedAugmentForThisVisualization.find( (savedObject) => get(savedObject, 'pluginResourceId', '') === detectorToUnlink.id ); - - const savedObjectToUnlinkId = get(savedAugmentToUnlink[0], 'id', ''); + const savedObjectToUnlinkId = get(savedAugmentToUnlink, 'id', ''); await savedObjectLoader .delete(savedObjectToUnlinkId) - .then((resp: any) => {}) .catch((error) => { core.notifications.toasts.addDanger( prettifyErrorMessage( @@ -237,42 +239,10 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); }; - // This method is only here for development/testing purposes. - const getSavedObjects = async () => { - const resp = await getSavedFeatureAnywhereLoader().findAll(); - //console.log('response: ' + JSON.stringify(resp)); - }; - + // TODO: this part is incomplete because it is pending on complete the work for associating an existing + // detector which is dependent on changes in the action.tsx code that jackie will merge in const onAssociateExistingDetector = async () => { - console.log('inside create anomaly detector'); - }; - // This method is only here for development/testing purposes. - const createSavedObjects = async () => { - enum VisLayerTypes { - PointInTimeEvents = 'PointInTimeEvents', - } - const fn = { - type: VisLayerTypes.PointInTimeEvents, - name: 'test-fn', - args: { - testArg: selectedDetectors[0].id, - }, - } as VisLayerExpressionFn; - console.log('all Detectors: ' + JSON.stringify(allDetectors)); - const savedObjectToCreate = { - title: 'test-title', - pluginResourceId: 'bNZIp4UB3stq6UHwpWwS', - visId: embeddable.vis.id, - savedObjectType: 'visualization', - visLayerExpressionFn: fn, - } as ISavedAugmentVis; - - const savedObject = await createAugmentVisSavedObject(savedObjectToCreate); - console.log('savedObject: ' + JSON.stringify(savedObject)); - - const response = await savedObject.save({}); - console.log('response: ' + JSON.stringify(response)); - getDetectors(); + console.log('inside create anomaly detector'); }; const handleUnlinkDetectorAction = (detector: DetectorListItem) => { @@ -286,7 +256,6 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { affectedDetector: detector, }); } else { - // might not need this core.notifications.toasts.addWarning( 'Make sure selected detector has not been deleted' ); @@ -300,19 +269,23 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { const renderEmptyMessage = () => { if (isLoading) { - return 'Loading detectors...' + return 'Loading detectors...'; } else if (!isEmpty(selectedDetectors)) { - return (); + return ( + + ); } else { - return (); + return ( + + ); } - } + }; const tableProps = { items: selectedDetectors, @@ -330,57 +303,39 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { message: renderEmptyMessage(), }; return ( - - //
- - - -

Associated anomaly detectors

-
-
- - {getUnlinkConfirmModal()} - {/* below buttons are just here for development/testing purposes*/} - - - -

{embeddableTitle}

-
-
- - { - onAssociateExistingDetector(); - }} - > - Associate a detector - - -
- - - - { - createSavedObjects(); - }} - > - Create saved objects{' '} - - - - { - getSavedObjects(); - }} - > - Get Saved Objects - - -
-
- //
+
+ + + +

+ Associated anomaly detectors +

+
+
+ + {getUnlinkConfirmModal()} + + + +

{embeddableTitle}

+
+
+ + { + onAssociateExistingDetector(); + }} + > + Associate a detector + + +
+ + +
+
+
); }; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss index 2243b5f9..e6520e0e 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss @@ -1,16 +1,16 @@ @import '@elastic/eui/src/global_styling/variables/index'; -// .associated-detectors { -// height: 100%; -// display: flex; -// flex-direction: column; +.associated-detectors { + height: 100%; + display: flex; + flex-direction: column; -// .euiFlyoutBody__overflowContent { -// height: 100%; -// padding-bottom: 0; -// } + .euiFlyoutBody__overflowContent { + height: 100%; + padding-bottom: 0; + } -// &__flex-group { -// height: 100%; -// } -// } + &__flex-group { + height: 100%; + } +} diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx index 6441fa37..b4668a4f 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx @@ -17,7 +17,6 @@ import { PLUGIN_NAME } from '../../../../utils/constants'; import { Detector } from '../../../../models/interfaces'; export const renderState = (state: DETECTOR_STATE) => { - console.log('detector State: ' + state); return ( //@ts-ignore {state} diff --git a/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/index.js b/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/index.js deleted file mode 100644 index c0d80471..00000000 --- a/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/index.js +++ /dev/null @@ -1,131 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { - EuiText, - EuiHorizontalRule, - EuiFlexItem, - EuiFlexGroup, - EuiFlyoutHeader, - EuiFlyoutBody, - EuiTitle, - EuiCheckableCard, - EuiSpacer, - EuiFlyout, -} from '@elastic/eui'; -// import { -// OuiCheckableCard -// } from '@elastic/eui'; -import './styles.scss'; -import { EmbeddablePanel } from '../../../../../../src/plugins/embeddable/public'; -import DetectorDetails from './DetectorDetails'; -import Features from './Features'; - -const accordions = [ - 'detectorDetails', - 'features', - 'shingleSize', - 'alerts', - 'triggers', -].reduce((acc, cur) => ({ ...acc, [cur]: cur }), {}); - -function CreateAnomalyDetector({ embeddable }) { - const [radio, setRadio] = useState('createRadio'); - const [accordionOpen, setAccordionOpen] = useState(accordions.triggers); - - return ( -
- - - -

Add anomaly detector

-
-
- - - - setRadio('createRadio')} - /> - - - setRadio('associateRadio')} - /> - - - - - This is a short description of the feature to get users exicted. - Learn more in the documentation. - - - {/* - -
- Promise.resolve([])} - getAllEmbeddableFactories={() => []} - getEmbeddableFactory={() => null} - notifications={{}} - application={{}} - overlays={{}} - inspector={{ isAvailable: () => null }} - SavedObjectFinder={() => null} - /> -
-
- - -
DETECTOR DETAILS
- - } - initialIsOpen={true} - forceState={accordionOpen === accordions.detectorDetails ? 'open' : 'closed'} - onToggle={() => - setAccordionOpen( - accordionOpen !== accordions.detectorDetails && accordions.detectorDetails - ) - } - > - - -
- - -
FEATURES
- - } - initialIsOpen={true} - // forceState={accordionOpen === accordions.detectorDetails ? 'open' : 'closed'} - // onToggle={() => - // setAccordionOpen( - // accordionOpen !== accordions.detectorDetails && accordions.detectorDetails - // ) - // } - > - -
- -
-
*/} -
-
-
- ); -} -export default CreateAnomalyDetector; From 60ebd324c2674e014c79ba2a5abac4c8c9aef81c Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Thu, 30 Mar 2023 12:06:21 -0700 Subject: [PATCH 08/17] more cleanup Signed-off-by: Amit Galitzky --- .../ConfirmUnlinkDetectorModal.tsx | 8 +- .../components/EmptyMessage/EmptyMessage.tsx | 8 +- .../containers/AssociatedDetectors.tsx | 8 +- .../AssociatedDetectors/index.ts | 8 +- .../AssociatedDetectors/styles.scss | 5 ++ .../AssociatedDetectors/utils/constants.tsx | 8 +- .../AssociatedDetectors/utils/helpers.tsx | 8 +- public/plugin.ts | 86 ------------------- public/plugin.tsx | 8 +- public/utils/contextMenu/action.tsx | 5 ++ 10 files changed, 17 insertions(+), 135 deletions(-) delete mode 100644 public/plugin.ts diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx index 607c4f8e..ec700bcb 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx @@ -1,12 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ import React, { useState } from 'react'; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx index aae18dc6..7e110e27 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx @@ -1,12 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ import { EuiEmptyPrompt, EuiText } from '@elastic/eui'; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index 55edd1ad..8efa02e4 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -1,12 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ import React, { useMemo, useEffect, useState } from 'react'; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.ts b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.ts index a25a81fc..39483649 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.ts +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/index.ts @@ -1,12 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ export { AssociatedDetectors } from './containers/AssociatedDetectors'; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss index e6520e0e..f78a0388 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss @@ -1,3 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + @import '@elastic/eui/src/global_styling/variables/index'; .associated-detectors { diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx index 16d43420..37236349 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/constants.tsx @@ -1,12 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ export enum ASSOCIATED_DETECTOR_ACTION { diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx index b4668a4f..1b155086 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx @@ -1,12 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ import React from 'react'; diff --git a/public/plugin.ts b/public/plugin.ts deleted file mode 100644 index 36e15cc3..00000000 --- a/public/plugin.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -import { - AppMountParameters, - CoreSetup, - CoreStart, - Plugin, -} from '../../../src/core/public'; -import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart } from '../../../src/plugins/embeddable/public'; -import { ACTION_AD } from './action/ad_dashboard_action'; -import { PLUGIN_NAME } from './utils/constants'; -import { getActions } from './utils/contextMenu/getActions'; -import { overlayAnomaliesFunction } from './expressions/overlay_anomalies'; -import { setClient, setEmbeddable, setOverlays } from './services'; -import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; -import { createStartServicesGetter } from '../../../src/plugins/opensearch_dashboards_utils/public'; - -declare module '../../../src/plugins/ui_actions/public' { - export interface ActionContextMapping { - [ACTION_AD]: {}; - } -} - -export interface AnomalyDetectionSetupDeps { - embeddable: EmbeddableSetup; -} - -export interface AnomalyDetectionStartDeps { - embeddable: EmbeddableStart; -} - -export class AnomalyDetectionOpenSearchDashboardsPlugin implements - Plugin { - - public setup(core: CoreSetup, plugins: any) { - core.application.register({ - id: PLUGIN_NAME, - title: 'Anomaly Detection', - category: { - id: 'opensearch', - label: 'OpenSearch Plugins', - order: 2000, - }, - order: 5000, - mount: async (params: AppMountParameters) => { - const { renderApp } = await import('./anomaly_detection_app'); - const [coreStart] = await core.getStartServices(); - return renderApp(coreStart, params); - }, - }); - - // Set the HTTP client so it can be pulled into expression fns to make - // direct server-side calls - setClient(core.http); - - // Create context menu actions. Pass core, to access service for flyouts. - const actions = getActions(); - - // Add actions to uiActions - actions.forEach((action) => { - plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, action); - }); - - // registers the expression function used to render anomalies on an Augmented Visualization - plugins.expressions.registerFunction(overlayAnomaliesFunction); - return {}; - } - - public start( - core: CoreStart, - {embeddable }: AnomalyDetectionStartDeps - ): AnomalyDetectionOpenSearchDashboardsPluginStart { - setEmbeddable(embeddable); - setOverlays(core.overlays); - return {}; - } -} \ No newline at end of file diff --git a/public/plugin.tsx b/public/plugin.tsx index deddbd00..1e281dfc 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -1,12 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ import { diff --git a/public/utils/contextMenu/action.tsx b/public/utils/contextMenu/action.tsx index 2a775c65..e0a21143 100644 --- a/public/utils/contextMenu/action.tsx +++ b/public/utils/contextMenu/action.tsx @@ -1,3 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + import React from 'react'; import { i18n } from '@osd/i18n'; import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; From 2e15ffe7e22d34792ca09c0eae73ce26fd58088b Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Fri, 5 May 2023 17:25:54 -0700 Subject: [PATCH 09/17] making changes based on new upper container Signed-off-by: Amit Galitzky --- .../components/EmptyMessage/EmptyMessage.tsx | 2 +- .../containers/AssociatedDetectors.tsx | 86 ++++++++++++-- .../AssociatedDetectors/utils/helpers.tsx | 6 +- .../Container/Container.tsx | 29 +++++ .../Container/index.ts | 8 ++ .../Container/styles.scss | 7 ++ public/plugin.tsx | 25 +++- public/utils/contextMenu/action.tsx | 110 ------------------ public/utils/contextMenu/styles.scss | 30 +++++ 9 files changed, 176 insertions(+), 127 deletions(-) create mode 100644 public/components/FeatureAnywhereContextMenu/Container/Container.tsx create mode 100644 public/components/FeatureAnywhereContextMenu/Container/index.ts create mode 100644 public/components/FeatureAnywhereContextMenu/Container/styles.scss delete mode 100644 public/utils/contextMenu/action.tsx create mode 100644 public/utils/contextMenu/styles.scss diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx index 7e110e27..1a833bd0 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx @@ -31,4 +31,4 @@ export const EmptyAssociatedDetectorFlyoutMessage = ( } /> -); +); \ No newline at end of file diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index 8efa02e4..c0d9e9bc 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -35,7 +35,7 @@ import { } from '../../../../../server/utils/helpers'; import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public'; import { EmptyAssociatedDetectorFlyoutMessage } from '../components/EmptyMessage/EmptyMessage'; -import { ISavedAugmentVis } from '../../../../../../../src/plugins/vis_augmenter/public'; +import { ISavedAugmentVis, VisLayerExpressionFn, createAugmentVisSavedObject } from '../../../../../../../src/plugins/vis_augmenter/public'; import { ASSOCIATED_DETECTOR_ACTION } from '../utils/constants'; import { ConfirmUnlinkDetectorModal } from '../components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal'; @@ -47,7 +47,7 @@ interface ConfirmModalState { affectedDetector: DetectorListItem; } -export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { +function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { const core = React.useContext(CoreServicesContext) as CoreStart; const dispatch = useDispatch(); const allDetectors = useSelector((state: AppState) => state.ad.detectorList); @@ -79,7 +79,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { } ); - // Establish savedObjectLoader for all operations on vis augmented saved objects + // Establish savedObjectLoader for all operations on vis_augment saved objects const savedObjectLoader: SavedObjectLoader = getSavedFeatureAnywhereLoader(); useEffect(() => { @@ -121,7 +121,7 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { getDetectors(); }, []); - // Handle all changes in the assoicated detectors such as unlinking or new detectors associated + // Handles all changes in the assoicated detectors such as unlinking or new detectors associated useEffect(() => { // Gets all augmented saved objects savedObjectLoader.findAll().then((resp: any) => { @@ -138,7 +138,15 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { setSelectedDetectors(curSelectedDetectors); setIsLoadingFinalDetectors(false); } - }); + } + ) + .catch((error) => { + core.notifications.toasts.addDanger( + prettifyErrorMessage( + `Unable to fetch associated detectors: ${error}` + ) + ); + }) }, [allDetectors]); // cross checks all the detectors that exist with all the savedAugment Objects to only display ones @@ -187,6 +195,17 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { const savedObjectToUnlinkId = get(savedAugmentToUnlink, 'id', ''); await savedObjectLoader .delete(savedObjectToUnlinkId) + .then( async (resp:any )=> { + console.log("inside delete after unlinking") + core.notifications.toasts.addSuccess( + `Detector created: ${values.name}` + ); + // core.notifications.toasts.addSuccess({ + // title: `Association removed between the ${detectorToUnlink.name} + // and the ${embeddableTitle} visualization`, + // text: 'The detector\'s anomalies do not appear on the visualization. Refresh your dashboard to update the visualization', + // }) + }) .catch((error) => { core.notifications.toasts.addDanger( prettifyErrorMessage( @@ -233,12 +252,52 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { dispatch(getDetectorList(GET_ALL_DETECTORS_QUERY_PARAMS)); }; - // TODO: this part is incomplete because it is pending on complete the work for associating an existing - // detector which is dependent on changes in the action.tsx code that jackie will merge in + // TODO: this part is incomplete because it is pending on a different PR that will have all the associate existing changes const onAssociateExistingDetector = async () => { console.log('inside create anomaly detector'); }; + // This method is only here for development/testing purposes. + const createSavedObjects = async () => { + enum VisLayerTypes { + PointInTimeEvents = 'PointInTimeEvents', + } + console.log('all Detectors: ' + JSON.stringify(allDetectors)); + + const fn = { + type: VisLayerTypes.PointInTimeEvents, + name: 'overlay_anomalies', + args: { + detectorId: 'pggP7ocBbTXmavYrSMaD', + }, + } as VisLayerExpressionFn; + + // const fn = { + // type: VisLayerTypes.PointInTimeEvents, + // name: 'test-fn', + // args: { + // testArg: 'bNZIp4UB3stq6UHwpWwS', + // }, + // } as VisLayerExpressionFn; + + const savedObjectToCreate = { + title: 'test-title', + pluginResourceId: 'pggP7ocBbTXmavYrSMaD', + visId: embeddable.vis.id, + visLayerExpressionFn: fn, + } as ISavedAugmentVis; + + const savedObject = await createAugmentVisSavedObject(savedObjectToCreate); + console.log('savedObject: ' + JSON.stringify(savedObject)); + + const response = await savedObject.save({}); + console.log('response: ' + JSON.stringify(response)); + core.notifications.toasts.addSuccess( + `Detector created: ${values.name}` + ); + getDetectors(); + }; + const handleUnlinkDetectorAction = (detector: DetectorListItem) => { setDetectorToUnlink(detector); if (!isEmpty(detector)) { @@ -328,8 +387,19 @@ export const AssociatedDetectors = ({ embeddable, closeFlyout }) => { + + { + createSavedObjects(); + }} + > + Create saved objects{' '} + +
); -}; +} + +export default AssociatedDetectors; \ No newline at end of file diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx index 1b155086..7f663caa 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx @@ -58,8 +58,8 @@ export const getColumns = ({ handleUnlinkDetectorAction }) => actions: [ { type: 'icon', - name: 'Unlink Detector', - description: 'Unlink Detector', + name: 'Remove association', + description: 'Remove association', icon: 'unlink', onClick: handleUnlinkDetectorAction, }, @@ -72,4 +72,4 @@ export const search = { incremental: true, schema: true, }, -}; +}; \ No newline at end of file diff --git a/public/components/FeatureAnywhereContextMenu/Container/Container.tsx b/public/components/FeatureAnywhereContextMenu/Container/Container.tsx new file mode 100644 index 00000000..ba7a4f7d --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/Container/Container.tsx @@ -0,0 +1,29 @@ +import React, { useState } from 'react'; +import './styles.scss'; +import AssociatedDetectors from '../AssociatedDetectors/containers/AssociatedDetectors'; + +const Container = ({ startingFlyout, ...props }) => { + const { embeddable } = props; + const index = [{ label: embeddable?.vis?.data?.indexPattern?.title }]; + const [mode, setMode] = useState(startingFlyout); + const [selectedDetectorId, setSelectedDetectorId] = useState(); + + const Flyout = { + associated: AssociatedDetectors, + }[mode]; + + return ( + + ); +}; + +export default Container; diff --git a/public/components/FeatureAnywhereContextMenu/Container/index.ts b/public/components/FeatureAnywhereContextMenu/Container/index.ts new file mode 100644 index 00000000..6a0f64ac --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/Container/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import Container from './Container'; + +export default Container; diff --git a/public/components/FeatureAnywhereContextMenu/Container/styles.scss b/public/components/FeatureAnywhereContextMenu/Container/styles.scss new file mode 100644 index 00000000..376c8557 --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/Container/styles.scss @@ -0,0 +1,7 @@ +.context-menu { + &__flyout { + &.euiFlyout--medium { + width: 740px; + } + } +} diff --git a/public/plugin.tsx b/public/plugin.tsx index 1e281dfc..2f48bae4 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -1,19 +1,27 @@ /* - * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. */ import { AppMountParameters, CoreSetup, - CoreStart, Plugin, + PluginInitializerContext, } from '../../../src/core/public'; import { CONTEXT_MENU_TRIGGER } from '../../../src/plugins/embeddable/public'; -import { ACTION_AD, createADAction } from './action/ad_dashboard_action'; +import { ACTION_AD } from './action/ad_dashboard_action'; import { PLUGIN_NAME } from './utils/constants'; -import { getActions } from './utils/contextMenu/action'; +import { getActions } from './utils/contextMenu/getActions'; import { setSavedFeatureAnywhereLoader } from './services'; +import { overlayAnomaliesFunction } from './expressions/overlay_anomalies'; +import { setClient } from './services'; declare module '../../../src/plugins/ui_actions/public' { export interface ActionContextMapping { @@ -46,12 +54,19 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin implements Plugin { actions.forEach((action) => { plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, action); }); + + // Set the HTTP client so it can be pulled into expression fns to make + // direct server-side calls + setClient(core.http); + + // registers the expression function used to render anomalies on an Augmented Visualization + plugins.expressions.registerFunction(overlayAnomaliesFunction); + return {}; } public start(core: CoreStart, plugins) { setSavedFeatureAnywhereLoader(plugins.visAugmenter.savedAugmentVisLoader); return {}; } - public stop() {} } diff --git a/public/utils/contextMenu/action.tsx b/public/utils/contextMenu/action.tsx deleted file mode 100644 index e0a21143..00000000 --- a/public/utils/contextMenu/action.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { i18n } from '@osd/i18n'; -import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; -import { toMountPoint } from '../../../../../src/plugins/opensearch_dashboards_react/public'; -import { Action } from '../../../../../src/plugins/ui_actions/public'; -import { createADAction } from '../../action/ad_dashboard_action'; -import CreateAnomalyDetector from '../../components/FeatureAnywhereContextMenu/CreateAnomalyDetector'; -import { AssociatedDetectors } from '../../components/FeatureAnywhereContextMenu/AssociatedDetectors'; -import { CoreServicesContext } from '../../components/CoreServices/CoreServices'; -import { Provider } from 'react-redux'; -import configureStore from '../../redux/configureStore'; - -// This is used to create all actions in the same context menu -const grouping: Action['grouping'] = [ - { - id: 'ad-dashboard-context-menu', - getDisplayName: () => 'Anomaly Detector', - getIconType: () => 'apmTrace', - order: 200, - }, -]; - -export const getActions = ({ core }) => - [ - { - grouping, - id: 'createAnomalyDetector', - title: i18n.translate( - 'dashboard.actions.adMenuItem.createAnomalyDetector.displayName', - { - defaultMessage: 'Create anomaly detector', - } - ), - icon: 'plusInCircle' as EuiIconType, - order: 100, - onClick: async ({ embeddable }) => { - const services = await core.getStartServices(); - const http = services[0].http; - const store = configureStore(http); - const openFlyout = services[0].overlays.openFlyout; - openFlyout( - toMountPoint( - - - - - - ), - { size: 'm' } - ); - }, - }, - { - grouping, - id: 'manageAnomalyDetector', - title: i18n.translate( - 'dashboard.actions.alertingMenuItem.manageAnomalyDetector.displayName', - { - defaultMessage: 'Manage anomaly detector', - } - ), - icon: 'wrench' as EuiIconType, - order: 99, - onClick: async ({ embeddable }) => { - const services = await core.getStartServices(); - const http = services[0].http; - const store = configureStore(http); - const openFlyout = services[0].overlays.openFlyout; - const overlay = openFlyout( - toMountPoint( - - - overlay.close(), - core, - services, - }} - /> - - - ), - { size: 'm' } - ); - }, - }, - { - id: 'documentation', - title: i18n.translate( - 'dashboard.actions.adMenuItem.documentation.displayName', - { - defaultMessage: 'Documentation', - } - ), - icon: 'documentation' as EuiIconType, - order: 98, - onClick: () => { - window.open( - 'https://opensearch.org/docs/latest/monitoring-plugins/alerting/index/', - '_blank' - ); - }, - }, - ].map((options) => createADAction({ ...options, grouping })); diff --git a/public/utils/contextMenu/styles.scss b/public/utils/contextMenu/styles.scss new file mode 100644 index 00000000..3bc3ddfe --- /dev/null +++ b/public/utils/contextMenu/styles.scss @@ -0,0 +1,30 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +@import '@elastic/eui/src/global_styling/variables/index'; + +.ad-dashboards-context-menu { + &__text-content { + &:hover { + text-decoration: none; + } + } + + &__no-action { + cursor: default; + + &:hover, + &:active { + text-decoration: none; + background-color: inherit; + } + } + + &__view-events-text { + h5 { + color: inherit; + } + } +} \ No newline at end of file From de163a4ae738dcb9bdb8e2b078e6ddb13246e849 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 8 May 2023 21:22:59 +0300 Subject: [PATCH 10/17] ran prettier Signed-off-by: Amit Galitzky --- public/action/ad_dashboard_action.tsx | 2 +- .../components/EmptyMessage/EmptyMessage.tsx | 2 +- .../containers/AssociatedDetectors.tsx | 151 +++++++++--------- .../AssociatedDetectors/styles.scss | 2 +- .../AssociatedDetectors/utils/helpers.tsx | 2 +- public/utils/contextMenu/getActions.tsx | 2 +- public/utils/contextMenu/styles.scss | 2 +- 7 files changed, 82 insertions(+), 81 deletions(-) diff --git a/public/action/ad_dashboard_action.tsx b/public/action/ad_dashboard_action.tsx index 0be356ed..1330eaea 100644 --- a/public/action/ad_dashboard_action.tsx +++ b/public/action/ad_dashboard_action.tsx @@ -75,4 +75,4 @@ export const createADAction = ({ onClick({ embeddable }); }, - }); \ No newline at end of file + }); diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx index 1a833bd0..7e110e27 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx @@ -31,4 +31,4 @@ export const EmptyAssociatedDetectorFlyoutMessage = ( } /> -); \ No newline at end of file +); diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index c0d9e9bc..866978d7 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -35,7 +35,11 @@ import { } from '../../../../../server/utils/helpers'; import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public'; import { EmptyAssociatedDetectorFlyoutMessage } from '../components/EmptyMessage/EmptyMessage'; -import { ISavedAugmentVis, VisLayerExpressionFn, createAugmentVisSavedObject } from '../../../../../../../src/plugins/vis_augmenter/public'; +import { + ISavedAugmentVis, + VisLayerExpressionFn, + createAugmentVisSavedObject, +} from '../../../../../../../src/plugins/vis_augmenter/public'; import { ASSOCIATED_DETECTOR_ACTION } from '../utils/constants'; import { ConfirmUnlinkDetectorModal } from '../components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal'; @@ -124,29 +128,28 @@ function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { // Handles all changes in the assoicated detectors such as unlinking or new detectors associated useEffect(() => { // Gets all augmented saved objects - savedObjectLoader.findAll().then((resp: any) => { - if (resp != undefined) { - const savedAugmentObjectsArr: ISavedAugmentVis[] = get( - resp, - 'hits', - [] - ); - const curSelectedDetectors = getAssociatedDetectors( - Object.values(allDetectors), - savedAugmentObjectsArr + savedObjectLoader + .findAll() + .then((resp: any) => { + if (resp != undefined) { + const savedAugmentObjectsArr: ISavedAugmentVis[] = get( + resp, + 'hits', + [] + ); + const curSelectedDetectors = getAssociatedDetectors( + Object.values(allDetectors), + savedAugmentObjectsArr + ); + setSelectedDetectors(curSelectedDetectors); + setIsLoadingFinalDetectors(false); + } + }) + .catch((error) => { + core.notifications.toasts.addDanger( + prettifyErrorMessage(`Unable to fetch associated detectors: ${error}`) ); - setSelectedDetectors(curSelectedDetectors); - setIsLoadingFinalDetectors(false); - } - } - ) - .catch((error) => { - core.notifications.toasts.addDanger( - prettifyErrorMessage( - `Unable to fetch associated detectors: ${error}` - ) - ); - }) + }); }, [allDetectors]); // cross checks all the detectors that exist with all the savedAugment Objects to only display ones @@ -195,13 +198,13 @@ function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { const savedObjectToUnlinkId = get(savedAugmentToUnlink, 'id', ''); await savedObjectLoader .delete(savedObjectToUnlinkId) - .then( async (resp:any )=> { - console.log("inside delete after unlinking") + .then(async (resp: any) => { + console.log('inside delete after unlinking'); core.notifications.toasts.addSuccess( `Detector created: ${values.name}` ); // core.notifications.toasts.addSuccess({ - // title: `Association removed between the ${detectorToUnlink.name} + // title: `Association removed between the ${detectorToUnlink.name} // and the ${embeddableTitle} visualization`, // text: 'The detector\'s anomalies do not appear on the visualization. Refresh your dashboard to update the visualization', // }) @@ -257,46 +260,44 @@ function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { console.log('inside create anomaly detector'); }; - // This method is only here for development/testing purposes. - const createSavedObjects = async () => { - enum VisLayerTypes { - PointInTimeEvents = 'PointInTimeEvents', - } - console.log('all Detectors: ' + JSON.stringify(allDetectors)); - - const fn = { - type: VisLayerTypes.PointInTimeEvents, - name: 'overlay_anomalies', - args: { - detectorId: 'pggP7ocBbTXmavYrSMaD', - }, - } as VisLayerExpressionFn; - - // const fn = { - // type: VisLayerTypes.PointInTimeEvents, - // name: 'test-fn', - // args: { - // testArg: 'bNZIp4UB3stq6UHwpWwS', - // }, - // } as VisLayerExpressionFn; - - const savedObjectToCreate = { - title: 'test-title', - pluginResourceId: 'pggP7ocBbTXmavYrSMaD', - visId: embeddable.vis.id, - visLayerExpressionFn: fn, - } as ISavedAugmentVis; - - const savedObject = await createAugmentVisSavedObject(savedObjectToCreate); - console.log('savedObject: ' + JSON.stringify(savedObject)); - - const response = await savedObject.save({}); - console.log('response: ' + JSON.stringify(response)); - core.notifications.toasts.addSuccess( - `Detector created: ${values.name}` - ); - getDetectors(); - }; + // This method is only here for development/testing purposes. + const createSavedObjects = async () => { + enum VisLayerTypes { + PointInTimeEvents = 'PointInTimeEvents', + } + console.log('all Detectors: ' + JSON.stringify(allDetectors)); + + const fn = { + type: VisLayerTypes.PointInTimeEvents, + name: 'overlay_anomalies', + args: { + detectorId: 'pggP7ocBbTXmavYrSMaD', + }, + } as VisLayerExpressionFn; + + // const fn = { + // type: VisLayerTypes.PointInTimeEvents, + // name: 'test-fn', + // args: { + // testArg: 'bNZIp4UB3stq6UHwpWwS', + // }, + // } as VisLayerExpressionFn; + + const savedObjectToCreate = { + title: 'test-title', + pluginResourceId: 'pggP7ocBbTXmavYrSMaD', + visId: embeddable.vis.id, + visLayerExpressionFn: fn, + } as ISavedAugmentVis; + + const savedObject = await createAugmentVisSavedObject(savedObjectToCreate); + console.log('savedObject: ' + JSON.stringify(savedObject)); + + const response = await savedObject.save({}); + console.log('response: ' + JSON.stringify(response)); + core.notifications.toasts.addSuccess(`Detector created: ${values.name}`); + getDetectors(); + }; const handleUnlinkDetectorAction = (detector: DetectorListItem) => { setDetectorToUnlink(detector); @@ -388,18 +389,18 @@ function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { - { - createSavedObjects(); - }} - > - Create saved objects{' '} - - + { + createSavedObjects(); + }} + > + Create saved objects{' '} + + ); } -export default AssociatedDetectors; \ No newline at end of file +export default AssociatedDetectors; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss index f78a0388..192c2cda 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss @@ -2,7 +2,7 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ - + @import '@elastic/eui/src/global_styling/variables/index'; .associated-detectors { diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx index 7f663caa..c6125537 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/utils/helpers.tsx @@ -72,4 +72,4 @@ export const search = { incremental: true, schema: true, }, -}; \ No newline at end of file +}; diff --git a/public/utils/contextMenu/getActions.tsx b/public/utils/contextMenu/getActions.tsx index 4dcb05f6..692beaa4 100644 --- a/public/utils/contextMenu/getActions.tsx +++ b/public/utils/contextMenu/getActions.tsx @@ -81,4 +81,4 @@ export const getActions = () => { }, }, ].map((options) => createADAction({ ...options, grouping })); -}; \ No newline at end of file +}; diff --git a/public/utils/contextMenu/styles.scss b/public/utils/contextMenu/styles.scss index 3bc3ddfe..0c7a7d45 100644 --- a/public/utils/contextMenu/styles.scss +++ b/public/utils/contextMenu/styles.scss @@ -27,4 +27,4 @@ color: inherit; } } -} \ No newline at end of file +} From 467c7108ff50130b4cdd56c20e49ab0a43311a9d Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 8 May 2023 23:21:17 +0300 Subject: [PATCH 11/17] fix notification and clean up associated detectors Signed-off-by: Amit Galitzky --- .../containers/AssociatedDetectors.tsx | 73 ++----------------- 1 file changed, 7 insertions(+), 66 deletions(-) diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index 866978d7..258c89b8 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -18,8 +18,6 @@ import { import { get, isEmpty } from 'lodash'; import '../styles.scss'; import { getColumns } from '../utils/helpers'; -import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices'; -import { CoreStart } from '../../../../../../../src/core/public'; import { useDispatch, useSelector } from 'react-redux'; import { AppState } from '../../../../redux/reducers'; import { DetectorListItem } from '../../../../models/interfaces'; @@ -35,11 +33,7 @@ import { } from '../../../../../server/utils/helpers'; import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public'; import { EmptyAssociatedDetectorFlyoutMessage } from '../components/EmptyMessage/EmptyMessage'; -import { - ISavedAugmentVis, - VisLayerExpressionFn, - createAugmentVisSavedObject, -} from '../../../../../../../src/plugins/vis_augmenter/public'; +import { ISavedAugmentVis } from '../../../../../../../src/plugins/vis_augmenter/public'; import { ASSOCIATED_DETECTOR_ACTION } from '../utils/constants'; import { ConfirmUnlinkDetectorModal } from '../components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal'; @@ -51,8 +45,7 @@ interface ConfirmModalState { affectedDetector: DetectorListItem; } -function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { - const core = React.useContext(CoreServicesContext) as CoreStart; +function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { const dispatch = useDispatch(); const allDetectors = useSelector((state: AppState) => state.ad.detectorList); const isRequestingFromES = useSelector( @@ -199,15 +192,11 @@ function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { await savedObjectLoader .delete(savedObjectToUnlinkId) .then(async (resp: any) => { - console.log('inside delete after unlinking'); - core.notifications.toasts.addSuccess( - `Detector created: ${values.name}` - ); - // core.notifications.toasts.addSuccess({ - // title: `Association removed between the ${detectorToUnlink.name} - // and the ${embeddableTitle} visualization`, - // text: 'The detector\'s anomalies do not appear on the visualization. Refresh your dashboard to update the visualization', - // }) + core.notifications.toasts.addSuccess({ + title: `Association removed between the ${detectorToUnlink.name} + and the ${embeddableTitle} visualization`, + text: "The detector's anomalies do not appear on the visualization. Refresh your dashboard to update the visualization", + }); }) .catch((error) => { core.notifications.toasts.addDanger( @@ -260,45 +249,6 @@ function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { console.log('inside create anomaly detector'); }; - // This method is only here for development/testing purposes. - const createSavedObjects = async () => { - enum VisLayerTypes { - PointInTimeEvents = 'PointInTimeEvents', - } - console.log('all Detectors: ' + JSON.stringify(allDetectors)); - - const fn = { - type: VisLayerTypes.PointInTimeEvents, - name: 'overlay_anomalies', - args: { - detectorId: 'pggP7ocBbTXmavYrSMaD', - }, - } as VisLayerExpressionFn; - - // const fn = { - // type: VisLayerTypes.PointInTimeEvents, - // name: 'test-fn', - // args: { - // testArg: 'bNZIp4UB3stq6UHwpWwS', - // }, - // } as VisLayerExpressionFn; - - const savedObjectToCreate = { - title: 'test-title', - pluginResourceId: 'pggP7ocBbTXmavYrSMaD', - visId: embeddable.vis.id, - visLayerExpressionFn: fn, - } as ISavedAugmentVis; - - const savedObject = await createAugmentVisSavedObject(savedObjectToCreate); - console.log('savedObject: ' + JSON.stringify(savedObject)); - - const response = await savedObject.save({}); - console.log('response: ' + JSON.stringify(response)); - core.notifications.toasts.addSuccess(`Detector created: ${values.name}`); - getDetectors(); - }; - const handleUnlinkDetectorAction = (detector: DetectorListItem) => { setDetectorToUnlink(detector); if (!isEmpty(detector)) { @@ -388,15 +338,6 @@ function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { - - { - createSavedObjects(); - }} - > - Create saved objects{' '} - - From c92ac94bc91d595adac1a3670332c1ce78a86cc9 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Thu, 11 May 2023 04:52:03 +0300 Subject: [PATCH 12/17] addressing comments Signed-off-by: Amit Galitzky --- .../ConfirmUnlinkDetectorModal.tsx | 4 +- .../containers/AssociatedDetectors.tsx | 48 +++++++------------ .../AssociatedDetectors/styles.scss | 2 - .../Container/Container.tsx | 7 ++- .../Container/styles.scss | 7 --- .../containers/DocumentationTitle.tsx | 2 +- .../DocumentationTitle/index.ts | 8 ++++ public/utils/contextMenu/getActions.tsx | 4 ++ public/utils/contextMenu/styles.scss | 30 ------------ 9 files changed, 37 insertions(+), 75 deletions(-) delete mode 100644 public/components/FeatureAnywhereContextMenu/Container/styles.scss create mode 100644 public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.ts delete mode 100644 public/utils/contextMenu/styles.scss diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx index ec700bcb..25687ed5 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal.tsx @@ -39,9 +39,7 @@ export const ConfirmUnlinkDetectorModal = ( maxWidth={450} > - - {'Remove association?'}  - + {'Remove association?'} diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index 258c89b8..549eed5a 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -212,20 +212,6 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { }); }; - const getUnlinkConfirmModal = () => { - if (confirmModalState.isOpen) { - return ( - - ); - } - }; - const handleHideModal = () => { setConfirmModalState({ ...confirmModalState, @@ -245,25 +231,19 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { }; // TODO: this part is incomplete because it is pending on a different PR that will have all the associate existing changes - const onAssociateExistingDetector = async () => { + const openAssociateDetectorFlyout = async () => { console.log('inside create anomaly detector'); }; const handleUnlinkDetectorAction = (detector: DetectorListItem) => { setDetectorToUnlink(detector); - if (!isEmpty(detector)) { - setConfirmModalState({ - isOpen: true, - action: ASSOCIATED_DETECTOR_ACTION.UNLINK, - isListLoading: false, - isRequestingToClose: false, - affectedDetector: detector, - }); - } else { - core.notifications.toasts.addWarning( - 'Make sure selected detector has not been deleted' - ); - } + setConfirmModalState({ + isOpen: true, + action: ASSOCIATED_DETECTOR_ACTION.UNLINK, + isListLoading: false, + isRequestingToClose: false, + affectedDetector: detector, + }); }; const columns = useMemo( @@ -317,7 +297,15 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { - {getUnlinkConfirmModal()} + {confirmModalState.isOpen ? ( + + ) : null} @@ -329,7 +317,7 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { fill iconType="link" onClick={() => { - onAssociateExistingDetector(); + openAssociateDetectorFlyout(); }} > Associate a detector diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss index 192c2cda..6598f00e 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/styles.scss @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -@import '@elastic/eui/src/global_styling/variables/index'; - .associated-detectors { height: 100%; display: flex; diff --git a/public/components/FeatureAnywhereContextMenu/Container/Container.tsx b/public/components/FeatureAnywhereContextMenu/Container/Container.tsx index ba7a4f7d..ad7a6538 100644 --- a/public/components/FeatureAnywhereContextMenu/Container/Container.tsx +++ b/public/components/FeatureAnywhereContextMenu/Container/Container.tsx @@ -1,10 +1,13 @@ import React, { useState } from 'react'; import './styles.scss'; import AssociatedDetectors from '../AssociatedDetectors/containers/AssociatedDetectors'; +import { get } from 'lodash'; const Container = ({ startingFlyout, ...props }) => { const { embeddable } = props; - const index = [{ label: embeddable?.vis?.data?.indexPattern?.title }]; + const indices: { label: string }[] = [ + { label: get(embeddable, 'vis.data.indexPattern.title', '') }, + ]; const [mode, setMode] = useState(startingFlyout); const [selectedDetectorId, setSelectedDetectorId] = useState(); @@ -18,7 +21,7 @@ const Container = ({ startingFlyout, ...props }) => { ...props, setMode, mode, - index, + indices, selectedDetectorId, setSelectedDetectorId, }} diff --git a/public/components/FeatureAnywhereContextMenu/Container/styles.scss b/public/components/FeatureAnywhereContextMenu/Container/styles.scss deleted file mode 100644 index 376c8557..00000000 --- a/public/components/FeatureAnywhereContextMenu/Container/styles.scss +++ /dev/null @@ -1,7 +0,0 @@ -.context-menu { - &__flyout { - &.euiFlyout--medium { - width: 740px; - } - } -} diff --git a/public/components/FeatureAnywhereContextMenu/DocumentationTitle/containers/DocumentationTitle.tsx b/public/components/FeatureAnywhereContextMenu/DocumentationTitle/containers/DocumentationTitle.tsx index 22d2ac3c..3ee81e65 100644 --- a/public/components/FeatureAnywhereContextMenu/DocumentationTitle/containers/DocumentationTitle.tsx +++ b/public/components/FeatureAnywhereContextMenu/DocumentationTitle/containers/DocumentationTitle.tsx @@ -25,4 +25,4 @@ const DocumentationTitle = () => ( ); -export default DocumentationTitle; \ No newline at end of file +export default DocumentationTitle; diff --git a/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.ts b/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.ts new file mode 100644 index 00000000..e9f1bd89 --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import DocumentationTitle from './containers/DocumentationTitle'; + +export default DocumentationTitle; diff --git a/public/utils/contextMenu/getActions.tsx b/public/utils/contextMenu/getActions.tsx index 692beaa4..69a9a6c8 100644 --- a/public/utils/contextMenu/getActions.tsx +++ b/public/utils/contextMenu/getActions.tsx @@ -7,9 +7,13 @@ import { createADAction } from '../../action/ad_dashboard_action'; import AnywhereParentFlyout from '../../components/FeatureAnywhereContextMenu/AnywhereParentFlyout'; import { Provider } from 'react-redux'; import configureStore from '../../redux/configureStore'; +<<<<<<< HEAD import DocumentationTitle from '../../components/FeatureAnywhereContextMenu/DocumentationTitle/containers/DocumentationTitle'; import { AD_DOCS_LINK, APM_TRACE } from '../constants'; import { getClient, getOverlays } from '../../../public/services'; +======= +import DocumentationTitle from '../../../public/components/FeatureAnywhereContextMenu/DocumentationTitle/containers/DocumentationTitle'; +>>>>>>> 327dfbd (addressing comments) // This is used to create all actions in the same context menu const grouping: Action['grouping'] = [ diff --git a/public/utils/contextMenu/styles.scss b/public/utils/contextMenu/styles.scss deleted file mode 100644 index 0c7a7d45..00000000 --- a/public/utils/contextMenu/styles.scss +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -@import '@elastic/eui/src/global_styling/variables/index'; - -.ad-dashboards-context-menu { - &__text-content { - &:hover { - text-decoration: none; - } - } - - &__no-action { - cursor: default; - - &:hover, - &:active { - text-decoration: none; - background-color: inherit; - } - } - - &__view-events-text { - h5 { - color: inherit; - } - } -} From 7a27a5f18b536bd2c9c26ad656483454add7c5ec Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Tue, 16 May 2023 19:27:03 +0300 Subject: [PATCH 13/17] renaming some files and adding index.ts Signed-off-by: Amit Galitzky --- .../AnywhereParentFlyout.tsx | 12 ++++--- .../AnywhereParentFlyout/index.ts | 8 +++++ .../EmptyAssociatedDetectorMessage.tsx} | 4 +-- .../AssociatedDetectors/components/index.ts | 7 ++++ .../containers/AssociatedDetectors.tsx | 10 +++--- .../Container/Container.tsx | 32 ------------------- .../Container/index.ts | 8 ----- public/utils/contextMenu/getActions.tsx | 6 +--- 8 files changed, 30 insertions(+), 57 deletions(-) create mode 100644 public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.ts rename public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/{EmptyMessage/EmptyMessage.tsx => EmptyAssociatedDetectorMessage/EmptyAssociatedDetectorMessage.tsx} (89%) create mode 100644 public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/index.ts delete mode 100644 public/components/FeatureAnywhereContextMenu/Container/Container.tsx delete mode 100644 public/components/FeatureAnywhereContextMenu/Container/index.ts diff --git a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/AnywhereParentFlyout.tsx b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/AnywhereParentFlyout.tsx index 2a54a169..d106b332 100644 --- a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/AnywhereParentFlyout.tsx +++ b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/AnywhereParentFlyout.tsx @@ -1,10 +1,11 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 */ import React, { useState } from 'react'; import { get } from 'lodash'; import AddAnomalyDetector from '../CreateAnomalyDetector'; +import AssociatedDetectors from '../AssociatedDetectors/containers/AssociatedDetectors'; import { getEmbeddable } from '../../../../public/services'; const AnywhereParentFlyout = ({ startingFlyout, ...props }) => { @@ -18,6 +19,7 @@ const AnywhereParentFlyout = ({ startingFlyout, ...props }) => { const AnywhereFlyout = { create: AddAnomalyDetector, + associated: AssociatedDetectors, }[mode]; return ( @@ -30,8 +32,8 @@ const AnywhereParentFlyout = ({ startingFlyout, ...props }) => { selectedDetectorId, setSelectedDetectorId, }} - /> + /> ); }; -export default AnywhereParentFlyout; \ No newline at end of file +export default AnywhereParentFlyout; diff --git a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.ts b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.ts new file mode 100644 index 00000000..591d4b6d --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import AnywhereParentFlyout from './AnywhereParentFlyout'; + +export default AnywhereParentFlyout; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyAssociatedDetectorMessage/EmptyAssociatedDetectorMessage.tsx similarity index 89% rename from public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx rename to public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyAssociatedDetectorMessage/EmptyAssociatedDetectorMessage.tsx index 7e110e27..d005e087 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyMessage/EmptyMessage.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/EmptyAssociatedDetectorMessage/EmptyAssociatedDetectorMessage.tsx @@ -13,9 +13,7 @@ interface EmptyDetectorProps { embeddableTitle: string; } -export const EmptyAssociatedDetectorFlyoutMessage = ( - props: EmptyDetectorProps -) => ( +export const EmptyAssociatedDetectorMessage = (props: EmptyDetectorProps) => ( No anomaly detectors to display} titleSize="s" diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/index.ts b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/index.ts new file mode 100644 index 00000000..92d619eb --- /dev/null +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/components/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { ConfirmUnlinkDetectorModal } from './ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal'; +export { EmptyAssociatedDetectorMessage } from './EmptyAssociatedDetectorMessage/EmptyAssociatedDetectorMessage'; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index 549eed5a..deef439c 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -32,10 +32,12 @@ import { NO_PERMISSIONS_KEY_WORD, } from '../../../../../server/utils/helpers'; import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public'; -import { EmptyAssociatedDetectorFlyoutMessage } from '../components/EmptyMessage/EmptyMessage'; +import { + EmptyAssociatedDetectorMessage, + ConfirmUnlinkDetectorModal, +} from '../components'; import { ISavedAugmentVis } from '../../../../../../../src/plugins/vis_augmenter/public'; import { ASSOCIATED_DETECTOR_ACTION } from '../utils/constants'; -import { ConfirmUnlinkDetectorModal } from '../components/ConfirmUnlinkDetectorModal/ConfirmUnlinkDetectorModal'; interface ConfirmModalState { isOpen: boolean; @@ -256,14 +258,14 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { return 'Loading detectors...'; } else if (!isEmpty(selectedDetectors)) { return ( - ); } else { return ( - diff --git a/public/components/FeatureAnywhereContextMenu/Container/Container.tsx b/public/components/FeatureAnywhereContextMenu/Container/Container.tsx deleted file mode 100644 index ad7a6538..00000000 --- a/public/components/FeatureAnywhereContextMenu/Container/Container.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { useState } from 'react'; -import './styles.scss'; -import AssociatedDetectors from '../AssociatedDetectors/containers/AssociatedDetectors'; -import { get } from 'lodash'; - -const Container = ({ startingFlyout, ...props }) => { - const { embeddable } = props; - const indices: { label: string }[] = [ - { label: get(embeddable, 'vis.data.indexPattern.title', '') }, - ]; - const [mode, setMode] = useState(startingFlyout); - const [selectedDetectorId, setSelectedDetectorId] = useState(); - - const Flyout = { - associated: AssociatedDetectors, - }[mode]; - - return ( - - ); -}; - -export default Container; diff --git a/public/components/FeatureAnywhereContextMenu/Container/index.ts b/public/components/FeatureAnywhereContextMenu/Container/index.ts deleted file mode 100644 index 6a0f64ac..00000000 --- a/public/components/FeatureAnywhereContextMenu/Container/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import Container from './Container'; - -export default Container; diff --git a/public/utils/contextMenu/getActions.tsx b/public/utils/contextMenu/getActions.tsx index 69a9a6c8..4dcb05f6 100644 --- a/public/utils/contextMenu/getActions.tsx +++ b/public/utils/contextMenu/getActions.tsx @@ -7,13 +7,9 @@ import { createADAction } from '../../action/ad_dashboard_action'; import AnywhereParentFlyout from '../../components/FeatureAnywhereContextMenu/AnywhereParentFlyout'; import { Provider } from 'react-redux'; import configureStore from '../../redux/configureStore'; -<<<<<<< HEAD import DocumentationTitle from '../../components/FeatureAnywhereContextMenu/DocumentationTitle/containers/DocumentationTitle'; import { AD_DOCS_LINK, APM_TRACE } from '../constants'; import { getClient, getOverlays } from '../../../public/services'; -======= -import DocumentationTitle from '../../../public/components/FeatureAnywhereContextMenu/DocumentationTitle/containers/DocumentationTitle'; ->>>>>>> 327dfbd (addressing comments) // This is used to create all actions in the same context menu const grouping: Action['grouping'] = [ @@ -85,4 +81,4 @@ export const getActions = () => { }, }, ].map((options) => createADAction({ ...options, grouping })); -}; +}; \ No newline at end of file From 9c0719d0dd2bf5bc6a0ed7a68db591c7387da72d Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Tue, 16 May 2023 19:56:14 +0300 Subject: [PATCH 14/17] Added license to new files Signed-off-by: Amit Galitzky --- public/utils/contextMenu/getActions.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/utils/contextMenu/getActions.tsx b/public/utils/contextMenu/getActions.tsx index 4dcb05f6..03ba8fd4 100644 --- a/public/utils/contextMenu/getActions.tsx +++ b/public/utils/contextMenu/getActions.tsx @@ -1,3 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + import React from 'react'; import { i18n } from '@osd/i18n'; import { EuiIconType } from '@elastic/eui'; From 3d9a4ebd0b18013865b3aac80c81af9cbb233c88 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 22 May 2023 11:29:45 -0700 Subject: [PATCH 15/17] clean up after rebase Signed-off-by: Amit Galitzky --- opensearch_dashboards.json | 1 - public/action/ad_dashboard_action.tsx | 6 +- .../AnywhereParentFlyout/index.tsx | 2 +- .../DocumentationTitle/index.tsx | 2 +- public/plugin.ts | 85 +++++++++++++++++++ public/plugin.tsx | 72 ---------------- public/services.ts | 3 - public/utils/constants.ts | 3 +- public/utils/contextMenu/getActions.tsx | 15 ++-- 9 files changed, 98 insertions(+), 91 deletions(-) create mode 100644 public/plugin.ts delete mode 100644 public/plugin.tsx diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 3789dcb9..21cd2fbb 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -16,7 +16,6 @@ "visAugmenter", "opensearchDashboardsUtils" ], - "optionalPlugins": [], "server": true, "ui": true } diff --git a/public/action/ad_dashboard_action.tsx b/public/action/ad_dashboard_action.tsx index 1330eaea..a845351a 100644 --- a/public/action/ad_dashboard_action.tsx +++ b/public/action/ad_dashboard_action.tsx @@ -1,6 +1,6 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 */ import { IEmbeddable } from '../../../../src/plugins/dashboard/public/embeddable_plugin'; import { diff --git a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.tsx b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.tsx index cca0078b..591d4b6d 100644 --- a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.tsx +++ b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.tsx @@ -5,4 +5,4 @@ import AnywhereParentFlyout from './AnywhereParentFlyout'; -export default AnywhereParentFlyout; \ No newline at end of file +export default AnywhereParentFlyout; diff --git a/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.tsx b/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.tsx index 03b2fb80..e9f1bd89 100644 --- a/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.tsx +++ b/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.tsx @@ -5,4 +5,4 @@ import DocumentationTitle from './containers/DocumentationTitle'; -export default DocumentationTitle; \ No newline at end of file +export default DocumentationTitle; diff --git a/public/plugin.ts b/public/plugin.ts new file mode 100644 index 00000000..a54bf191 --- /dev/null +++ b/public/plugin.ts @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import { + AppMountParameters, + CoreSetup, + CoreStart, + Plugin, + } from '../../../src/core/public'; + import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart } from '../../../src/plugins/embeddable/public'; + import { ACTION_AD } from './action/ad_dashboard_action'; + import { PLUGIN_NAME } from './utils/constants'; + import { getActions } from './utils/contextMenu/getActions'; + import { overlayAnomaliesFunction } from './expressions/overlay_anomalies'; + import { setClient, setEmbeddable, setOverlays } from './services'; + import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; + + declare module '../../../src/plugins/ui_actions/public' { + export interface ActionContextMapping { + [ACTION_AD]: {}; + } + } + + export interface AnomalyDetectionSetupDeps { + embeddable: EmbeddableSetup; + } + + export interface AnomalyDetectionStartDeps { + embeddable: EmbeddableStart; + } + + export class AnomalyDetectionOpenSearchDashboardsPlugin implements + Plugin { + + public setup(core: CoreSetup, plugins: any) { + core.application.register({ + id: PLUGIN_NAME, + title: 'Anomaly Detection', + category: { + id: 'opensearch', + label: 'OpenSearch Plugins', + order: 2000, + }, + order: 5000, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./anomaly_detection_app'); + const [coreStart] = await core.getStartServices(); + return renderApp(coreStart, params); + }, + }); + + // Set the HTTP client so it can be pulled into expression fns to make + // direct server-side calls + setClient(core.http); + + // Create context menu actions. Pass core, to access service for flyouts. + const actions = getActions(); + + // Add actions to uiActions + actions.forEach((action) => { + plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, action); + }); + + // registers the expression function used to render anomalies on an Augmented Visualization + plugins.expressions.registerFunction(overlayAnomaliesFunction); + return {}; + } + + public start( + core: CoreStart, + {embeddable }: AnomalyDetectionStartDeps + ): AnomalyDetectionOpenSearchDashboardsPluginStart { + setEmbeddable(embeddable); + setOverlays(core.overlays); + return {}; + } + } \ No newline at end of file diff --git a/public/plugin.tsx b/public/plugin.tsx deleted file mode 100644 index 2f48bae4..00000000 --- a/public/plugin.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -import { - AppMountParameters, - CoreSetup, - Plugin, - PluginInitializerContext, -} from '../../../src/core/public'; -import { CONTEXT_MENU_TRIGGER } from '../../../src/plugins/embeddable/public'; -import { ACTION_AD } from './action/ad_dashboard_action'; -import { PLUGIN_NAME } from './utils/constants'; -import { getActions } from './utils/contextMenu/getActions'; -import { setSavedFeatureAnywhereLoader } from './services'; -import { overlayAnomaliesFunction } from './expressions/overlay_anomalies'; -import { setClient } from './services'; - -declare module '../../../src/plugins/ui_actions/public' { - export interface ActionContextMapping { - [ACTION_AD]: {}; - } -} - -export class AnomalyDetectionOpenSearchDashboardsPlugin implements Plugin { - public setup(core: CoreSetup, plugins) { - core.application.register({ - id: PLUGIN_NAME, - title: 'Anomaly Detection', - category: { - id: 'opensearch', - label: 'OpenSearch Plugins', - order: 2000, - }, - order: 5000, - mount: async (params: AppMountParameters) => { - const { renderApp } = await import('./anomaly_detection_app'); - const [coreStart] = await core.getStartServices(); - return renderApp(coreStart, params); - }, - }); - - // Create context menu actions. Pass core, to access service for flyouts. - const actions = getActions({ core }); - - // Add actions to uiActions - actions.forEach((action) => { - plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, action); - }); - - // Set the HTTP client so it can be pulled into expression fns to make - // direct server-side calls - setClient(core.http); - - // registers the expression function used to render anomalies on an Augmented Visualization - plugins.expressions.registerFunction(overlayAnomaliesFunction); - return {}; - } - - public start(core: CoreStart, plugins) { - setSavedFeatureAnywhereLoader(plugins.visAugmenter.savedAugmentVisLoader); - return {}; - } - public stop() {} -} diff --git a/public/services.ts b/public/services.ts index 32561618..3857a95f 100644 --- a/public/services.ts +++ b/public/services.ts @@ -13,12 +13,9 @@ export const [getSavedFeatureAnywhereLoader, setSavedFeatureAnywhereLoader] = export const [getClient, setClient] = createGetterSetter('http'); -<<<<<<< HEAD export const [getEmbeddable, setEmbeddable] = createGetterSetter('Embeddable'); export const [getOverlays, setOverlays] = createGetterSetter('Overlays'); -======= ->>>>>>> a9065fb (prettier formating and merge conflicts) diff --git a/public/utils/constants.ts b/public/utils/constants.ts index 099e6a7e..17a8b86a 100644 --- a/public/utils/constants.ts +++ b/public/utils/constants.ts @@ -53,7 +53,8 @@ export const ANOMALY_RESULT_INDEX = '.opendistro-anomaly-results'; export const BASE_DOCS_LINK = 'https://opensearch.org/docs/monitoring-plugins'; -export const AD_DOCS_LINK = 'https://opensearch.org/docs/latest/observing-your-data/ad/index/'; +export const AD_DOCS_LINK = + 'https://opensearch.org/docs/latest/observing-your-data/ad/index/'; export const MAX_DETECTORS = 1000; diff --git a/public/utils/contextMenu/getActions.tsx b/public/utils/contextMenu/getActions.tsx index 03ba8fd4..0c1302e4 100644 --- a/public/utils/contextMenu/getActions.tsx +++ b/public/utils/contextMenu/getActions.tsx @@ -36,10 +36,10 @@ export const getActions = () => { toMountPoint( overlay.close()} - /> + startingFlyout={startingFlyout} + embeddable={embeddable} + closeFlyout={() => overlay.close()} + /> ), { size: 'm', className: 'context-menu__flyout' } @@ -79,11 +79,8 @@ export const getActions = () => { icon: 'documentation' as EuiIconType, order: 98, onClick: () => { - window.open( - AD_DOCS_LINK, - '_blank' - ); + window.open(AD_DOCS_LINK, '_blank'); }, }, ].map((options) => createADAction({ ...options, grouping })); -}; \ No newline at end of file +}; From e62cea8fc3872b01931b1c04d5c2205547edc482 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 22 May 2023 13:23:40 -0700 Subject: [PATCH 16/17] addressed more comments Signed-off-by: Amit Galitzky --- .../AnywhereParentFlyout.tsx | 2 - .../containers/AssociatedDetectors.tsx | 14 +- public/plugin.ts | 152 ++++++++++-------- 3 files changed, 88 insertions(+), 80 deletions(-) diff --git a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/AnywhereParentFlyout.tsx b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/AnywhereParentFlyout.tsx index d106b332..70c27e68 100644 --- a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/AnywhereParentFlyout.tsx +++ b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/AnywhereParentFlyout.tsx @@ -4,7 +4,6 @@ */ import React, { useState } from 'react'; import { get } from 'lodash'; -import AddAnomalyDetector from '../CreateAnomalyDetector'; import AssociatedDetectors from '../AssociatedDetectors/containers/AssociatedDetectors'; import { getEmbeddable } from '../../../../public/services'; @@ -18,7 +17,6 @@ const AnywhereParentFlyout = ({ startingFlyout, ...props }) => { const [selectedDetectorId, setSelectedDetectorId] = useState(); const AnywhereFlyout = { - create: AddAnomalyDetector, associated: AssociatedDetectors, }[mode]; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index deef439c..85f7c9a5 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -162,7 +162,7 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { // Map all detector IDs for all the found augmented vis objects const savedAugmentDetectorsSet = new Set( savedAugmentForThisVisualization.map((savedObject) => - get(savedObject, 'pluginResourceId', '') + get(savedObject, 'pluginResource.id', '') ) ); @@ -177,22 +177,20 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { setIsLoadingFinalDetectors(true); await savedObjectLoader.findAll().then(async (resp: any) => { if (resp != undefined) { - const savedAugmentObjects: ISavedAugmentVis[] = get(resp, 'hits', []); // gets all the saved object for this visualization const savedAugmentForThisVisualization: ISavedAugmentVis[] = - savedAugmentObjects.filter( - (savedObj) => get(savedObj, 'visId', '') === embeddable.vis.id + get(resp, 'hits', [] as ISavedAugmentVis[]).filter( + (savedObj: ISavedAugmentVis[]) => get(savedObj, 'visId', '') === embeddable.vis.id ); - // find saved Augment object matching detector we want to unlink + // find saved augment object matching detector we want to unlink // There should only be one detector and vis pairing const savedAugmentToUnlink = savedAugmentForThisVisualization.find( (savedObject) => - get(savedObject, 'pluginResourceId', '') === detectorToUnlink.id + get(savedObject, 'pluginResource.id', '') === detectorToUnlink.id ); - const savedObjectToUnlinkId = get(savedAugmentToUnlink, 'id', ''); await savedObjectLoader - .delete(savedObjectToUnlinkId) + .delete(get(savedAugmentToUnlink, 'id', '')) .then(async (resp: any) => { core.notifications.toasts.addSuccess({ title: `Association removed between the ${detectorToUnlink.name} diff --git a/public/plugin.ts b/public/plugin.ts index a54bf191..a5df7ed5 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -10,76 +10,88 @@ */ import { - AppMountParameters, - CoreSetup, - CoreStart, - Plugin, - } from '../../../src/core/public'; - import { CONTEXT_MENU_TRIGGER, EmbeddableSetup, EmbeddableStart } from '../../../src/plugins/embeddable/public'; - import { ACTION_AD } from './action/ad_dashboard_action'; - import { PLUGIN_NAME } from './utils/constants'; - import { getActions } from './utils/contextMenu/getActions'; - import { overlayAnomaliesFunction } from './expressions/overlay_anomalies'; - import { setClient, setEmbeddable, setOverlays } from './services'; - import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; - - declare module '../../../src/plugins/ui_actions/public' { - export interface ActionContextMapping { - [ACTION_AD]: {}; - } + AppMountParameters, + CoreSetup, + CoreStart, + Plugin, +} from '../../../src/core/public'; +import { + CONTEXT_MENU_TRIGGER, + EmbeddableSetup, + EmbeddableStart, +} from '../../../src/plugins/embeddable/public'; +import { ACTION_AD } from './action/ad_dashboard_action'; +import { PLUGIN_NAME } from './utils/constants'; +import { getActions } from './utils/contextMenu/getActions'; +import { overlayAnomaliesFunction } from './expressions/overlay_anomalies'; +import { + setClient, + setEmbeddable, + setOverlays, + setSavedFeatureAnywhereLoader, +} from './services'; +import { AnomalyDetectionOpenSearchDashboardsPluginStart } from 'public'; +import { VisAugmenterStart } from '../../../src/plugins/vis_augmenter/public'; + +declare module '../../../src/plugins/ui_actions/public' { + export interface ActionContextMapping { + [ACTION_AD]: {}; } - - export interface AnomalyDetectionSetupDeps { - embeddable: EmbeddableSetup; +} + +export interface AnomalyDetectionSetupDeps { + embeddable: EmbeddableSetup; +} + +export interface AnomalyDetectionStartDeps { + embeddable: EmbeddableStart; + visAugmenter: VisAugmenterStart; +} + +export class AnomalyDetectionOpenSearchDashboardsPlugin + implements Plugin +{ + public setup(core: CoreSetup, plugins: any) { + core.application.register({ + id: PLUGIN_NAME, + title: 'Anomaly Detection', + category: { + id: 'opensearch', + label: 'OpenSearch Plugins', + order: 2000, + }, + order: 5000, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./anomaly_detection_app'); + const [coreStart] = await core.getStartServices(); + return renderApp(coreStart, params); + }, + }); + + // Set the HTTP client so it can be pulled into expression fns to make + // direct server-side calls + setClient(core.http); + + // Create context menu actions. Pass core, to access service for flyouts. + const actions = getActions(); + + // Add actions to uiActions + actions.forEach((action) => { + plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, action); + }); + + // registers the expression function used to render anomalies on an Augmented Visualization + plugins.expressions.registerFunction(overlayAnomaliesFunction); + return {}; } - - export interface AnomalyDetectionStartDeps { - embeddable: EmbeddableStart; + + public start( + core: CoreStart, + { embeddable, visAugmenter }: AnomalyDetectionStartDeps + ): AnomalyDetectionOpenSearchDashboardsPluginStart { + setEmbeddable(embeddable); + setOverlays(core.overlays); + setSavedFeatureAnywhereLoader(visAugmenter.savedAugmentVisLoader); + return {}; } - - export class AnomalyDetectionOpenSearchDashboardsPlugin implements - Plugin { - - public setup(core: CoreSetup, plugins: any) { - core.application.register({ - id: PLUGIN_NAME, - title: 'Anomaly Detection', - category: { - id: 'opensearch', - label: 'OpenSearch Plugins', - order: 2000, - }, - order: 5000, - mount: async (params: AppMountParameters) => { - const { renderApp } = await import('./anomaly_detection_app'); - const [coreStart] = await core.getStartServices(); - return renderApp(coreStart, params); - }, - }); - - // Set the HTTP client so it can be pulled into expression fns to make - // direct server-side calls - setClient(core.http); - - // Create context menu actions. Pass core, to access service for flyouts. - const actions = getActions(); - - // Add actions to uiActions - actions.forEach((action) => { - plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, action); - }); - - // registers the expression function used to render anomalies on an Augmented Visualization - plugins.expressions.registerFunction(overlayAnomaliesFunction); - return {}; - } - - public start( - core: CoreStart, - {embeddable }: AnomalyDetectionStartDeps - ): AnomalyDetectionOpenSearchDashboardsPluginStart { - setEmbeddable(embeddable); - setOverlays(core.overlays); - return {}; - } - } \ No newline at end of file +} From f187620709eb1002a09b9a298aed1b7b8a3f2942 Mon Sep 17 00:00:00 2001 From: Amit Galitzky Date: Mon, 22 May 2023 14:51:46 -0700 Subject: [PATCH 17/17] added notifications service as a getter-setter Signed-off-by: Amit Galitzky --- .../AnywhereParentFlyout/index.ts | 8 ----- .../containers/AssociatedDetectors.tsx | 29 ++++++++++++------- .../DocumentationTitle/index.ts | 8 ----- public/plugin.ts | 2 ++ public/services.ts | 13 +++++++-- 5 files changed, 31 insertions(+), 29 deletions(-) delete mode 100644 public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.ts delete mode 100644 public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.ts diff --git a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.ts b/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.ts deleted file mode 100644 index 591d4b6d..00000000 --- a/public/components/FeatureAnywhereContextMenu/AnywhereParentFlyout/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import AnywhereParentFlyout from './AnywhereParentFlyout'; - -export default AnywhereParentFlyout; diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx index 85f7c9a5..c0b4f64f 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx +++ b/public/components/FeatureAnywhereContextMenu/AssociatedDetectors/containers/AssociatedDetectors.tsx @@ -21,7 +21,10 @@ import { getColumns } from '../utils/helpers'; import { useDispatch, useSelector } from 'react-redux'; import { AppState } from '../../../../redux/reducers'; import { DetectorListItem } from '../../../../models/interfaces'; -import { getSavedFeatureAnywhereLoader } from '../../../../services'; +import { + getSavedFeatureAnywhereLoader, + getNotifications, +} from '../../../../services'; import { GET_ALL_DETECTORS_QUERY_PARAMS, SINGLE_DETECTOR_NOT_FOUND_MSG, @@ -47,7 +50,7 @@ interface ConfirmModalState { affectedDetector: DetectorListItem; } -function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { +function AssociatedDetectors({ embeddable, closeFlyout, setMode }) { const dispatch = useDispatch(); const allDetectors = useSelector((state: AppState) => state.ad.detectorList); const isRequestingFromES = useSelector( @@ -81,13 +84,15 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { // Establish savedObjectLoader for all operations on vis_augment saved objects const savedObjectLoader: SavedObjectLoader = getSavedFeatureAnywhereLoader(); + const notifications = getNotifications(); + useEffect(() => { if ( errorGettingDetectors && !errorGettingDetectors.includes(SINGLE_DETECTOR_NOT_FOUND_MSG) ) { console.error(errorGettingDetectors); - core.notifications.toasts.addDanger( + notifications.toasts.addDanger( typeof errorGettingDetectors === 'string' && errorGettingDetectors.includes(NO_PERMISSIONS_KEY_WORD) ? prettifyErrorMessage(errorGettingDetectors) @@ -141,7 +146,7 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { } }) .catch((error) => { - core.notifications.toasts.addDanger( + notifications.toasts.addDanger( prettifyErrorMessage(`Unable to fetch associated detectors: ${error}`) ); }); @@ -178,10 +183,14 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { await savedObjectLoader.findAll().then(async (resp: any) => { if (resp != undefined) { // gets all the saved object for this visualization - const savedAugmentForThisVisualization: ISavedAugmentVis[] = - get(resp, 'hits', [] as ISavedAugmentVis[]).filter( - (savedObj: ISavedAugmentVis[]) => get(savedObj, 'visId', '') === embeddable.vis.id - ); + const savedAugmentForThisVisualization: ISavedAugmentVis[] = get( + resp, + 'hits', + [] as ISavedAugmentVis[] + ).filter( + (savedObj: ISavedAugmentVis[]) => + get(savedObj, 'visId', '') === embeddable.vis.id + ); // find saved augment object matching detector we want to unlink // There should only be one detector and vis pairing @@ -192,14 +201,14 @@ function AssociatedDetectors({ embeddable, closeFlyout, core, setMode }) { await savedObjectLoader .delete(get(savedAugmentToUnlink, 'id', '')) .then(async (resp: any) => { - core.notifications.toasts.addSuccess({ + notifications.toasts.addSuccess({ title: `Association removed between the ${detectorToUnlink.name} and the ${embeddableTitle} visualization`, text: "The detector's anomalies do not appear on the visualization. Refresh your dashboard to update the visualization", }); }) .catch((error) => { - core.notifications.toasts.addDanger( + notifications.toasts.addDanger( prettifyErrorMessage( `Error unlinking selected detector: ${error}` ) diff --git a/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.ts b/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.ts deleted file mode 100644 index e9f1bd89..00000000 --- a/public/components/FeatureAnywhereContextMenu/DocumentationTitle/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import DocumentationTitle from './containers/DocumentationTitle'; - -export default DocumentationTitle; diff --git a/public/plugin.ts b/public/plugin.ts index a5df7ed5..f3f4669e 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -27,6 +27,7 @@ import { overlayAnomaliesFunction } from './expressions/overlay_anomalies'; import { setClient, setEmbeddable, + setNotifications, setOverlays, setSavedFeatureAnywhereLoader, } from './services'; @@ -92,6 +93,7 @@ export class AnomalyDetectionOpenSearchDashboardsPlugin setEmbeddable(embeddable); setOverlays(core.overlays); setSavedFeatureAnywhereLoader(visAugmenter.savedAugmentVisLoader); + setNotifications(core.notifications); return {}; } } diff --git a/public/services.ts b/public/services.ts index 3857a95f..1908f443 100644 --- a/public/services.ts +++ b/public/services.ts @@ -3,7 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { CoreStart, OverlayStart } from '../../../src/core/public'; +import { + CoreStart, + NotificationsStart, + OverlayStart, +} from '../../../src/core/public'; import { EmbeddableStart } from '../../../src/plugins/embeddable/public'; import { createGetterSetter } from '../../../src/plugins/opensearch_dashboards_utils/public'; import { SavedObjectLoader } from '../../../src/plugins/saved_objects/public'; @@ -14,8 +18,11 @@ export const [getSavedFeatureAnywhereLoader, setSavedFeatureAnywhereLoader] = export const [getClient, setClient] = createGetterSetter('http'); -export const [getEmbeddable, setEmbeddable] = +export const [getEmbeddable, setEmbeddable] = createGetterSetter('Embeddable'); -export const [getOverlays, setOverlays] = +export const [getOverlays, setOverlays] = createGetterSetter('Overlays'); + +export const [getNotifications, setNotifications] = + createGetterSetter('Notifications');