From db4af39b4af10d25e8f691795a95eb1191ce1354 Mon Sep 17 00:00:00 2001 From: Amardeepsingh Siglani Date: Tue, 20 Aug 2024 15:55:21 -0700 Subject: [PATCH 01/11] Updated all pages with new header UI (#1056) * updated all pages with new header UI Signed-off-by: Amardeepsingh Siglani * fixed UTs Signed-off-by: Amardeepsingh Siglani * fixed cross cluster API; cypress workflow Signed-off-by: Amardeepsingh Siglani * fixed data source logic on page load for view/edit monitor; removed redundant code; updated tests Signed-off-by: Amardeepsingh Siglani --------- Signed-off-by: Amardeepsingh Siglani --- .github/workflows/cypress-workflow.yml | 2 +- .../composite_level_monitor_spec.js | 4 +- opensearch_dashboards.json | 1 + .../components/ContentPanel/ContentPanel.js | 5 +- .../DeleteModal/DeleteMonitorModal.tsx | 3 +- .../AddAlertingMonitor.test.js | 5 + .../AssociatedMonitors/helpers.js | 3 +- .../AlertsDashboardFlyoutComponent.js | 3 +- ...lertsDashboardFlyoutComponent.test.js.snap | 2 +- .../__snapshots__/FormikSwitch.test.js.snap | 2 +- public/components/PageHeader/PageHeader.tsx | 43 +++++ .../FeatureChart/FeatureChart.test.js | 2 +- .../containers/CreateMonitor/CreateMonitor.js | 11 +- .../CreateMonitor/CreateMonitor.test.js | 7 +- .../AcknowledgeAlertsModal.test.js | 2 + .../DashboardControls/DashboardControls.js | 12 +- .../DashboardControls.test.js.snap | 2 +- .../DashboardEmptyPrompt.js | 12 +- .../DashboardEmptyPrompt.test.js | 5 + .../DashboardEmptyPrompt.test.js.snap | 2 +- .../pages/Dashboard/containers/Dashboard.js | 13 +- .../Dashboard/containers/Dashboard.test.js | 5 + .../__snapshots__/Dashboard.test.js.snap | 54 ++++-- public/pages/Dashboard/utils/tableUtils.js | 7 +- .../DestinationsActions.js | 3 - .../FullPageNotificationsInfoCallOut.test.js | 5 + .../NotificationsInfoCallOut.test.js | 5 + .../DestinationsList/DestinationsList.test.js | 5 + public/pages/Main/Main.js | 22 ++- public/pages/Main/Main.test.js | 5 + .../MonitorOverview/MonitorOverview.js | 5 +- .../containers/MonitorDetails.js | 173 ++++++++++-------- .../containers/Triggers/Triggers.js | 8 - .../containers/Triggers/Triggers.test.js | 10 - .../MonitorActions/MonitorActions.js | 28 +-- .../MonitorActions/MonitorActions.test.js | 5 + .../__snapshots__/MonitorActions.test.js.snap | 4 +- .../MonitorControls/MonitorControls.js | 4 +- .../MonitorEmptyPrompt/MonitorEmptyPrompt.js | 3 +- .../MonitorEmptyPrompt.test.js.snap | 2 +- .../Monitors/containers/Monitors/Monitors.js | 36 ++-- .../containers/Monitors/Monitors.test.js | 6 +- .../__snapshots__/Monitors.test.js.snap | 6 + .../containers/Monitors/utils/tableUtils.js | 6 +- public/pages/utils/helpers.js | 2 +- public/plugin.tsx | 21 ++- public/services/services.ts | 9 + public/utils/helpers.js | 17 +- server/services/CrossClusterService.js | 1 + test/utils/helpers.js | 17 +- 50 files changed, 406 insertions(+), 209 deletions(-) create mode 100644 public/components/PageHeader/PageHeader.tsx diff --git a/.github/workflows/cypress-workflow.yml b/.github/workflows/cypress-workflow.yml index 1bd8083e8..442c41fb2 100644 --- a/.github/workflows/cypress-workflow.yml +++ b/.github/workflows/cypress-workflow.yml @@ -34,7 +34,7 @@ jobs: - name: Run Opensearch with plugin run: | cd alerting - ./gradlew run -Dopensearch.version=${{ env.OPENSEARCH_VERSION }} & + ./gradlew :alerting:run -Dopensearch.version=${{ env.OPENSEARCH_VERSION }} & timeout 300 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:9200)" != "200" ]]; do sleep 5; done' - name: Checkout OpenSearch Dashboards uses: actions/checkout@v2 diff --git a/cypress/integration/composite_level_monitor_spec.js b/cypress/integration/composite_level_monitor_spec.js index dc2c72cf4..d09a8f65a 100644 --- a/cypress/integration/composite_level_monitor_spec.js +++ b/cypress/integration/composite_level_monitor_spec.js @@ -75,7 +75,7 @@ describe('CompositeLevelMonitor', () => { .type('{backspace}') .type('Composite trigger'); - cy.intercept('api/alerting/workflows').as('createMonitorRequest'); + cy.intercept('api/alerting/workflows?*').as('createMonitorRequest'); cy.intercept(`api/alerting/monitors?*`).as('getMonitorsRequest'); cy.get('button').contains('Create').click({ force: true }); @@ -116,7 +116,7 @@ describe('CompositeLevelMonitor', () => { cy.visit( `${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/monitors/${ createdMonitor._id - }?action=update-monitor&type=workflow` + }?action=update-monitor&type=workflow&dataSourceId=` ); } else { cy.log('Failed to get created monitor ', SAMPLE_VISUAL_EDITOR_MONITOR); diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index aa5a9468a..ae8d94367 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -7,6 +7,7 @@ "requiredPlugins": [ "uiActions", "dashboard", + "navigation", "embeddable", "opensearchDashboardsReact", "savedObjects", diff --git a/public/components/ContentPanel/ContentPanel.js b/public/components/ContentPanel/ContentPanel.js index cb9133377..445b05183 100644 --- a/public/components/ContentPanel/ContentPanel.js +++ b/public/components/ContentPanel/ContentPanel.js @@ -17,6 +17,7 @@ const ContentPanel = ({ horizontalRuleClassName = '', actions, children, + panelOptions = {}, }) => ( @@ -38,7 +39,9 @@ const ContentPanel = ({ {description} - + {!panelOptions.hideTitleBorder && ( + + )}
{children}
diff --git a/public/components/DeleteModal/DeleteMonitorModal.tsx b/public/components/DeleteModal/DeleteMonitorModal.tsx index 18b280ff4..681752903 100644 --- a/public/components/DeleteModal/DeleteMonitorModal.tsx +++ b/public/components/DeleteModal/DeleteMonitorModal.tsx @@ -9,7 +9,6 @@ import { EuiLink, EuiOverlayMask } from '@elastic/eui'; -import { PLUGIN_NAME } from '../../../utils/constants'; import { getDataSourceQueryObj, constructUrlFromDataSource } from '../../pages/utils/helpers'; interface DeleteModalProps { @@ -55,7 +54,7 @@ export const DeleteMonitorModal = ({ {`The monitor ${monitorNames[0]} is currently being used as a delegate monitor for composite monitors. Unlink from the following composite monitors before deleting this monitor:`} { associatedWorkflows ?
    - {associatedWorkflows.map(({ id, name }) =>
  • {name}
  • )} + {associatedWorkflows.map(({ id, name }) =>
  • {name}
  • )}
: null } diff --git a/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/AddAlertingMonitor.test.js b/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/AddAlertingMonitor.test.js index 6a5e8534f..4697f5351 100644 --- a/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/AddAlertingMonitor.test.js +++ b/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/AddAlertingMonitor.test.js @@ -8,6 +8,11 @@ import { httpServiceMock, notificationServiceMock } from '../../../../../../src/ import { shallow } from 'enzyme'; import AddAlertingMonitor from './AddAlertingMonitor'; import { setClient, setNotifications } from '../../../services'; +import { setupCoreStart } from '../../../../test/utils/helpers'; + +beforeAll(() => { + setupCoreStart(); +}); describe('AddAlertingMonitor', () => { const httpClient = httpServiceMock.createStartContract(); diff --git a/public/components/FeatureAnywhereContextMenu/AssociatedMonitors/helpers.js b/public/components/FeatureAnywhereContextMenu/AssociatedMonitors/helpers.js index 602e6ca85..35d9011dc 100644 --- a/public/components/FeatureAnywhereContextMenu/AssociatedMonitors/helpers.js +++ b/public/components/FeatureAnywhereContextMenu/AssociatedMonitors/helpers.js @@ -7,7 +7,6 @@ import React, { useMemo } from 'react'; import { EuiHealth, EuiLink } from '@elastic/eui'; import { stateToLabel } from '../../../utils/contextMenu/monitors'; import { dateOptionsLong } from '../../../utils/contextMenu/helpers'; -import { PLUGIN_NAME } from '../../../../utils/constants'; import { constructUrlFromDataSource } from '../../../pages/utils/helpers'; export const useColumns = ({ onUnlink, onEdit }) => { @@ -20,7 +19,7 @@ export const useColumns = ({ onUnlink, onEdit }) => { truncateText: true, width: '50%', render: (name, monitor) => ( - + {name} ), diff --git a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js index bd82b436e..a45774ac8 100644 --- a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js +++ b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js @@ -21,7 +21,6 @@ import { EuiSmallButtonIcon, } from '@elastic/eui'; import { getTime } from '../../../../pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats'; -import { PLUGIN_NAME } from '../../../../../utils/constants'; import { ALERT_STATE, DEFAULT_EMPTY_DATA, @@ -585,7 +584,7 @@ export default class AlertsDashboardFlyoutComponent extends Component { displayTableTabs = false; break; } - const monitorUrl = `${PLUGIN_NAME}#/monitors/${monitor_id}${ + const monitorUrl = `#/monitors/${monitor_id}${ monitorType === MONITOR_TYPE.COMPOSITE_LEVEL ? '?type=workflow' : '' }`; diff --git a/public/components/Flyout/flyouts/components/__snapshots__/AlertsDashboardFlyoutComponent.test.js.snap b/public/components/Flyout/flyouts/components/__snapshots__/AlertsDashboardFlyoutComponent.test.js.snap index 539f38a99..a34f10592 100644 --- a/public/components/Flyout/flyouts/components/__snapshots__/AlertsDashboardFlyoutComponent.test.js.snap +++ b/public/components/Flyout/flyouts/components/__snapshots__/AlertsDashboardFlyoutComponent.test.js.snap @@ -71,7 +71,7 @@ exports[`AlertsDashboardFlyoutComponent renders 1`] = `

diff --git a/public/components/FormControls/FormikSwitch/__snapshots__/FormikSwitch.test.js.snap b/public/components/FormControls/FormikSwitch/__snapshots__/FormikSwitch.test.js.snap index 989233815..8860aac81 100644 --- a/public/components/FormControls/FormikSwitch/__snapshots__/FormikSwitch.test.js.snap +++ b/public/components/FormControls/FormikSwitch/__snapshots__/FormikSwitch.test.js.snap @@ -2,7 +2,7 @@ exports[`FormikSwitch renders 1`] = `
-
    -
  • - -
  • -
  • - -
  • -
- - -
`; diff --git a/public/pages/Dashboard/containers/Dashboard.js b/public/pages/Dashboard/containers/Dashboard.js index 0838fe007..2005bee81 100644 --- a/public/pages/Dashboard/containers/Dashboard.js +++ b/public/pages/Dashboard/containers/Dashboard.js @@ -9,10 +9,12 @@ import queryString from 'query-string'; import { EuiBasicTable, EuiSmallButton, - EuiHorizontalRule, EuiIcon, EuiToolTip, EuiSmallButtonIcon, + EuiFlexItem, + EuiPagination, + EuiFlexGroup, } from '@elastic/eui'; import ContentPanel from '../../../components/ContentPanel'; import DashboardEmptyPrompt from '../components/DashboardEmptyPrompt'; @@ -530,6 +532,7 @@ export default class Dashboard extends Component { }; const useUpdatedUx = !perAlertView && getUseUpdatedUx(); + const shouldShowPagination = !perAlertView && totalAlerts > 0; return ( <> @@ -546,6 +549,7 @@ export default class Dashboard extends Component { bodyStyles={{ padding: 'initial' }} actions={useUpdatedUx ? undefined : actions()} panelOptions={{ hideTitleBorder: useUpdatedUx }} + panelStyles={{ padding: useUpdatedUx && totalAlerts < 1 ? '16px 16px 0px' : '16px' }} > - - + {shouldShowPagination && ( + + + + + + )} + {this.state.showAlertsModal && this.renderModal()} diff --git a/public/pages/Dashboard/containers/FindingsDashboard.js b/public/pages/Dashboard/containers/FindingsDashboard.js index 61414901b..0bd386aef 100644 --- a/public/pages/Dashboard/containers/FindingsDashboard.js +++ b/public/pages/Dashboard/containers/FindingsDashboard.js @@ -206,7 +206,7 @@ export default class FindingsDashboard extends Component { bodyStyles={{ padding: 'initial' }} > {!isPreview && ( - + )} - - @@ -128,8 +132,7 @@ exports[`Dashboard renders in flyout 1`] = ` className="euiPanel euiPanel--paddingMedium euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow" style={ Object { - "paddingLeft": "0px", - "paddingRight": "0px", + "padding": "16px", } } > @@ -301,6 +304,11 @@ exports[`Dashboard renders in flyout 1`] = ` onSeverityChange={[Function]} onStateChange={[Function]} pageCount={1} + panelStyles={ + Object { + "padding": "8px 0px 16px", + } + } search="" severity="ALL" state="ALL" @@ -309,7 +317,7 @@ exports[`Dashboard renders in flyout 1`] = ` gutterSize="s" style={ Object { - "padding": "0px 5px", + "padding": "8px 0px 16px", } } > @@ -317,7 +325,7 @@ exports[`Dashboard renders in flyout 1`] = ` className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--directionRow euiFlexGroup--responsive" style={ Object { - "padding": "0px 5px", + "padding": "8px 0px 16px", } } > @@ -569,216 +577,9 @@ exports[`Dashboard renders in flyout 1`] = ` - -
- - - -
-
- -
-
@@ -1736,8 +1541,7 @@ exports[`Dashboard renders with alert by triggers view 1`] = ` className="euiPanel euiPanel--paddingMedium euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow" style={ Object { - "paddingLeft": "0px", - "paddingRight": "0px", + "padding": "16px", } } > @@ -1970,6 +1774,11 @@ exports[`Dashboard renders with alert by triggers view 1`] = ` onSeverityChange={[Function]} onStateChange={[Function]} pageCount={1} + panelStyles={ + Object { + "padding": "0px 0px 16px", + } + } search="" severity="ALL" state="ALL" @@ -1978,7 +1787,7 @@ exports[`Dashboard renders with alert by triggers view 1`] = ` gutterSize="s" style={ Object { - "padding": "0px 5px", + "padding": "0px 0px 16px", } } > @@ -1986,7 +1795,7 @@ exports[`Dashboard renders with alert by triggers view 1`] = ` className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--directionRow euiFlexGroup--responsive" style={ Object { - "padding": "0px 5px", + "padding": "0px 0px 16px", } } > @@ -2412,216 +2221,9 @@ exports[`Dashboard renders with alert by triggers view 1`] = ` - -
- - - -
-
- -
-
@@ -3792,8 +3398,7 @@ exports[`Dashboard renders with per alert view 1`] = ` className="euiPanel euiPanel--paddingMedium euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow" style={ Object { - "paddingLeft": "0px", - "paddingRight": "0px", + "padding": "16px", } } > @@ -3965,6 +3570,11 @@ exports[`Dashboard renders with per alert view 1`] = ` onSeverityChange={[Function]} onStateChange={[Function]} pageCount={1} + panelStyles={ + Object { + "padding": "8px 0px 16px", + } + } search="" severity="ALL" state="ALL" @@ -3973,7 +3583,7 @@ exports[`Dashboard renders with per alert view 1`] = ` gutterSize="s" style={ Object { - "padding": "0px 5px", + "padding": "8px 0px 16px", } } > @@ -3981,7 +3591,7 @@ exports[`Dashboard renders with per alert view 1`] = ` className="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--directionRow euiFlexGroup--responsive" style={ Object { - "padding": "0px 5px", + "padding": "8px 0px 16px", } } > @@ -4407,216 +4017,9 @@ exports[`Dashboard renders with per alert view 1`] = ` - -
- - - -
-
- -
-
{!defaultRoute && ( - + {this.tabs.map(this.renderTab)} )} -
+
`; diff --git a/public/pages/Main/Main.js b/public/pages/Main/Main.js index 8813eda35..ca67c308e 100644 --- a/public/pages/Main/Main.js +++ b/public/pages/Main/Main.js @@ -146,7 +146,7 @@ class Main extends Component { -
+
{ diff --git a/public/pages/MonitorDetails/components/MonitorOverview/__snapshots__/MonitorOverview.test.js.snap b/public/pages/MonitorDetails/components/MonitorOverview/__snapshots__/MonitorOverview.test.js.snap index 94bcb3dc6..5eb075a99 100644 --- a/public/pages/MonitorDetails/components/MonitorOverview/__snapshots__/MonitorOverview.test.js.snap +++ b/public/pages/MonitorDetails/components/MonitorOverview/__snapshots__/MonitorOverview.test.js.snap @@ -3,7 +3,7 @@ exports[`MonitorOverview renders 1`] = `
+ + + + + ); monitorActions.push( @@ -518,7 +521,7 @@ export default class MonitorDetails extends Component { } return ( -
+
{this.renderNoTriggersCallOut()} - + {!useUpdatedUx && } Acknowledge , + { + this.onCloseActions(); + this.props.onClickEdit(); + }} + disabled={isEditDisabled} + > + Edit + , + Create monitor ); @@ -112,15 +129,6 @@ export default class MonitorActions extends Component { - - - Edit - - { }); test('does not call onClickEdit when Edit is clicked and edit is disabled', () => { - wrapper.find('[data-test-subj="editButton"]').hostNodes().simulate('click'); + wrapper.find('[data-test-subj="actionsButton"]').hostNodes().simulate('click'); + wrapper.find('[data-test-subj="editItem"]').hostNodes().simulate('click'); expect(props.onClickEdit).toHaveBeenCalledTimes(0); }); test('calls onClickEdit when Edit is clicked and isEditDisabled=false', () => { const props = getProps(); wrapper.setProps({ ...props, isEditDisabled: false }); - wrapper.find('[data-test-subj="editButton"]').hostNodes().simulate('click'); + wrapper.find('[data-test-subj="actionsButton"]').hostNodes().simulate('click'); + wrapper.find('[data-test-subj="editItem"]').hostNodes().simulate('click'); expect(props.onClickEdit).toHaveBeenCalledTimes(1); }); }); diff --git a/public/pages/Monitors/components/MonitorActions/__snapshots__/MonitorActions.test.js.snap b/public/pages/Monitors/components/MonitorActions/__snapshots__/MonitorActions.test.js.snap index 28675fe19..27f98b44e 100644 --- a/public/pages/Monitors/components/MonitorActions/__snapshots__/MonitorActions.test.js.snap +++ b/public/pages/Monitors/components/MonitorActions/__snapshots__/MonitorActions.test.js.snap @@ -35,26 +35,6 @@ exports[`MonitorActions renders 1`] = `
-
- -
@@ -65,8 +45,11 @@ exports[`MonitorActions renders 1`] = ` rel="noreferrer" > +
+ EuiIconMock +
diff --git a/public/pages/Monitors/components/MonitorControls/MonitorControls.js b/public/pages/Monitors/components/MonitorControls/MonitorControls.js index 56c329855..593fb92a1 100644 --- a/public/pages/Monitors/components/MonitorControls/MonitorControls.js +++ b/public/pages/Monitors/components/MonitorControls/MonitorControls.js @@ -34,7 +34,7 @@ const MonitorControls = ({ onPageClick, monitorActions = null, }) => ( - + {monitorActions && {monitorActions}} - - - ); diff --git a/public/pages/Monitors/containers/Monitors/Monitors.js b/public/pages/Monitors/containers/Monitors/Monitors.js index 22be5d5f4..cb822a7ec 100644 --- a/public/pages/Monitors/containers/Monitors/Monitors.js +++ b/public/pages/Monitors/containers/Monitors/Monitors.js @@ -6,7 +6,7 @@ import React, { Component } from 'react'; import _ from 'lodash'; import queryString from 'query-string'; -import { EuiBasicTable, EuiHorizontalRule } from '@elastic/eui'; +import { EuiBasicTable } from '@elastic/eui'; import AcknowledgeModal from '../../components/AcknowledgeModal'; import ContentPanel from '../../../../components/ContentPanel'; import MonitorActions from '../../components/MonitorActions'; @@ -285,7 +285,7 @@ export default class Monitors extends Component { const { selectedItems: [{ id }], } = this.state; - if (id) this.props.history.push(`/monitors/${id}?action=${MONITOR_ACTIONS.UPDATE_MONITOR}`); + if (id) this.props.history.push(`/monitors/${id}?action=${MONITOR_ACTIONS.EDIT_MONITOR}`); } onClickEnable(item) { @@ -467,6 +467,7 @@ export default class Monitors extends Component { bodyStyles={{ padding: 'initial' }} title={useUpdatedUx ? undefined : 'Monitors'} panelOptions={{ hideTitleBorder: useUpdatedUx }} + panelStyles={{ padding: useUpdatedUx && totalMonitors < 1 ? '16px 16px 0px' : '16px' }} > - - {showAcknowledgeModal && ( { expect(onClickEdit).toHaveBeenCalled(); expect(historyMock.push).toHaveBeenCalled(); - expect(historyMock.push).toHaveBeenCalledWith(`/monitors/random_id?action=update-monitor`); + expect(historyMock.push).toHaveBeenCalledWith(`/monitors/random_id?action=edit-monitor`); }); test('onClickEnable calls updateMonitors with monitor and enable:true update', () => { diff --git a/public/pages/Monitors/containers/Monitors/__snapshots__/Monitors.test.js.snap b/public/pages/Monitors/containers/Monitors/__snapshots__/Monitors.test.js.snap index 28233c7c4..8c0f71f42 100644 --- a/public/pages/Monitors/containers/Monitors/__snapshots__/Monitors.test.js.snap +++ b/public/pages/Monitors/containers/Monitors/__snapshots__/Monitors.test.js.snap @@ -24,6 +24,11 @@ exports[`Monitors renders 1`] = ` "hideTitleBorder": undefined, } } + panelStyles={ + Object { + "padding": "16px", + } + } title="Monitors" > - Date: Wed, 18 Sep 2024 09:53:32 +0800 Subject: [PATCH 11/11] Context aware alert analysis (#996) * support date_nanos type Signed-off-by: Hailong Cui * support context aware alert analysis Signed-off-by: Hailong Cui * Register summary generation type of IncontextInsight for alert summarization Signed-off-by: Songkan Tang * Fix dashboard unit test failure Signed-off-by: Songkan Tang * Make each alert register its own IncontextInsight Signed-off-by: Songkan Tang * Enable context aware alert only if feature flag is enabled Signed-off-by: Songkan Tang * Avoid unnecessary change and minorly change summary question Signed-off-by: Songkan Tang * Fix undefined alert name Signed-off-by: Songkan Tang * Pass monitor type to additional info object of contextProvider Signed-off-by: Songkan Tang * Address some comments and change feature flag Signed-off-by: Songkan Tang * Add assistant capabilities check to control component rendering Signed-off-by: Songkan Tang * Fix mismatched unit test snapshots Signed-off-by: Songkan Tang * Handle the edge case of multiple indices in search and return more information in additionalInfo Signed-off-by: Songkan Tang * Reduce llm context input size by taking topN active alerts Signed-off-by: Songkan Tang * Distinguish source data and aggregation that trigger the alert Signed-off-by: Songkan Tang * Rename the capability UI rendering flag per assistant plugin change Signed-off-by: Songkan Tang * Remove alert sample data per current requirement from context Signed-off-by: Songkan Tang --------- Signed-off-by: Hailong Cui Signed-off-by: Songkan Tang Co-authored-by: Hailong Cui --- opensearch_dashboards.json | 1 + public/app.js | 4 +- .../Dashboard/containers/Dashboard.test.js | 3 + public/pages/Dashboard/utils/tableUtils.js | 105 +++++++++++++++++- public/pages/Home/Home.js | 2 +- public/pages/Main/Main.js | 2 +- public/plugin.tsx | 9 +- public/services/services.ts | 5 + public/types.ts | 12 ++ 9 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 public/types.ts diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 8f8f1a39d..12c57b351 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -17,6 +17,7 @@ "opensearchDashboardsUtils", "contentManagement" ], + "optionalPlugins": ["assistantDashboards"], "server": true, "ui": true, "supportedOSDataSourceVersions": ">=2.13.0", diff --git a/public/app.js b/public/app.js index 5a40bf94e..8ea486028 100644 --- a/public/app.js +++ b/public/app.js @@ -52,7 +52,9 @@ export function renderApp(coreStart, params, defaultRoute) { defaultRoute: defaultRoute, }} > -
} /> +
} + /> , diff --git a/public/pages/Dashboard/containers/Dashboard.test.js b/public/pages/Dashboard/containers/Dashboard.test.js index 9fb6dda12..c5486e7b2 100644 --- a/public/pages/Dashboard/containers/Dashboard.test.js +++ b/public/pages/Dashboard/containers/Dashboard.test.js @@ -9,6 +9,7 @@ import { mount } from 'enzyme'; import Dashboard from './Dashboard'; import { historyMock, httpClientMock } from '../../../../test/mocks'; import { setupCoreStart } from '../../../../test/utils/helpers'; +import { setAssistantDashboards } from '../../../services'; const location = { hash: '', @@ -62,6 +63,8 @@ beforeAll(() => { }); describe('Dashboard', () => { + setAssistantDashboards({ getFeatureStatus: () => ({ chat: false, alertInsight: false }) }); + beforeEach(() => { jest.clearAllMocks(); }); diff --git a/public/pages/Dashboard/utils/tableUtils.js b/public/pages/Dashboard/utils/tableUtils.js index 800767679..9959af144 100644 --- a/public/pages/Dashboard/utils/tableUtils.js +++ b/public/pages/Dashboard/utils/tableUtils.js @@ -7,7 +7,9 @@ import React from 'react'; import _ from 'lodash'; import { EuiLink, EuiToolTip } from '@elastic/eui'; import moment from 'moment'; -import { ALERT_STATE, DEFAULT_EMPTY_DATA } from '../../../utils/constants'; +import { ALERT_STATE, DEFAULT_EMPTY_DATA, MONITOR_TYPE } from '../../../utils/constants'; +import { getApplication, getAssistantDashboards } from '../../../services'; +import { getDataSourceQueryObj } from '../../../pages/utils/helpers'; export const renderTime = (time, options = { showFromNow: false }) => { const momentTime = moment(time); @@ -131,8 +133,10 @@ export const alertColumns = ( sortable: true, truncateText: false, render: (total, alert) => { - return ( + const alertId = `alerts_${alert.alerts[0].id}`; + const component = ( { openFlyout({ ...alert, @@ -152,6 +156,103 @@ export const alertColumns = ( {`${total} alerts`} ); + const contextProvider = async () => { + // 1. get monitor definition + const dataSourceQuery = getDataSourceQueryObj(); + const monitorResp = await httpClient.get( + `../api/alerting/monitors/${alert.monitor_id}`, + dataSourceQuery + ); + const monitorDefinition = monitorResp.resp; + delete monitorDefinition.ui_metadata; + delete monitorDefinition.data_sources; + + let monitorDefinitionStr = JSON.stringify(monitorDefinition); + + // 2. get data triggers the alert + let alertTriggeredByValue = ''; + let dsl = ''; + let index = ''; + if ( + monitorResp.resp.monitor_type === MONITOR_TYPE.QUERY_LEVEL || + monitorResp.resp.monitor_type === MONITOR_TYPE.BUCKET_LEVEL + ) { + const search = monitorResp.resp.inputs[0].search; + const indices = String(search.indices); + const splitIndices = indices.split(','); + index = splitIndices.length > 0 ? splitIndices[0].trim() : ''; + let query = JSON.stringify(search.query); + // Only keep the query part + dsl = JSON.stringify({ query: search.query.query }); + if (query.indexOf('{{period_end}}') !== -1) { + query = query.replaceAll('{{period_end}}', alert.start_time); + const alertStartTime = moment.utc(alert.start_time).format('YYYY-MM-DDTHH:mm:ss'); + dsl = dsl.replaceAll('{{period_end}}', alertStartTime); + // as we changed the format, remove it + dsl = dsl.replaceAll('"format":"epoch_millis",', ''); + monitorDefinitionStr = monitorDefinitionStr.replaceAll( + '{{period_end}}', + alertStartTime + ); + // as we changed the format, remove it + monitorDefinitionStr = monitorDefinitionStr.replaceAll('"format":"epoch_millis",', ''); + } + if (index) { + const alertData = await httpClient.post(`/api/console/proxy`, { + query: { + path: `${index}/_search`, + method: 'GET', + dataSourceId: dataSourceQuery ? dataSourceQuery.query.dataSourceId : '', + }, + body: query, + prependBasePath: true, + asResponse: true, + withLongNumeralsSupport: true, + }); + + alertTriggeredByValue = JSON.stringify( + alertData.body.aggregations?.metric.value || alertData.body.hits.total.value + ); + } + } + + const filteredAlert = { ...alert }; + const topN = 10; + const activeAlerts = alert.alerts.filter((alert) => alert.state === 'ACTIVE'); + // Reduce llm input token size by taking topN active alerts + filteredAlert.alerts = activeAlerts.slice(0, topN); + + // 3. build the context + return { + context: ` + Here is the detail information about alert ${alert.trigger_name} + ### Monitor definition\n ${monitorDefinitionStr}\n + ### Active Alert\n ${JSON.stringify(filteredAlert)}\n + ### Value triggers this alert\n ${alertTriggeredByValue}\n + ### Alert query DSL ${dsl} \n`, + additionalInfo: { + monitorType: monitorResp.resp.monitor_type, + dsl: dsl, + index: index, + }, + }; + }; + + const assistantEnabled = getApplication().capabilities?.assistant?.enabled === true; + const assistantFeatureStatus = getAssistantDashboards().getFeatureStatus(); + if (assistantFeatureStatus.alertInsight && assistantEnabled) { + getAssistantDashboards().registerIncontextInsight([ + { + key: alertId, + type: 'generate', + suggestions: [`Please summarize this alert, do not use any tool.`], + contextProvider, + }, + ]); + return getAssistantDashboards().renderIncontextInsight({ children: component }); + } else { + return component; + } }, }, { diff --git a/public/pages/Home/Home.js b/public/pages/Home/Home.js index 9174e1556..d5df162dc 100644 --- a/public/pages/Home/Home.js +++ b/public/pages/Home/Home.js @@ -125,7 +125,7 @@ export default class Home extends Component { /> )} /> - +
diff --git a/public/pages/Main/Main.js b/public/pages/Main/Main.js index ca67c308e..26633c14b 100644 --- a/public/pages/Main/Main.js +++ b/public/pages/Main/Main.js @@ -91,7 +91,7 @@ class Main extends Component { handleDataSourceChange = ([dataSource]) => { const dataSourceId = dataSource?.id; - const dataSourceLabel = dataSource?.label + const dataSourceLabel = dataSource?.label; if (this.props.dataSourceEnabled && dataSourceId === undefined) { getNotifications().toasts.addDanger('Unable to set data source.'); } else if (this.state.selectedDataSourceId != dataSourceId) { diff --git a/public/plugin.tsx b/public/plugin.tsx index f0aa4431f..dc4e6d6ce 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -21,9 +21,10 @@ import { alertingTriggerAd } from './utils/contextMenu/triggers'; import { ExpressionsSetup } from '../../../src/plugins/expressions/public'; import { UiActionsSetup } from '../../../src/plugins/ui_actions/public'; import { overlayAlertsFunction } from './expressions/overlay_alerts'; -import { setClient, setEmbeddable, setNotifications, setOverlays, setSavedAugmentVisLoader, setUISettings, setQueryService, setSavedObjectsClient, setDataSourceEnabled, setDataSourceManagementPlugin, setNavigationUI, setApplication, setContentManagementStart } from './services'; +import { setClient, setEmbeddable, setNotifications, setOverlays, setSavedAugmentVisLoader, setUISettings, setQueryService, setSavedObjectsClient, setDataSourceEnabled, setDataSourceManagementPlugin, setNavigationUI, setApplication, setContentManagementStart, setAssistantDashboards } from './services'; import { VisAugmenterStart } from '../../../src/plugins/vis_augmenter/public'; import { DataPublicPluginStart } from '../../../src/plugins/data/public'; +import { AssistantSetup } from './types'; import { DataSourceManagementPluginSetup } from '../../../src/plugins/data_source_management/public'; import { DataSourcePluginSetup } from '../../../src/plugins/data_source/public'; import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public'; @@ -47,6 +48,7 @@ export interface AlertingSetupDeps { uiActions: UiActionsSetup; dataSourceManagement: DataSourceManagementPluginSetup; dataSource: DataSourcePluginSetup; + assistantDashboards?: AssistantSetup; } export interface AlertingStartDeps { @@ -69,14 +71,13 @@ export class AlertingPlugin implements Plugin(this.updateDefaultRouteOfManagementApplications); - public setup(core: CoreSetup, { expressions, uiActions, dataSourceManagement, dataSource }: AlertingSetupDeps) { + public setup(core: CoreSetup, { expressions, uiActions, dataSourceManagement, dataSource, assistantDashboards }: AlertingSetupDeps) { const mountWrapper = async (params: AppMountParameters, redirect: string) => { const { renderApp } = await import("./app"); const [coreStart] = await core.getStartServices(); return renderApp(coreStart, params, redirect); }; - core.application.register({ id: PLUGIN_NAME, title: 'Alerting', @@ -178,6 +179,8 @@ export class AlertingPlugin implements Plugin ({ chat: false, alertInsight: false }) }); + setUISettings(core.uiSettings); // Set the HTTP client so it can be pulled into expression fns to make diff --git a/public/services/services.ts b/public/services/services.ts index 8d098463e..f908cbb82 100644 --- a/public/services/services.ts +++ b/public/services/services.ts @@ -14,6 +14,7 @@ import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public'; import { ContentManagementPluginStart } from '../../../../src/plugins/content_management/public'; import { createNullableGetterSetter } from './utils/helper'; +import { AssistantSetup } from '../types'; const ServicesContext = createContext(null); @@ -30,6 +31,10 @@ export const [getSavedAugmentVisLoader, setSavedAugmentVisLoader] = createGetter export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); +export const [getAssistantDashboards, setAssistantDashboards] = createGetterSetter< + AssistantSetup | {} +>('assistantDashboards'); + export const [getEmbeddable, setEmbeddable] = createGetterSetter('embeddable'); export const [getOverlays, setOverlays] = diff --git a/public/types.ts b/public/types.ts new file mode 100644 index 000000000..4734f3a91 --- /dev/null +++ b/public/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Introduce a compile dependency on dashboards-assistant + * as alerting need some types from the plugin. + * It will give a type error when dashboards-assistant is not installed so add a ts-ignore to suppress the error. + */ +// @ts-ignore +export type { AssistantSetup } from '../../dashboards-assistant/public';