From 28b81623d0cd21bc84f358e656aa146ee6416051 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 9 Jun 2023 17:18:39 +0200
Subject: [PATCH 01/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
yarn.lock | 108 ++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 97 insertions(+), 11 deletions(-)
diff --git a/yarn.lock b/yarn.lock
index a004c785f..2bd4694c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -232,6 +232,90 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
+"@node-rs/xxhash-android-arm-eabi@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-android-arm-eabi/-/xxhash-android-arm-eabi-1.4.0.tgz#55ace4d3882686d1e379aaf613e1338d78f13fc8"
+ integrity sha512-JuZNqt5/znWkIGteikQdS+HT9S0JsMYi06S4yzU/sMKLCIPvD0MnCTXlYtuDcgRIKScCaepAsSQVomnAyLFNNA==
+
+"@node-rs/xxhash-android-arm64@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-android-arm64/-/xxhash-android-arm64-1.4.0.tgz#2290c53ceabda804afb4c45679613d833a6385a0"
+ integrity sha512-BZzQO5jlgsIr9HhiqTwZjYqlfVeZiu+7PaoAdNEOq+i/SjyAqv1jGSkyek4rBSAiodyNkXcbE0eQtomeN6a55w==
+
+"@node-rs/xxhash-darwin-arm64@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-darwin-arm64/-/xxhash-darwin-arm64-1.4.0.tgz#96df4f48b13deb6899e84ed0882bdbd0a4856f13"
+ integrity sha512-JlEAzTsQaqJaWVse/JP//6QKBIhzqzTlvNY4uEbi8TaZMfvDDhW//ClXM6CkSV799GJxAYPu1LXa4+OeBQpa7Q==
+
+"@node-rs/xxhash-darwin-x64@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-darwin-x64/-/xxhash-darwin-x64-1.4.0.tgz#9df3ca3a87354dd5386aadfa20ad032a299c2b8f"
+ integrity sha512-9ycVJfzLvw1wc6Tgq0giLkMn5nGOBawTeOA17t27dQFdY/scZPz583DO7w+eznMnlzUXwoLiloanUebRhy+piQ==
+
+"@node-rs/xxhash-freebsd-x64@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-freebsd-x64/-/xxhash-freebsd-x64-1.4.0.tgz#24b0c0bfd33429303688b4af78f9d323daa0fb5b"
+ integrity sha512-vFRDr6qA0gHWQDjuSxXcdzM4Ppk+5VebEhc76zkWrRVc6RG60fxLo5B4j6QwMwXGTYaG8HMv/nQhAgbnOCWWxQ==
+
+"@node-rs/xxhash-linux-arm-gnueabihf@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm-gnueabihf/-/xxhash-linux-arm-gnueabihf-1.4.0.tgz#4c09f70cd39429fb1a52f3567085e949603d4817"
+ integrity sha512-0KS6y1caqbtPanos9XNMekWpozCHA6QSlQzaZyn9Hn+Z+mYpR5+NoWixefhp06jt59qF9+LkkF3C9fSEHYmq/w==
+
+"@node-rs/xxhash-linux-arm64-gnu@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm64-gnu/-/xxhash-linux-arm64-gnu-1.4.0.tgz#e92d7026614506fb4db309977127fd8589fabd7c"
+ integrity sha512-QI97JK2qiQhVgRtUBMgA1ZjPLpwnz11SE2Mw1jryejmyH9EXKKiCyt2FweO6MVP7bEuMxcdajBho4pEL7s/QsA==
+
+"@node-rs/xxhash-linux-arm64-musl@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm64-musl/-/xxhash-linux-arm64-musl-1.4.0.tgz#a8b16233a86c116e6af32a69278248d17b2d09e7"
+ integrity sha512-dtMid4OMkNBYGJkjoT1jdkENpV8m8MGp3lliDN8C+2znZUQM8KFRTXRkfaq4lgzu3Y2XeYzsLOoBsBd3Hgf7gA==
+
+"@node-rs/xxhash-linux-x64-gnu@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-x64-gnu/-/xxhash-linux-x64-gnu-1.4.0.tgz#385ec91396ebaa2b73abf419be3971ec893dcbd1"
+ integrity sha512-OeOQL10cG62wL1IVoeC74xESmefHU7r3xiZMTP2hK5Dh3FdF2sa3x/Db9BcGXlaokg/lMGDxuTuzOLC2Rv/wlQ==
+
+"@node-rs/xxhash-linux-x64-musl@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-x64-musl/-/xxhash-linux-x64-musl-1.4.0.tgz#715bb962502b0ec69e1fc19db22ac035c63d30c7"
+ integrity sha512-kZ8wNi5bH9b+ZpuPlSbFd6JXk8CKbfCvCPZ0Vk0IqLkzB6PihQflnZPM9r0QZ2jtFgyfWmpbFK4YxwX9YcyLog==
+
+"@node-rs/xxhash-win32-arm64-msvc@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-arm64-msvc/-/xxhash-win32-arm64-msvc-1.4.0.tgz#4a3a4ebcb50c73e4309e429b28eb44dbf8f7f71f"
+ integrity sha512-Ggv66jlhQvj4XgQqNgl2JKQ7My/97PvPZi5jKbcS7t65wJC36J6XERQwRPdupO8UH63XfPqb7HJqrgmiz8tmlA==
+
+"@node-rs/xxhash-win32-ia32-msvc@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-ia32-msvc/-/xxhash-win32-ia32-msvc-1.4.0.tgz#fdfdb43e41113a8baf15779ca53bb637d2e1bc8f"
+ integrity sha512-mYpF1+7unqKKGsPn7Y8X6SqP2Bc5BU5dsHBKhAGAuvrMg9W63zM+YWM8/fpNGfFlOrjiKRvXHZ96nrZyzoxeBw==
+
+"@node-rs/xxhash-win32-x64-msvc@1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-x64-msvc/-/xxhash-win32-x64-msvc-1.4.0.tgz#aee714a4ae0121f3947f94139adf13f5b6d93d12"
+ integrity sha512-rKuqWHuQNlrfjIOkQW3oCBta/GUlyVoUkKB13aVr8uixOs/eneuDaYJx2h02FAAWlWCKADFnMxgDl0LVFBy53w==
+
+"@node-rs/xxhash@^1.3.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@node-rs/xxhash/-/xxhash-1.4.0.tgz#1e75850e0e530c9224e8e5ba4056d52e8868291b"
+ integrity sha512-UpSOParhMqbQ7hsYovN2e+uqvWqHJiCDvFl8gDzMcXgBY/PkI2zo2zhdRAZdz48c6/dke+0WjCKy90wDVQxS6g==
+ optionalDependencies:
+ "@node-rs/xxhash-android-arm-eabi" "1.4.0"
+ "@node-rs/xxhash-android-arm64" "1.4.0"
+ "@node-rs/xxhash-darwin-arm64" "1.4.0"
+ "@node-rs/xxhash-darwin-x64" "1.4.0"
+ "@node-rs/xxhash-freebsd-x64" "1.4.0"
+ "@node-rs/xxhash-linux-arm-gnueabihf" "1.4.0"
+ "@node-rs/xxhash-linux-arm64-gnu" "1.4.0"
+ "@node-rs/xxhash-linux-arm64-musl" "1.4.0"
+ "@node-rs/xxhash-linux-x64-gnu" "1.4.0"
+ "@node-rs/xxhash-linux-x64-musl" "1.4.0"
+ "@node-rs/xxhash-win32-arm64-msvc" "1.4.0"
+ "@node-rs/xxhash-win32-ia32-msvc" "1.4.0"
+ "@node-rs/xxhash-win32-x64-msvc" "1.4.0"
+
"@samverschueren/stream-to-observable@^0.3.0":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301"
@@ -2868,7 +2952,7 @@ loader-runner@^2.4.0:
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
-loader-utils@1.4.2, loader-utils@^1.2.3:
+loader-utils@1.4.2, loader-utils@^2.0.4:
version "1.4.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
@@ -4402,11 +4486,12 @@ tapable@^1.0.0, tapable@^1.1.3:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
-terser-webpack-plugin@^1.4.3:
- version "1.4.5"
- resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b"
- integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==
+"terser-webpack-plugin@npm:@amoo-miki/terser-webpack-plugin@1.4.5-rc.2":
+ version "1.4.5-rc.2"
+ resolved "https://registry.yarnpkg.com/@amoo-miki/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5-rc.2.tgz#046c062ef22c126c2544718674bc6624e3651b9c"
+ integrity sha512-JFSGSzsWgSHEqQXlnHDh3gw+jdVdVlWM2Irdps9P/yWYNY/5VjuG8sdoW4mbuP8/HM893/k8N+ipbeqsd8/xpA==
dependencies:
+ "@node-rs/xxhash" "^1.3.0"
cacache "^12.0.2"
find-cache-dir "^2.1.0"
is-wsl "^1.1.0"
@@ -4699,11 +4784,12 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1:
source-list-map "^2.0.0"
source-map "~0.6.1"
-webpack@^4.41.5:
- version "4.46.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542"
- integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==
+"webpack@npm:@amoo-miki/webpack@4.46.0-rc.2":
+ version "4.46.0-rc.2"
+ resolved "https://registry.yarnpkg.com/@amoo-miki/webpack/-/webpack-4.46.0-rc.2.tgz#36824597c14557a7bb0a8e13203e30275e7b02bd"
+ integrity sha512-Y/ZqxTHOoDF1kz3SR63Y9SZGTDUpZNNFrisTRHofWhP8QvNX3LMN+TCmEP56UfLaiLVKMcaiFjx8kFb2TgyBaQ==
dependencies:
+ "@node-rs/xxhash" "^1.3.0"
"@webassemblyjs/ast" "1.9.0"
"@webassemblyjs/helper-module-context" "1.9.0"
"@webassemblyjs/wasm-edit" "1.9.0"
@@ -4716,7 +4802,7 @@ webpack@^4.41.5:
eslint-scope "^4.0.3"
json-parse-better-errors "^1.0.2"
loader-runner "^2.4.0"
- loader-utils "^1.2.3"
+ loader-utils "^2.0.4"
memory-fs "^0.4.1"
micromatch "^3.1.10"
mkdirp "^0.5.3"
@@ -4724,7 +4810,7 @@ webpack@^4.41.5:
node-libs-browser "^2.2.1"
schema-utils "^1.0.0"
tapable "^1.1.3"
- terser-webpack-plugin "^1.4.3"
+ terser-webpack-plugin "npm:@amoo-miki/terser-webpack-plugin@1.4.5-rc.2"
watchpack "^1.7.4"
webpack-sources "^1.4.1"
From 11b5b0dfa726b07c0c2eddc5fc7a439bb6d41db7 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 15 Jun 2023 19:51:12 +0200
Subject: [PATCH 02/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/AssociateMonitors.tsx | 29 ++
.../components/MonitorsList.tsx | 230 +++++++++++++++
.../utils/constants.js | 1 +
.../components/MonitorType/MonitorType.js | 31 +-
.../components/Schedule/Schedule.js | 4 +-
.../containers/CreateMonitor/CreateMonitor.js | 104 ++++---
.../CreateMonitor/utils/constants.js | 2 +
.../CreateMonitor/utils/formikToMonitor.js | 60 +++-
.../MonitorDetails/MonitorDetails.js | 4 -
.../WorkflowDetails/WorkflowDetails.tsx | 69 +++++
.../components/Action/actions/Message.js | 17 +-
.../ExpressionQuery/ExpressionQuery.js | 275 ++++++++++++++++++
.../ExpressionQuery/ExpressionQuery.test.ts | 14 +
.../CreateTrigger/utils/formikToTrigger.js | 90 ++++++
.../CompositeMonitorsAlertTrigger.js | 38 +++
.../DefineCompositeLevelTrigger.js | 169 +++++++++++
.../NotificationConfigDialog.js | 149 ++++++++++
.../TriggerNotifications.js | 140 +++++++++
.../TriggerNotificationsContent.js | 91 ++++++
.../DefineCompositeLevelTrigger/index.js | 8 +
public/utils/constants.js | 1 +
server/clusters/alerting/alertingPlugin.js | 8 +
server/routes/monitors.js | 10 +
server/services/MonitorService.js | 22 ++
yarn.lock | 108 +------
25 files changed, 1524 insertions(+), 150 deletions(-)
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
create mode 100644 public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
create mode 100644 public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
create mode 100644 public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.test.ts
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/index.js
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
new file mode 100644
index 000000000..9bac7683d
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment } from 'react';
+import { EuiSpacer, EuiText } from '@elastic/eui';
+import MonitorsList from './components/MonitorsList';
+
+const AssociateMonitors = ({ monitors, options, history }) => {
+ const onUpdate = () => {};
+
+ return (
+
+
+ Associate monitors
+
+
+ Associate two or more monitors to run as part of this flow.
+
+
+
+
+
+
+ );
+};
+
+export default AssociateMonitors;
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
new file mode 100644
index 000000000..2fc7eccf1
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
@@ -0,0 +1,230 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment, useState, useEffect } from 'react';
+import * as _ from 'lodash';
+import {
+ EuiButton,
+ EuiButtonIcon,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSpacer,
+ EuiText,
+ EuiToolTip,
+} from '@elastic/eui';
+import {
+ FormikComboBox,
+ FormikInputWrapper,
+ FormikFormRow,
+} from '../../../../../components/FormControls';
+
+const MonitorsList = ({ monitors = [], options = [], history }) => {
+ const [selectedOptions, setSelectedOptions] = useState({});
+ const [monitorOptions, setMonitorOptions] = useState([]);
+ const [monitorFields, setMonitorFields] = useState(
+ _.reduce(
+ monitors.length ? monitors : [0, 1],
+ (result, value, key) => {
+ result.push(key);
+ return result;
+ },
+ []
+ )
+ );
+
+ useEffect(() => {
+ const newOptions = [...options].map((monitor) => ({
+ label: monitor.monitor_name,
+ value: monitor.monitor_id,
+ }));
+ setMonitorOptions(newOptions);
+
+ let newSelected = monitors.length ? monitors : [];
+ setSelectedOptions(Object.assign({}, newSelected));
+ }, []);
+
+ const onChange = (options, monitorIdx, form) => {
+ let newSelected = {
+ ...selectedOptions,
+ };
+ if (options[0]) {
+ newSelected[monitorIdx] = options[0];
+ } else {
+ delete newSelected[monitorIdx];
+ }
+ setSelectedOptions(newSelected);
+
+ updateMonitorOptions(newSelected);
+
+ onBlur(monitorIdx, form);
+ };
+
+ const updateMonitorOptions = (selected) => {
+ const newMonitorOptions = [...monitorOptions];
+ newMonitorOptions.forEach((mon) => {
+ mon.disabled = isSelected(selected, mon);
+ });
+ setMonitorOptions([...newMonitorOptions]);
+ };
+
+ const onBlur = (monitorIdx, form) => {
+ form.setFieldTouched('associatedMonitors', true);
+ form.setFieldTouched(`associatedMonitor_${monitorIdx}`, true);
+
+ form.setFieldValue('associatedMonitors', Object.values(selectedOptions));
+ form.setFieldError('associatedMonitors', validate());
+ };
+
+ const isSelected = (selected, monitor) => {
+ let isSelected = false;
+ for (const key in selected) {
+ if (selected.hasOwnProperty(key)) {
+ if (_.isEqual(selected[key], monitor)) {
+ isSelected = true;
+ break;
+ }
+ }
+ }
+ return isSelected;
+ };
+
+ const onAddMonitor = () => {
+ let nextIndex = Math.max(...monitorFields) + 1;
+ const newMonitorFields = [...monitorFields, nextIndex];
+ setMonitorFields(newMonitorFields);
+ };
+
+ const onRemoveMonitor = (monitorIdx, idx, form) => {
+ const newSelected = { ...selectedOptions };
+ delete newSelected[monitorIdx];
+ setSelectedOptions(newSelected);
+
+ const newMonitorFields = [...monitorFields];
+ newMonitorFields.splice(idx, 1);
+ setMonitorFields(newMonitorFields);
+
+ updateMonitorOptions(newSelected);
+
+ onBlur(monitorIdx, form);
+ };
+
+ const isValid = () => Object.keys(selectedOptions).length > 1;
+ const validate = () => {
+ if (!isValid()) return 'Required.';
+ };
+
+ return (
+ validate(),
+ }}
+ render={({ field, form }) => (
+ form.touched['associatedMonitors'] && !isValid(),
+ error: () => validate(),
+ }}
+ >
+
+ {monitorFields.map((monitorIdx, idx) => (
+
+
+ onChange(options, monitorIdx, form),
+ onBlur: (e, field, form) => onBlur(monitorIdx, form),
+ options: monitorOptions,
+ singleSelection: { asPlainText: true },
+ selectedOptions: selectedOptions[monitorIdx]
+ ? [selectedOptions[monitorIdx]]
+ : undefined,
+ 'data-test-subj': `monitors_list_${monitorIdx}`,
+ fullWidth: true,
+ }}
+ />
+
+ {selectedOptions[monitorIdx] && (
+
+
+
+
+
+ )}
+ {monitorFields.length > 2 && (
+
+
+ onRemoveMonitor(monitorIdx, idx, form)}
+ />
+
+
+ )}
+
+ ))}
+
+ onAddMonitor()}
+ disabled={
+ monitorFields.length >= 10 ||
+ monitorOptions.length <= Object.keys(selectedOptions).length
+ }
+ >
+ Associate another monitor
+
+
+ You can associate up to {10 - monitorFields.length} more monitors.
+
+
+
+ )}
+ />
+ );
+};
+
+export default MonitorsList;
diff --git a/public/pages/CreateMonitor/components/DocumentLevelMonitorQueries/utils/constants.js b/public/pages/CreateMonitor/components/DocumentLevelMonitorQueries/utils/constants.js
index 497d882eb..d9494b581 100644
--- a/public/pages/CreateMonitor/components/DocumentLevelMonitorQueries/utils/constants.js
+++ b/public/pages/CreateMonitor/components/DocumentLevelMonitorQueries/utils/constants.js
@@ -6,6 +6,7 @@
import { OPERATORS_MAP } from '../../MonitorExpressions/expressions/utils/constants';
export const DOC_LEVEL_INPUT_FIELD = 'doc_level_input';
+export const COMPOSITE_INPUT_FIELD = 'composite_input';
/**
* A list of the operators currently supported for defining queries through the UI.
diff --git a/public/pages/CreateMonitor/components/MonitorType/MonitorType.js b/public/pages/CreateMonitor/components/MonitorType/MonitorType.js
index 1ba2a1ef9..ebb45aac0 100644
--- a/public/pages/CreateMonitor/components/MonitorType/MonitorType.js
+++ b/public/pages/CreateMonitor/components/MonitorType/MonitorType.js
@@ -25,6 +25,9 @@ const onChangeDefinition = (e, form) => {
form.setFieldValue('searchType', FORMIK_INITIAL_VALUES.searchType);
form.setFieldValue('triggerDefinitions', FORMIK_INITIAL_TRIGGER_VALUES.triggerConditions);
switch (type) {
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ form.setFieldValue('searchType', SEARCH_TYPE.GRAPH);
+ break;
case MONITOR_TYPE.CLUSTER_METRICS:
form.setFieldValue('searchType', SEARCH_TYPE.CLUSTER_METRICS);
break;
@@ -56,9 +59,17 @@ const clusterMetricsDescription = (
);
-const documentLevelDescription = ( // TODO DRAFT: confirm wording
+const documentLevelDescription = // TODO DRAFT: confirm wording
+ (
+
+ Per document monitors allow you to run queries on new documents as they're indexed.
+
+ );
+
+const compositeLevelDescription = (
- Per document monitors allow you to run queries on new documents as they're indexed.
+ Composite monitors allow you to monitor the states of existing monitors and to reduce alert
+ noise.
);
@@ -128,6 +139,22 @@ const MonitorType = ({ values }) => (
}}
/>
+
+ onChangeDefinition(e, form),
+ children: compositeLevelDescription,
+ 'data-test-subj': 'compositeLevelMonitorRadioCard',
+ }}
+ />
+
);
diff --git a/public/pages/CreateMonitor/components/Schedule/Schedule.js b/public/pages/CreateMonitor/components/Schedule/Schedule.js
index f5e16378a..4a3fe134a 100644
--- a/public/pages/CreateMonitor/components/Schedule/Schedule.js
+++ b/public/pages/CreateMonitor/components/Schedule/Schedule.js
@@ -11,7 +11,7 @@ import Interval from './Frequencies/Interval';
const Schedule = ({ isAd }) => (
- Schedule
+ Define workflow schedule
{isAd ? (
@@ -31,7 +31,7 @@ const Schedule = ({ isAd }) => (
) : (
-
+
)}
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index bee4ae8a2..700f31039 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -32,6 +32,8 @@ import {
} from '../../../CreateTrigger/containers/CreateTrigger/utils/formikToTrigger';
import { triggerToFormik } from '../../../CreateTrigger/containers/CreateTrigger/utils/triggerToFormik';
import { TRIGGER_TYPE } from '../../../CreateTrigger/containers/CreateTrigger/utils/constants';
+import WorkflowDetails from '../WorkflowDetails/WorkflowDetails';
+import CompositeMonitorsAlertTrigger from '../../../CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger';
export default class CreateMonitor extends Component {
static defaultProps = {
@@ -110,9 +112,14 @@ export default class CreateMonitor extends Component {
async onCreate(monitor, { setSubmitting, setErrors }) {
const { httpClient, notifications } = this.props;
try {
- const resp = await httpClient.post('../api/alerting/monitors', {
- body: JSON.stringify(monitor),
- });
+ const resp = await httpClient.post(
+ `../api/alerting/${
+ monitor.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? 'workflows' : 'monitors'
+ }`,
+ {
+ body: JSON.stringify(monitor),
+ }
+ );
setSubmitting(false);
const {
ok,
@@ -242,6 +249,8 @@ export default class CreateMonitor extends Component {
monitor = { ...monitor, ...triggers };
}
+ console.log('Monitor', monitor);
+ console.log('Value', values);
if (edit) this.onUpdate(monitor, formikBag);
else this.onCreate(monitor, formikBag);
}
@@ -288,42 +297,67 @@ export default class CreateMonitor extends Component {
detectorId={this.props.detectorId}
setFlyout={this.props.setFlyout}
/>
+
- {values.searchType !== SEARCH_TYPE.AD && (
-
-
-
-
- )}
+
+
+
-
- {(triggerArrayHelpers) => (
-
+ {values.searchType !== SEARCH_TYPE.AD &&
+ values.monitor_type !== MONITOR_TYPE.COMPOSITE_LEVEL && (
+
+
+
+
)}
-
+
+ {values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? (
+
+ ) : (
+
+ {(triggerArrayHelpers) => (
+
+ )}
+
+ )}
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
index b846481f3..2973014e7 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
@@ -61,6 +61,8 @@ export const FORMIK_INITIAL_VALUES = {
bucketUnitOfTime: 'h', // m = minute, h = hour, d = day
filters: [], // array of FORMIK_INITIAL_WHERE_EXPRESSION_VALUES
detectorId: '',
+ associatedMonitors: [],
+ expressionQuery: null,
};
export const FORMIK_INITIAL_AGG_VALUES = {
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor.js
index 03d19ee2c..ec0b03e28 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor.js
@@ -14,6 +14,7 @@ import {
getApiType,
} from '../../../components/ClusterMetricsMonitor/utils/clusterMetricsMonitorHelpers';
import {
+ COMPOSITE_INPUT_FIELD,
DOC_LEVEL_INPUT_FIELD,
DOC_LEVEL_QUERY_MAP,
} from '../../../components/DocumentLevelMonitorQueries/utils/constants';
@@ -29,11 +30,37 @@ export function formikToMonitor(values) {
[DOC_LEVEL_INPUT_FIELD]: formikToDocLevelQueriesUiMetadata(values),
search: { searchType: values.searchType },
};
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return {
+ [COMPOSITE_INPUT_FIELD]: formikToCompositeUiMetadata(values),
+ search: { searchType: values.searchType },
+ };
default:
return { search: formikToUiSearch(values) };
}
};
+ if (values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL) {
+ const enabled_time = new Date();
+ return {
+ last_update_time: enabled_time.getTime(),
+ owner: 'alerting',
+ type: 'workflow',
+ enabled_time: enabled_time.getTime(),
+ enabled: !values.disabled,
+ workflow_type: 'composite',
+ schema_version: 0,
+ name: values.name,
+ schedule,
+ inputs: [formikToInputs(values)],
+ triggers: [],
+ ui_metadata: {
+ schedule: uiSchedule,
+ ...monitorUiMetadata(),
+ },
+ };
+ }
+
return {
name: values.name,
type: 'monitor',
@@ -56,11 +83,26 @@ export function formikToInputs(values) {
return formikToClusterMetricsInput(values);
case MONITOR_TYPE.DOC_LEVEL:
return formikToDocLevelInput(values);
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return formikToCompositeInput(values);
default:
return formikToSearch(values);
}
}
+export function formikToCompositeInput(values) {
+ return {
+ composite_input: {
+ sequence: {
+ delegates: values.associatedMonitors.map((monitor, idx) => ({
+ order: idx + 1,
+ monitor_id: monitor.value,
+ })),
+ },
+ },
+ };
+}
+
export function formikToSearch(values) {
const isAD = values.searchType === SEARCH_TYPE.AD;
let query = isAD ? formikToAdQuery(values) : formikToQuery(values);
@@ -144,15 +186,8 @@ export function formikToAd(values) {
}
export function formikToUiSearch(values) {
- const {
- searchType,
- timeField,
- aggregations,
- groupBy,
- bucketValue,
- bucketUnitOfTime,
- filters,
- } = values;
+ const { searchType, timeField, aggregations, groupBy, bucketValue, bucketUnitOfTime, filters } =
+ values;
return {
searchType,
timeField,
@@ -270,6 +305,13 @@ export function formikToDocLevelQueriesUiMetadata(values) {
return { queries: _.get(values, 'queries', []) };
}
+export function formikToCompositeUiMetadata(values) {
+ return {
+ associatedMonitors: _.get(values, 'associatedMonitors', []),
+ query: _.get(values, '', ''),
+ };
+}
+
export function formikToCompositeAggregation(values) {
const { aggregations, groupBy } = values;
diff --git a/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js b/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
index 327b886f5..fe766b79b 100644
--- a/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
+++ b/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
@@ -8,7 +8,6 @@ import { EuiSpacer } from '@elastic/eui';
import ContentPanel from '../../../../components/ContentPanel';
import FormikFieldText from '../../../../components/FormControls/FormikFieldText';
import { hasError, isInvalid, required, validateMonitorName } from '../../../../utils/validate';
-import Schedule from '../../components/Schedule';
import MonitorDefinitionCard from '../../components/MonitorDefinitionCard';
import MonitorType from '../../components/MonitorType';
import AnomalyDetectors from '../AnomalyDetectors/AnomalyDetectors';
@@ -102,9 +101,6 @@ const MonitorDetails = ({
{anomalyDetectorContent.content}
) : null}
-
-
-
);
};
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
new file mode 100644
index 000000000..62868e509
--- /dev/null
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
@@ -0,0 +1,69 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment, useEffect, useState } from 'react';
+import ContentPanel from '../../../../components/ContentPanel';
+import Schedule from '../../components/Schedule';
+import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonitors';
+import { EuiSpacer } from '@elastic/eui';
+
+const WorkflowDetails = ({ isAd, isComposite, httpClient, history }) => {
+ const [selectedMonitors, setSelectedMonitors] = useState([]);
+ const [monitorOptions, setMonitorOptions] = useState([]);
+
+ const getMonitors = async () => {
+ const response = await httpClient.get('../api/alerting/monitors', {
+ query: {
+ from: 0,
+ size: 5000,
+ search: '',
+ sortField: 'name',
+ sortDirection: 'desc',
+ state: 'all',
+ },
+ });
+
+ if (response.ok) {
+ const { monitors, totalMonitors } = response;
+ return monitors.map((monitor) => ({ monitor_id: monitor.id, monitor_name: monitor.name }));
+ } else {
+ console.log('error getting monitors:', response);
+ return [];
+ }
+ };
+
+ useEffect(() => {
+ getMonitors().then((monitors) => {
+ setMonitorOptions(monitors);
+ });
+ }, []);
+
+ return (
+
+
+ {isComposite && (
+
+
+
+
+ )}
+
+ );
+};
+
+export default WorkflowDetails;
diff --git a/public/pages/CreateTrigger/components/Action/actions/Message.js b/public/pages/CreateTrigger/components/Action/actions/Message.js
index d754125d0..7c5f5f5af 100644
--- a/public/pages/CreateTrigger/components/Action/actions/Message.js
+++ b/public/pages/CreateTrigger/components/Action/actions/Message.js
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import _ from 'lodash';
import Mustache from 'mustache';
import {
@@ -181,6 +181,10 @@ export default function Message(
actionableAlertsSelections = [];
_.set(action, 'action_execution_policy.action_execution_scope', actionExecutionScopeId);
break;
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ displayActionableAlertsOptions = false;
+ displayThrottlingSettings = true;
+ break;
default:
displayActionableAlertsOptions = false;
displayThrottlingSettings = actionExecutionScopeId !== NOTIFY_OPTIONS_VALUES.PER_EXECUTION;
@@ -204,6 +208,17 @@ export default function Message(
preview = err.message;
console.error('There was an error rendering mustache template', err);
}
+
+ console.log(
+ 'MESSAGE',
+ action,
+ context,
+ index,
+ isSubjectDisabled,
+ sendTestMessage,
+ fieldPath,
+ values
+ );
return (
{!isSubjectDisabled ? (
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
new file mode 100644
index 000000000..1997017e7
--- /dev/null
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -0,0 +1,275 @@
+import React, { useEffect, useState } from 'react';
+import {
+ EuiFlexItem,
+ EuiFlexGroup,
+ EuiPopover,
+ EuiComboBox,
+ EuiButtonIcon,
+ EuiExpression,
+} from '@elastic/eui';
+import * as _ from 'lodash';
+import { FormikFormRow, FormikInputWrapper } from '../../../../components/FormControls';
+
+const ExpressionQuery = ({
+ selections,
+ dataTestSubj,
+ onChange,
+ value,
+ defaultText,
+ label,
+ name = 'expressionQueries',
+}) => {
+ const DEFAULT_DESCRIPTION = defaultText ? defaultText : 'Select';
+ const OPERATORS = ['AND', 'OR', 'NOT'];
+ const [usedExpressions, setUsedExpressions] = useState([]);
+
+ useEffect(() => {
+ let expressions = [];
+ if (value?.length) {
+ let values = [...value];
+ if (OPERATORS.indexOf(values[0]?.description) === -1) values = ['', ...values];
+
+ let counter = 0;
+ values.map((exp, idx) => {
+ if (idx % 2 === 0) {
+ expressions.push({
+ description: exp.description,
+ isOpen: false,
+ monitor_name: '',
+ monitor_id: '',
+ });
+ counter++;
+ } else {
+ const currentIndex = idx - counter;
+ expressions[currentIndex] = { ...expressions[currentIndex], ...exp };
+ }
+ });
+ } else {
+ expressions = [];
+ }
+
+ setUsedExpressions(expressions);
+ }, []);
+
+ const getValue = (expressions) =>
+ expressions.map((exp) => ({
+ condition: _.toLower(exp.description),
+ monitor_id: exp.monitor_id,
+ monitor_name: exp.name,
+ }));
+
+ const changeMonitor = (selection, exp, idx, form) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions[idx] = {
+ ...expressions[idx],
+ monitor_id: selection[0].monitor_id,
+ monitor_name: selection[0].label,
+ };
+
+ setUsedExpressions(expressions);
+ onBlur(form, expressions);
+ };
+
+ const changeCondition = (selection, exp, idx, form) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions[idx] = { ...expressions[idx], description: selection[0].description };
+ setUsedExpressions(expressions);
+ onBlur(form, expressions);
+ };
+
+ const onBlur = (form, expressions) => {
+ form.setFieldTouched('expressionQueries', true);
+ form.setFieldValue('triggerDefinitions[0].triggerConditions', getValue(expressions));
+ form.setFieldError('expressionQueries', validate());
+ };
+
+ const openPopover = (idx) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions[idx] = { ...expressions[idx], isOpen: !expressions[idx].isOpen };
+ setUsedExpressions(expressions);
+ };
+
+ const closePopover = (idx) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions[idx] = { ...expressions[idx], isOpen: false };
+ setUsedExpressions(expressions);
+ };
+
+ const renderOptions = (expression, idx, form) => (
+
+
+ changeCondition(selection, expression, idx, form)}
+ options={[
+ { description: '', label: '' },
+ { description: 'AND', label: 'AND' },
+ { description: 'OR', label: 'OR' },
+ { description: 'NOT', label: 'NOT' },
+ ]}
+ />
+
+ {renderMonitorOptions(expression, idx, form)}
+
+ {
+ const usedExp = _.cloneDeep(usedExpressions);
+ usedExp.splice(idx, 1);
+ usedExp.length && (usedExp[0].description = '');
+ setUsedExpressions([...usedExp]);
+ }}
+ iconType={'trash'}
+ color="danger"
+ aria-label={'Remove condition'}
+ style={{ marginTop: '4px' }}
+ />
+
+
+ );
+
+ const renderMonitorOptions = (expression, idx, form) => (
+
changeMonitor(selection, expression, idx, form)}
+ selectedOptions={[
+ {
+ label: expression.monitor_name,
+ monitor_id: expression.monitor_id,
+ },
+ ]}
+ style={{ width: '250px' }}
+ options={(() => {
+ const differences = _.differenceBy(selections, usedExpressions, 'monitor_id');
+ return [
+ {
+ monitor_id: expression.monitor_id,
+ label: expression.monitor_name,
+ },
+ ...differences.map((sel) => ({
+ monitor_id: sel.monitor_id,
+ label: sel.label,
+ })),
+ ];
+ })()}
+ />
+ );
+
+ const isValid = () => usedExpressions.length > 1;
+
+ const validate = () => {
+ if (!isValid()) return 'At least two monitors should be selected.';
+ };
+
+ return (
+ validate(),
+ }}
+ render={({ field, form }) => (
+ form.touched['expressionQueries'] && !isValid(),
+ error: () => validate(),
+ }}
+ >
+
+ {!usedExpressions.length && (
+
+ onBlur(form, usedExpressions)}
+ />
+ }
+ isOpen={false}
+ panelPaddingSize="s"
+ anchorPosition="rightDown"
+ closePopover={() => onBlur(form, usedExpressions)}
+ />
+
+ )}
+ {usedExpressions.map((expression, idx) => (
+
+ {
+ e.preventDefault();
+ openPopover(idx);
+ }}
+ />
+ }
+ isOpen={expression.isOpen}
+ closePopover={() => closePopover(idx)}
+ panelPaddingSize="s"
+ anchorPosition="rightDown"
+ >
+ {renderOptions(expression, idx, form)}
+
+
+ ))}
+ {selections.length > usedExpressions.length && (
+
+ {
+ const expressions = _.cloneDeep(usedExpressions);
+ const differences = _.differenceBy(selections, expressions, 'monitor_id');
+ const newExpressions = [
+ ...expressions,
+ {
+ description: usedExpressions.length ? 'AND' : '',
+ isOpen: false,
+ monitor_name: differences[0]?.label,
+ monitor_id: differences[0]?.monitor_id,
+ },
+ ];
+
+ setUsedExpressions(newExpressions);
+ onBlur(form, newExpressions);
+ }}
+ color={'primary'}
+ iconType="plusInCircleFilled"
+ aria-label={'Add one more condition'}
+ data-test-subj={'condition-add-selection-btn'}
+ style={{ marginTop: '1px' }}
+ />
+
+ )}
+
+
+ )}
+ />
+ );
+};
+
+export default ExpressionQuery;
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.test.ts b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.test.ts
new file mode 100644
index 000000000..49e1b709c
--- /dev/null
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.test.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from '@testing-library/react';
+
+describe(' spec', () => {
+ it('renders the component', () => {
+ const tree = render( );
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
index dfae41837..7d34ad1f2 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
@@ -35,6 +35,8 @@ export function formikToTriggerDefinition(values, monitorUiMetadata) {
return formikToBucketLevelTrigger(values, monitorUiMetadata);
case MONITOR_TYPE.DOC_LEVEL:
return formikToDocumentLevelTrigger(values, monitorUiMetadata);
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return formikToCompositeLevelTrigger(values, monitorUiMetadata);
default:
return formikToQueryLevelTrigger(values, monitorUiMetadata);
}
@@ -86,6 +88,20 @@ export function formikToDocumentLevelTrigger(values, monitorUiMetadata) {
};
}
+export function formikToCompositeLevelTrigger(values, monitorUiMetadata) {
+ const condition = formikToCompositeTriggerCondition(values, monitorUiMetadata);
+ const actions = formikToCompositeTriggerAction(values);
+ return {
+ chained_alert_trigger: {
+ id: values.id,
+ name: values.name,
+ severity: values.severity,
+ condition: condition,
+ actions: actions,
+ },
+ };
+}
+
export function formikToDocumentLevelTriggerCondition(values, monitorUiMetadata) {
const triggerConditions = _.get(values, 'triggerConditions', []);
const searchType = _.get(monitorUiMetadata, 'search.searchType', SEARCH_TYPE.QUERY);
@@ -99,6 +115,28 @@ export function formikToDocumentLevelTriggerCondition(values, monitorUiMetadata)
};
}
+export function formikToCompositeTriggerCondition(values, monitorUiMetadata) {
+ const conditionMap = {
+ and: '&&',
+ or: '||',
+ not: '!',
+ '': '',
+ };
+
+ const triggerConditions = _.get(values, 'triggerConditions', []);
+ const source = triggerConditions.reduce((query, expression) => {
+ query += ` ${conditionMap[expression.condition]} (monitor[id=${expression.monitor_id}])`;
+ return query.trim();
+ }, '');
+
+ return {
+ script: {
+ lang: 'painless',
+ source: source,
+ },
+ };
+}
+
export function getDocumentLevelScriptSource(conditions) {
const scriptSourceContents = [];
conditions.forEach((condition) => {
@@ -171,6 +209,51 @@ export function formikToBucketLevelTriggerAction(values) {
return actions;
}
+export function formikToCompositeTriggerAction(values) {
+ const actions = values.actions;
+ const executionPolicyPath = 'action_execution_policy.action_execution_scope';
+ if (actions && actions.length > 0) {
+ return actions.map((action) => {
+ let formattedAction = _.cloneDeep(action);
+
+ switch (formattedAction.throttle_enabled) {
+ case true:
+ _.set(formattedAction, 'throttle.unit', FORMIK_INITIAL_ACTION_VALUES.throttle.unit);
+ break;
+ case false:
+ formattedAction = _.omit(formattedAction, ['throttle']);
+ break;
+ }
+
+ const notifyOption = _.get(formattedAction, `${executionPolicyPath}`);
+ const notifyOptionId = _.isString(notifyOption) ? notifyOption : _.keys(notifyOption)[0];
+ switch (notifyOptionId) {
+ case NOTIFY_OPTIONS_VALUES.PER_ALERT:
+ const actionableAlerts = _.get(
+ formattedAction,
+ `${executionPolicyPath}.${NOTIFY_OPTIONS_VALUES.PER_ALERT}.actionable_alerts`,
+ []
+ );
+ _.set(
+ formattedAction,
+ `${executionPolicyPath}.${NOTIFY_OPTIONS_VALUES.PER_ALERT}.actionable_alerts`,
+ actionableAlerts.map((entry) => entry.value)
+ );
+ break;
+ case NOTIFY_OPTIONS_VALUES.PER_EXECUTION:
+ _.set(
+ formattedAction,
+ `${executionPolicyPath}.${NOTIFY_OPTIONS_VALUES.PER_EXECUTION}`,
+ {}
+ );
+ break;
+ }
+ return formattedAction;
+ });
+ }
+ return actions;
+}
+
export function formikToTriggerUiMetadata(values, monitorUiMetadata) {
switch (monitorUiMetadata.monitor_type) {
case MONITOR_TYPE.QUERY_LEVEL:
@@ -221,6 +304,13 @@ export function formikToTriggerUiMetadata(values, monitorUiMetadata) {
docLevelTriggersUiMetadata[trigger.name] = triggerMetadata;
});
return docLevelTriggersUiMetadata;
+
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ const compositeTriggersUiMetadata = {};
+ _.get(values, 'triggerDefinitions', []).forEach((trigger) => {
+ compositeTriggersUiMetadata[trigger.name] = _.get(trigger, 'triggerConditions', []);
+ });
+ return compositeTriggersUiMetadata;
}
}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
new file mode 100644
index 000000000..b5d40400a
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
@@ -0,0 +1,38 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment } from 'react';
+import DefineCompositeLevelTrigger from './DefineCompositeLevelTrigger';
+
+const CompositeMonitorsAlertTrigger = ({
+ edit,
+ triggerArrayHelpers,
+ monitor,
+ monitorValues,
+ triggerValues,
+ isDarkMode,
+ httpClient,
+ notifications,
+ notificationService,
+ plugins,
+}) => {
+ return (
+
+
+
+ );
+};
+export default CompositeMonitorsAlertTrigger;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
new file mode 100644
index 000000000..035817ff0
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -0,0 +1,169 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import _ from 'lodash';
+import { EuiAccordion, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
+import { FormikFieldText, FormikSelect } from '../../../../components/FormControls';
+import { hasError, isInvalid } from '../../../../utils/validate';
+import { DEFAULT_TRIGGER_NAME, SEVERITY_OPTIONS } from '../../utils/constants';
+import { validateTriggerName } from '../DefineTrigger/utils/validation';
+import ExpressionQuery from '../../components/ExpressionQuery/ExpressionQuery';
+import TriggerNotifications from './TriggerNotifications';
+import ContentPanel from '../../../../components/ContentPanel';
+import { FORMIK_INITIAL_TRIGGER_VALUES } from '../CreateTrigger/utils/constants';
+
+const defaultRowProps = {
+ label: 'Trigger name',
+ style: { paddingLeft: '10px' },
+ isInvalid,
+ error: hasError,
+};
+
+const defaultInputProps = { isInvalid };
+
+const selectFieldProps = {
+ validate: () => {},
+};
+
+const selectRowProps = {
+ label: 'Severity level',
+ style: { paddingLeft: '10px', marginTop: '0px' },
+ isInvalid,
+ error: hasError,
+};
+
+const selectInputProps = {
+ options: SEVERITY_OPTIONS,
+};
+
+const propTypes = {
+ monitorValues: PropTypes.object.isRequired,
+ triggerValues: PropTypes.object.isRequired,
+ isDarkMode: PropTypes.bool.isRequired,
+};
+
+export const titleTemplate = (title, subTitle) => (
+
+ {title}
+ {subTitle && (
+
+ {subTitle}
+
+ )}
+
+
+);
+
+class DefineCompositeLevelTrigger extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ }
+
+ render() {
+ const {
+ edit,
+ monitorValues,
+ triggers,
+ triggerValues,
+ isDarkMode,
+ httpClient,
+ notifications,
+ notificationService,
+ plugins,
+ selectedMonitors,
+ } = this.props;
+ const fieldPath = `triggerDefinitions[0].`;
+ const triggerName = _.get(triggerValues, `${fieldPath}name`, DEFAULT_TRIGGER_NAME);
+ const triggerDefinitions = _.get(triggerValues, 'triggerDefinitions', []);
+ _.set(triggerValues, 'triggerDefinitions', [
+ {
+ ...FORMIK_INITIAL_TRIGGER_VALUES,
+ ...triggerDefinitions[0],
+ severity: 1,
+ name: triggerName,
+ },
+ ]);
+
+ const monitorList = monitorValues?.associatedMonitors
+ ? monitorValues.associatedMonitors?.map((monitor) => ({
+ label: monitor.label.replaceAll(' ', '_'),
+ monitor_id: monitor.value,
+ }))
+ : [];
+
+ return (
+
+
+
+
+
+
+
+ [monitor, { description: 'and' }]))
+ .slice(0, -1)}
+ onChange={() => {}}
+ dataTestSubj={'composite_expression_query'}
+ defaultText={'Select associated monitor'}
+ />
+
+
+
+ {titleTemplate('Alert severity')}
+
+
+
+
+
+
+ );
+ }
+}
+
+DefineCompositeLevelTrigger.propTypes = propTypes;
+
+export default DefineCompositeLevelTrigger;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
new file mode 100644
index 000000000..aedb55566
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
@@ -0,0 +1,149 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment } from 'react';
+import {
+ EuiButton,
+ EuiSpacer,
+ EuiModal,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+} from '@elastic/eui';
+import { titleTemplate } from './DefineCompositeLevelTrigger';
+import Message from '../../components/Action/actions';
+import { DEFAULT_MESSAGE_SOURCE, FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
+import { getTriggerContext } from '../../utils/helper';
+import { formikToMonitor } from '../../../CreateMonitor/containers/CreateMonitor/utils/formikToMonitor';
+import _ from 'lodash';
+import { formikToTrigger } from '../CreateTrigger/utils/formikToTrigger';
+import { MONITOR_TYPE } from '../../../../utils/constants';
+import { TRIGGER_TYPE } from '../CreateTrigger/utils/constants';
+import { backendErrorNotification } from '../../../../utils/helpers';
+import { checkForError } from '../ConfigureActions/ConfigureActions';
+
+const NotificationConfigDialog = ({
+ channel,
+ closeModal,
+ triggerValues,
+ httpClient,
+ notifications,
+}) => {
+ const triggerIndex = 0;
+ const monitor = formikToMonitor(triggerValues);
+ const context = getTriggerContext({}, monitor, triggerValues, 0);
+
+ const initialActionValues = _.cloneDeep(FORMIK_INITIAL_ACTION_VALUES);
+ let action = _.get(triggerValues, 'triggerDefinitions[0].actions[0]', {
+ ...initialActionValues,
+ });
+
+ const sendTestMessage = async (index) => {
+ const flattenedDestinations = [];
+ // TODO: For bucket-level triggers, sendTestMessage will only send a test message if there is
+ // at least one bucket of data from the monitor input query.
+ let testTrigger = _.cloneDeep(
+ formikToTrigger(triggerValues, monitor.ui_metadata)[triggerIndex]
+ );
+ let action;
+ let condition;
+
+ switch (monitor.monitor_type) {
+ case MONITOR_TYPE.BUCKET_LEVEL:
+ action = _.get(testTrigger, `${TRIGGER_TYPE.BUCKET_LEVEL}.actions[${index}]`);
+ condition = {
+ ..._.get(testTrigger, `${TRIGGER_TYPE.BUCKET_LEVEL}.condition`),
+ buckets_path: { _count: '_count' },
+ script: {
+ source: 'params._count >= 0',
+ },
+ };
+ _.set(testTrigger, `${TRIGGER_TYPE.BUCKET_LEVEL}.actions`, [action]);
+ _.set(testTrigger, `${TRIGGER_TYPE.BUCKET_LEVEL}.condition`, condition);
+ break;
+ case MONITOR_TYPE.DOC_LEVEL:
+ action = _.get(testTrigger, `${TRIGGER_TYPE.DOC_LEVEL}.actions[${index}]`);
+ condition = {
+ ..._.get(testTrigger, `${TRIGGER_TYPE.DOC_LEVEL}.condition`),
+ script: { lang: 'painless', source: 'return true' },
+ };
+ _.set(testTrigger, `${TRIGGER_TYPE.DOC_LEVEL}.actions`, [action]);
+ _.set(testTrigger, `${TRIGGER_TYPE.DOC_LEVEL}.condition`, condition);
+ break;
+ default:
+ action = _.get(testTrigger, `actions[${index}]`);
+ condition = {
+ ..._.get(testTrigger, 'condition'),
+ script: { lang: 'painless', source: 'return true' },
+ };
+ _.set(testTrigger, 'actions', [action]);
+ _.set(testTrigger, 'condition', condition);
+ break;
+ }
+
+ const testMonitor = { ...monitor, triggers: [{ ...testTrigger }] };
+
+ try {
+ const response = await httpClient.post('../api/alerting/monitors/_execute', {
+ query: { dryrun: false },
+ body: JSON.stringify(testMonitor),
+ });
+ let error = null;
+ if (response.ok) {
+ error = checkForError(response, error);
+ if (!_.isEmpty(action.destination_id)) {
+ const destinationName = _.get(
+ _.find(flattenedDestinations, { value: action.destination_id }),
+ 'label'
+ );
+ notifications.toasts.addSuccess(`Test message sent to "${destinationName}."`);
+ }
+ }
+ if (error || !response.ok) {
+ const errorMessage = error == null ? response.resp : error;
+ console.error('There was an error trying to send test message', errorMessage);
+ backendErrorNotification(notifications, 'send', 'test message', errorMessage);
+ }
+ } catch (err) {
+ console.error('There was an error trying to send test message', err);
+ }
+ };
+
+ console.log('ACTION', action);
+ return (
+ closeModal()}>
+
+
+ Configure notification
+
+
+
+ {titleTemplate('Customize message')}
+
+
+
+
+
+
+ closeModal()}>Close
+ closeModal()} fill>
+ Update
+
+
+
+ );
+};
+
+export default NotificationConfigDialog;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
new file mode 100644
index 000000000..67eb337af
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
@@ -0,0 +1,140 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment, useState, useEffect } from 'react';
+import {
+ EuiSpacer,
+ EuiButton,
+ EuiText,
+ EuiAccordion,
+ EuiHorizontalRule,
+ EuiButtonIcon,
+} from '@elastic/eui';
+import TriggerNotificationsContent from './TriggerNotificationsContent';
+import { titleTemplate } from './DefineCompositeLevelTrigger';
+import { MAX_CHANNELS_RESULT_SIZE, OS_NOTIFICATION_PLUGIN } from '../../../../utils/constants';
+import { CHANNEL_TYPES } from '../../utils/constants';
+
+const TriggerNotifications = ({
+ httpClient,
+ actions = [],
+ plugins,
+ notifications,
+ notificationService,
+ triggerValues,
+}) => {
+ const [channels, setChannels] = useState([]);
+ const [options, setOptions] = useState([]);
+
+ useEffect(() => {
+ let newChannels = [...actions];
+ if (_.isEmpty(newChannels))
+ newChannels = [
+ {
+ name: '',
+ id: '',
+ },
+ ];
+ setChannels(newChannels);
+
+ getChannels().then((channels) => setOptions(channels));
+ }, []);
+
+ const getChannels = async () => {
+ const hasNotificationPlugin = plugins.indexOf(OS_NOTIFICATION_PLUGIN) !== -1;
+
+ let channels = [];
+ let index = 0;
+ const getChannels = async () => {
+ const getChannelsQuery = {
+ from_index: index,
+ max_items: MAX_CHANNELS_RESULT_SIZE,
+ config_type: CHANNEL_TYPES,
+ sort_field: 'name',
+ sort_order: 'asc',
+ };
+
+ const channelsResponse = await notificationService.getChannels(getChannelsQuery);
+
+ channels = channels.concat(
+ channelsResponse.items.map((channel) => ({
+ label: channel.name,
+ value: channel.config_id,
+ type: channel.config_type,
+ description: channel.description,
+ }))
+ );
+
+ if (channelsResponse.total && channels.length < channelsResponse.total) {
+ index += MAX_CHANNELS_RESULT_SIZE;
+ await getChannels();
+ }
+ };
+
+ if (hasNotificationPlugin) {
+ await getChannels();
+ }
+
+ return channels;
+ };
+
+ const onAddNotification = () => {
+ const newChannels = [...channels];
+ newChannels.push({
+ label: '',
+ value: '',
+ });
+ setChannels(newChannels);
+ };
+
+ const onRemoveNotification = (idx) => {
+ const newChannels = [...channels];
+ newChannels.splice(idx, 1);
+ setChannels(newChannels);
+ };
+
+ return (
+
+ {titleTemplate('Notifications')}
+
+ {channels.length &&
+ channels.map((channel, idx) => (
+ {`Notification ${idx + 1}`}}
+ paddingSize={'s'}
+ extraAction={
+ channels.length > 1 && (
+ onRemoveNotification(idx)}
+ size={'s'}
+ />
+ )
+ }
+ >
+
+
+ ))}
+
+ onAddNotification()}>Add notification
+
+ );
+};
+
+export default TriggerNotifications;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
new file mode 100644
index 000000000..3fd2dabb5
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
@@ -0,0 +1,91 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment, useState } from 'react';
+import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiSpacer } from '@elastic/eui';
+import { FormikComboBox } from '../../../../components/FormControls';
+import NotificationConfigDialog from './NotificationConfigDialog';
+import _ from 'lodash';
+import { FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
+
+const TriggerNotificationsContent = ({
+ channel,
+ options,
+ idx,
+ triggerValues,
+ httpClient,
+ notifications,
+}) => {
+ const [selected, setSelected] = useState([]);
+ const [isModalVisible, setIsModalVisible] = useState(false);
+
+ const onChange = (selectedOptions) => {
+ setSelected(selectedOptions);
+ const initialActionValues = _.cloneDeep(FORMIK_INITIAL_ACTION_VALUES);
+ _.set(triggerValues, 'triggerDefinitions[0].actions[0]', initialActionValues);
+ };
+
+ const showConfig = (channels) => setIsModalVisible(true);
+
+ return (
+
+
+
+
+ onChange(selectedOptions),
+ singleSelection: { asPlainText: true },
+ }}
+ />
+
+
+
+ Manage channels
+
+
+
+ {selected.length ? (
+
+
+ showConfig()}>
+ Configure notification
+
+
+ ) : null}
+
+ {isModalVisible && (
+ setIsModalVisible(false)}
+ channel={channel}
+ triggerValues={triggerValues}
+ httpClient={httpClient}
+ notifications={notifications}
+ />
+ )}
+
+ );
+};
+
+export default TriggerNotificationsContent;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/index.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/index.js
new file mode 100644
index 000000000..dff1a2d91
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/index.js
@@ -0,0 +1,8 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import DefineCompositeLevelTrigger from './DefineCompositeLevelTrigger';
+
+export default DefineCompositeLevelTrigger;
diff --git a/public/utils/constants.js b/public/utils/constants.js
index 9511ae0d2..cfe51489c 100644
--- a/public/utils/constants.js
+++ b/public/utils/constants.js
@@ -30,6 +30,7 @@ export const MONITOR_TYPE = {
BUCKET_LEVEL: 'bucket_level_monitor',
CLUSTER_METRICS: 'cluster_metrics_monitor',
DOC_LEVEL: 'doc_level_monitor',
+ COMPOSITE_LEVEL: 'composite',
};
export const DESTINATION_ACTIONS = {
diff --git a/server/clusters/alerting/alertingPlugin.js b/server/clusters/alerting/alertingPlugin.js
index 8564d0020..46cde5562 100644
--- a/server/clusters/alerting/alertingPlugin.js
+++ b/server/clusters/alerting/alertingPlugin.js
@@ -46,6 +46,14 @@ export default function alertingPlugin(Client, config, components) {
method: 'POST',
});
+ alerting.createWorkflow = ca({
+ url: {
+ fmt: `${API_ROUTE_PREFIX}/workflows?refresh=wait_for`,
+ },
+ needBody: true,
+ method: 'POST',
+ });
+
alerting.deleteMonitor = ca({
url: {
fmt: `${MONITOR_BASE_API}/<%=monitorId%>`,
diff --git a/server/routes/monitors.js b/server/routes/monitors.js
index 6cc4967c0..a447327df 100644
--- a/server/routes/monitors.js
+++ b/server/routes/monitors.js
@@ -45,6 +45,16 @@ export default function (services, router) {
monitorService.createMonitor
);
+ router.post(
+ {
+ path: '/api/alerting/workflows',
+ validate: {
+ body: schema.any(),
+ },
+ },
+ monitorService.createWorkflow
+ );
+
router.post(
{
path: '/api/alerting/monitors/_execute',
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 81d788520..0ed7554ac 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -35,6 +35,28 @@ export default class MonitorService {
}
};
+ createWorkflow = async (context, req, res) => {
+ try {
+ const params = { body: req.body };
+ const { callAsCurrentUser } = await this.esDriver.asScoped(req);
+ const createResponse = await callAsCurrentUser('alerting.createWorkflow', params);
+ return res.ok({
+ body: {
+ ok: true,
+ resp: createResponse,
+ },
+ });
+ } catch (err) {
+ console.error('Alerting - MonitorService - createWorkflow:', err);
+ return res.ok({
+ body: {
+ ok: false,
+ resp: err.message,
+ },
+ });
+ }
+ };
+
deleteMonitor = async (context, req, res) => {
try {
const { id } = req.params;
diff --git a/yarn.lock b/yarn.lock
index 4c675f913..d35b800d1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -232,90 +232,6 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
-"@node-rs/xxhash-android-arm-eabi@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-android-arm-eabi/-/xxhash-android-arm-eabi-1.4.0.tgz#55ace4d3882686d1e379aaf613e1338d78f13fc8"
- integrity sha512-JuZNqt5/znWkIGteikQdS+HT9S0JsMYi06S4yzU/sMKLCIPvD0MnCTXlYtuDcgRIKScCaepAsSQVomnAyLFNNA==
-
-"@node-rs/xxhash-android-arm64@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-android-arm64/-/xxhash-android-arm64-1.4.0.tgz#2290c53ceabda804afb4c45679613d833a6385a0"
- integrity sha512-BZzQO5jlgsIr9HhiqTwZjYqlfVeZiu+7PaoAdNEOq+i/SjyAqv1jGSkyek4rBSAiodyNkXcbE0eQtomeN6a55w==
-
-"@node-rs/xxhash-darwin-arm64@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-darwin-arm64/-/xxhash-darwin-arm64-1.4.0.tgz#96df4f48b13deb6899e84ed0882bdbd0a4856f13"
- integrity sha512-JlEAzTsQaqJaWVse/JP//6QKBIhzqzTlvNY4uEbi8TaZMfvDDhW//ClXM6CkSV799GJxAYPu1LXa4+OeBQpa7Q==
-
-"@node-rs/xxhash-darwin-x64@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-darwin-x64/-/xxhash-darwin-x64-1.4.0.tgz#9df3ca3a87354dd5386aadfa20ad032a299c2b8f"
- integrity sha512-9ycVJfzLvw1wc6Tgq0giLkMn5nGOBawTeOA17t27dQFdY/scZPz583DO7w+eznMnlzUXwoLiloanUebRhy+piQ==
-
-"@node-rs/xxhash-freebsd-x64@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-freebsd-x64/-/xxhash-freebsd-x64-1.4.0.tgz#24b0c0bfd33429303688b4af78f9d323daa0fb5b"
- integrity sha512-vFRDr6qA0gHWQDjuSxXcdzM4Ppk+5VebEhc76zkWrRVc6RG60fxLo5B4j6QwMwXGTYaG8HMv/nQhAgbnOCWWxQ==
-
-"@node-rs/xxhash-linux-arm-gnueabihf@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm-gnueabihf/-/xxhash-linux-arm-gnueabihf-1.4.0.tgz#4c09f70cd39429fb1a52f3567085e949603d4817"
- integrity sha512-0KS6y1caqbtPanos9XNMekWpozCHA6QSlQzaZyn9Hn+Z+mYpR5+NoWixefhp06jt59qF9+LkkF3C9fSEHYmq/w==
-
-"@node-rs/xxhash-linux-arm64-gnu@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm64-gnu/-/xxhash-linux-arm64-gnu-1.4.0.tgz#e92d7026614506fb4db309977127fd8589fabd7c"
- integrity sha512-QI97JK2qiQhVgRtUBMgA1ZjPLpwnz11SE2Mw1jryejmyH9EXKKiCyt2FweO6MVP7bEuMxcdajBho4pEL7s/QsA==
-
-"@node-rs/xxhash-linux-arm64-musl@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-arm64-musl/-/xxhash-linux-arm64-musl-1.4.0.tgz#a8b16233a86c116e6af32a69278248d17b2d09e7"
- integrity sha512-dtMid4OMkNBYGJkjoT1jdkENpV8m8MGp3lliDN8C+2znZUQM8KFRTXRkfaq4lgzu3Y2XeYzsLOoBsBd3Hgf7gA==
-
-"@node-rs/xxhash-linux-x64-gnu@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-x64-gnu/-/xxhash-linux-x64-gnu-1.4.0.tgz#385ec91396ebaa2b73abf419be3971ec893dcbd1"
- integrity sha512-OeOQL10cG62wL1IVoeC74xESmefHU7r3xiZMTP2hK5Dh3FdF2sa3x/Db9BcGXlaokg/lMGDxuTuzOLC2Rv/wlQ==
-
-"@node-rs/xxhash-linux-x64-musl@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-linux-x64-musl/-/xxhash-linux-x64-musl-1.4.0.tgz#715bb962502b0ec69e1fc19db22ac035c63d30c7"
- integrity sha512-kZ8wNi5bH9b+ZpuPlSbFd6JXk8CKbfCvCPZ0Vk0IqLkzB6PihQflnZPM9r0QZ2jtFgyfWmpbFK4YxwX9YcyLog==
-
-"@node-rs/xxhash-win32-arm64-msvc@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-arm64-msvc/-/xxhash-win32-arm64-msvc-1.4.0.tgz#4a3a4ebcb50c73e4309e429b28eb44dbf8f7f71f"
- integrity sha512-Ggv66jlhQvj4XgQqNgl2JKQ7My/97PvPZi5jKbcS7t65wJC36J6XERQwRPdupO8UH63XfPqb7HJqrgmiz8tmlA==
-
-"@node-rs/xxhash-win32-ia32-msvc@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-ia32-msvc/-/xxhash-win32-ia32-msvc-1.4.0.tgz#fdfdb43e41113a8baf15779ca53bb637d2e1bc8f"
- integrity sha512-mYpF1+7unqKKGsPn7Y8X6SqP2Bc5BU5dsHBKhAGAuvrMg9W63zM+YWM8/fpNGfFlOrjiKRvXHZ96nrZyzoxeBw==
-
-"@node-rs/xxhash-win32-x64-msvc@1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash-win32-x64-msvc/-/xxhash-win32-x64-msvc-1.4.0.tgz#aee714a4ae0121f3947f94139adf13f5b6d93d12"
- integrity sha512-rKuqWHuQNlrfjIOkQW3oCBta/GUlyVoUkKB13aVr8uixOs/eneuDaYJx2h02FAAWlWCKADFnMxgDl0LVFBy53w==
-
-"@node-rs/xxhash@^1.3.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@node-rs/xxhash/-/xxhash-1.4.0.tgz#1e75850e0e530c9224e8e5ba4056d52e8868291b"
- integrity sha512-UpSOParhMqbQ7hsYovN2e+uqvWqHJiCDvFl8gDzMcXgBY/PkI2zo2zhdRAZdz48c6/dke+0WjCKy90wDVQxS6g==
- optionalDependencies:
- "@node-rs/xxhash-android-arm-eabi" "1.4.0"
- "@node-rs/xxhash-android-arm64" "1.4.0"
- "@node-rs/xxhash-darwin-arm64" "1.4.0"
- "@node-rs/xxhash-darwin-x64" "1.4.0"
- "@node-rs/xxhash-freebsd-x64" "1.4.0"
- "@node-rs/xxhash-linux-arm-gnueabihf" "1.4.0"
- "@node-rs/xxhash-linux-arm64-gnu" "1.4.0"
- "@node-rs/xxhash-linux-arm64-musl" "1.4.0"
- "@node-rs/xxhash-linux-x64-gnu" "1.4.0"
- "@node-rs/xxhash-linux-x64-musl" "1.4.0"
- "@node-rs/xxhash-win32-arm64-msvc" "1.4.0"
- "@node-rs/xxhash-win32-ia32-msvc" "1.4.0"
- "@node-rs/xxhash-win32-x64-msvc" "1.4.0"
-
"@samverschueren/stream-to-observable@^0.3.0":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301"
@@ -2952,7 +2868,7 @@ loader-runner@^2.4.0:
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
-loader-utils@1.4.2, loader-utils@^2.0.4:
+loader-utils@1.4.2, loader-utils@^1.2.3:
version "1.4.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
@@ -4491,12 +4407,11 @@ tapable@^1.0.0, tapable@^1.1.3:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
-"terser-webpack-plugin@npm:@amoo-miki/terser-webpack-plugin@1.4.5-rc.2":
- version "1.4.5-rc.2"
- resolved "https://registry.yarnpkg.com/@amoo-miki/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5-rc.2.tgz#046c062ef22c126c2544718674bc6624e3651b9c"
- integrity sha512-JFSGSzsWgSHEqQXlnHDh3gw+jdVdVlWM2Irdps9P/yWYNY/5VjuG8sdoW4mbuP8/HM893/k8N+ipbeqsd8/xpA==
+terser-webpack-plugin@^1.4.3:
+ version "1.4.5"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b"
+ integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==
dependencies:
- "@node-rs/xxhash" "^1.3.0"
cacache "^12.0.2"
find-cache-dir "^2.1.0"
is-wsl "^1.1.0"
@@ -4789,12 +4704,11 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1:
source-list-map "^2.0.0"
source-map "~0.6.1"
-"webpack@npm:@amoo-miki/webpack@4.46.0-rc.2":
- version "4.46.0-rc.2"
- resolved "https://registry.yarnpkg.com/@amoo-miki/webpack/-/webpack-4.46.0-rc.2.tgz#36824597c14557a7bb0a8e13203e30275e7b02bd"
- integrity sha512-Y/ZqxTHOoDF1kz3SR63Y9SZGTDUpZNNFrisTRHofWhP8QvNX3LMN+TCmEP56UfLaiLVKMcaiFjx8kFb2TgyBaQ==
+webpack@^4.41.5:
+ version "4.46.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542"
+ integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==
dependencies:
- "@node-rs/xxhash" "^1.3.0"
"@webassemblyjs/ast" "1.9.0"
"@webassemblyjs/helper-module-context" "1.9.0"
"@webassemblyjs/wasm-edit" "1.9.0"
@@ -4807,7 +4721,7 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1:
eslint-scope "^4.0.3"
json-parse-better-errors "^1.0.2"
loader-runner "^2.4.0"
- loader-utils "^2.0.4"
+ loader-utils "^1.2.3"
memory-fs "^0.4.1"
micromatch "^3.1.10"
mkdirp "^0.5.3"
@@ -4815,7 +4729,7 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1:
node-libs-browser "^2.2.1"
schema-utils "^1.0.0"
tapable "^1.1.3"
- terser-webpack-plugin "npm:@amoo-miki/terser-webpack-plugin@1.4.5-rc.2"
+ terser-webpack-plugin "^1.4.3"
watchpack "^1.7.4"
webpack-sources "^1.4.1"
From a8aa47a40617bd14c8290682c8fb3e1f2233774c Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 15 Jun 2023 20:16:28 +0200
Subject: [PATCH 03/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/components/MonitorsList.tsx | 2 +-
.../components/Action/actions/Message.js | 12 +-----------
.../components/ExpressionQuery/ExpressionQuery.js | 1 -
.../DefineCompositeLevelTrigger.js | 6 ++----
.../NotificationConfigDialog.js | 4 ++--
.../TriggerNotificationsContent.js | 11 ++++++++++-
6 files changed, 16 insertions(+), 20 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
index 2fc7eccf1..e636cddd6 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
@@ -20,7 +20,7 @@ import {
FormikFormRow,
} from '../../../../../components/FormControls';
-const MonitorsList = ({ monitors = [], options = [], history }) => {
+const MonitorsList = ({ monitors = [], options = [] }) => {
const [selectedOptions, setSelectedOptions] = useState({});
const [monitorOptions, setMonitorOptions] = useState([]);
const [monitorFields, setMonitorFields] = useState(
diff --git a/public/pages/CreateTrigger/components/Action/actions/Message.js b/public/pages/CreateTrigger/components/Action/actions/Message.js
index 7c5f5f5af..a5456d291 100644
--- a/public/pages/CreateTrigger/components/Action/actions/Message.js
+++ b/public/pages/CreateTrigger/components/Action/actions/Message.js
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState, useEffect } from 'react';
+import React, { useState } from 'react';
import _ from 'lodash';
import Mustache from 'mustache';
import {
@@ -209,16 +209,6 @@ export default function Message(
console.error('There was an error rendering mustache template', err);
}
- console.log(
- 'MESSAGE',
- action,
- context,
- index,
- isSubjectDisabled,
- sendTestMessage,
- fieldPath,
- values
- );
return (
{!isSubjectDisabled ? (
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index 1997017e7..65ac5f761 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -13,7 +13,6 @@ import { FormikFormRow, FormikInputWrapper } from '../../../../components/FormCo
const ExpressionQuery = ({
selections,
dataTestSubj,
- onChange,
value,
defaultText,
label,
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index 035817ff0..0f70bb6dc 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -6,11 +6,10 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
-import { EuiAccordion, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
+import { EuiSpacer, EuiText } from '@elastic/eui';
import { FormikFieldText, FormikSelect } from '../../../../components/FormControls';
import { hasError, isInvalid } from '../../../../utils/validate';
import { DEFAULT_TRIGGER_NAME, SEVERITY_OPTIONS } from '../../utils/constants';
-import { validateTriggerName } from '../DefineTrigger/utils/validation';
import ExpressionQuery from '../../components/ExpressionQuery/ExpressionQuery';
import TriggerNotifications from './TriggerNotifications';
import ContentPanel from '../../../../components/ContentPanel';
@@ -75,10 +74,9 @@ class DefineCompositeLevelTrigger extends Component {
notifications,
notificationService,
plugins,
- selectedMonitors,
} = this.props;
const fieldPath = `triggerDefinitions[0].`;
- const triggerName = _.get(triggerValues, `${fieldPath}name`, DEFAULT_TRIGGER_NAME);
+ const triggerName = _.get(triggerValues, `${fieldPath}name`, 'Trigger');
const triggerDefinitions = _.get(triggerValues, 'triggerDefinitions', []);
_.set(triggerValues, 'triggerDefinitions', [
{
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
index aedb55566..375075426 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { Fragment } from 'react';
+import React from 'react';
import {
EuiButton,
EuiSpacer,
@@ -15,7 +15,7 @@ import {
} from '@elastic/eui';
import { titleTemplate } from './DefineCompositeLevelTrigger';
import Message from '../../components/Action/actions';
-import { DEFAULT_MESSAGE_SOURCE, FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
+import { FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
import { getTriggerContext } from '../../utils/helper';
import { formikToMonitor } from '../../../CreateMonitor/containers/CreateMonitor/utils/formikToMonitor';
import _ from 'lodash';
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
index 3fd2dabb5..a54fee443 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
@@ -24,7 +24,16 @@ const TriggerNotificationsContent = ({
const onChange = (selectedOptions) => {
setSelected(selectedOptions);
const initialActionValues = _.cloneDeep(FORMIK_INITIAL_ACTION_VALUES);
- _.set(triggerValues, 'triggerDefinitions[0].actions[0]', initialActionValues);
+ _.set(triggerValues, 'triggerDefinitions[0].actions[0]', {
+ ...initialActionValues,
+ destination_id: selectedOptions[0]?.value,
+ subject_template: {
+ lang: 'mustache',
+ source: 'Monitor {{ctx.monitor.name}} triggered an alert {{ctx.trigger.name}}',
+ },
+ });
+
+ console.log('CHANNEL SELECTED', _.get(triggerValues, 'triggerDefinitions[0].actions[0]', {}));
};
const showConfig = (channels) => setIsModalVisible(true);
From 83d6eb6218c28e7a16a649b9bf458f9a7c108e6f Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 16 Jun 2023 11:52:47 +0200
Subject: [PATCH 04/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../containers/CreateMonitor/CreateMonitor.js | 11 +++-
.../CreateMonitor/utils/formikToMonitor.js | 4 +-
.../ExpressionQuery/ExpressionQuery.js | 1 +
.../CreateTrigger/utils/constants.js | 1 +
.../CreateTrigger/utils/formikToTrigger.js | 1 +
.../DefineCompositeLevelTrigger.js | 4 +-
.../NotificationConfigDialog.js | 62 +++++--------------
.../TriggerNotifications.js | 45 +++++++-------
.../TriggerNotificationsContent.js | 11 ++--
9 files changed, 59 insertions(+), 81 deletions(-)
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index 700f31039..f98a29a17 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -114,7 +114,9 @@ export default class CreateMonitor extends Component {
try {
const resp = await httpClient.post(
`../api/alerting/${
- monitor.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? 'workflows' : 'monitors'
+ monitor.workflow_type && monitor.workflow_type === MONITOR_TYPE.COMPOSITE_LEVEL
+ ? 'workflows'
+ : 'monitors'
}`,
{
body: JSON.stringify(monitor),
@@ -183,6 +185,9 @@ export default class CreateMonitor extends Component {
case MONITOR_TYPE.DOC_LEVEL:
triggerType = TRIGGER_TYPE.DOC_LEVEL;
break;
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ triggerType = TRIGGER_TYPE.COMPOSITE_LEVEL;
+ break;
default:
triggerType = TRIGGER_TYPE.QUERY_LEVEL;
break;
@@ -249,8 +254,8 @@ export default class CreateMonitor extends Component {
monitor = { ...monitor, ...triggers };
}
- console.log('Monitor', monitor);
- console.log('Value', values);
+ if (monitor.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL) delete monitor.monitor_type;
+
if (edit) this.onUpdate(monitor, formikBag);
else this.onCreate(monitor, formikBag);
}
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor.js
index ec0b03e28..6c57cb5a8 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor.js
@@ -48,7 +48,8 @@ export function formikToMonitor(values) {
type: 'workflow',
enabled_time: enabled_time.getTime(),
enabled: !values.disabled,
- workflow_type: 'composite',
+ monitor_type: MONITOR_TYPE.COMPOSITE_LEVEL,
+ workflow_type: MONITOR_TYPE.COMPOSITE_LEVEL,
schema_version: 0,
name: values.name,
schedule,
@@ -56,6 +57,7 @@ export function formikToMonitor(values) {
triggers: [],
ui_metadata: {
schedule: uiSchedule,
+ monitor_type: values.monitor_type,
...monitorUiMetadata(),
},
};
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index 65ac5f761..13180ce09 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -17,6 +17,7 @@ const ExpressionQuery = ({
defaultText,
label,
name = 'expressionQueries',
+ triggerValues,
}) => {
const DEFAULT_DESCRIPTION = defaultText ? defaultText : 'Select';
const OPERATORS = ['AND', 'OR', 'NOT'];
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
index 3af7fc473..a84dea9b1 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
@@ -9,6 +9,7 @@ export const TRIGGER_TYPE = {
ALERT_TRIGGER: 'alerting_trigger',
QUERY_LEVEL: 'query_level_trigger',
DOC_LEVEL: 'document_level_trigger',
+ COMPOSITE_LEVEL: 'composite_level_trigger',
};
export const FORMIK_INITIAL_BUCKET_SELECTOR_VALUES = {
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
index 7d34ad1f2..0ef88c089 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
@@ -293,6 +293,7 @@ export function formikToTriggerUiMetadata(values, monitorUiMetadata) {
bucketLevelTriggersUiMetadata[trigger.name] = triggerMetadata;
});
return bucketLevelTriggersUiMetadata;
+
case MONITOR_TYPE.DOC_LEVEL:
const docLevelTriggersUiMetadata = {};
_.get(values, 'triggerDefinitions', []).forEach((trigger) => {
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index 0f70bb6dc..651084030 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -86,7 +86,7 @@ class DefineCompositeLevelTrigger extends Component {
name: triggerName,
},
]);
-
+ const triggerActions = _.get(triggerValues, 'triggerDefinitions[0].actions', []);
const monitorList = monitorValues?.associatedMonitors
? monitorValues.associatedMonitors?.map((monitor) => ({
label: monitor.label.replaceAll(' ', '_'),
@@ -135,6 +135,7 @@ class DefineCompositeLevelTrigger extends Component {
onChange={() => {}}
dataTestSubj={'composite_expression_query'}
defaultText={'Select associated monitor'}
+ triggerValues={triggerValues}
/>
@@ -156,6 +157,7 @@ class DefineCompositeLevelTrigger extends Component {
notifications={notifications}
notificationService={notificationService}
triggerValues={triggerValues}
+ triggerActions={triggerActions}
/>
);
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
index 375075426..76c08283e 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
@@ -20,8 +20,6 @@ import { getTriggerContext } from '../../utils/helper';
import { formikToMonitor } from '../../../CreateMonitor/containers/CreateMonitor/utils/formikToMonitor';
import _ from 'lodash';
import { formikToTrigger } from '../CreateTrigger/utils/formikToTrigger';
-import { MONITOR_TYPE } from '../../../../utils/constants';
-import { TRIGGER_TYPE } from '../CreateTrigger/utils/constants';
import { backendErrorNotification } from '../../../../utils/helpers';
import { checkForError } from '../ConfigureActions/ConfigureActions';
@@ -31,58 +29,31 @@ const NotificationConfigDialog = ({
triggerValues,
httpClient,
notifications,
+ actionIndex,
}) => {
const triggerIndex = 0;
const monitor = formikToMonitor(triggerValues);
+ delete monitor.monitor_type;
const context = getTriggerContext({}, monitor, triggerValues, 0);
const initialActionValues = _.cloneDeep(FORMIK_INITIAL_ACTION_VALUES);
- let action = _.get(triggerValues, 'triggerDefinitions[0].actions[0]', {
+ let action = _.get(triggerValues, `triggerDefinitions[0].actions[${actionIndex}]`, {
...initialActionValues,
});
+ console.log('Initial actions', action);
const sendTestMessage = async (index) => {
- const flattenedDestinations = [];
- // TODO: For bucket-level triggers, sendTestMessage will only send a test message if there is
- // at least one bucket of data from the monitor input query.
let testTrigger = _.cloneDeep(
formikToTrigger(triggerValues, monitor.ui_metadata)[triggerIndex]
);
- let action;
- let condition;
- switch (monitor.monitor_type) {
- case MONITOR_TYPE.BUCKET_LEVEL:
- action = _.get(testTrigger, `${TRIGGER_TYPE.BUCKET_LEVEL}.actions[${index}]`);
- condition = {
- ..._.get(testTrigger, `${TRIGGER_TYPE.BUCKET_LEVEL}.condition`),
- buckets_path: { _count: '_count' },
- script: {
- source: 'params._count >= 0',
- },
- };
- _.set(testTrigger, `${TRIGGER_TYPE.BUCKET_LEVEL}.actions`, [action]);
- _.set(testTrigger, `${TRIGGER_TYPE.BUCKET_LEVEL}.condition`, condition);
- break;
- case MONITOR_TYPE.DOC_LEVEL:
- action = _.get(testTrigger, `${TRIGGER_TYPE.DOC_LEVEL}.actions[${index}]`);
- condition = {
- ..._.get(testTrigger, `${TRIGGER_TYPE.DOC_LEVEL}.condition`),
- script: { lang: 'painless', source: 'return true' },
- };
- _.set(testTrigger, `${TRIGGER_TYPE.DOC_LEVEL}.actions`, [action]);
- _.set(testTrigger, `${TRIGGER_TYPE.DOC_LEVEL}.condition`, condition);
- break;
- default:
- action = _.get(testTrigger, `actions[${index}]`);
- condition = {
- ..._.get(testTrigger, 'condition'),
- script: { lang: 'painless', source: 'return true' },
- };
- _.set(testTrigger, 'actions', [action]);
- _.set(testTrigger, 'condition', condition);
- break;
- }
+ const action = _.get(testTrigger, `chained_alert_trigger.actions[${index}]`);
+ const condition = {
+ ..._.get(testTrigger, 'chained_alert_trigger.condition'),
+ script: { lang: 'painless', source: 'return true' },
+ };
+ _.set(testTrigger, 'actions', [action]);
+ _.set(testTrigger, 'condition', condition);
const testMonitor = { ...monitor, triggers: [{ ...testTrigger }] };
@@ -94,13 +65,8 @@ const NotificationConfigDialog = ({
let error = null;
if (response.ok) {
error = checkForError(response, error);
- if (!_.isEmpty(action.destination_id)) {
- const destinationName = _.get(
- _.find(flattenedDestinations, { value: action.destination_id }),
- 'label'
- );
- notifications.toasts.addSuccess(`Test message sent to "${destinationName}."`);
- }
+ if (!_.isEmpty(action.destination_id))
+ notifications.toasts.addSuccess(`Test message sent to "${action.name}."`);
}
if (error || !response.ok) {
const errorMessage = error == null ? response.resp : error;
@@ -127,7 +93,7 @@ const NotificationConfigDialog = ({
{
- const [channels, setChannels] = useState([]);
+ const [actions, setActions] = useState([]);
const [options, setOptions] = useState([]);
useEffect(() => {
- let newChannels = [...actions];
- if (_.isEmpty(newChannels))
- newChannels = [
+ let newActions = [...triggerActions];
+ if (_.isEmpty(newActions))
+ newActions = [
{
name: '',
id: '',
},
];
- setChannels(newChannels);
+ setActions(newActions);
getChannels().then((channels) => setOptions(channels));
}, []);
@@ -81,40 +81,40 @@ const TriggerNotifications = ({
};
const onAddNotification = () => {
- const newChannels = [...channels];
- newChannels.push({
+ const newActions = [...actions];
+ newActions.push({
label: '',
value: '',
});
- setChannels(newChannels);
+ setActions(newActions);
};
const onRemoveNotification = (idx) => {
- const newChannels = [...channels];
- newChannels.splice(idx, 1);
- setChannels(newChannels);
+ const newActions = [...actions];
+ newActions.splice(idx, 1);
+ setActions(newActions);
};
return (
{titleTemplate('Notifications')}
- {channels.length &&
- channels.map((channel, idx) => (
+ {actions.length &&
+ actions.map((channel, actionIndex) => (
{`Notification ${idx + 1}`}}
+ title={`Notification ${actionIndex + 1}`}
+ key={`notification-accordion-${actionIndex}`}
+ id={`notification-accordion-${actionIndex}`}
+ initialIsOpen={!actionIndex}
+ buttonContent={{`Notification ${actionIndex + 1}`} }
paddingSize={'s'}
extraAction={
- channels.length > 1 && (
+ actions.length > 1 && (
onRemoveNotification(idx)}
+ onClick={() => onRemoveNotification(actionIndex)}
size={'s'}
/>
)
@@ -123,11 +123,10 @@ const TriggerNotifications = ({
))}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
index a54fee443..e3abb9d29 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
@@ -13,7 +13,7 @@ import { FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
const TriggerNotificationsContent = ({
channel,
options,
- idx,
+ actionIndex,
triggerValues,
httpClient,
notifications,
@@ -23,17 +23,17 @@ const TriggerNotificationsContent = ({
const onChange = (selectedOptions) => {
setSelected(selectedOptions);
+
const initialActionValues = _.cloneDeep(FORMIK_INITIAL_ACTION_VALUES);
- _.set(triggerValues, 'triggerDefinitions[0].actions[0]', {
+ _.set(triggerValues, `triggerDefinitions[0].actions[${actionIndex}]`, {
...initialActionValues,
destination_id: selectedOptions[0]?.value,
+ name: selectedOptions[0]?.label,
subject_template: {
lang: 'mustache',
source: 'Monitor {{ctx.monitor.name}} triggered an alert {{ctx.trigger.name}}',
},
});
-
- console.log('CHANNEL SELECTED', _.get(triggerValues, 'triggerDefinitions[0].actions[0]', {}));
};
const showConfig = (channels) => setIsModalVisible(true);
@@ -49,7 +49,7 @@ const TriggerNotificationsContent = ({
}}
>
)}
From 567db1dc601e249b17e6d0346322f080558ee031 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 16 Jun 2023 12:04:36 +0200
Subject: [PATCH 05/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../components/ExpressionQuery/ExpressionQuery.js | 5 ++---
.../containers/CreateTrigger/utils/formikToTrigger.js | 2 +-
.../DefineCompositeLevelTrigger.js | 4 ++--
.../TriggerNotificationsContent.js | 2 +-
4 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index 13180ce09..97bf47b0d 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -16,8 +16,7 @@ const ExpressionQuery = ({
value,
defaultText,
label,
- name = 'expressionQueries',
- triggerValues,
+ formikName = 'expressionQueries',
}) => {
const DEFAULT_DESCRIPTION = defaultText ? defaultText : 'Select';
const OPERATORS = ['AND', 'OR', 'NOT'];
@@ -79,7 +78,7 @@ const ExpressionQuery = ({
const onBlur = (form, expressions) => {
form.setFieldTouched('expressionQueries', true);
- form.setFieldValue('triggerDefinitions[0].triggerConditions', getValue(expressions));
+ form.setFieldValue(formikName, getValue(expressions));
form.setFieldError('expressionQueries', validate());
};
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
index 0ef88c089..83e09017c 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
@@ -115,7 +115,7 @@ export function formikToDocumentLevelTriggerCondition(values, monitorUiMetadata)
};
}
-export function formikToCompositeTriggerCondition(values, monitorUiMetadata) {
+export function formikToCompositeTriggerCondition(values) {
const conditionMap = {
and: '&&',
or: '||',
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index 651084030..a06b8aa22 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -9,7 +9,7 @@ import _ from 'lodash';
import { EuiSpacer, EuiText } from '@elastic/eui';
import { FormikFieldText, FormikSelect } from '../../../../components/FormControls';
import { hasError, isInvalid } from '../../../../utils/validate';
-import { DEFAULT_TRIGGER_NAME, SEVERITY_OPTIONS } from '../../utils/constants';
+import { SEVERITY_OPTIONS } from '../../utils/constants';
import ExpressionQuery from '../../components/ExpressionQuery/ExpressionQuery';
import TriggerNotifications from './TriggerNotifications';
import ContentPanel from '../../../../components/ContentPanel';
@@ -123,7 +123,7 @@ class DefineCompositeLevelTrigger extends Component {
setIsModalVisible(true);
+ const showConfig = () => setIsModalVisible(true);
return (
From f6b39fefef4b60c690277bbde6eb141c743fbd46 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Sun, 18 Jun 2023 20:31:08 +0200
Subject: [PATCH 06/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/AssociateMonitors.tsx | 4 +--
.../components/MonitorsList.tsx | 12 ++++---
.../containers/CreateMonitor/CreateMonitor.js | 1 +
.../CreateMonitor/utils/monitorToFormik.js | 4 +++
.../WorkflowDetails/WorkflowDetails.tsx | 14 ++++++--
.../ExpressionQuery/ExpressionQuery.js | 1 +
.../CreateTrigger/utils/constants.js | 2 +-
.../CreateTrigger/utils/triggerToFormik.js | 26 ++++++++++++++
.../DefineCompositeLevelTrigger.js | 1 -
.../NotificationConfigDialog.js | 33 +++++++++++++-----
.../TriggerNotifications.js | 2 +-
.../TriggerNotificationsContent.js | 4 +++
.../containers/MonitorDetails.js | 9 ++---
.../containers/Triggers/Triggers.js | 2 ++
server/clusters/alerting/alertingPlugin.js | 14 ++++++++
server/routes/monitors.js | 12 +++++++
server/services/MonitorService.js | 34 +++++++++++++++++++
server/services/utils/constants.js | 1 +
18 files changed, 151 insertions(+), 25 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
index 9bac7683d..3b96a01aa 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
@@ -7,7 +7,7 @@ import React, { Fragment } from 'react';
import { EuiSpacer, EuiText } from '@elastic/eui';
import MonitorsList from './components/MonitorsList';
-const AssociateMonitors = ({ monitors, options, history }) => {
+const AssociateMonitors = ({ monitors, options, history, values }) => {
const onUpdate = () => {};
return (
@@ -21,7 +21,7 @@ const AssociateMonitors = ({ monitors, options, history }) => {
-
+
);
};
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
index e636cddd6..830212845 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
@@ -20,9 +20,10 @@ import {
FormikFormRow,
} from '../../../../../components/FormControls';
-const MonitorsList = ({ monitors = [], options = [] }) => {
+const MonitorsList = ({ monitors = [], options = [], values }) => {
const [selectedOptions, setSelectedOptions] = useState({});
const [monitorOptions, setMonitorOptions] = useState([]);
+
const [monitorFields, setMonitorFields] = useState(
_.reduce(
monitors.length ? monitors : [0, 1],
@@ -41,9 +42,11 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
}));
setMonitorOptions(newOptions);
- let newSelected = monitors.length ? monitors : [];
- setSelectedOptions(Object.assign({}, newSelected));
- }, []);
+ // let newSelected = monitors.length ? monitors : [];
+ // setSelectedOptions(Object.assign({}, newSelected));
+
+ // _.set(values, 'associatedMonitors', Object.values(newSelected));
+ }, [monitors, options, values]);
const onChange = (options, monitorIdx, form) => {
let newSelected = {
@@ -72,7 +75,6 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
const onBlur = (monitorIdx, form) => {
form.setFieldTouched('associatedMonitors', true);
form.setFieldTouched(`associatedMonitor_${monitorIdx}`, true);
-
form.setFieldValue('associatedMonitors', Object.values(selectedOptions));
form.setFieldError('associatedMonitors', validate());
};
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index f98a29a17..0da64435b 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -306,6 +306,7 @@ export default class CreateMonitor extends Component {
{
+const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values }) => {
const [selectedMonitors, setSelectedMonitors] = useState([]);
const [monitorOptions, setMonitorOptions] = useState([]);
@@ -17,7 +17,7 @@ const WorkflowDetails = ({ isAd, isComposite, httpClient, history }) => {
const response = await httpClient.get('../api/alerting/monitors', {
query: {
from: 0,
- size: 5000,
+ size: 1000,
search: '',
sortField: 'name',
sortDirection: 'desc',
@@ -37,8 +37,15 @@ const WorkflowDetails = ({ isAd, isComposite, httpClient, history }) => {
useEffect(() => {
getMonitors().then((monitors) => {
setMonitorOptions(monitors);
+
+ // const getMonitorById = (id) => monitors.find((mon) => mon.monitor_id === id);
+ // const newSelectedMonitors = values.inputs.map((monitor) => ({
+ // value: monitor.monitor_id,
+ // label: getMonitorById(monitor.monitor_id)?.monitor_name,
+ // }));
+ // setSelectedMonitors(newSelectedMonitors);
});
- }, []);
+ }, [values]);
return (
{
monitors={selectedMonitors}
options={monitorOptions}
history={history}
+ values={values}
/>
)}
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index 97bf47b0d..39e06ed53 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -23,6 +23,7 @@ const ExpressionQuery = ({
const [usedExpressions, setUsedExpressions] = useState([]);
useEffect(() => {
+ debugger;
let expressions = [];
if (value?.length) {
let values = [...value];
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
index a84dea9b1..7a4ee9f61 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
@@ -9,7 +9,7 @@ export const TRIGGER_TYPE = {
ALERT_TRIGGER: 'alerting_trigger',
QUERY_LEVEL: 'query_level_trigger',
DOC_LEVEL: 'document_level_trigger',
- COMPOSITE_LEVEL: 'composite_level_trigger',
+ COMPOSITE_LEVEL: 'chained_alert_trigger',
};
export const FORMIK_INITIAL_BUCKET_SELECTOR_VALUES = {
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
index 87afacc19..06ca721ae 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
@@ -35,6 +35,8 @@ export function triggerDefinitionToFormik(trigger, monitor) {
return bucketLevelTriggerToFormik(trigger, monitor);
case MONITOR_TYPE.DOC_LEVEL:
return documentLevelTriggerToFormik(trigger, monitor);
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return compositeTriggerToFormik(trigger, monitor);
default:
return queryLevelTriggerToFormik(trigger, monitor);
}
@@ -216,6 +218,30 @@ export function documentLevelTriggerToFormik(trigger, monitor) {
};
}
+export function compositeTriggerToFormik(trigger, monitor) {
+ const {
+ id,
+ name,
+ severity,
+ condition: { script },
+ actions,
+ minTimeBetweenExecutions,
+ rollingWindowSize,
+ } = trigger[TRIGGER_TYPE.COMPOSITE_LEVEL];
+ const triggerUiMetadata = _.get(monitor, `ui_metadata.triggers[${name}]`);
+ return {
+ ..._.cloneDeep(FORMIK_INITIAL_TRIGGER_VALUES),
+ id,
+ name,
+ severity,
+ script,
+ actions: getExecutionPolicyActions(actions),
+ minTimeBetweenExecutions,
+ rollingWindowSize,
+ triggerConditions: triggerUiMetadata,
+ };
+}
+
export function getExecutionPolicyActions(actions) {
const executionPolicyPath = 'action_execution_policy.action_execution_scope';
return _.cloneDeep(actions).map((action) => {
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index a06b8aa22..0733aaae0 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -93,7 +93,6 @@ class DefineCompositeLevelTrigger extends Component {
monitor_id: monitor.value,
}))
: [];
-
return (
{
- let testTrigger = _.cloneDeep(
- formikToTrigger(triggerValues, monitor.ui_metadata)[triggerIndex]
- );
+ const mon = _.cloneDeep(monitor);
+ const tv = _.cloneDeep(triggerValues);
+ let testTrigger = _.cloneDeep(formikToTrigger(tv, mon.ui_metadata)[triggerIndex]);
+ testTrigger = {
+ ...testTrigger,
+ name: _.get(tv, 'triggerDefinitions[0].name', ''),
+ severity: _.get(tv, 'triggerDefinitions[0].severity', ''),
+ };
const action = _.get(testTrigger, `chained_alert_trigger.actions[${index}]`);
const condition = {
..._.get(testTrigger, 'chained_alert_trigger.condition'),
script: { lang: 'painless', source: 'return true' },
};
- _.set(testTrigger, 'actions', [action]);
- _.set(testTrigger, 'condition', condition);
- const testMonitor = { ...monitor, triggers: [{ ...testTrigger }] };
+ let triggers = _.cloneDeep(testTrigger);
+
+ delete triggers.chained_alert_trigger;
+ delete triggers.min_time_between_executions;
+ delete triggers.rolling_window_size;
+
+ _.set(triggers, 'actions', [action]);
+ _.set(triggers, 'condition', condition);
+
+ const testMonitor = { ...monitor, triggers: [{ ...triggers }] };
+
+ // clean up actions and triggers
+ delete testMonitor.enabled_time;
+ delete testMonitor.last_update_time;
+ delete testMonitor.schema_version;
+ delete testMonitor.ui_metadata.composite_input;
+ delete testMonitor.ui_metadata.monitor_type;
try {
const response = await httpClient.post('../api/alerting/monitors/_execute', {
@@ -78,7 +96,6 @@ const NotificationConfigDialog = ({
}
};
- console.log('ACTION', action);
return (
closeModal()}>
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
index cbce16083..8eaeeeb7a 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
@@ -40,7 +40,7 @@ const TriggerNotifications = ({
setActions(newActions);
getChannels().then((channels) => setOptions(channels));
- }, []);
+ }, [triggerValues]);
const getChannels = async () => {
const hasNotificationPlugin = plugins.indexOf(OS_NOTIFICATION_PLUGIN) !== -1;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
index c448a673a..f82574f9a 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
@@ -9,6 +9,7 @@ import { FormikComboBox } from '../../../../components/FormControls';
import NotificationConfigDialog from './NotificationConfigDialog';
import _ from 'lodash';
import { FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
+import { NOTIFY_OPTIONS_VALUES } from '../../components/Action/actions/Message';
const TriggerNotificationsContent = ({
channel,
@@ -33,6 +34,9 @@ const TriggerNotificationsContent = ({
lang: 'mustache',
source: 'Monitor {{ctx.monitor.name}} triggered an alert {{ctx.trigger.name}}',
},
+ action_execution_policy: {
+ action_execution_scope: NOTIFY_OPTIONS_VALUES.PER_ALERT,
+ },
});
};
diff --git a/public/pages/MonitorDetails/containers/MonitorDetails.js b/public/pages/MonitorDetails/containers/MonitorDetails.js
index 1a461e8e0..bf216009a 100644
--- a/public/pages/MonitorDetails/containers/MonitorDetails.js
+++ b/public/pages/MonitorDetails/containers/MonitorDetails.js
@@ -72,6 +72,8 @@ export default class MonitorDetails extends Component {
};
}
+ isWorkflow = () => new URLSearchParams(this.props.location.search).get('type') === 'workflow';
+
componentDidMount() {
this.getMonitor(this.props.match.params.monitorId);
}
@@ -112,7 +114,7 @@ export default class MonitorDetails extends Component {
getMonitor = (id) => {
const { httpClient } = this.props;
httpClient
- .get(`../api/alerting/monitors/${id}`)
+ .get(`../api/alerting/${this.isWorkflow() ? 'workflows' : 'monitors'}/${id}`)
.then((resp) => {
const {
ok,
@@ -450,9 +452,8 @@ export default class MonitorDetails extends Component {
{this.renderTableTabs()}
{this.state.tabContent}
- ) : (
- this.renderAlertsTable()
- )}
+ ) : // this.renderAlertsTable()
+ null}
{isJsonModalOpen && (
diff --git a/public/pages/MonitorDetails/containers/Triggers/Triggers.js b/public/pages/MonitorDetails/containers/Triggers/Triggers.js
index 8a12a5373..63838636f 100644
--- a/public/pages/MonitorDetails/containers/Triggers/Triggers.js
+++ b/public/pages/MonitorDetails/containers/Triggers/Triggers.js
@@ -24,6 +24,8 @@ export function getUnwrappedTriggers(monitor) {
return trigger[TRIGGER_TYPE.BUCKET_LEVEL];
case MONITOR_TYPE.DOC_LEVEL:
return trigger[TRIGGER_TYPE.DOC_LEVEL];
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return trigger[TRIGGER_TYPE.COMPOSITE_LEVEL];
default:
return trigger[TRIGGER_TYPE.QUERY_LEVEL];
}
diff --git a/server/clusters/alerting/alertingPlugin.js b/server/clusters/alerting/alertingPlugin.js
index 46cde5562..aa3b5c317 100644
--- a/server/clusters/alerting/alertingPlugin.js
+++ b/server/clusters/alerting/alertingPlugin.js
@@ -9,6 +9,7 @@ import {
DESTINATION_BASE_API,
EMAIL_ACCOUNT_BASE_API,
EMAIL_GROUP_BASE_API,
+ WORKFLOW_BASE_API,
} from '../../services/utils/constants';
export default function alertingPlugin(Client, config, components) {
@@ -25,6 +26,19 @@ export default function alertingPlugin(Client, config, components) {
method: 'GET',
});
+ alerting.getWorkflow = ca({
+ url: {
+ fmt: `${API_ROUTE_PREFIX}/workflows/<%=monitorId%>`,
+ req: {
+ monitorId: {
+ type: 'string',
+ required: true,
+ },
+ },
+ },
+ method: 'GET',
+ });
+
alerting.getMonitor = ca({
url: {
fmt: `${MONITOR_BASE_API}/<%=monitorId%>`,
diff --git a/server/routes/monitors.js b/server/routes/monitors.js
index a447327df..f8a21a9c2 100644
--- a/server/routes/monitors.js
+++ b/server/routes/monitors.js
@@ -68,6 +68,18 @@ export default function (services, router) {
monitorService.executeMonitor
);
+ router.get(
+ {
+ path: '/api/alerting/workflows/{id}',
+ validate: {
+ params: schema.object({
+ id: schema.string(),
+ }),
+ },
+ },
+ monitorService.getWorkflow
+ );
+
router.get(
{
path: '/api/alerting/monitors/{id}',
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 0ed7554ac..08c1ce2cb 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -146,6 +146,40 @@ export default class MonitorService {
}
};
+ getWorkflow = async (context, req, res) => {
+ try {
+ const { id } = req.params;
+ const params = { monitorId: id };
+ const { callAsCurrentUser } = await this.esDriver.asScoped(req);
+ const getResponse = await callAsCurrentUser('alerting.getWorkflow', params);
+ const monitor = _.get(getResponse, 'workflow', null);
+ const version = _.get(getResponse, '_version', null);
+ const ifSeqNo = _.get(getResponse, '_seq_no', null);
+ const ifPrimaryTerm = _.get(getResponse, '_primary_term', null);
+ monitor.monitor_type = monitor.workflow_type;
+
+ return res.ok({
+ body: {
+ ok: true,
+ resp: monitor,
+ activeCount: 0,
+ dayCount: 0,
+ version,
+ ifSeqNo,
+ ifPrimaryTerm,
+ },
+ });
+ } catch (err) {
+ console.error('Alerting - MonitorService - getMonitor:', err);
+ return res.ok({
+ body: {
+ ok: false,
+ resp: err.message,
+ },
+ });
+ }
+ };
+
updateMonitor = async (context, req, res) => {
try {
const { id } = req.params;
diff --git a/server/services/utils/constants.js b/server/services/utils/constants.js
index d43b8fe8a..c8ba9897a 100644
--- a/server/services/utils/constants.js
+++ b/server/services/utils/constants.js
@@ -5,6 +5,7 @@
export const API_ROUTE_PREFIX = '/_plugins/_alerting';
export const MONITOR_BASE_API = `${API_ROUTE_PREFIX}/monitors`;
+export const WORKFLOW_BASE_API = `${API_ROUTE_PREFIX}/workflows`;
export const AD_BASE_API = `/_plugins/_anomaly_detection/detectors`;
export const DESTINATION_BASE_API = `${API_ROUTE_PREFIX}/destinations`;
export const EMAIL_ACCOUNT_BASE_API = `${DESTINATION_BASE_API}/email_accounts`;
From 2201c0e3ca70fed18ceed5029a0ca078a56c8c9f Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Mon, 19 Jun 2023 15:27:31 +0200
Subject: [PATCH 07/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../components/MonitorsList.tsx | 9 ++-
.../ExpressionQuery/ExpressionQuery.js | 8 +-
.../CreateTrigger/utils/triggerToFormik.js | 4 -
.../NotificationConfigDialog.js | 74 ++++++++++++-------
.../TriggerNotifications.js | 1 +
.../containers/Monitors/utils/tableUtils.js | 5 +-
server/services/MonitorService.js | 2 -
7 files changed, 62 insertions(+), 41 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
index 830212845..4403171ca 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
@@ -61,7 +61,7 @@ const MonitorsList = ({ monitors = [], options = [], values }) => {
updateMonitorOptions(newSelected);
- onBlur(monitorIdx, form);
+ updateFormik(monitorIdx, form);
};
const updateMonitorOptions = (selected) => {
@@ -72,7 +72,7 @@ const MonitorsList = ({ monitors = [], options = [], values }) => {
setMonitorOptions([...newMonitorOptions]);
};
- const onBlur = (monitorIdx, form) => {
+ const updateFormik = (monitorIdx, form) => {
form.setFieldTouched('associatedMonitors', true);
form.setFieldTouched(`associatedMonitor_${monitorIdx}`, true);
form.setFieldValue('associatedMonitors', Object.values(selectedOptions));
@@ -109,10 +109,11 @@ const MonitorsList = ({ monitors = [], options = [], values }) => {
updateMonitorOptions(newSelected);
- onBlur(monitorIdx, form);
+ updateFormik(monitorIdx, form);
};
const isValid = () => Object.keys(selectedOptions).length > 1;
+
const validate = () => {
if (!isValid()) return 'Required.';
};
@@ -149,7 +150,7 @@ const MonitorsList = ({ monitors = [], options = [], values }) => {
!selectedOptions[monitorIdx],
placeholder: 'Select a monitor',
onChange: (options, field, form) => onChange(options, monitorIdx, form),
- onBlur: (e, field, form) => onBlur(monitorIdx, form),
+ onBlur: (e, field, form) => updateFormik(monitorIdx, form),
options: monitorOptions,
singleSelection: { asPlainText: true },
selectedOptions: selectedOptions[monitorIdx]
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index 39e06ed53..c4c5a1703 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -23,7 +23,6 @@ const ExpressionQuery = ({
const [usedExpressions, setUsedExpressions] = useState([]);
useEffect(() => {
- debugger;
let expressions = [];
if (value?.length) {
let values = [...value];
@@ -164,10 +163,13 @@ const ExpressionQuery = ({
/>
);
- const isValid = () => usedExpressions.length > 1;
+ const isValid = () => selections.length > 1 && usedExpressions.length > 1;
const validate = () => {
- if (!isValid()) return 'At least two monitors should be selected.';
+ if (selections.length < 2)
+ return 'Trigger condition requires at least two associated monitors.';
+ if (usedExpressions.length < 2)
+ return 'Trigger condition requires at least two monitors selected.';
};
return (
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
index 06ca721ae..69171ddaf 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
@@ -225,8 +225,6 @@ export function compositeTriggerToFormik(trigger, monitor) {
severity,
condition: { script },
actions,
- minTimeBetweenExecutions,
- rollingWindowSize,
} = trigger[TRIGGER_TYPE.COMPOSITE_LEVEL];
const triggerUiMetadata = _.get(monitor, `ui_metadata.triggers[${name}]`);
return {
@@ -236,8 +234,6 @@ export function compositeTriggerToFormik(trigger, monitor) {
severity,
script,
actions: getExecutionPolicyActions(actions),
- minTimeBetweenExecutions,
- rollingWindowSize,
triggerConditions: triggerUiMetadata,
};
}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
index dcbd7b812..b994220c7 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import {
EuiButton,
EuiSpacer,
@@ -22,6 +22,7 @@ import _ from 'lodash';
import { formikToTrigger } from '../CreateTrigger/utils/formikToTrigger';
import { backendErrorNotification } from '../../../../utils/helpers';
import { checkForError } from '../ConfigureActions/ConfigureActions';
+import { TRIGGER_TYPE } from '../CreateTrigger/utils/constants';
const NotificationConfigDialog = ({
channel,
@@ -41,39 +42,53 @@ const NotificationConfigDialog = ({
...initialActionValues,
});
+ const fieldPath = 'triggerDefinitions[0]';
+ const [initialValues, setInitialValues] = useState({});
+
+ useEffect(() => {
+ setInitialValues({
+ subject_source: _.get(
+ triggerValues,
+ `${fieldPath}actions.${actionIndex}.subject_template.source`,
+ ''
+ ),
+ message_source: _.get(
+ triggerValues,
+ `${fieldPath}actions.${actionIndex}.message_template.source`,
+ ''
+ ),
+ throttle_enabled: _.get(
+ triggerValues,
+ `${fieldPath}actions.${actionIndex}.throttle_enabled`,
+ ''
+ ),
+ throttle_value: _.get(triggerValues, `${fieldPath}actions.${actionIndex}.throttle.value`, ''),
+ });
+ }, []);
+
const sendTestMessage = async (index) => {
- const mon = _.cloneDeep(monitor);
- const tv = _.cloneDeep(triggerValues);
- let testTrigger = _.cloneDeep(formikToTrigger(tv, mon.ui_metadata)[triggerIndex]);
+ const monitorData = _.cloneDeep(monitor);
+ let testTrigger = _.cloneDeep(
+ formikToTrigger(triggerValues, monitorData.ui_metadata)[triggerIndex]
+ );
testTrigger = {
...testTrigger,
- name: _.get(tv, 'triggerDefinitions[0].name', ''),
- severity: _.get(tv, 'triggerDefinitions[0].severity', ''),
+ name: _.get(triggerValues, 'triggerDefinitions[0].name', ''),
+ severity: _.get(triggerValues, 'triggerDefinitions[0].severity', ''),
};
- const action = _.get(testTrigger, `chained_alert_trigger.actions[${index}]`);
+ const action = _.get(testTrigger, `${TRIGGER_TYPE.COMPOSITE_LEVEL}.actions[${index}]`);
const condition = {
- ..._.get(testTrigger, 'chained_alert_trigger.condition'),
+ ..._.get(testTrigger, `${TRIGGER_TYPE.COMPOSITE_LEVEL}.condition`),
script: { lang: 'painless', source: 'return true' },
};
- let triggers = _.cloneDeep(testTrigger);
-
- delete triggers.chained_alert_trigger;
- delete triggers.min_time_between_executions;
- delete triggers.rolling_window_size;
+ delete testTrigger[TRIGGER_TYPE.COMPOSITE_LEVEL];
- _.set(triggers, 'actions', [action]);
- _.set(triggers, 'condition', condition);
+ _.set(testTrigger, 'actions', [action]);
+ _.set(testTrigger, 'condition', condition);
- const testMonitor = { ...monitor, triggers: [{ ...triggers }] };
-
- // clean up actions and triggers
- delete testMonitor.enabled_time;
- delete testMonitor.last_update_time;
- delete testMonitor.schema_version;
- delete testMonitor.ui_metadata.composite_input;
- delete testMonitor.ui_metadata.monitor_type;
+ const testMonitor = { ...monitor, triggers: [{ ...testTrigger }] };
try {
const response = await httpClient.post('../api/alerting/monitors/_execute', {
@@ -96,8 +111,17 @@ const NotificationConfigDialog = ({
}
};
+ const clearConfig = () => {
+ _.set(
+ triggerValues,
+ `${fieldPath}actions.${actionIndex}.subject_template.source`,
+ initialValues.subject_source
+ );
+ closeModal();
+ };
+
return (
- closeModal()}>
+ clearConfig()}>
Configure notification
@@ -120,7 +144,7 @@ const NotificationConfigDialog = ({
/>
- closeModal()}>Close
+ clearConfig()}>Close
closeModal()} fill>
Update
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
index 8eaeeeb7a..da0ebba04 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
@@ -92,6 +92,7 @@ const TriggerNotifications = ({
const onRemoveNotification = (idx) => {
const newActions = [...actions];
newActions.splice(idx, 1);
+ _.set(triggerValues, 'triggerDefinitions[0].actions', newActions);
setActions(newActions);
};
diff --git a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
index 4aab897e9..60201bd1f 100644
--- a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
+++ b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
@@ -22,7 +22,7 @@ export const columns = [
sortable: true,
textOnly: true,
render: (name, item) => (
-
+
{name}
),
@@ -39,8 +39,7 @@ export const columns = [
2. Monitors are created when security plugin is disabled, these will have empty User object.
(`monitor.user.name`, `monitor.user.roles` are empty )
3. Monitors are created when security plugin is enabled, these will have an User object. */
- render: (_, item) =>
- item.monitor.user && item.monitor.user.name ? item.monitor.user.name : '-',
+ render: (_, item) => (item.user && item.user.name ? item.user.name : '-'),
},
{
field: 'latestAlert',
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 08c1ce2cb..0e1512509 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -231,7 +231,6 @@ export default class MonitorService {
};
}
- const filter = [{ term: { 'monitor.type': 'monitor' } }];
if (state !== 'all') {
const enabled = state === 'enabled';
filter.push({ term: { 'monitor.enabled': enabled } });
@@ -252,7 +251,6 @@ export default class MonitorService {
...monitorSortPageData,
query: {
bool: {
- filter,
must,
},
},
From 654a5f10dea751dbee4db65ae18b1bffee47b315 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Mon, 19 Jun 2023 15:32:49 +0200
Subject: [PATCH 08/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../NotificationConfigDialog.js | 21 +++----------------
1 file changed, 3 insertions(+), 18 deletions(-)
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
index b994220c7..42468477a 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
@@ -47,22 +47,7 @@ const NotificationConfigDialog = ({
useEffect(() => {
setInitialValues({
- subject_source: _.get(
- triggerValues,
- `${fieldPath}actions.${actionIndex}.subject_template.source`,
- ''
- ),
- message_source: _.get(
- triggerValues,
- `${fieldPath}actions.${actionIndex}.message_template.source`,
- ''
- ),
- throttle_enabled: _.get(
- triggerValues,
- `${fieldPath}actions.${actionIndex}.throttle_enabled`,
- ''
- ),
- throttle_value: _.get(triggerValues, `${fieldPath}actions.${actionIndex}.throttle.value`, ''),
+ [`action${actionIndex}`]: _.get(triggerValues, `${fieldPath}actions.${actionIndex}`, ''),
});
}, []);
@@ -114,8 +99,8 @@ const NotificationConfigDialog = ({
const clearConfig = () => {
_.set(
triggerValues,
- `${fieldPath}actions.${actionIndex}.subject_template.source`,
- initialValues.subject_source
+ `${fieldPath}actions.${actionIndex}`,
+ initialValues[`action${actionIndex}`]
);
closeModal();
};
From 39b4d0d5c28bb55eea5c601a076edb03a4b46cd6 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Mon, 19 Jun 2023 15:57:24 +0200
Subject: [PATCH 09/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../components/AssociateMonitors/components/MonitorsList.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
index 4403171ca..b9df3cf81 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
@@ -69,6 +69,7 @@ const MonitorsList = ({ monitors = [], options = [], values }) => {
newMonitorOptions.forEach((mon) => {
mon.disabled = isSelected(selected, mon);
});
+
setMonitorOptions([...newMonitorOptions]);
};
@@ -83,7 +84,7 @@ const MonitorsList = ({ monitors = [], options = [], values }) => {
let isSelected = false;
for (const key in selected) {
if (selected.hasOwnProperty(key)) {
- if (_.isEqual(selected[key], monitor)) {
+ if (selected[key].value === monitor.value) {
isSelected = true;
break;
}
From be4b4bfe1e3f889afd88ecb1c65ee1e1dbe525b8 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Mon, 19 Jun 2023 18:05:39 +0200
Subject: [PATCH 10/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/AssociateMonitors.tsx | 4 +--
.../components/MonitorsList.tsx | 15 ++++++----
.../WorkflowDetails/WorkflowDetails.tsx | 29 ++++++++++---------
.../DefineCompositeLevelTrigger.js | 1 +
4 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
index 3b96a01aa..ede80d1d2 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
@@ -7,7 +7,7 @@ import React, { Fragment } from 'react';
import { EuiSpacer, EuiText } from '@elastic/eui';
import MonitorsList from './components/MonitorsList';
-const AssociateMonitors = ({ monitors, options, history, values }) => {
+const AssociateMonitors = ({ monitors, options }) => {
const onUpdate = () => {};
return (
@@ -21,7 +21,7 @@ const AssociateMonitors = ({ monitors, options, history, values }) => {
-
+
);
};
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
index b9df3cf81..fd2bed0fb 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
@@ -20,7 +20,7 @@ import {
FormikFormRow,
} from '../../../../../components/FormControls';
-const MonitorsList = ({ monitors = [], options = [], values }) => {
+const MonitorsList = ({ monitors = [], options = [] }) => {
const [selectedOptions, setSelectedOptions] = useState({});
const [monitorOptions, setMonitorOptions] = useState([]);
@@ -42,11 +42,14 @@ const MonitorsList = ({ monitors = [], options = [], values }) => {
}));
setMonitorOptions(newOptions);
- // let newSelected = monitors.length ? monitors : [];
- // setSelectedOptions(Object.assign({}, newSelected));
-
- // _.set(values, 'associatedMonitors', Object.values(newSelected));
- }, [monitors, options, values]);
+ let newSelected = monitors.length
+ ? monitors.map((monitor) => ({
+ label: monitor.monitor_name,
+ value: monitor.monitor_id,
+ }))
+ : [];
+ setSelectedOptions(Object.assign({}, newSelected));
+ }, [monitors, options]);
const onChange = (options, monitorIdx, form) => {
let newSelected = {
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
index b294c466c..ac7b69a97 100644
--- a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
@@ -8,6 +8,7 @@ import ContentPanel from '../../../../components/ContentPanel';
import Schedule from '../../components/Schedule';
import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonitors';
import { EuiSpacer } from '@elastic/eui';
+import * as _ from 'lodash';
const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values }) => {
const [selectedMonitors, setSelectedMonitors] = useState([]);
@@ -38,14 +39,21 @@ const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values }) =>
getMonitors().then((monitors) => {
setMonitorOptions(monitors);
- // const getMonitorById = (id) => monitors.find((mon) => mon.monitor_id === id);
- // const newSelectedMonitors = values.inputs.map((monitor) => ({
- // value: monitor.monitor_id,
- // label: getMonitorById(monitor.monitor_id)?.monitor_name,
- // }));
- // setSelectedMonitors(newSelectedMonitors);
+ const inputIds = values.inputs?.map((input) => input.monitor_id);
+ if (inputIds && inputIds.length) {
+ const selected = monitors.filter((monitor) => inputIds.indexOf(monitor.monitor_id) !== -1);
+ setSelectedMonitors(selected);
+ _.set(
+ values,
+ 'associatedMonitors',
+ selected.map((monitor) => ({
+ value: monitor.monitor_id,
+ label: monitor.monitor_name,
+ }))
+ );
+ }
});
- }, [values]);
+ }, [values.inputs]);
return (
{isComposite && (
-
+
)}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index 0733aaae0..37129cbc3 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -75,6 +75,7 @@ class DefineCompositeLevelTrigger extends Component {
notificationService,
plugins,
} = this.props;
+
const fieldPath = `triggerDefinitions[0].`;
const triggerName = _.get(triggerValues, `${fieldPath}name`, 'Trigger');
const triggerDefinitions = _.get(triggerValues, 'triggerDefinitions', []);
From 1792d4cf0adab2367798100c2e15f9fafe01e4f9 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Mon, 19 Jun 2023 19:27:12 +0200
Subject: [PATCH 11/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../NotificationConfigDialog.js | 1 -
.../TriggerNotifications.js | 7 ++++---
.../TriggerNotificationsContent.js | 17 +++++++++++++----
.../MonitorDetails/containers/MonitorDetails.js | 15 +++++++++++----
server/clusters/alerting/alertingPlugin.js | 15 +++++++++++++++
server/routes/monitors.js | 17 +++++++++++++++++
server/services/MonitorService.js | 6 +++++-
7 files changed, 65 insertions(+), 13 deletions(-)
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
index 42468477a..eef6535e7 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
@@ -25,7 +25,6 @@ import { checkForError } from '../ConfigureActions/ConfigureActions';
import { TRIGGER_TYPE } from '../CreateTrigger/utils/constants';
const NotificationConfigDialog = ({
- channel,
closeModal,
triggerValues,
httpClient,
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
index da0ebba04..bd5389fbe 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
@@ -37,10 +37,11 @@ const TriggerNotifications = ({
id: '',
},
];
+
setActions(newActions);
getChannels().then((channels) => setOptions(channels));
- }, [triggerValues]);
+ }, [triggerValues, plugins]);
const getChannels = async () => {
const hasNotificationPlugin = plugins.indexOf(OS_NOTIFICATION_PLUGIN) !== -1;
@@ -101,7 +102,7 @@ const TriggerNotifications = ({
{titleTemplate('Notifications')}
{actions.length &&
- actions.map((channel, actionIndex) => (
+ actions.map((action, actionIndex) => (
{
+ setSelected([
+ {
+ label: action.name,
+ value: action.config_id,
+ type: action.config_type,
+ description: action.description,
+ },
+ ]);
+ }, [action]);
+
const onChange = (selectedOptions) => {
setSelected(selectedOptions);
@@ -64,7 +75,6 @@ const TriggerNotificationsContent = ({
}}
inputProps={{
isInvalid: !selected.length,
- value: channel,
placeholder: 'Select a channel to get notified',
options: options,
selectedOptions: selected,
@@ -91,7 +101,6 @@ const TriggerNotificationsContent = ({
{isModalVisible && (
setIsModalVisible(false)}
- channel={channel}
triggerValues={triggerValues}
httpClient={httpClient}
notifications={notifications}
diff --git a/public/pages/MonitorDetails/containers/MonitorDetails.js b/public/pages/MonitorDetails/containers/MonitorDetails.js
index bf216009a..babe7e62d 100644
--- a/public/pages/MonitorDetails/containers/MonitorDetails.js
+++ b/public/pages/MonitorDetails/containers/MonitorDetails.js
@@ -170,10 +170,17 @@ export default class MonitorDetails extends Component {
this.setState({ updating: true });
return httpClient
- .put(`../api/alerting/monitors/${monitorId}`, {
- query: { ...query },
- body: JSON.stringify({ ...monitor, ...update }),
- })
+ .put(
+ `../api/alerting/${
+ monitor.workflow_type && monitor.workflow_type === MONITOR_TYPE.COMPOSITE_LEVEL
+ ? 'workflows'
+ : 'monitors'
+ }/${monitorId}`,
+ {
+ query: { ...query },
+ body: JSON.stringify({ ...monitor, ...update }),
+ }
+ )
.then((resp) => {
if (resp.ok) {
const { version: monitorVersion } = resp;
diff --git a/server/clusters/alerting/alertingPlugin.js b/server/clusters/alerting/alertingPlugin.js
index aa3b5c317..743705bb3 100644
--- a/server/clusters/alerting/alertingPlugin.js
+++ b/server/clusters/alerting/alertingPlugin.js
@@ -96,6 +96,21 @@ export default function alertingPlugin(Client, config, components) {
method: 'PUT',
});
+ // TODO DRAFT: May need to add 'refresh' assignment here again.
+ alerting.updateWorkflow = ca({
+ url: {
+ fmt: `${API_ROUTE_PREFIX}/workflows/<%=monitorId%>`,
+ req: {
+ monitorId: {
+ type: 'string',
+ required: true,
+ },
+ },
+ },
+ needBody: true,
+ method: 'PUT',
+ });
+
alerting.getMonitors = ca({
url: {
fmt: `${MONITOR_BASE_API}/_search`,
diff --git a/server/routes/monitors.js b/server/routes/monitors.js
index f8a21a9c2..4d46437bc 100644
--- a/server/routes/monitors.js
+++ b/server/routes/monitors.js
@@ -109,6 +109,23 @@ export default function (services, router) {
monitorService.updateMonitor
);
+ router.put(
+ {
+ path: '/api/alerting/workflows/{id}',
+ validate: {
+ params: schema.object({
+ id: schema.string(),
+ }),
+ query: schema.object({
+ ifSeqNo: schema.maybe(schema.number()),
+ ifPrimaryTerm: schema.maybe(schema.number()),
+ }),
+ body: schema.any(),
+ },
+ },
+ monitorService.updateMonitor
+ );
+
router.delete(
{
path: '/api/alerting/monitors/{id}',
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 0e1512509..8e4cec408 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -184,6 +184,7 @@ export default class MonitorService {
try {
const { id } = req.params;
const params = { monitorId: id, body: req.body, refresh: 'wait_for' };
+ const { type } = req.body;
// TODO DRAFT: Are we sure we need to include ifSeqNo and ifPrimaryTerm from the UI side when updating monitors?
const { ifSeqNo, ifPrimaryTerm } = req.query;
@@ -193,7 +194,10 @@ export default class MonitorService {
}
const { callAsCurrentUser } = await this.esDriver.asScoped(req);
- const updateResponse = await callAsCurrentUser('alerting.updateMonitor', params);
+ const updateResponse = await callAsCurrentUser(
+ `alerting.${type === 'workflow' ? 'updateWorkflow' : 'updateMonitor'}`,
+ params
+ );
const { _version, _id } = updateResponse;
return res.ok({
body: {
From 8e5723bfec2caf4bc2a59556cb73077e45248275 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Tue, 20 Jun 2023 11:24:47 +0200
Subject: [PATCH 12/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../sample_composite_level_monitor.json | 218 ++++++++++++++++++
.../composite_level_monitor_spec.js | 122 ++++++++++
cypress/support/commands.js | 48 +++-
cypress/support/constants.js | 1 +
package.json | 3 +
.../__snapshots__/MonitorType.test.js.snap | 62 +++++
.../AnomalyDetector.test.js.snap | 12 +
.../__snapshots__/CreateMonitor.test.js.snap | 2 +
.../__snapshots__/DefineMonitor.test.js.snap | 2 +
.../__snapshots__/MonitorIndex.test.js.snap | 10 +
.../DefineCompositeLevelTrigger.js | 6 +-
.../AcknowledgeAlertsModal.test.js.snap | 2 +
12 files changed, 476 insertions(+), 12 deletions(-)
create mode 100644 cypress/fixtures/sample_composite_level_monitor.json
create mode 100644 cypress/integration/composite_level_monitor_spec.js
diff --git a/cypress/fixtures/sample_composite_level_monitor.json b/cypress/fixtures/sample_composite_level_monitor.json
new file mode 100644
index 000000000..20a0fed18
--- /dev/null
+++ b/cypress/fixtures/sample_composite_level_monitor.json
@@ -0,0 +1,218 @@
+{
+ "sample_composite_monitor": {
+ "type": "workflow",
+ "schema_version": 0,
+ "name": "sample_component_level_monitor",
+ "workflow_type": "composite",
+ "enabled": true,
+ "enabled_time": 1686908176848,
+ "schedule": {
+ "period": {
+ "interval": 1,
+ "unit": "MINUTES"
+ }
+ },
+ "inputs": [
+ {
+ "composite_input": {
+ "sequence": {
+ "delegates": [
+ {
+ "order": 1,
+ "monitor_id": "qdYBw4gB2qeAWe54jQyZ"
+ },
+ {
+ "order": 2,
+ "monitor_id": "rtYBw4gB2qeAWe54wAx5"
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "triggers": [
+ {
+ "chained_alert_trigger": {
+ "id": "pNaQw4gB2qeAWe54Fg2U",
+ "name": "sample_trigger",
+ "severity": "1",
+ "condition": {
+ "script": {
+ "source": "(monitor[id=qdYBw4gB2qeAWe54jQyZ]) && (monitor[id=rtYBw4gB2qeAWe54wAx5])",
+ "lang": "painless"
+ }
+ },
+ "actions": [
+ {
+ "id": "pdaQw4gB2qeAWe54Fg2U",
+ "name": "sample_channel",
+ "destination_id": "6dYFw4gB2qeAWe54NgyL",
+ "message_template": {
+ "source": "Monitor {{ctx.monitor.name}} just entered alert status. Please investigate the issue.\n - Trigger: {{ctx.trigger.name}}\n - Severity: {{ctx.trigger.severity}}\n - Period start: {{ctx.periodStart}}\n - Period end: {{ctx.periodEnd}}",
+ "lang": "mustache"
+ },
+ "throttle_enabled": false,
+ "subject_template": {
+ "source": "Monitor {{ctx.monitor.name}} triggered an alert {{ctx.trigger.name}}",
+ "lang": "mustache"
+ }
+ }
+ ]
+ }
+ }
+ ],
+ "last_update_time": 1686908180116,
+ "owner": "alerting",
+ "monitor_type": "composite"
+ },
+ "sample_composite_index": {
+ "mappings": {
+ "properties": {
+ "audit_category": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "audit_node_host_name": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ },
+ "audit_node_id": {
+ "type": "text",
+ "fields": {
+ "keyword": {
+ "type": "keyword",
+ "ignore_above": 256
+ }
+ }
+ }
+ }
+ }
+ },
+ "sample_composite_associated_monitor_1": {
+ "name": "monitor_1",
+ "type": "monitor",
+ "monitor_type": "doc_level_monitor",
+ "enabled": false,
+ "schedule": {
+ "period": {
+ "unit": "MINUTES",
+ "interval": 1
+ }
+ },
+ "inputs": [
+ {
+ "doc_level_input": {
+ "description": "",
+ "indices": ["sample_index_1"],
+ "queries": [
+ {
+ "id": "monitor_1_query_1",
+ "name": "monitor_1_query_1",
+ "query": "NOT (audit_category:\"sample_text\")",
+ "tags": []
+ },
+ {
+ "id": "monitor_1_query_2",
+ "name": "monitor_1_query_2",
+ "query": "NOT (audit_node_host_name:\"sample_text\")",
+ "tags": []
+ },
+ {
+ "id": "monitor_1_query_3",
+ "name": "monitor_1_query_3",
+ "query": "NOT (audit_node_id:\"sample_text\")",
+ "tags": []
+ }
+ ]
+ }
+ }
+ ],
+ "triggers": [
+ {
+ "document_level_trigger": {
+ "id": "sample_trigger_id_1",
+ "name": "monitor_1_query_2",
+ "severity": "1",
+ "condition": {
+ "script": {
+ "source": "query[name=monitor_1_query_1] || query[name=monitor_1_query_2] && query[name=monitor_1_query_3]",
+ "lang": "painless"
+ }
+ },
+ "actions": []
+ }
+ }
+ ]
+ },
+ "sample_composite_associated_monitor_2": {
+ "name": "monitor_2",
+ "type": "monitor",
+ "monitor_type": "doc_level_monitor",
+ "enabled": false,
+ "schedule": {
+ "period": {
+ "unit": "MINUTES",
+ "interval": 1
+ }
+ },
+ "inputs": [
+ {
+ "doc_level_input": {
+ "description": "",
+ "indices": ["sample_index_2"],
+ "queries": [
+ {
+ "id": "monitor_2_query_1",
+ "name": "monitor_2_query_1",
+ "query": "NOT (audit_category:\"sample_text\")",
+ "tags": []
+ },
+ {
+ "id": "monitor_2_query_2",
+ "name": "monitor_2_query_2",
+ "query": "NOT (audit_node_host_name:\"sample_text\")",
+ "tags": []
+ },
+ {
+ "id": "monitor_2_query_3",
+ "name": "monitor_2_query_3",
+ "query": "NOT (audit_node_id:\"sample_text\")",
+ "tags": []
+ }
+ ]
+ }
+ }
+ ],
+ "triggers": [
+ {
+ "document_level_trigger": {
+ "id": "sample_trigger_2",
+ "name": "monitor_2_query_2",
+ "severity": "1",
+ "condition": {
+ "script": {
+ "source": "query[name=monitor_2_query_1] || query[name=monitor_2_query_2] && query[name=monitor_2_query_3]",
+ "lang": "painless"
+ }
+ },
+ "actions": []
+ }
+ }
+ ]
+ },
+ "sample_composite_associated_index_document": {
+ "audit_category": "FAILED_LOGIN",
+ "audit_node_host_name": "127.0.0.1",
+ "audit_node_id": "sample_node_id"
+ }
+}
diff --git a/cypress/integration/composite_level_monitor_spec.js b/cypress/integration/composite_level_monitor_spec.js
new file mode 100644
index 000000000..854e72fc5
--- /dev/null
+++ b/cypress/integration/composite_level_monitor_spec.js
@@ -0,0 +1,122 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { PLUGIN_NAME } from '../support/constants';
+import sampleCompositeJson from '../fixtures/sample_composite_level_monitor.json';
+
+const sample_index_1 = 'sample_index_1';
+const sample_index_2 = 'sample_index_2';
+const SAMPLE_VISUAL_EDITOR_MONITOR = 'sample_visual_editor_composite_level_monitor';
+const SAMPLE_COMPOSITE_LEVEL_MONITOR = 'sample_composite_level_monitor';
+
+const clearAll = () => {
+ cy.deleteAllMonitors();
+ cy.deleteIndexByName(sample_index_1);
+ cy.deleteIndexByName(sample_index_2);
+ cy.deleteAllAlerts();
+};
+
+describe('CompositeLevelMonitor', () => {
+ before(() => {
+ clearAll();
+
+ // Create indices
+ cy.createIndexByName(sample_index_1, sampleCompositeJson.sample_composite_index);
+ cy.createIndexByName(sample_index_2, sampleCompositeJson.sample_composite_index);
+
+ // Create asociated monitors
+ cy.createMonitor(sampleCompositeJson.sample_composite_associated_monitor_1);
+ cy.createMonitor(sampleCompositeJson.sample_composite_associated_monitor_2);
+ });
+
+ beforeEach(() => {
+ // Set welcome screen tracking to false
+ localStorage.setItem('home:welcome:show', 'false');
+
+ // Visit Alerting OpenSearch Dashboards
+ cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/monitors`);
+
+ // Common text to wait for to confirm page loaded, give up to 20 seconds for initial load
+ cy.contains('Create monitor', { timeout: 20000 });
+ });
+
+ describe('can be created', () => {
+ beforeEach(() => {
+ // Go to create monitor page
+ cy.contains('Create monitor').click({ force: true });
+
+ // Select the Composite-Level Monitor type
+ cy.get('[data-test-subj="compositeLevelMonitorRadioCard"]').click({ force: true });
+ });
+
+ it('by visual editor', () => {
+ // Select visual editor for method of definition
+ cy.get('[data-test-subj="visualEditorRadioCard"]').click({ force: true });
+
+ // Wait for input to load and then type in the monitor name
+ cy.get('input[name="name"]').type(SAMPLE_VISUAL_EDITOR_MONITOR);
+
+ // Select associated monitors
+ cy.get('[data-test-subj="monitors_list_0"]')
+ .type(sampleCompositeJson.sample_composite_associated_monitor_1.name)
+ .type('{enter}');
+ cy.get('[data-test-subj="monitors_list_1"]')
+ .type(sampleCompositeJson.sample_composite_associated_monitor_2.name)
+ .type('{enter}');
+
+ // Type trigger name
+ cy.get('[data-test-subj="composite-trigger-name"]')
+ .type('{selectall}')
+ .type('{backspace}')
+ .type('Composite trigger');
+
+ // Add associated monitors to condition
+ cy.get('[data-test-subj="condition-add-selection-btn"]').click();
+ cy.get('[data-test-subj="condition-add-selection-btn"]').click();
+
+ // TODO: Test with Notifications plugin
+ // Select notification channel
+ // cy.get('[title="Notification 1"]').type('Channel name');
+
+ cy.intercept('api/alerting/workflows').as('createMonitorRequest');
+ cy.intercept('api/alerting/monitors').as('getMonitorsRequest');
+ cy.get('button').contains('Create').click({ force: true });
+
+ // Wait for monitor to be created
+ cy.wait('@createMonitorRequest').then((interceptor) => {
+ const monitorID = interceptor.response.body.resp._id;
+
+ cy.contains('Loading monitors');
+ cy.wait('@getMonitorsRequest');
+
+ // Let monitor's table render the rows before querying
+ cy.wait(1000).then(() => {
+ cy.get('table tbody td').contains(SAMPLE_VISUAL_EDITOR_MONITOR);
+
+ // Load sample data
+ cy.insertDocumentToIndex(
+ sample_index_1,
+ undefined,
+ sampleCompositeJson.sample_composite_associated_index_document
+ );
+ cy.insertDocumentToIndex(
+ sample_index_2,
+ undefined,
+ sampleCompositeJson.sample_composite_associated_index_document
+ );
+
+ cy.wait(1000).then(() => {
+ cy.executeCompositeMonitor(monitorID);
+
+ cy.get('[role="tab"]').contains('Alerts').click();
+ cy.get('table tbody td').contains('Composite trigger');
+ });
+ });
+ });
+ });
+ });
+
+ after(() => clearAll());
+});
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index b3d4b1abd..9568d43ba 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -85,6 +85,23 @@ Cypress.Commands.add('createAndExecuteMonitor', (monitorJSON) => {
);
});
+Cypress.Commands.add('executeCompositeMonitor', (monitorID) => {
+ cy.request('POST', `${Cypress.env('opensearch')}${API.WORKFLOW_BASE}/${monitorID}/_execute`);
+});
+
+Cypress.Commands.add('deleteAllAlerts', () => {
+ cy.request({
+ method: 'POST',
+ url: `${Cypress.env('opensearch')}/.opendistro-alerting-alert*/_delete_by_query`,
+ body: {
+ query: {
+ match_all: {},
+ },
+ },
+ failOnStatusCode: false,
+ });
+});
+
Cypress.Commands.add('deleteMonitorByName', (monitorName) => {
const body = {
query: {
@@ -110,9 +127,7 @@ Cypress.Commands.add('deleteAllMonitors', () => {
const body = {
size: 200,
query: {
- exists: {
- field: 'monitor',
- },
+ match_all: {},
},
};
cy.request({
@@ -122,11 +137,18 @@ Cypress.Commands.add('deleteAllMonitors', () => {
body,
}).then((response) => {
if (response.status === 200) {
- for (let i = 0; i < response.body.hits.total.value; i++) {
- cy.request(
- 'DELETE',
- `${Cypress.env('opensearch')}${API.MONITOR_BASE}/${response.body.hits.hits[i]._id}`
- );
+ const monitors = response.body.hits.hits.sort((monitor) =>
+ monitor._source.type === 'workflow' ? -1 : 1
+ );
+ console.log('MONITORS', monitors);
+ for (let i = 0; i < monitors.length; i++) {
+ cy.request({
+ method: 'DELETE',
+ url: `${Cypress.env('opensearch')}${
+ monitors[i]._source.type === 'workflow' ? API.WORKFLOW_BASE : API.MONITOR_BASE
+ }/${monitors[i]._id}`,
+ failOnStatusCode: false,
+ });
}
} else {
cy.log('Failed to get all monitors.', response);
@@ -134,12 +156,16 @@ Cypress.Commands.add('deleteAllMonitors', () => {
});
});
-Cypress.Commands.add('createIndexByName', (indexName) => {
- cy.request('PUT', `${Cypress.env('opensearch')}/${indexName}`);
+Cypress.Commands.add('createIndexByName', (indexName, body = {}) => {
+ cy.request('PUT', `${Cypress.env('opensearch')}/${indexName}`, body);
});
Cypress.Commands.add('deleteIndexByName', (indexName) => {
- cy.request('DELETE', `${Cypress.env('opensearch')}/${indexName}`);
+ cy.request({
+ method: 'DELETE',
+ url: `${Cypress.env('opensearch')}/${indexName}`,
+ failOnStatusCode: false,
+ });
});
Cypress.Commands.add('insertDocumentToIndex', (indexName, documentId, documentBody) => {
diff --git a/cypress/support/constants.js b/cypress/support/constants.js
index 66865cff7..6fb34cffc 100644
--- a/cypress/support/constants.js
+++ b/cypress/support/constants.js
@@ -13,6 +13,7 @@ export const INDEX = {
export const API = {
MONITOR_BASE: `${API_ROUTE_PREFIX}/monitors`,
+ WORKFLOW_BASE: `${API_ROUTE_PREFIX}/workflows`,
DESTINATION_BASE: `${API_ROUTE_PREFIX}/destinations`,
};
diff --git a/package.json b/package.json
index 2c0c5084b..9f46a36de 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,9 @@
"lint": "../../node_modules/.bin/eslint '**/*.js' -c .eslintrc --ignore-path .gitignore",
"test:jest:windows": "SET TZ=UTC ../../node_modules/.bin/jest --config ./test/jest.config.js",
"test:jest": "TZ=UTC ../../node_modules/.bin/jest --config ./test/jest.config.js",
+ "test:jest:update-snapshots": "yarn run test:jest -u",
+ "cypress:open": "cypress open",
+ "cypress:run": "cypress run",
"build": "yarn plugin-helpers build",
"plugin-helpers": "node ../../scripts/plugin_helpers",
"postbuild": "echo Renaming build artifact to [$npm_package_config_id-$npm_package_version.zip] && mv build/$npm_package_config_id*.zip build/$npm_package_config_id-$npm_package_version.zip"
diff --git a/public/pages/CreateMonitor/components/MonitorType/__snapshots__/MonitorType.test.js.snap b/public/pages/CreateMonitor/components/MonitorType/__snapshots__/MonitorType.test.js.snap
index 4178b864f..d817580f5 100644
--- a/public/pages/CreateMonitor/components/MonitorType/__snapshots__/MonitorType.test.js.snap
+++ b/public/pages/CreateMonitor/components/MonitorType/__snapshots__/MonitorType.test.js.snap
@@ -262,5 +262,67 @@ exports[`MonitorType renders 1`] = `
+
`;
diff --git a/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap b/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
index ad99e0e92..d59bbb56f 100644
--- a/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
+++ b/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
@@ -6,6 +6,7 @@ exports[`AnomalyDetectors renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -13,6 +14,7 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -70,6 +72,7 @@ exports[`AnomalyDetectors renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -77,6 +80,7 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -195,6 +199,7 @@ exports[`AnomalyDetectors renders 1`] = `
"initialValues": Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -202,6 +207,7 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -278,6 +284,7 @@ exports[`AnomalyDetectors renders 1`] = `
"values": Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -285,6 +292,7 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -409,6 +417,7 @@ exports[`AnomalyDetectors renders 1`] = `
"initialValues": Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -416,6 +425,7 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -492,6 +502,7 @@ exports[`AnomalyDetectors renders 1`] = `
"values": Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -499,6 +510,7 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap b/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
index a0ddd0f2c..32b0b9450 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
@@ -14,6 +14,7 @@ exports[`CreateMonitor renders 1`] = `
"adResultIndex": undefined,
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -21,6 +22,7 @@ exports[`CreateMonitor renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
diff --git a/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap b/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
index 3bcc5af6f..4d811f708 100644
--- a/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
+++ b/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
@@ -11,6 +11,7 @@ exports[`DefineMonitor renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -18,6 +19,7 @@ exports[`DefineMonitor renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
diff --git a/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap b/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
index 9745f7cf0..09351d512 100644
--- a/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
+++ b/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
@@ -6,6 +6,7 @@ exports[`MonitorIndex renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -13,6 +14,7 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -143,6 +145,7 @@ exports[`MonitorIndex renders 1`] = `
"initialValues": Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -150,6 +153,7 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -226,6 +230,7 @@ exports[`MonitorIndex renders 1`] = `
"values": Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -233,6 +238,7 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -373,6 +379,7 @@ exports[`MonitorIndex renders 1`] = `
"initialValues": Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -380,6 +387,7 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -456,6 +464,7 @@ exports[`MonitorIndex renders 1`] = `
"values": Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -463,6 +472,7 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index 37129cbc3..6737854a4 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -117,7 +117,11 @@ class DefineCompositeLevelTrigger extends Component {
paddingLeft: 0,
},
}}
- inputProps={{ ...defaultInputProps, value: triggerName }}
+ inputProps={{
+ ...defaultInputProps,
+ value: triggerName,
+ 'data-test-subj': 'composite-trigger-name',
+ }}
/>
diff --git a/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap b/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap
index 2286b8431..659fd3a86 100644
--- a/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap
+++ b/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap
@@ -35,6 +35,7 @@ exports[`AcknowledgeAlertsModal renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -42,6 +43,7 @@ exports[`AcknowledgeAlertsModal renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
+ "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
From 12f0e6f5b1d0b5e3b5784e64f0d87f53ef6782f2 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Tue, 20 Jun 2023 14:24:17 +0200
Subject: [PATCH 13/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../composite_level_monitor_spec.js | 55 +++++++++++--------
cypress/support/commands.js | 4 ++
2 files changed, 36 insertions(+), 23 deletions(-)
diff --git a/cypress/integration/composite_level_monitor_spec.js b/cypress/integration/composite_level_monitor_spec.js
index 854e72fc5..9581422d6 100644
--- a/cypress/integration/composite_level_monitor_spec.js
+++ b/cypress/integration/composite_level_monitor_spec.js
@@ -15,7 +15,9 @@ const clearAll = () => {
cy.deleteAllMonitors();
cy.deleteIndexByName(sample_index_1);
cy.deleteIndexByName(sample_index_2);
- cy.deleteAllAlerts();
+
+ // wait until alerts index finishes writing docs
+ cy.wait(1000).then(() => cy.deleteAllAlerts());
};
describe('CompositeLevelMonitor', () => {
@@ -81,7 +83,7 @@ describe('CompositeLevelMonitor', () => {
// cy.get('[title="Notification 1"]').type('Channel name');
cy.intercept('api/alerting/workflows').as('createMonitorRequest');
- cy.intercept('api/alerting/monitors').as('getMonitorsRequest');
+ cy.intercept(`api/alerting/monitors?*`).as('getMonitorsRequest');
cy.get('button').contains('Create').click({ force: true });
// Wait for monitor to be created
@@ -89,29 +91,36 @@ describe('CompositeLevelMonitor', () => {
const monitorID = interceptor.response.body.resp._id;
cy.contains('Loading monitors');
- cy.wait('@getMonitorsRequest');
-
- // Let monitor's table render the rows before querying
- cy.wait(1000).then(() => {
- cy.get('table tbody td').contains(SAMPLE_VISUAL_EDITOR_MONITOR);
-
- // Load sample data
- cy.insertDocumentToIndex(
- sample_index_1,
- undefined,
- sampleCompositeJson.sample_composite_associated_index_document
- );
- cy.insertDocumentToIndex(
- sample_index_2,
- undefined,
- sampleCompositeJson.sample_composite_associated_index_document
- );
+ cy.wait('@getMonitorsRequest').then((interceptor) => {
+ const monitors = interceptor.response.body.monitors;
+ const monitor1 = monitors.filter((monitor) => monitor.name === 'monitor_1');
+ const monitor2 = monitors.filter((monitor) => monitor.name === 'monitor_2');
+ // Let monitor's table render the rows before querying
cy.wait(1000).then(() => {
- cy.executeCompositeMonitor(monitorID);
-
- cy.get('[role="tab"]').contains('Alerts').click();
- cy.get('table tbody td').contains('Composite trigger');
+ cy.get('table tbody td').contains(SAMPLE_VISUAL_EDITOR_MONITOR);
+
+ // Load sample data
+ cy.insertDocumentToIndex(
+ sample_index_1,
+ undefined,
+ sampleCompositeJson.sample_composite_associated_index_document
+ );
+ cy.insertDocumentToIndex(
+ sample_index_2,
+ undefined,
+ sampleCompositeJson.sample_composite_associated_index_document
+ );
+
+ cy.wait(1000).then(() => {
+ cy.executeCompositeMonitor(monitorID);
+ debugger;
+ monitor1[0] && cy.executeMonitor(monitor1[0].id);
+ monitor2[0] && cy.executeMonitor(monitor2[0].id);
+
+ cy.get('[role="tab"]').contains('Alerts').click();
+ cy.get('table tbody td').contains('Composite trigger');
+ });
});
});
});
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 9568d43ba..4bfcd67f0 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -85,6 +85,10 @@ Cypress.Commands.add('createAndExecuteMonitor', (monitorJSON) => {
);
});
+Cypress.Commands.add('executeMonitor', (monitorID) => {
+ cy.request('POST', `${Cypress.env('opensearch')}${API.MONITOR_BASE}/${monitorID}/_execute`);
+});
+
Cypress.Commands.add('executeCompositeMonitor', (monitorID) => {
cy.request('POST', `${Cypress.env('opensearch')}${API.WORKFLOW_BASE}/${monitorID}/_execute`);
});
From 88d7ac99a372a647c7773e8397a9d71872e535ad Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Tue, 20 Jun 2023 18:44:53 +0200
Subject: [PATCH 14/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../CreateMonitor/utils/constants.js | 2 +-
.../CreateMonitor/utils/monitorToFormik.js | 14 ++++-
.../WorkflowDetails/WorkflowDetails.tsx | 44 +++++++-------
.../ExpressionQuery/ExpressionQuery.js | 29 ++--------
.../CreateTrigger/utils/formikToTrigger.js | 7 ++-
.../CreateTrigger/utils/triggerToFormik.js | 10 +++-
.../DefineCompositeLevelTrigger.js | 58 ++++++++++++++++---
7 files changed, 103 insertions(+), 61 deletions(-)
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
index 2973014e7..e2109ec67 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
@@ -62,7 +62,7 @@ export const FORMIK_INITIAL_VALUES = {
filters: [], // array of FORMIK_INITIAL_WHERE_EXPRESSION_VALUES
detectorId: '',
associatedMonitors: [],
- expressionQuery: null,
+ expressionQueries: [],
};
export const FORMIK_INITIAL_AGG_VALUES = {
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
index 43d460a40..d2614c1ed 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
@@ -41,7 +41,19 @@ export default function monitorToFormik(monitor) {
case MONITOR_TYPE.DOC_LEVEL:
return docLevelInputToFormik(monitor);
case MONITOR_TYPE.COMPOSITE_LEVEL:
- return { inputs: _.get(monitor, 'inputs[0].composite_input.sequence.delegates', []) };
+ const associatedMonitors = _.get(
+ monitor,
+ 'inputs[0].composite_input.sequence.delegates',
+ []
+ );
+
+ return {
+ inputs: associatedMonitors,
+ associatedMonitors: associatedMonitors.map((mon) => ({
+ label: '',
+ value: mon.monitor_id,
+ })),
+ };
default:
return {
index: indicesToFormik(inputs[0].search.indices),
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
index ac7b69a97..a00669255 100644
--- a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
@@ -10,33 +10,33 @@ import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonit
import { EuiSpacer } from '@elastic/eui';
import * as _ from 'lodash';
+export const getMonitors = async (httpClient) => {
+ const response = await httpClient.get('../api/alerting/monitors', {
+ query: {
+ from: 0,
+ size: 1000,
+ search: '',
+ sortField: 'name',
+ sortDirection: 'desc',
+ state: 'all',
+ },
+ });
+
+ if (response.ok) {
+ const { monitors, totalMonitors } = response;
+ return monitors.map((monitor) => ({ monitor_id: monitor.id, monitor_name: monitor.name }));
+ } else {
+ console.log('error getting monitors:', response);
+ return [];
+ }
+};
+
const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values }) => {
const [selectedMonitors, setSelectedMonitors] = useState([]);
const [monitorOptions, setMonitorOptions] = useState([]);
- const getMonitors = async () => {
- const response = await httpClient.get('../api/alerting/monitors', {
- query: {
- from: 0,
- size: 1000,
- search: '',
- sortField: 'name',
- sortDirection: 'desc',
- state: 'all',
- },
- });
-
- if (response.ok) {
- const { monitors, totalMonitors } = response;
- return monitors.map((monitor) => ({ monitor_id: monitor.id, monitor_name: monitor.name }));
- } else {
- console.log('error getting monitors:', response);
- return [];
- }
- };
-
useEffect(() => {
- getMonitors().then((monitors) => {
+ getMonitors(httpClient).then((monitors) => {
setMonitorOptions(monitors);
const inputIds = values.inputs?.map((input) => input.monitor_id);
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index c4c5a1703..f642de55f 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -17,38 +17,17 @@ const ExpressionQuery = ({
defaultText,
label,
formikName = 'expressionQueries',
+ triggerValues,
}) => {
const DEFAULT_DESCRIPTION = defaultText ? defaultText : 'Select';
- const OPERATORS = ['AND', 'OR', 'NOT'];
const [usedExpressions, setUsedExpressions] = useState([]);
useEffect(() => {
- let expressions = [];
if (value?.length) {
- let values = [...value];
- if (OPERATORS.indexOf(values[0]?.description) === -1) values = ['', ...values];
-
- let counter = 0;
- values.map((exp, idx) => {
- if (idx % 2 === 0) {
- expressions.push({
- description: exp.description,
- isOpen: false,
- monitor_name: '',
- monitor_id: '',
- });
- counter++;
- } else {
- const currentIndex = idx - counter;
- expressions[currentIndex] = { ...expressions[currentIndex], ...exp };
- }
- });
- } else {
- expressions = [];
+ setUsedExpressions(value);
+ _.set(triggerValues, formikName, getValue(value));
}
-
- setUsedExpressions(expressions);
- }, []);
+ }, [value]);
const getValue = (expressions) =>
expressions.map((exp) => ({
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
index 83e09017c..2c1e7d332 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
@@ -125,14 +125,15 @@ export function formikToCompositeTriggerCondition(values) {
const triggerConditions = _.get(values, 'triggerConditions', []);
const source = triggerConditions.reduce((query, expression) => {
- query += ` ${conditionMap[expression.condition]} (monitor[id=${expression.monitor_id}])`;
- return query.trim();
+ query += ` ${conditionMap[expression.condition]} monitor[id=${expression.monitor_id}]`;
+ query = query.trim();
+ return query;
}, '');
return {
script: {
lang: 'painless',
- source: source,
+ source: `(${source})`,
},
};
}
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
index 69171ddaf..4b45d270c 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
@@ -14,6 +14,7 @@ import {
ACTIONABLE_ALERTS_OPTIONS_LABELS,
NOTIFY_OPTIONS_VALUES,
} from '../../../components/Action/actions/Message';
+import { convertQueryToExpressions } from '../../DefineCompositeLevelTrigger/DefineCompositeLevelTrigger';
export function triggerToFormik(trigger, monitor) {
return _.isArray(trigger)
@@ -226,7 +227,12 @@ export function compositeTriggerToFormik(trigger, monitor) {
condition: { script },
actions,
} = trigger[TRIGGER_TYPE.COMPOSITE_LEVEL];
- const triggerUiMetadata = _.get(monitor, `ui_metadata.triggers[${name}]`);
+ // TODO this should be saved in ui_metadata, currently workflows don't save ui_metadata
+ // const triggerUiMetadata = _.get(monitor, `ui_metadata.triggers[${name}]`);
+ const triggerConditions = convertQueryToExpressions(
+ monitor.triggers[0].chained_alert_trigger.condition.script.source,
+ []
+ );
return {
..._.cloneDeep(FORMIK_INITIAL_TRIGGER_VALUES),
id,
@@ -234,7 +240,7 @@ export function compositeTriggerToFormik(trigger, monitor) {
severity,
script,
actions: getExecutionPolicyActions(actions),
- triggerConditions: triggerUiMetadata,
+ triggerConditions: triggerConditions, //triggerUiMetadata,
};
}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index 6737854a4..441d9c0ea 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -14,6 +14,7 @@ import ExpressionQuery from '../../components/ExpressionQuery/ExpressionQuery';
import TriggerNotifications from './TriggerNotifications';
import ContentPanel from '../../../../components/ContentPanel';
import { FORMIK_INITIAL_TRIGGER_VALUES } from '../CreateTrigger/utils/constants';
+import { getMonitors } from '../../../CreateMonitor/containers/WorkflowDetails/WorkflowDetails';
const defaultRowProps = {
label: 'Trigger name',
@@ -57,19 +58,64 @@ export const titleTemplate = (title, subTitle) => (
);
+export const convertQueryToExpressions = (query, monitors) => {
+ const conditionMap = {
+ '&&': 'and',
+ '||': 'or',
+ '!': 'not',
+ '': '',
+ };
+ const queryToExpressionRegex = new RegExp('(&& )?(\\|\\| )?(monitor\\[id=(.*?)\\])', 'g');
+ const matcher = query.matchAll(queryToExpressionRegex);
+ let match;
+ let expressions = [];
+ while ((match = matcher.next().value)) {
+ const monitorId = match[4]?.trim();
+ const monitor = monitors.filter((mon) => mon.monitor_id === monitorId);
+ expressions.push({
+ description: conditionMap[match[1]?.trim()] || '',
+ isOpen: false,
+ monitor_name: monitor[0]?.monitor_name,
+ monitor_id: monitorId,
+ });
+ }
+
+ return expressions;
+};
+
class DefineCompositeLevelTrigger extends Component {
constructor(props) {
super(props);
- this.state = {};
+ this.state = {
+ expressions: [],
+ };
+ }
+
+ componentDidMount() {
+ getMonitors(this.props.httpClient).then((monitors) => {
+ const inputIds = this.props.monitorValues.inputs?.map((input) => input.monitor_id);
+ if (inputIds && inputIds.length) {
+ const selectedMonitors = monitors.filter(
+ (monitor) => inputIds.indexOf(monitor.monitor_id) !== -1
+ );
+
+ const expressions = convertQueryToExpressions(
+ this.props.triggerValues.triggerDefinitions[0].script.source,
+ selectedMonitors
+ );
+
+ this.setState({
+ expressions,
+ });
+ }
+ });
}
render() {
const {
edit,
monitorValues,
- triggers,
triggerValues,
- isDarkMode,
httpClient,
notifications,
notificationService,
@@ -94,6 +140,7 @@ class DefineCompositeLevelTrigger extends Component {
monitor_id: monitor.value,
}))
: [];
+
return (
[monitor, { description: 'and' }]))
- .slice(0, -1)}
- onChange={() => {}}
+ value={this.state.expressions}
dataTestSubj={'composite_expression_query'}
defaultText={'Select associated monitor'}
triggerValues={triggerValues}
From 11ebb8dc48bcef49b70ae5353410dacc5b76d8f5 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Wed, 21 Jun 2023 11:52:39 +0200
Subject: [PATCH 15/63] [FEATURE] Add composite monitor type #573
Signed-off-by: Jovan Cvetkovic
---
.../composite_level_monitor_spec.js | 25 +-
.../AssociateMonitors/AssociateMonitors.tsx | 109 ++++++++-
.../containers/CreateMonitor/CreateMonitor.js | 1 +
.../WorkflowDetails/WorkflowDetails.tsx | 12 +-
.../ExpressionQuery/ExpressionQuery.js | 216 +++++++++++-------
.../CreateTrigger/utils/formikToTrigger.js | 6 +-
.../DefineCompositeLevelTrigger.js | 3 +-
7 files changed, 276 insertions(+), 96 deletions(-)
diff --git a/cypress/integration/composite_level_monitor_spec.js b/cypress/integration/composite_level_monitor_spec.js
index 9581422d6..006c742bd 100644
--- a/cypress/integration/composite_level_monitor_spec.js
+++ b/cypress/integration/composite_level_monitor_spec.js
@@ -44,6 +44,7 @@ describe('CompositeLevelMonitor', () => {
cy.contains('Create monitor', { timeout: 20000 });
});
+ let monitorId;
describe('can be created', () => {
beforeEach(() => {
// Go to create monitor page
@@ -88,7 +89,7 @@ describe('CompositeLevelMonitor', () => {
// Wait for monitor to be created
cy.wait('@createMonitorRequest').then((interceptor) => {
- const monitorID = interceptor.response.body.resp._id;
+ monitorId = interceptor.response.body.resp._id;
cy.contains('Loading monitors');
cy.wait('@getMonitorsRequest').then((interceptor) => {
@@ -113,8 +114,7 @@ describe('CompositeLevelMonitor', () => {
);
cy.wait(1000).then(() => {
- cy.executeCompositeMonitor(monitorID);
- debugger;
+ cy.executeCompositeMonitor(monitorId);
monitor1[0] && cy.executeMonitor(monitor1[0].id);
monitor2[0] && cy.executeMonitor(monitor2[0].id);
@@ -127,5 +127,24 @@ describe('CompositeLevelMonitor', () => {
});
});
+ describe('can be edited', () => {
+ beforeEach(() => {
+ if (monitorId) {
+ cy.visit(
+ `${Cypress.env(
+ 'opensearch_dashboards'
+ )}/app/${PLUGIN_NAME}#/monitors/${monitorId}?action=update-monitor&type=workflow`
+ );
+ } else {
+ throw new Error(`Monitor with ID: ${monitorId} not found or not created.`);
+ }
+ });
+
+ it('by visual editor', () => {
+ // Verify edit page
+ cy.contains('Edit monitor', { timeout: 20000 });
+ });
+ });
+
after(() => clearAll());
});
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
index ede80d1d2..e47558c52 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
@@ -3,12 +3,86 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { Fragment } from 'react';
+import React, { Fragment, useState, useEffect, useCallback } from 'react';
import { EuiSpacer, EuiText } from '@elastic/eui';
import MonitorsList from './components/MonitorsList';
+import { FormikCodeEditor } from '../../../../components/FormControls';
+import * as _ from 'lodash';
+import { isInvalid, hasError, validateExtractionQuery } from '../../../../utils/validate';
-const AssociateMonitors = ({ monitors, options }) => {
- const onUpdate = () => {};
+const AssociateMonitors = ({
+ monitors,
+ options,
+ searchType = 'graph',
+ isDarkMode,
+ monitorValues,
+}) => {
+ const [graphUi, setGraphUi] = useState(searchType === 'graph');
+
+ const queryTemplate = {
+ sequence: {
+ delegates: [],
+ },
+ };
+
+ const delegatesToMonitors = (value) =>
+ value.sequence.delegates.map((monitor) => ({
+ label: '',
+ value: monitor.monitor_id,
+ }));
+
+ useEffect(() => {
+ if (monitors?.length) {
+ const value = { ...queryTemplate };
+ monitors.map((monitor, idx) => {
+ let delegate = {
+ order: idx + 1,
+ monitor_id: monitor.monitor_id,
+ };
+ value.sequence.delegates.push(delegate);
+ _.set(monitorValues, `associatedMonitor_${idx}`, {
+ label: monitor.monitor_name || '',
+ value: monitor.monitor_id,
+ });
+ });
+
+ _.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
+ _.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
+ } else {
+ if (options?.length) {
+ const value = { ...queryTemplate };
+ const firstTwo = options.slice(0, 2);
+ firstTwo.map((monitor, idx) => {
+ value.sequence.delegates.push({
+ order: idx + 1,
+ monitor_id: monitor.monitor_id,
+ });
+ });
+
+ try {
+ _.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
+ _.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
+ } catch (e) {
+ console.log('No monitor options are available.');
+ }
+ }
+ }
+
+ setGraphUi(searchType === 'graph');
+ }, [searchType, monitors, options]);
+
+ const onCodeChange = useCallback(
+ (query, field, form) => {
+ form.setFieldValue('associatedMonitorsEditor', query);
+ try {
+ const code = JSON.parse(query);
+ form.setFieldValue('associatedMonitors', delegatesToMonitors(code));
+ } catch (e) {
+ console.error('Invalid json.');
+ }
+ },
+ [options, monitors]
+ );
return (
@@ -16,12 +90,37 @@ const AssociateMonitors = ({ monitors, options }) => {
Associate monitors
- Associate two or more monitors to run as part of this flow.
+ Associate two or more monitors to run as part of this workflow.
-
+ {graphUi ? (
+
+ ) : (
+ form.setFieldTouched('associatedMonitorsEditor', true),
+ 'data-test-subj': 'associatedMonitorsCodeEditor',
+ }}
+ />
+ )}
);
};
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index 0da64435b..c00dfb5c1 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -306,6 +306,7 @@ export default class CreateMonitor extends Component {
{
}
};
-const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values }) => {
+const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values, isDarkMode }) => {
const [selectedMonitors, setSelectedMonitors] = useState([]);
const [monitorOptions, setMonitorOptions] = useState([]);
@@ -40,7 +40,7 @@ const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values }) =>
setMonitorOptions(monitors);
const inputIds = values.inputs?.map((input) => input.monitor_id);
- if (inputIds && inputIds.length) {
+ if (inputIds?.length) {
const selected = monitors.filter((monitor) => inputIds.indexOf(monitor.monitor_id) !== -1);
setSelectedMonitors(selected);
_.set(
@@ -70,7 +70,13 @@ const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values }) =>
{isComposite && (
-
+
)}
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index f642de55f..bb0447797 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -9,6 +9,7 @@ import {
} from '@elastic/eui';
import * as _ from 'lodash';
import { FormikFormRow, FormikInputWrapper } from '../../../../components/FormControls';
+import { FormikCodeEditor } from '../../../../components/FormControls';
const ExpressionQuery = ({
selections,
@@ -18,16 +19,34 @@ const ExpressionQuery = ({
label,
formikName = 'expressionQueries',
triggerValues,
+ isDarkMode = false,
}) => {
const DEFAULT_DESCRIPTION = defaultText ? defaultText : 'Select';
const [usedExpressions, setUsedExpressions] = useState([]);
+ const [graphUi, setGraphUi] = useState(triggerValues.searchType === 'graph');
+ const [editorValue, setEditorValue] = useState('');
+
+ const getQueryTemplate = (monitor_id) => `monitor[id=${monitor_id}]`;
+ const queryConditionOperator = '&&';
useEffect(() => {
if (value?.length) {
setUsedExpressions(value);
_.set(triggerValues, formikName, getValue(value));
}
- }, [value]);
+
+ setGraphUi(triggerValues.searchType === 'graph');
+
+ if (selections?.length) {
+ const editorValues = [];
+ selections.map((selection) => {
+ editorValues.push(getQueryTemplate(selection.monitor_id));
+ });
+ const script = editorValues.join(` ${queryConditionOperator} `);
+ setEditorValue(script);
+ _.set(triggerValues, 'triggerDefinitions[0].script.source', script);
+ }
+ }, [value, triggerValues.searchType]);
const getValue = (expressions) =>
expressions.map((exp) => ({
@@ -155,7 +174,7 @@ const ExpressionQuery = ({
validate(),
+ validate: () => graphUi && validate(),
}}
render={({ field, form }) => (
form.touched['expressionQueries'] && !isValid(),
- error: () => validate(),
+ isInvalid: () => form.touched['expressionQueries'] && graphUi && !isValid(),
+ error: () => graphUi && validate(),
+ style: {
+ maxWidth: 'inherit',
+ },
}}
>
-
- {!usedExpressions.length && (
-
- onBlur(form, usedExpressions)}
- />
- }
- isOpen={false}
- panelPaddingSize="s"
- anchorPosition="rightDown"
- closePopover={() => onBlur(form, usedExpressions)}
- />
-
- )}
- {usedExpressions.map((expression, idx) => (
-
- {
- e.preventDefault();
- openPopover(idx);
- }}
- />
- }
- isOpen={expression.isOpen}
- closePopover={() => closePopover(idx)}
- panelPaddingSize="s"
- anchorPosition="rightDown"
- >
- {renderOptions(expression, idx, form)}
-
-
- ))}
- {selections.length > usedExpressions.length && (
-
- {
- const expressions = _.cloneDeep(usedExpressions);
- const differences = _.differenceBy(selections, expressions, 'monitor_id');
- const newExpressions = [
- ...expressions,
- {
- description: usedExpressions.length ? 'AND' : '',
- isOpen: false,
- monitor_name: differences[0]?.label,
- monitor_id: differences[0]?.monitor_id,
- },
- ];
+ {graphUi ? (
+
+ {!usedExpressions.length && (
+
+ onBlur(form, usedExpressions)}
+ />
+ }
+ isOpen={false}
+ panelPaddingSize="s"
+ anchorPosition="rightDown"
+ closePopover={() => onBlur(form, usedExpressions)}
+ />
+
+ )}
+ {usedExpressions.map((expression, idx) => (
+
+ {
+ e.preventDefault();
+ openPopover(idx);
+ }}
+ />
+ }
+ isOpen={expression.isOpen}
+ closePopover={() => closePopover(idx)}
+ panelPaddingSize="s"
+ anchorPosition="rightDown"
+ >
+ {renderOptions(expression, idx, form)}
+
+
+ ))}
+ {selections.length > usedExpressions.length && (
+
+ {
+ const expressions = _.cloneDeep(usedExpressions);
+ const differences = _.differenceBy(selections, expressions, 'monitor_id');
+ const newExpressions = [
+ ...expressions,
+ {
+ description: usedExpressions.length ? 'AND' : '',
+ isOpen: false,
+ monitor_name: differences[0]?.label,
+ monitor_id: differences[0]?.monitor_id,
+ },
+ ];
- setUsedExpressions(newExpressions);
- onBlur(form, newExpressions);
- }}
- color={'primary'}
- iconType="plusInCircleFilled"
- aria-label={'Add one more condition'}
- data-test-subj={'condition-add-selection-btn'}
- style={{ marginTop: '1px' }}
- />
-
- )}
-
+ setUsedExpressions(newExpressions);
+ onBlur(form, newExpressions);
+ }}
+ color={'primary'}
+ iconType="plusInCircleFilled"
+ aria-label={'Add one more condition'}
+ data-test-subj={'condition-add-selection-btn'}
+ style={{ marginTop: '1px' }}
+ />
+
+ )}
+
+ ) : (
+ {
+ _.set(triggerValues, 'triggerDefinitions[0].script.source', query);
+ form.setFieldValue('expressionQueries', query);
+ },
+ onBlur: (e, field, form) => {
+ console.log('### triggerValues', triggerValues);
+ form.setFieldTouched('expressionQueries', true);
+ },
+ 'data-test-subj': 'expressionQueriesCodeEditor',
+ }}
+ />
+ )}
)}
/>
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
index 2c1e7d332..afdc5979c 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
@@ -124,12 +124,16 @@ export function formikToCompositeTriggerCondition(values) {
};
const triggerConditions = _.get(values, 'triggerConditions', []);
- const source = triggerConditions.reduce((query, expression) => {
+ let source = triggerConditions.reduce((query, expression) => {
query += ` ${conditionMap[expression.condition]} monitor[id=${expression.monitor_id}]`;
query = query.trim();
return query;
}, '');
+ if (!source) {
+ source = _.get(values, 'script.source', '');
+ }
+
return {
script: {
lang: 'painless',
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index 441d9c0ea..091300499 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -136,7 +136,7 @@ class DefineCompositeLevelTrigger extends Component {
const triggerActions = _.get(triggerValues, 'triggerDefinitions[0].actions', []);
const monitorList = monitorValues?.associatedMonitors
? monitorValues.associatedMonitors?.map((monitor) => ({
- label: monitor.label.replaceAll(' ', '_'),
+ label: monitor.label?.replaceAll(' ', '_'),
monitor_id: monitor.value,
}))
: [];
@@ -184,6 +184,7 @@ class DefineCompositeLevelTrigger extends Component {
dataTestSubj={'composite_expression_query'}
defaultText={'Select associated monitor'}
triggerValues={triggerValues}
+ isDarkMode={this.props.isDarkMode}
/>
From 59ec9d16a28c02b1f7cc635200563f808c63e5f2 Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Wed, 21 Jun 2023 19:58:53 -0700
Subject: [PATCH 16/63] new columns with deletion modal for monitors list;
added condition column to triggers
Signed-off-by: Amardeepsingh Siglani
---
public/components/Breadcrumbs/Breadcrumbs.js | 5 +-
.../DeleteModal/DeleteMonitorModal.tsx | 62 ++++++++
.../containers/MonitorDetails.js | 4 +-
.../containers/Triggers/Triggers.js | 17 +-
.../MonitorActions/MonitorActions.js | 3 +-
.../Monitors/containers/Monitors/Monitors.js | 148 ++++++++++--------
.../containers/Monitors/utils/helpers.js | 18 +++
.../containers/Monitors/utils/tableUtils.js | 27 +++-
server/services/AlertService.js | 2 +
server/services/MonitorService.js | 11 +-
10 files changed, 215 insertions(+), 82 deletions(-)
create mode 100644 public/components/DeleteModal/DeleteMonitorModal.tsx
diff --git a/public/components/Breadcrumbs/Breadcrumbs.js b/public/components/Breadcrumbs/Breadcrumbs.js
index 3ba41a8ff..1fa9d6ebd 100644
--- a/public/components/Breadcrumbs/Breadcrumbs.js
+++ b/public/components/Breadcrumbs/Breadcrumbs.js
@@ -106,7 +106,7 @@ export async function getBreadcrumb(route, routeState, httpClient) {
// This condition is true for any auto generated 20 character long,
// URL-safe, base64-encoded document ID by opensearch
if (RegExp(/^[0-9a-z_-]{20}$/i).test(base)) {
- const { action } = queryString.parse(`?${queryParams}`);
+ const { action, type } = queryString.parse(`?${queryParams}`);
switch (action) {
case DESTINATION_ACTIONS.UPDATE_DESTINATION:
const destinationName = _.get(routeState, 'destinationToEdit.name', base);
@@ -119,7 +119,8 @@ export async function getBreadcrumb(route, routeState, httpClient) {
// TODO::Everything else is considered as monitor, we should break this.
let monitorName = base;
try {
- const response = await httpClient.get(`../api/alerting/monitors/${base}`);
+ const searchPool = type === 'workflow' ? 'workflows' : 'monitors';
+ const response = await httpClient.get(`../api/alerting/${searchPool}/${base}`);
if (response.ok) {
monitorName = response.resp.name;
}
diff --git a/public/components/DeleteModal/DeleteMonitorModal.tsx b/public/components/DeleteModal/DeleteMonitorModal.tsx
new file mode 100644
index 000000000..7b12c4436
--- /dev/null
+++ b/public/components/DeleteModal/DeleteMonitorModal.tsx
@@ -0,0 +1,62 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { ChangeEvent, Component } from 'react';
+import {
+ EuiConfirmModal,
+ EuiOverlayMask
+} from '@elastic/eui';
+
+interface DeleteModalProps {
+ monitorNames: string[];
+ onClickDelete: (event?: any) => void;
+ closeDeleteModal: (event?: any) => void;
+}
+
+export const DEFAULT_DELETION_TEXT = 'delete';
+
+export default class DeleteMonitorModal extends Component {
+ render() {
+ const {
+ monitorNames,
+ closeDeleteModal,
+ onClickDelete
+ } = this.props;
+
+ let warningHeading = `Delete monitor ${monitorNames[0]}?`;
+ let warningBody: React.ReactNode = 'This action cannot be undone.';
+
+ if (monitorNames.length > 1) {
+ warningHeading = `Delete ${monitorNames.length} monitors?`;
+ warningBody = (
+ <>
+ {`The following monitors will be permanently deleted. ${warningBody}`}
+
+ {monitorNames.map((name, idx) => {name} )}
+
+ >
+ )
+ }
+
+ return (
+
+ {
+ onClickDelete();
+ closeDeleteModal();
+ }}
+ cancelButtonText={'Cancel'}
+ confirmButtonText={'Delete'}
+ buttonColor={'danger'}
+ defaultFocusedButton="confirm"
+ >
+ {warningBody}
+
+
+ );
+ }
+}
diff --git a/public/pages/MonitorDetails/containers/MonitorDetails.js b/public/pages/MonitorDetails/containers/MonitorDetails.js
index babe7e62d..adc7bb3f0 100644
--- a/public/pages/MonitorDetails/containers/MonitorDetails.js
+++ b/public/pages/MonitorDetails/containers/MonitorDetails.js
@@ -72,7 +72,9 @@ export default class MonitorDetails extends Component {
};
}
- isWorkflow = () => new URLSearchParams(this.props.location.search).get('type') === 'workflow';
+ isWorkflow = () => {
+ return new URLSearchParams(this.props.location.search).get('type') === 'workflow';
+ };
componentDidMount() {
this.getMonitor(this.props.match.params.monitorId);
diff --git a/public/pages/MonitorDetails/containers/Triggers/Triggers.js b/public/pages/MonitorDetails/containers/Triggers/Triggers.js
index 63838636f..3109cd19a 100644
--- a/public/pages/MonitorDetails/containers/Triggers/Triggers.js
+++ b/public/pages/MonitorDetails/containers/Triggers/Triggers.js
@@ -98,7 +98,15 @@ export default class Triggers extends Component {
name: 'Name',
sortable: true,
truncateText: true,
- width: '25%',
+ width: '15%',
+ },
+ {
+ name: 'Condition -- (formatting pending...)',
+ truncateText: true,
+ render: (item) => {
+ return item.condition.script.source;
+ },
+ width: '50%',
},
{
field: 'actions',
@@ -106,18 +114,19 @@ export default class Triggers extends Component {
sortable: true,
truncateText: false,
render: (actions) => actions.length,
- width: '25%',
+ width: '15%',
},
{
field: 'severity',
name: 'Severity',
sortable: true,
truncateText: false,
- width: '50%',
+ width: '20%',
},
];
const sorting = { sort: { field, direction } };
+ const items = getUnwrappedTriggers(monitor);
return (
{
// TODO: Support bulk acknowledge alerts across multiple monitors after figuring out the correct parameter for getAlerts API.
// Disabling the acknowledge button for now when more than 1 monitors selected.
- const { isEditDisabled } = this.props;
+ const { isEditDisabled, isDeleteDisabled } = this.props;
const actions = [
Delete
,
diff --git a/public/pages/Monitors/containers/Monitors/Monitors.js b/public/pages/Monitors/containers/Monitors/Monitors.js
index 9367aafa0..c9c8926d8 100644
--- a/public/pages/Monitors/containers/Monitors/Monitors.js
+++ b/public/pages/Monitors/containers/Monitors/Monitors.js
@@ -18,6 +18,7 @@ import { columns as staticColumns } from './utils/tableUtils';
import { MONITOR_ACTIONS, MONITOR_TYPE } from '../../../../utils/constants';
import { backendErrorNotification } from '../../../../utils/helpers';
import { displayAcknowledgedAlertsToast } from '../../../Dashboard/utils/helpers';
+import DeleteMonitorModal from '../../../../components/DeleteModal/DeleteMonitorModal';
const MAX_MONITOR_COUNT = 1000;
@@ -45,6 +46,7 @@ export default class Monitors extends Component {
monitors: [],
monitorState: state,
loadingMonitors: true,
+ monitorItemsToDelete: undefined,
};
this.getMonitors = _.debounce(this.getMonitors.bind(this), 500, { leading: true });
@@ -135,17 +137,25 @@ export default class Monitors extends Component {
const unwrappedMonitors = [];
monitors.forEach((monitor) => {
const monitorType = _.get(monitor, 'monitor.monitor.monitor_type', 'monitor.monitor_type');
+ let unwrappedMonitor = monitor.monitor;
switch (monitorType) {
case MONITOR_TYPE.CLUSTER_METRICS:
- let unwrappedMonitor = monitor.monitor;
_.set(monitor, 'monitor', unwrappedMonitor.monitor);
_.set(monitor, 'name', monitor.monitor.name);
_.set(monitor, 'enabled', monitor.monitor.enabled);
- unwrappedMonitors.push(monitor);
+ _.set(monitor, 'item_type', monitorType);
break;
default:
- unwrappedMonitors.push(monitor);
+ _.set(
+ monitor,
+ 'item_type',
+ unwrappedMonitor.monitor_type || unwrappedMonitor.workflow_type,
+ '-'
+ );
+ break;
}
+
+ unwrappedMonitors.push(monitor);
});
return unwrappedMonitors;
};
@@ -294,7 +304,9 @@ export default class Monitors extends Component {
}
onClickDelete(item) {
- this.deleteMonitors([item]);
+ this.setState({
+ monitorItemsToDelete: [item],
+ });
}
onClickDisable(item) {
@@ -310,7 +322,7 @@ export default class Monitors extends Component {
}
onBulkDelete() {
- this.deleteMonitors(this.state.selectedItems);
+ this.setState({ monitorItemsToDelete: this.state.selectedItems });
}
onBulkDisable() {
@@ -404,67 +416,77 @@ export default class Monitors extends Component {
};
return (
-
- }
- bodyStyles={{ padding: 'initial' }}
- title="Monitors"
- >
-
-
-
-
- {showAcknowledgeModal && (
-
+
+ }
+ bodyStyles={{ padding: 'initial' }}
+ title="Monitors"
+ >
+
- )}
-
+
+ {showAcknowledgeModal && (
+
- }
- onChange={this.onTableChange}
- pagination={pagination}
- selection={selection}
- sorting={sorting}
- />
-
+ )}
+
+
+ }
+ onChange={this.onTableChange}
+ pagination={pagination}
+ selection={selection}
+ sorting={sorting}
+ />
+
+ {this.state.monitorItemsToDelete && (
+ item.name)}
+ closeDeleteModal={() => this.setState({ monitorItemsToDelete: undefined })}
+ onClickDelete={() => this.deleteMonitors(this.state.monitorItemsToDelete)}
+ />
+ )}
+ >
);
}
}
diff --git a/public/pages/Monitors/containers/Monitors/utils/helpers.js b/public/pages/Monitors/containers/Monitors/utils/helpers.js
index 806fb74d0..847bd3a5e 100644
--- a/public/pages/Monitors/containers/Monitors/utils/helpers.js
+++ b/public/pages/Monitors/containers/Monitors/utils/helpers.js
@@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { MONITOR_TYPE } from '../../../../../utils/constants';
import { DEFAULT_QUERY_PARAMS } from './constants';
import queryString from 'query-string';
@@ -25,3 +26,20 @@ export function getURLQueryParams(location) {
state,
};
}
+
+export function getItemLevelType(itemType) {
+ switch (itemType) {
+ case MONITOR_TYPE.QUERY_LEVEL:
+ return 'Per query';
+ case MONITOR_TYPE.BUCKET_LEVEL:
+ return 'Per bucket';
+ case MONITOR_TYPE.CLUSTER_METRICS:
+ return 'Per cluster metrics';
+ case MONITOR_TYPE.DOC_LEVEL:
+ return 'Per document';
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return 'Composite';
+ default:
+ return '-';
+ }
+}
diff --git a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
index 60201bd1f..ad2ae9248 100644
--- a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
+++ b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
@@ -8,6 +8,7 @@ import { EuiLink } from '@elastic/eui';
import moment from 'moment';
import { DEFAULT_EMPTY_DATA } from '../../../../../utils/constants';
import { PLUGIN_NAME } from '../../../../../../utils/constants';
+import { getItemLevelType } from './helpers';
const renderTime = (time) => {
const momentTime = moment(time);
@@ -22,11 +23,28 @@ export const columns = [
sortable: true,
textOnly: true,
render: (name, item) => (
-
+
{name}
),
},
+ {
+ field: 'enabled',
+ name: 'State',
+ sortable: false,
+ truncateText: false,
+ render: (enabled) => (enabled ? 'Enabled' : 'Disabled'),
+ },
+ {
+ field: 'item_type',
+ name: 'Type',
+ sortable: false,
+ truncateText: false,
+ render: (item_type) => getItemLevelType(item_type),
+ },
{
field: 'user',
name: 'Last updated by',
@@ -48,13 +66,6 @@ export const columns = [
truncateText: true,
textOnly: true,
},
- {
- field: 'enabled',
- name: 'State',
- sortable: false,
- truncateText: false,
- render: (enabled) => (enabled ? 'Enabled' : 'Disabled'),
- },
{
field: 'lastNotificationTime',
name: 'Last notification time',
diff --git a/server/services/AlertService.js b/server/services/AlertService.js
index 80d0a1d8b..6f00b2589 100644
--- a/server/services/AlertService.js
+++ b/server/services/AlertService.js
@@ -19,6 +19,7 @@ export default class AlertService {
}
getAlerts = async (context, req, res) => {
+ console.log('****** GET ALERTS *****');
const {
from = 0,
size = 20,
@@ -78,6 +79,7 @@ export default class AlertService {
const { callAsCurrentUser } = this.esDriver.asScoped(req);
try {
const resp = await callAsCurrentUser('alerting.getAlerts', params);
+ console.log(params);
const alerts = resp.alerts.map((hit) => {
const alert = hit;
const id = hit.alert_id;
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 8e4cec408..1869c5324 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -80,6 +80,7 @@ export default class MonitorService {
};
getMonitor = async (context, req, res) => {
+ console.log('****** GET MONITOR *****');
try {
const { id } = req.params;
const params = { monitorId: id };
@@ -147,6 +148,7 @@ export default class MonitorService {
};
getWorkflow = async (context, req, res) => {
+ console.log('****** GET WORKFLOW *****');
try {
const { id } = req.params;
const params = { monitorId: id };
@@ -170,7 +172,7 @@ export default class MonitorService {
},
});
} catch (err) {
- console.error('Alerting - MonitorService - getMonitor:', err);
+ console.error('Alerting - MonitorService - getWorkflow:', err);
return res.ok({
body: {
ok: false,
@@ -218,6 +220,7 @@ export default class MonitorService {
};
getMonitors = async (context, req, res) => {
+ console.log('****** GET MONITORS *****');
try {
const { from, size, search, sortDirection, sortField, state } = req.query;
@@ -235,9 +238,11 @@ export default class MonitorService {
};
}
+ const should = [];
if (state !== 'all') {
const enabled = state === 'enabled';
- filter.push({ term: { 'monitor.enabled': enabled } });
+ should.push({ term: { 'monitor.enabled': enabled } });
+ should.push({ term: { 'workflow.enabled': enabled } });
}
const monitorSorts = { name: 'monitor.name.keyword' };
@@ -255,7 +260,7 @@ export default class MonitorService {
...monitorSortPageData,
query: {
bool: {
- must,
+ should,
},
},
},
From 727a6920847cc8dd8dd699fac2127ec34cb74f17 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 11:05:20 +0200
Subject: [PATCH 17/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/AssociateMonitors.tsx | 2 +-
.../ExpressionQuery/ExpressionQuery.js | 149 ++++++++++--------
2 files changed, 81 insertions(+), 70 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
index e47558c52..2203f5371 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
@@ -49,7 +49,7 @@ const AssociateMonitors = ({
_.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
_.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
} else {
- if (options?.length) {
+ if (options?.length && !graphUi) {
const value = { ...queryTemplate };
const firstTwo = options.slice(0, 2);
firstTwo.map((monitor, idx) => {
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index bb0447797..3713712a5 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -21,8 +21,19 @@ const ExpressionQuery = ({
triggerValues,
isDarkMode = false,
}) => {
- const DEFAULT_DESCRIPTION = defaultText ? defaultText : 'Select';
- const [usedExpressions, setUsedExpressions] = useState([]);
+ const DEFAULT_CONDITION = 'AND';
+ const DEFAULT_NAME = defaultText ? defaultText : 'Select';
+ const DEFAULT_EXPRESSION = {
+ description: '',
+ isOpen: false,
+ monitor_id: '',
+ monitor_name: DEFAULT_NAME,
+ };
+ const DEFAULT_NEXT_EXPRESSION = {
+ ...DEFAULT_EXPRESSION,
+ description: DEFAULT_CONDITION,
+ };
+ const [usedExpressions, setUsedExpressions] = useState([DEFAULT_EXPRESSION]);
const [graphUi, setGraphUi] = useState(triggerValues.searchType === 'graph');
const [editorValue, setEditorValue] = useState('');
@@ -46,7 +57,7 @@ const ExpressionQuery = ({
setEditorValue(script);
_.set(triggerValues, 'triggerDefinitions[0].script.source', script);
}
- }, [value, triggerValues.searchType]);
+ }, [value, triggerValues.searchType, selections]);
const getValue = (expressions) =>
expressions.map((exp) => ({
@@ -57,42 +68,79 @@ const ExpressionQuery = ({
const changeMonitor = (selection, exp, idx, form) => {
const expressions = _.cloneDeep(usedExpressions);
- expressions[idx] = {
- ...expressions[idx],
- monitor_id: selection[0].monitor_id,
- monitor_name: selection[0].label,
- };
+ let monitor = selection[0];
+ if (monitor) {
+ expressions[idx] = {
+ ...expressions[idx],
+ monitor_id: monitor.monitor_id,
+ monitor_name: monitor.label,
+ };
+ } else {
+ expressions[idx] = idx ? DEFAULT_NEXT_EXPRESSION : DEFAULT_EXPRESSION;
+ }
setUsedExpressions(expressions);
- onBlur(form, expressions);
+ onChange(form, expressions);
};
const changeCondition = (selection, exp, idx, form) => {
const expressions = _.cloneDeep(usedExpressions);
expressions[idx] = { ...expressions[idx], description: selection[0].description };
setUsedExpressions(expressions);
- onBlur(form, expressions);
+ onChange(form, expressions);
};
- const onBlur = (form, expressions) => {
+ const onChange = (form, expressions) => {
form.setFieldTouched('expressionQueries', true);
form.setFieldValue(formikName, getValue(expressions));
+ };
+
+ const onBlur = (form, expressions) => {
+ onChange(form, expressions);
form.setFieldError('expressionQueries', validate());
};
- const openPopover = (idx) => {
+ const openPopover = (idx = 0, form) => {
const expressions = _.cloneDeep(usedExpressions);
expressions[idx] = { ...expressions[idx], isOpen: !expressions[idx].isOpen };
setUsedExpressions(expressions);
};
- const closePopover = (idx) => {
+ const closePopover = (idx, form) => {
const expressions = _.cloneDeep(usedExpressions);
expressions[idx] = { ...expressions[idx], isOpen: false };
setUsedExpressions(expressions);
+ onBlur(form, usedExpressions);
+ form.setFieldTouched(`expressionQueries_${idx}`, true);
+ };
+
+ const onRemoveExpression = (idx) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions.splice(idx, 1);
+ expressions.length && (expressions[0].description = '');
+
+ if (!expressions?.length) {
+ expressions.push(DEFAULT_EXPRESSION);
+ }
+ setUsedExpressions([...expressions]);
+ };
+
+ const hasInvalidExpression = () => {
+ return !!usedExpressions.filter((expression) => expression.monitor_id === '')?.length;
+ };
+
+ const isValid = () =>
+ selections.length > 1 && usedExpressions.length > 1 && !hasInvalidExpression();
+
+ const validate = () => {
+ if (selections.length < 2)
+ return 'Trigger condition requires at least two associated monitors.';
+ if (usedExpressions.length < 2)
+ return 'Trigger condition requires at least two monitors selected.';
+ if (hasInvalidExpression()) return 'Invalid expressions.';
};
- const renderOptions = (expression, idx, form) => (
+ const renderOptions = (expression, idx = 0, form) => (
changeCondition(selection, expression, idx, form)}
+ onBlur={() => onBlur(form, usedExpressions)}
options={[
{ description: '', label: '' },
{ description: 'AND', label: 'AND' },
@@ -118,12 +167,7 @@ const ExpressionQuery = ({
{
- const usedExp = _.cloneDeep(usedExpressions);
- usedExp.splice(idx, 1);
- usedExp.length && (usedExp[0].description = '');
- setUsedExpressions([...usedExp]);
- }}
+ onClick={() => onRemoveExpression(idx)}
iconType={'trash'}
color="danger"
aria-label={'Remove condition'}
@@ -138,6 +182,7 @@ const ExpressionQuery = ({
singleSelection={{ asPlainText: true }}
compressed
onChange={(selection) => changeMonitor(selection, expression, idx, form)}
+ onBlur={() => onBlur(form, usedExpressions)}
selectedOptions={[
{
label: expression.monitor_name,
@@ -161,15 +206,6 @@ const ExpressionQuery = ({
/>
);
- const isValid = () => selections.length > 1 && usedExpressions.length > 1;
-
- const validate = () => {
- if (selections.length < 2)
- return 'Trigger condition requires at least two associated monitors.';
- if (usedExpressions.length < 2)
- return 'Trigger condition requires at least two monitors selected.';
- };
-
return (
form.touched['expressionQueries'] && graphUi && !isValid(),
+ isInvalid: () => {
+ return form.touched['expressionQueries'] && graphUi && !isValid();
+ },
error: () => graphUi && validate(),
style: {
maxWidth: 'inherit',
@@ -195,46 +233,29 @@ const ExpressionQuery = ({
data-test-subj={dataTestSubj}
className={'expressionQueries'}
>
- {!usedExpressions.length && (
-
- onBlur(form, usedExpressions)}
- />
- }
- isOpen={false}
- panelPaddingSize="s"
- anchorPosition="rightDown"
- closePopover={() => onBlur(form, usedExpressions)}
- />
-
- )}
{usedExpressions.map((expression, idx) => (
{
e.preventDefault();
- openPopover(idx);
+ openPopover(idx, form);
}}
/>
}
isOpen={expression.isOpen}
- closePopover={() => closePopover(idx)}
+ closePopover={() => closePopover(idx, form)}
panelPaddingSize="s"
anchorPosition="rightDown"
>
@@ -247,19 +268,10 @@ const ExpressionQuery = ({
{
const expressions = _.cloneDeep(usedExpressions);
- const differences = _.differenceBy(selections, expressions, 'monitor_id');
- const newExpressions = [
- ...expressions,
- {
- description: usedExpressions.length ? 'AND' : '',
- isOpen: false,
- monitor_name: differences[0]?.label,
- monitor_id: differences[0]?.monitor_id,
- },
- ];
-
- setUsedExpressions(newExpressions);
- onBlur(form, newExpressions);
+ expressions.push({
+ ...DEFAULT_NEXT_EXPRESSION,
+ });
+ setUsedExpressions(expressions);
}}
color={'primary'}
iconType="plusInCircleFilled"
@@ -290,7 +302,6 @@ const ExpressionQuery = ({
form.setFieldValue('expressionQueries', query);
},
onBlur: (e, field, form) => {
- console.log('### triggerValues', triggerValues);
form.setFieldTouched('expressionQueries', true);
},
'data-test-subj': 'expressionQueriesCodeEditor',
From bf802c11609f2ccdb6f7eca84aa7632e9ccc52b6 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 11:05:56 +0200
Subject: [PATCH 18/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../CreateTrigger/components/ExpressionQuery/ExpressionQuery.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index 3713712a5..8c4a7c443 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -100,7 +100,7 @@ const ExpressionQuery = ({
form.setFieldError('expressionQueries', validate());
};
- const openPopover = (idx = 0, form) => {
+ const openPopover = (idx = 0) => {
const expressions = _.cloneDeep(usedExpressions);
expressions[idx] = { ...expressions[idx], isOpen: !expressions[idx].isOpen };
setUsedExpressions(expressions);
From 475807fb61091d6b353502989ebf1789e24461f0 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 11:43:07 +0200
Subject: [PATCH 19/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../TriggerNotifications.js | 1 +
.../TriggerNotificationsContent.js | 39 ++++++++++++-------
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
index bd5389fbe..089e923bd 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
@@ -129,6 +129,7 @@ const TriggerNotifications = ({
notifications={notifications}
triggerValues={triggerValues}
httpClient={httpClient}
+ hasNotifications={plugins.indexOf(OS_NOTIFICATION_PLUGIN) !== -1}
/>
))}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
index 41cc9e919..b437f4404 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
@@ -10,6 +10,7 @@ import NotificationConfigDialog from './NotificationConfigDialog';
import _ from 'lodash';
import { FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
import { NOTIFY_OPTIONS_VALUES } from '../../components/Action/actions/Message';
+import { required } from '../../../../utils/validate';
const TriggerNotificationsContent = ({
action,
@@ -18,19 +19,21 @@ const TriggerNotificationsContent = ({
triggerValues,
httpClient,
notifications,
+ hasNotifications,
}) => {
const [selected, setSelected] = useState([]);
const [isModalVisible, setIsModalVisible] = useState(false);
useEffect(() => {
- setSelected([
- {
- label: action.name,
- value: action.config_id,
- type: action.config_type,
- description: action.description,
- },
- ]);
+ action?.config_id &&
+ setSelected([
+ {
+ label: action.name,
+ value: action.config_id,
+ type: action.config_type,
+ description: action.description,
+ },
+ ]);
}, [action]);
const onChange = (selectedOptions) => {
@@ -51,6 +54,11 @@ const TriggerNotificationsContent = ({
});
};
+ const onBlur = (e, field, form) => {
+ form.setFieldTouched(field.name, true);
+ form.setFieldError(field.name, required(form.values[field.name]));
+ };
+
const showConfig = () => setIsModalVisible(true);
return (
@@ -66,25 +74,28 @@ const TriggerNotificationsContent = ({
form.touched[name] && !selected?.length,
+ error: (name, form) => form.touched[name] && !selected?.length && 'Required.',
}}
inputProps={{
- isInvalid: !selected.length,
placeholder: 'Select a channel to get notified',
options: options,
selectedOptions: selected,
onChange: (selectedOptions) => onChange(selectedOptions),
+ onBlur: (e, field, form) => onBlur(e, field, form),
singleSelection: { asPlainText: true },
}}
/>
-
+
Manage channels
From c79666694451c29938385422c7c51a3af502ae34 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 11:50:26 +0200
Subject: [PATCH 20/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../{AssociateMonitors.tsx => AssociateMonitors.js} | 0
.../components/{MonitorsList.tsx => MonitorsList.js} | 2 +-
.../containers/WorkflowDetails/WorkflowDetails.tsx | 2 +-
3 files changed, 2 insertions(+), 2 deletions(-)
rename public/pages/CreateMonitor/components/AssociateMonitors/{AssociateMonitors.tsx => AssociateMonitors.js} (100%)
rename public/pages/CreateMonitor/components/AssociateMonitors/components/{MonitorsList.tsx => MonitorsList.js} (99%)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
similarity index 100%
rename from public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.tsx
rename to public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
similarity index 99%
rename from public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
rename to public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index fd2bed0fb..444c11aae 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.tsx
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -24,7 +24,7 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
const [selectedOptions, setSelectedOptions] = useState({});
const [monitorOptions, setMonitorOptions] = useState([]);
- const [monitorFields, setMonitorFields] = useState(
+ const [monitorFields, setMonitorFields] = useState(
_.reduce(
monitors.length ? monitors : [0, 1],
(result, value, key) => {
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
index d31cf77f9..e96434a72 100644
--- a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
@@ -6,7 +6,7 @@
import React, { Fragment, useEffect, useState } from 'react';
import ContentPanel from '../../../../components/ContentPanel';
import Schedule from '../../components/Schedule';
-import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonitors';
+import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonitors.js';
import { EuiSpacer } from '@elastic/eui';
import * as _ from 'lodash';
From 2ad090aa3f94eadf956a35104bfc3d1a0eb5c4aa Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 11:52:08 +0200
Subject: [PATCH 21/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/AssociateMonitors.js | 28 +++++++++----------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
index 2203f5371..e25f17c12 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
@@ -48,23 +48,21 @@ const AssociateMonitors = ({
_.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
_.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
- } else {
- if (options?.length && !graphUi) {
- const value = { ...queryTemplate };
- const firstTwo = options.slice(0, 2);
- firstTwo.map((monitor, idx) => {
- value.sequence.delegates.push({
- order: idx + 1,
- monitor_id: monitor.monitor_id,
- });
+ } else if (options?.length && !graphUi) {
+ const value = { ...queryTemplate };
+ const firstTwo = options.slice(0, 2);
+ firstTwo.map((monitor, idx) => {
+ value.sequence.delegates.push({
+ order: idx + 1,
+ monitor_id: monitor.monitor_id,
});
+ });
- try {
- _.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
- _.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
- } catch (e) {
- console.log('No monitor options are available.');
- }
+ try {
+ _.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
+ _.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
+ } catch (e) {
+ console.log('No monitor options are available.');
}
}
From bcd173bdfb1e72f73311fb05352b4dd32f7f054c Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 11:53:45 +0200
Subject: [PATCH 22/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../components/AssociateMonitors/AssociateMonitors.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
index e25f17c12..56b3b8df3 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
@@ -34,7 +34,7 @@ const AssociateMonitors = ({
useEffect(() => {
if (monitors?.length) {
const value = { ...queryTemplate };
- monitors.map((monitor, idx) => {
+ monitors.forEach((monitor, idx) => {
let delegate = {
order: idx + 1,
monitor_id: monitor.monitor_id,
From 8a024033ce77651fe4d0086d23ace253e7a1e6b0 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 11:55:48 +0200
Subject: [PATCH 23/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../components/AssociateMonitors/AssociateMonitors.js | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
index 56b3b8df3..59b55c658 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
@@ -46,8 +46,12 @@ const AssociateMonitors = ({
});
});
- _.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
- _.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
+ try {
+ _.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
+ _.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
+ } catch (e) {
+ console.log('No monitor options are available.');
+ }
} else if (options?.length && !graphUi) {
const value = { ...queryTemplate };
const firstTwo = options.slice(0, 2);
From 87829fd9e90ebd2d9c37a176b504fe88945f7c71 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 12:03:55 +0200
Subject: [PATCH 24/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/AssociateMonitors.js | 18 +++++++++-------
.../components/MonitorsList.js | 21 +++++++++++--------
2 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
index 59b55c658..761717927 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
@@ -19,6 +19,8 @@ const AssociateMonitors = ({
}) => {
const [graphUi, setGraphUi] = useState(searchType === 'graph');
+ const fieldName = 'associatedMonitors';
+ const fieldEditorName = 'associatedMonitorsEditor';
const queryTemplate = {
sequence: {
delegates: [],
@@ -47,8 +49,8 @@ const AssociateMonitors = ({
});
try {
- _.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
- _.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
+ _.set(monitorValues, fieldEditorName, JSON.stringify(value, null, 4));
+ _.set(monitorValues, fieldName, delegatesToMonitors(value));
} catch (e) {
console.log('No monitor options are available.');
}
@@ -63,8 +65,8 @@ const AssociateMonitors = ({
});
try {
- _.set(monitorValues, 'associatedMonitorsEditor', JSON.stringify(value, null, 4));
- _.set(monitorValues, 'associatedMonitors', delegatesToMonitors(value));
+ _.set(monitorValues, fieldEditorName, JSON.stringify(value, null, 4));
+ _.set(monitorValues, fieldName, delegatesToMonitors(value));
} catch (e) {
console.log('No monitor options are available.');
}
@@ -75,10 +77,10 @@ const AssociateMonitors = ({
const onCodeChange = useCallback(
(query, field, form) => {
- form.setFieldValue('associatedMonitorsEditor', query);
+ form.setFieldValue(fieldEditorName, query);
try {
const code = JSON.parse(query);
- form.setFieldValue('associatedMonitors', delegatesToMonitors(code));
+ form.setFieldValue(fieldName, delegatesToMonitors(code));
} catch (e) {
console.error('Invalid json.');
}
@@ -101,7 +103,7 @@ const AssociateMonitors = ({
) : (
form.setFieldTouched('associatedMonitorsEditor', true),
+ onBlur: (e, field, form) => form.setFieldTouched(fieldEditorName, true),
'data-test-subj': 'associatedMonitorsCodeEditor',
}}
/>
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index 444c11aae..4d75553c2 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -24,6 +24,9 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
const [selectedOptions, setSelectedOptions] = useState({});
const [monitorOptions, setMonitorOptions] = useState([]);
+ const fieldName = 'associatedMonitors';
+ const fieldInputNamePrefix = 'associatedMonitor_';
+
const [monitorFields, setMonitorFields] = useState(
_.reduce(
monitors.length ? monitors : [0, 1],
@@ -77,10 +80,10 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
};
const updateFormik = (monitorIdx, form) => {
- form.setFieldTouched('associatedMonitors', true);
- form.setFieldTouched(`associatedMonitor_${monitorIdx}`, true);
- form.setFieldValue('associatedMonitors', Object.values(selectedOptions));
- form.setFieldError('associatedMonitors', validate());
+ form.setFieldTouched(fieldName, true);
+ form.setFieldTouched(`${fieldInputNamePrefix}${monitorIdx}`, true);
+ form.setFieldValue(fieldName, Object.values(selectedOptions));
+ form.setFieldError(fieldName, validate());
};
const isSelected = (selected, monitor) => {
@@ -130,11 +133,11 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
}}
render={({ field, form }) => (
form.touched['associatedMonitors'] && !isValid(),
+ isInvalid: () => form.touched[fieldName] && !isValid(),
error: () => validate(),
}}
>
@@ -146,11 +149,11 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
>
onChange(options, monitorIdx, form),
From 4e66dd96d338fff7f20ae91d9a1a8ca7a9fede37 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 12:10:07 +0200
Subject: [PATCH 25/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../CreateMonitor/containers/CreateMonitor/CreateMonitor.js | 2 --
1 file changed, 2 deletions(-)
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index c00dfb5c1..5c5c9a8c3 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -254,8 +254,6 @@ export default class CreateMonitor extends Component {
monitor = { ...monitor, ...triggers };
}
- if (monitor.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL) delete monitor.monitor_type;
-
if (edit) this.onUpdate(monitor, formikBag);
else this.onCreate(monitor, formikBag);
}
From 277506ef53e14148e2b6bb05b1b55c35a0ca01b5 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 12:11:25 +0200
Subject: [PATCH 26/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../CreateTrigger/components/ExpressionQuery/ExpressionQuery.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index 8c4a7c443..82b4f4dee 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -22,7 +22,7 @@ const ExpressionQuery = ({
isDarkMode = false,
}) => {
const DEFAULT_CONDITION = 'AND';
- const DEFAULT_NAME = defaultText ? defaultText : 'Select';
+ const DEFAULT_NAME = defaultText ?? 'Select';
const DEFAULT_EXPRESSION = {
description: '',
isOpen: false,
From 64aca9250ec5a3350f293b8f462a7cbe60bc93ae Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 12:17:21 +0200
Subject: [PATCH 27/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../ExpressionQuery/ExpressionQuery.js | 27 ++++++++-----------
1 file changed, 11 insertions(+), 16 deletions(-)
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index 82b4f4dee..e15a14a25 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -33,6 +33,13 @@ const ExpressionQuery = ({
...DEFAULT_EXPRESSION,
description: DEFAULT_CONDITION,
};
+ const EXPRESSION_CONDITIONS_MAP = [
+ { description: '', label: '' },
+ { description: 'AND', label: 'AND' },
+ { description: 'OR', label: 'OR' },
+ { description: 'NOT', label: 'NOT' },
+ ];
+
const [usedExpressions, setUsedExpressions] = useState([DEFAULT_EXPRESSION]);
const [graphUi, setGraphUi] = useState(triggerValues.searchType === 'graph');
const [editorValue, setEditorValue] = useState('');
@@ -155,12 +162,7 @@ const ExpressionQuery = ({
]}
onChange={(selection) => changeCondition(selection, expression, idx, form)}
onBlur={() => onBlur(form, usedExpressions)}
- options={[
- { description: '', label: '' },
- { description: 'AND', label: 'AND' },
- { description: 'OR', label: 'OR' },
- { description: 'NOT', label: 'NOT' },
- ]}
+ options={EXPRESSION_CONDITIONS_MAP}
/>
{renderMonitorOptions(expression, idx, form)}
@@ -218,9 +220,7 @@ const ExpressionQuery = ({
form={form}
rowProps={{
label: label,
- isInvalid: () => {
- return form.touched['expressionQueries'] && graphUi && !isValid();
- },
+ isInvalid: () => form.touched['expressionQueries'] && graphUi && !isValid(),
error: () => graphUi && validate(),
style: {
maxWidth: 'inherit',
@@ -248,10 +248,7 @@ const ExpressionQuery = ({
description={expression.description}
value={expression.monitor_name}
isActive={!!selections?.length}
- onClick={(e) => {
- e.preventDefault();
- openPopover(idx, form);
- }}
+ onClick={(e) => openPopover(idx)}
/>
}
isOpen={expression.isOpen}
@@ -301,9 +298,7 @@ const ExpressionQuery = ({
_.set(triggerValues, 'triggerDefinitions[0].script.source', query);
form.setFieldValue('expressionQueries', query);
},
- onBlur: (e, field, form) => {
- form.setFieldTouched('expressionQueries', true);
- },
+ onBlur: (e, field, form) => form.setFieldTouched('expressionQueries', true),
'data-test-subj': 'expressionQueriesCodeEditor',
}}
/>
From f98973577b5e065e88076c155b9b48b933531b56 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Thu, 22 Jun 2023 12:20:32 +0200
Subject: [PATCH 28/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../ExpressionQuery/ExpressionQuery.js | 27 ++++++++++---------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
index e15a14a25..04280e723 100644
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useState, useCallback } from 'react';
import {
EuiFlexItem,
EuiFlexGroup,
@@ -121,16 +121,19 @@ const ExpressionQuery = ({
form.setFieldTouched(`expressionQueries_${idx}`, true);
};
- const onRemoveExpression = (idx) => {
- const expressions = _.cloneDeep(usedExpressions);
- expressions.splice(idx, 1);
- expressions.length && (expressions[0].description = '');
+ const onRemoveExpression = useCallback(
+ (idx) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions.splice(idx, 1);
+ expressions.length && (expressions[0].description = '');
- if (!expressions?.length) {
- expressions.push(DEFAULT_EXPRESSION);
- }
- setUsedExpressions([...expressions]);
- };
+ if (!expressions?.length) {
+ expressions.push(DEFAULT_EXPRESSION);
+ }
+ setUsedExpressions([...expressions]);
+ },
+ [usedExpressions]
+ );
const hasInvalidExpression = () => {
return !!usedExpressions.filter((expression) => expression.monitor_id === '')?.length;
@@ -214,7 +217,7 @@ const ExpressionQuery = ({
fieldProps={{
validate: () => graphUi && validate(),
}}
- render={({ field, form }) => (
+ render={({ form }) => (
openPopover(idx)}
+ onClick={() => openPopover(idx)}
/>
}
isOpen={expression.isOpen}
From ed738c8456e19ca6a7905d954dacf8ca6b95fa9c Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 23 Jun 2023 15:27:51 +0200
Subject: [PATCH 29/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/AssociateMonitors.js | 129 ++-----
.../components/MonitorsEditor.js | 86 +++++
.../components/MonitorsList.js | 187 ++++++----
.../containers/CreateMonitor/CreateMonitor.js | 14 +-
.../CreateMonitor/utils/constants.js | 14 +-
.../CreateMonitor/utils/formikToMonitor.js | 9 +-
.../CreateMonitor/utils/monitorToFormik.js | 12 +-
.../WorkflowDetails/WorkflowDetails.js | 39 +++
.../WorkflowDetails/WorkflowDetails.tsx | 86 -----
.../CompositeTriggerCondition.js | 93 +++++
.../ExpressionBuilder.js | 327 ++++++++++++++++++
.../ExpressionEditor.js | 52 +++
.../ExpressionQuery.test.ts | 0
.../ExpressionQuery/ExpressionQuery.js | 315 -----------------
.../CreateTrigger/utils/constants.js | 11 +
.../CreateTrigger/utils/formikToTrigger.js | 22 +-
.../CreateTrigger/utils/triggerToFormik.js | 15 +-
.../CompositeMonitorsAlertTrigger.js | 12 +-
.../DefineCompositeLevelTrigger.js | 108 +-----
19 files changed, 797 insertions(+), 734 deletions(-)
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.js
create mode 100644 public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
delete mode 100644 public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
rename public/pages/CreateTrigger/components/{ExpressionQuery => CompositeTriggerCondition}/ExpressionQuery.test.ts (100%)
delete mode 100644 public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
index 761717927..a14a59035 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
@@ -3,90 +3,38 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { Fragment, useState, useEffect, useCallback } from 'react';
+import React, { Fragment, useState, useEffect } from 'react';
import { EuiSpacer, EuiText } from '@elastic/eui';
import MonitorsList from './components/MonitorsList';
-import { FormikCodeEditor } from '../../../../components/FormControls';
-import * as _ from 'lodash';
-import { isInvalid, hasError, validateExtractionQuery } from '../../../../utils/validate';
-
-const AssociateMonitors = ({
- monitors,
- options,
- searchType = 'graph',
- isDarkMode,
- monitorValues,
-}) => {
- const [graphUi, setGraphUi] = useState(searchType === 'graph');
-
- const fieldName = 'associatedMonitors';
- const fieldEditorName = 'associatedMonitorsEditor';
- const queryTemplate = {
- sequence: {
- delegates: [],
+import MonitorsEditor from './components/MonitorsEditor';
+
+export const getMonitors = async (httpClient) => {
+ const response = await httpClient.get('../api/alerting/monitors', {
+ query: {
+ from: 0,
+ size: 1000,
+ search: '',
+ sortField: 'name',
+ sortDirection: 'desc',
+ state: 'all',
},
- };
+ });
+
+ if (response.ok) {
+ const { monitors, totalMonitors } = response;
+ return monitors.map((monitor) => ({ monitor_id: monitor.id, monitor_name: monitor.name }));
+ } else {
+ console.log('error getting monitors:', response);
+ return [];
+ }
+};
- const delegatesToMonitors = (value) =>
- value.sequence.delegates.map((monitor) => ({
- label: '',
- value: monitor.monitor_id,
- }));
+const AssociateMonitors = ({ isDarkMode, values, httpClient }) => {
+ const [graphUi, setGraphUi] = useState(false);
useEffect(() => {
- if (monitors?.length) {
- const value = { ...queryTemplate };
- monitors.forEach((monitor, idx) => {
- let delegate = {
- order: idx + 1,
- monitor_id: monitor.monitor_id,
- };
- value.sequence.delegates.push(delegate);
- _.set(monitorValues, `associatedMonitor_${idx}`, {
- label: monitor.monitor_name || '',
- value: monitor.monitor_id,
- });
- });
-
- try {
- _.set(monitorValues, fieldEditorName, JSON.stringify(value, null, 4));
- _.set(monitorValues, fieldName, delegatesToMonitors(value));
- } catch (e) {
- console.log('No monitor options are available.');
- }
- } else if (options?.length && !graphUi) {
- const value = { ...queryTemplate };
- const firstTwo = options.slice(0, 2);
- firstTwo.map((monitor, idx) => {
- value.sequence.delegates.push({
- order: idx + 1,
- monitor_id: monitor.monitor_id,
- });
- });
-
- try {
- _.set(monitorValues, fieldEditorName, JSON.stringify(value, null, 4));
- _.set(monitorValues, fieldName, delegatesToMonitors(value));
- } catch (e) {
- console.log('No monitor options are available.');
- }
- }
-
- setGraphUi(searchType === 'graph');
- }, [searchType, monitors, options]);
-
- const onCodeChange = useCallback(
- (query, field, form) => {
- form.setFieldValue(fieldEditorName, query);
- try {
- const code = JSON.parse(query);
- form.setFieldValue(fieldName, delegatesToMonitors(code));
- } catch (e) {
- console.error('Invalid json.');
- }
- },
- [options, monitors]
- );
+ setGraphUi(values.searchType === 'graph');
+ }, [values.searchType]);
return (
@@ -100,30 +48,9 @@ const AssociateMonitors = ({
{graphUi ? (
-
+
) : (
- form.setFieldTouched(fieldEditorName, true),
- 'data-test-subj': 'associatedMonitorsCodeEditor',
- }}
- />
+
)}
);
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.js
new file mode 100644
index 000000000..9d8bf16c8
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.js
@@ -0,0 +1,86 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useState, useEffect } from 'react';
+import { FormikCodeEditor } from '../../../../../components/FormControls';
+import { hasError, isInvalid, validateExtractionQuery } from '../../../../../utils/validate';
+
+const MonitorsEditor = ({ values, isDarkMode }) => {
+ const codeFieldName = 'associatedMonitorsEditor';
+ const formikValueName = 'associatedMonitors';
+ const [editorValue, setEditorValue] = useState('');
+
+ useEffect(() => {
+ try {
+ const code = JSON.stringify(values.associatedMonitors, null, 4);
+ // _.set(values, codeFieldName, code);
+ setEditorValue(code);
+ } catch (e) {}
+ }, [values.associatedMonitors]);
+
+ const onCodeChange = (codeValue, field, form) => {
+ form.setFieldValue(codeFieldName, codeValue);
+ form.setFieldTouched(codeFieldName, true);
+ setEditorValue(codeValue);
+
+ try {
+ const code = JSON.parse(codeValue); // test the code before setting it to formik
+ form.setFieldValue(formikValueName, code);
+ } catch (e) {
+ console.error('Invalid json.');
+ }
+ };
+
+ const isInvalid = (name, form) => {
+ if (form.touched[codeFieldName]) {
+ try {
+ const associatedMonitors = form.values[name];
+ const json = JSON.parse(associatedMonitors);
+ return !json.sequence?.delegates?.length;
+ } catch (e) {
+ return false;
+ }
+ }
+ };
+
+ const hasError = (name, form) => {
+ try {
+ const associatedMonitors = form.values[name];
+ const json = JSON.parse(associatedMonitors);
+ return (
+ json.sequence?.delegates?.length < 2 &&
+ 'Delegates list can not be empty or have less then two associated monitors.'
+ );
+ } catch (e) {
+ return 'Invalid json.';
+ }
+ };
+
+ return (
+ isInvalid(name, form),
+ // error: (name, form) => hasError(name, form),
+ }}
+ inputProps={{
+ mode: 'json',
+ width: '80%',
+ height: '300px',
+ theme: isDarkMode ? 'sense-dark' : 'github',
+ onChange: onCodeChange,
+ onBlur: (e, field, form) => form.setFieldTouched(codeFieldName, true),
+ value: editorValue,
+ 'data-test-subj': codeFieldName,
+ }}
+ />
+ );
+};
+
+export default MonitorsEditor;
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index 4d75553c2..0cd9dcb18 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -19,71 +19,118 @@ import {
FormikInputWrapper,
FormikFormRow,
} from '../../../../../components/FormControls';
+import { DEFAULT_ASSOCIATED_MONITORS_VALUE } from '../../../containers/CreateMonitor/utils/constants';
+import { getMonitors } from '../AssociateMonitors';
-const MonitorsList = ({ monitors = [], options = [] }) => {
- const [selectedOptions, setSelectedOptions] = useState({});
- const [monitorOptions, setMonitorOptions] = useState([]);
+const MonitorsList = ({ values, httpClient }) => {
+ const formikFieldName = 'associatedMonitorsList';
+ const formikValueName = 'associatedMonitors';
- const fieldName = 'associatedMonitors';
- const fieldInputNamePrefix = 'associatedMonitor_';
+ const [fields, setFields] = useState([0, 1]);
+ const [options, setOptions] = useState([]);
+ const [selection, setSelection] = useState({});
- const [monitorFields, setMonitorFields] = useState(
+ useEffect(() => {
+ const monitorOptions = _.get(values, 'monitorOptions', []);
+ if (monitorOptions.length) {
+ setOptions(monitorsToOptions(monitorOptions));
+ const selected = formikToSelection(monitorOptions);
+ setFields(generateFields(selected));
+ } else {
+ getMonitors(httpClient).then((monitors) => {
+ _.set(values, 'monitorOptions', monitors);
+
+ setOptions(monitorsToOptions(monitors));
+ const selected = formikToSelection(monitors);
+ setFields(generateFields(selected));
+ });
+ }
+ }, [values]);
+
+ const formikToSelection = (options) => {
+ const associatedMonitors = _.get(
+ values,
+ 'associatedMonitors',
+ DEFAULT_ASSOCIATED_MONITORS_VALUE
+ );
+ const selected = {};
+ associatedMonitors.sequence.delegates.forEach((monitor, index) => {
+ const filteredOption = options.filter((option) => option.monitor_id === monitor.monitor_id);
+ selected[index] = {
+ label: filteredOption[0]?.monitor_name || '',
+ value: monitor.monitor_id,
+ };
+ });
+
+ setSelection(selected);
+ return selected;
+ };
+
+ const generateFields = (selected) =>
_.reduce(
- monitors.length ? monitors : [0, 1],
+ Object.keys(selected).length > 1 ? Object.keys(selected) : [0, 1],
(result, value, key) => {
result.push(key);
return result;
},
[]
- )
- );
+ );
- useEffect(() => {
- const newOptions = [...options].map((monitor) => ({
+ const monitorsToOptions = (monitors) =>
+ monitors.map((monitor, index) => ({
label: monitor.monitor_name,
value: monitor.monitor_id,
}));
- setMonitorOptions(newOptions);
-
- let newSelected = monitors.length
- ? monitors.map((monitor) => ({
- label: monitor.monitor_name,
- value: monitor.monitor_id,
- }))
- : [];
- setSelectedOptions(Object.assign({}, newSelected));
- }, [monitors, options]);
const onChange = (options, monitorIdx, form) => {
- let newSelected = {
- ...selectedOptions,
+ let selected = {
+ ...selection,
};
if (options[0]) {
- newSelected[monitorIdx] = options[0];
+ selected[monitorIdx] = options[0];
} else {
- delete newSelected[monitorIdx];
+ delete selected[monitorIdx];
}
- setSelectedOptions(newSelected);
+ setSelection(selected);
- updateMonitorOptions(newSelected);
+ updateSelection(selected);
- updateFormik(monitorIdx, form);
+ setFormikValues(selected, monitorIdx, form);
};
- const updateMonitorOptions = (selected) => {
- const newMonitorOptions = [...monitorOptions];
+ const updateSelection = (selected) => {
+ const newMonitorOptions = [...options];
newMonitorOptions.forEach((mon) => {
mon.disabled = isSelected(selected, mon);
});
- setMonitorOptions([...newMonitorOptions]);
+ setOptions([...newMonitorOptions]);
};
- const updateFormik = (monitorIdx, form) => {
- form.setFieldTouched(fieldName, true);
- form.setFieldTouched(`${fieldInputNamePrefix}${monitorIdx}`, true);
- form.setFieldValue(fieldName, Object.values(selectedOptions));
- form.setFieldError(fieldName, validate());
+ const setFormikValues = (selected, monitorIdx, form) => {
+ // form.setFieldTouched(formikFieldName, true);
+ // form.setFieldTouched(`${formikFieldName}_${monitorIdx}`, true);
+ // form.setFieldError(formikFieldName, validate());
+
+ const associatedMonitors = _.get(
+ values,
+ 'associatedMonitors',
+ DEFAULT_ASSOCIATED_MONITORS_VALUE
+ );
+ associatedMonitors.sequence.delegates = selectionToFormik(selected);
+ form.setFieldValue(formikValueName, associatedMonitors);
+ };
+
+ const selectionToFormik = (selection) => {
+ const monitors = [];
+ Object.values(selection).forEach((monitor, index) => {
+ monitors.push({
+ order: index + 1,
+ monitor_id: monitor.value,
+ });
+ });
+
+ return monitors;
};
const isSelected = (selected, monitor) => {
@@ -100,26 +147,26 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
};
const onAddMonitor = () => {
- let nextIndex = Math.max(...monitorFields) + 1;
- const newMonitorFields = [...monitorFields, nextIndex];
- setMonitorFields(newMonitorFields);
+ let nextIndex = Math.max(...fields) + 1;
+ const newMonitorFields = [...fields, nextIndex];
+ setFields(newMonitorFields);
};
const onRemoveMonitor = (monitorIdx, idx, form) => {
- const newSelected = { ...selectedOptions };
- delete newSelected[monitorIdx];
- setSelectedOptions(newSelected);
+ const selected = { ...selection };
+ delete selected[monitorIdx];
+ setSelection(selected);
- const newMonitorFields = [...monitorFields];
+ const newMonitorFields = [...fields];
newMonitorFields.splice(idx, 1);
- setMonitorFields(newMonitorFields);
+ setFields(newMonitorFields);
- updateMonitorOptions(newSelected);
+ updateSelection(selected);
- updateFormik(monitorIdx, form);
+ setFormikValues(selected, monitorIdx, form);
};
- const isValid = () => Object.keys(selectedOptions).length > 1;
+ const isValid = () => Object.keys(selection).length > 1;
const validate = () => {
if (!isValid()) return 'Required.';
@@ -127,48 +174,46 @@ const MonitorsList = ({ monitors = [], options = [] }) => {
return (
validate(),
- }}
+ name={formikFieldName}
+ fieldProps={
+ {
+ // validate: () => validate(),
+ }
+ }
render={({ field, form }) => (
form.touched[fieldName] && !isValid(),
+ isInvalid: () => form.touched[formikFieldName] && !isValid(),
error: () => validate(),
}}
>
- {monitorFields.map((monitorIdx, idx) => (
+ {fields.map((monitorIdx, idx) => (
onChange(options, monitorIdx, form),
- onBlur: (e, field, form) => updateFormik(monitorIdx, form),
- options: monitorOptions,
+ // onBlur: (e, field, form) => setFormikValues(monitorIdx, form),
+ options: options,
singleSelection: { asPlainText: true },
- selectedOptions: selectedOptions[monitorIdx]
- ? [selectedOptions[monitorIdx]]
- : undefined,
+ selectedOptions: selection[monitorIdx] ? [selection[monitorIdx]] : undefined,
'data-test-subj': `monitors_list_${monitorIdx}`,
fullWidth: true,
}}
/>
- {selectedOptions[monitorIdx] && (
+ {selection[monitorIdx] && (
{
iconType={'inspect'}
color="text"
target={'_blank'}
- href={`alerting#/monitors/${selectedOptions[monitorIdx].value}?alertState=ALL&from=0&monitorIds=${selectedOptions[monitorIdx].value}&search=&severityLevel=ALL&size=20&sortDirection=desc&sortField=start_time`}
+ href={`alerting#/monitors/${selection[monitorIdx].value}?alertState=ALL&from=0&monitorIds=${selection[monitorIdx].value}&search=&severityLevel=ALL&size=20&sortDirection=desc&sortField=start_time`}
/>
)}
- {monitorFields.length > 2 && (
+ {fields.length > 2 && (
{
))}
- onAddMonitor()}
- disabled={
- monitorFields.length >= 10 ||
- monitorOptions.length <= Object.keys(selectedOptions).length
- }
- >
+ onAddMonitor()} disabled={fields.length >= 10}>
Associate another monitor
- You can associate up to {10 - monitorFields.length} more monitors.
+ You can associate up to {10 - fields.length} more monitors.
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index 5c5c9a8c3..de32bec4d 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -303,14 +303,7 @@ export default class CreateMonitor extends Component {
-
+
@@ -333,10 +326,7 @@ export default class CreateMonitor extends Component {
{values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? (
({
- order: idx + 1,
- monitor_id: monitor.value,
- })),
- },
- },
+ composite_input: values.associatedMonitors,
};
}
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
index d2614c1ed..945a1a3e0 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
@@ -41,18 +41,8 @@ export default function monitorToFormik(monitor) {
case MONITOR_TYPE.DOC_LEVEL:
return docLevelInputToFormik(monitor);
case MONITOR_TYPE.COMPOSITE_LEVEL:
- const associatedMonitors = _.get(
- monitor,
- 'inputs[0].composite_input.sequence.delegates',
- []
- );
-
return {
- inputs: associatedMonitors,
- associatedMonitors: associatedMonitors.map((mon) => ({
- label: '',
- value: mon.monitor_id,
- })),
+ associatedMonitors: _.get(monitor, 'inputs[0].composite_input', {}),
};
default:
return {
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
new file mode 100644
index 000000000..1d3129279
--- /dev/null
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { Fragment } from 'react';
+import ContentPanel from '../../../../components/ContentPanel';
+import Schedule from '../../components/Schedule';
+import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonitors';
+import { EuiSpacer } from '@elastic/eui';
+import { MONITOR_TYPE, SEARCH_TYPE } from '../../../../utils/constants';
+
+const WorkflowDetails = ({ values, isDarkMode, httpClient }) => {
+ const isAd = values.searchType === SEARCH_TYPE.AD;
+ const isComposite = values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL;
+ return (
+
+
+
+ {isComposite && (
+
+
+
+
+ )}
+
+ );
+};
+
+export default WorkflowDetails;
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
deleted file mode 100644
index e96434a72..000000000
--- a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React, { Fragment, useEffect, useState } from 'react';
-import ContentPanel from '../../../../components/ContentPanel';
-import Schedule from '../../components/Schedule';
-import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonitors.js';
-import { EuiSpacer } from '@elastic/eui';
-import * as _ from 'lodash';
-
-export const getMonitors = async (httpClient) => {
- const response = await httpClient.get('../api/alerting/monitors', {
- query: {
- from: 0,
- size: 1000,
- search: '',
- sortField: 'name',
- sortDirection: 'desc',
- state: 'all',
- },
- });
-
- if (response.ok) {
- const { monitors, totalMonitors } = response;
- return monitors.map((monitor) => ({ monitor_id: monitor.id, monitor_name: monitor.name }));
- } else {
- console.log('error getting monitors:', response);
- return [];
- }
-};
-
-const WorkflowDetails = ({ isAd, isComposite, httpClient, history, values, isDarkMode }) => {
- const [selectedMonitors, setSelectedMonitors] = useState([]);
- const [monitorOptions, setMonitorOptions] = useState([]);
-
- useEffect(() => {
- getMonitors(httpClient).then((monitors) => {
- setMonitorOptions(monitors);
-
- const inputIds = values.inputs?.map((input) => input.monitor_id);
- if (inputIds?.length) {
- const selected = monitors.filter((monitor) => inputIds.indexOf(monitor.monitor_id) !== -1);
- setSelectedMonitors(selected);
- _.set(
- values,
- 'associatedMonitors',
- selected.map((monitor) => ({
- value: monitor.monitor_id,
- label: monitor.monitor_name,
- }))
- );
- }
- });
- }, [values.inputs]);
-
- return (
-
-
- {isComposite && (
-
-
-
-
- )}
-
- );
-};
-
-export default WorkflowDetails;
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
new file mode 100644
index 000000000..2f4ee827c
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
@@ -0,0 +1,93 @@
+import React, { useEffect, useState, useCallback } from 'react';
+import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
+import { FormikFormRow, FormikInputWrapper } from '../../../../components/FormControls';
+import ExpressionBuilder from './ExpressionBuilder';
+import ExpressionEditor from './ExpressionEditor';
+
+export const conditionToExpressions = (query, monitors) => {
+ const conditionMap = {
+ '&&': 'and',
+ '||': 'or',
+ '!': 'not',
+ '': '',
+ };
+ const queryToExpressionRegex = new RegExp('( && || \\|\\| )?(monitor\\[id=(.*?)\\])', 'gm');
+ const matcher = query.matchAll(queryToExpressionRegex);
+ let match;
+ let expressions = [];
+ while ((match = matcher.next())) {
+ const monitorId = match[4]?.trim();
+ const monitor = monitors.filter((mon) => mon.monitor_id === monitorId);
+ expressions.push({
+ description: conditionMap[match[1]?.trim()] || '',
+ isOpen: false,
+ monitor_name: monitor[0]?.monitor_name,
+ monitor_id: monitorId,
+ });
+ }
+
+ return expressions;
+};
+
+const CompositeTriggerCondition = ({
+ label,
+ formikFieldPath = '',
+ formikFieldName = 'triggerCondition',
+ values,
+ isDarkMode = false,
+ httpClient,
+}) => {
+ const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
+ const [graphUi, setGraphUi] = useState(values.searchType === 'graph');
+
+ useEffect(() => {
+ setGraphUi(values.searchType === 'graph');
+ }, [values.searchType]);
+
+ const isValid = () => true;
+ const validate = () => {};
+ return (
+ graphUi && validate(),
+ }}
+ render={({ form }) => (
+ form.touched[formikFullFieldName] && !isValid(),
+ error: () => validate(),
+ style: {
+ maxWidth: 'inherit',
+ },
+ }}
+ >
+
+
+ {graphUi ? (
+
+ ) : (
+
+ )}
+
+
+
+ )}
+ />
+ );
+};
+
+export default CompositeTriggerCondition;
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
new file mode 100644
index 000000000..d70275b71
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
@@ -0,0 +1,327 @@
+import React, { useEffect, useState, useCallback } from 'react';
+import {
+ EuiFlexItem,
+ EuiFlexGroup,
+ EuiPopover,
+ EuiComboBox,
+ EuiButtonIcon,
+ EuiExpression,
+} from '@elastic/eui';
+import * as _ from 'lodash';
+import { FormikFormRow, FormikInputWrapper } from '../../../../components/FormControls';
+import { getMonitors } from '../../../CreateMonitor/components/AssociateMonitors/AssociateMonitors';
+
+export const conditionToExpressions = (condition, monitors) => {
+ if (!condition.length) return [];
+
+ const conditionMap = {
+ '&&': 'AND',
+ '||': 'OR',
+ '!': 'NOT',
+ '': '',
+ };
+ const queryToExpressionRegex = new RegExp(/( && || \|\| )?(monitor\[id=(.*?)\])/, 'gm');
+ const matcher = condition.matchAll(queryToExpressionRegex);
+ let match;
+ let expressions = [];
+ let counter = 0;
+ while ((match = matcher.next().value)) {
+ if (counter && !match[1]) return; // Didn't find condition after the first match
+
+ const monitorId = match[3]?.trim(); // match [3] is the monitor_id
+ const monitor = monitors.filter((mon) => mon.monitor_id === monitorId);
+ expressions.push({
+ description: conditionMap[match[1]?.trim()] || '', // match [1] is the description/condition
+ isOpen: false,
+ monitor_name: monitor[0]?.monitor_name,
+ monitor_id: monitorId,
+ });
+
+ counter++;
+ }
+
+ return expressions;
+};
+
+const ExpressionBuilder = ({ formikFieldPath = '', formikFieldName, values, httpClient }) => {
+ const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
+
+ const DEFAULT_CONDITION = 'AND';
+ const DEFAULT_NAME = 'Select associated monitor';
+ const DEFAULT_EXPRESSION = {
+ description: '',
+ isOpen: false,
+ monitor_id: '',
+ monitor_name: DEFAULT_NAME,
+ };
+ const DEFAULT_NEXT_EXPRESSION = {
+ ...DEFAULT_EXPRESSION,
+ description: DEFAULT_CONDITION,
+ };
+ const EXPRESSION_CONDITIONS_MAP = [
+ { description: '', label: '' },
+ { description: 'AND', label: 'AND' },
+ { description: 'OR', label: 'OR' },
+ { description: 'NOT', label: 'NOT' },
+ ];
+
+ const [usedExpressions, setUsedExpressions] = useState([DEFAULT_EXPRESSION]);
+ const [options, setOptions] = useState([]);
+
+ useEffect(() => {
+ const monitors = _.get(values, 'monitorOptions', []);
+ if (monitors.length) {
+ setInitialValues(monitors);
+ } else {
+ getMonitors(httpClient).then((monitors) => {
+ _.set(values, 'monitorOptions', monitors);
+ setInitialValues(monitors);
+ });
+ }
+ }, [values.associatedMonitors?.sequence?.delegates, values[formikFullFieldName]]);
+
+ const setInitialValues = (monitors) => {
+ const monitorOptions = [];
+ const associatedMonitors = _.get(values, 'associatedMonitors', {});
+ associatedMonitors.sequence.delegates.forEach((monitor, index) => {
+ const filteredOption = monitors.filter((option) => option.monitor_id === monitor.monitor_id);
+ monitorOptions.push({
+ label: filteredOption[0]?.monitor_name || '',
+ monitor_id: monitor.monitor_id,
+ });
+ });
+ setOptions(monitorOptions);
+
+ const condition = _.get(values, formikFullFieldName, '');
+ const expressions = conditionToExpressions(condition, monitors);
+ setUsedExpressions(expressions.length ? expressions : [DEFAULT_EXPRESSION]);
+ };
+
+ const expressionsToCondition = (expressions) => {
+ const conditionMap = {
+ AND: '&&',
+ OR: '||',
+ NOT: '!',
+ '': '',
+ };
+
+ const condition = expressions.reduce((query, expression) => {
+ if (expression?.monitor_id) {
+ query += ` ${conditionMap[expression.description]} monitor[id=${expression.monitor_id}]`;
+ query = query.trim();
+ }
+ return query;
+ }, '');
+
+ return `(${condition})`;
+ };
+
+ const changeMonitor = (selection, exp, idx, form) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ let monitor = selection[0];
+
+ if (monitor?.monitor_id) {
+ expressions[idx] = {
+ ...expressions[idx],
+ monitor_id: monitor.monitor_id,
+ monitor_name: monitor.label,
+ };
+ } else {
+ expressions[idx] = idx ? DEFAULT_NEXT_EXPRESSION : DEFAULT_EXPRESSION;
+ }
+
+ setUsedExpressions(expressions);
+ onChange(form, expressions);
+ };
+
+ const changeCondition = (selection, exp, idx, form) => {
+ const expressions = _.cloneDeep(usedExpressions);
+
+ expressions[idx] = { ...expressions[idx], description: selection[0].description };
+ setUsedExpressions(expressions);
+ onChange(form, expressions);
+ };
+
+ const onChange = (form, expressions) => {
+ form.setFieldValue(formikFullFieldName, expressionsToCondition(expressions));
+ };
+
+ const onBlur = (form, expressions) => {
+ onChange(form, expressions);
+ form.setFieldTouched(`${formikFullFieldName}_value`, true);
+ };
+
+ const openPopover = (idx = 0) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions[idx] = { ...expressions[idx], isOpen: !expressions[idx].isOpen };
+ setUsedExpressions(expressions);
+ };
+
+ const closePopover = (idx, form) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions[idx] = { ...expressions[idx], isOpen: false };
+ setUsedExpressions(expressions);
+ onBlur(form, expressions);
+ form.setFieldTouched(`expressionQueries_${idx}`, true);
+ };
+
+ const onRemoveExpression = useCallback(
+ (form, idx) => {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions.splice(idx, 1);
+ expressions.length && (expressions[0].description = '');
+
+ if (!expressions?.length) {
+ expressions.push(DEFAULT_EXPRESSION);
+ }
+ setUsedExpressions([...expressions]);
+ onChange(form, expressions);
+ },
+ [usedExpressions]
+ );
+
+ const hasInvalidExpression = () =>
+ !!usedExpressions.filter((expression) => expression.monitor_id === '')?.length;
+
+ const isValid = () => options.length > 1 && usedExpressions.length > 1 && !hasInvalidExpression();
+
+ const validate = () => {
+ if (options.length < 2) return 'Trigger condition requires at least two associated monitors.';
+ if (usedExpressions.length < 2)
+ return 'Trigger condition requires at least two monitors selected.';
+ if (hasInvalidExpression()) return 'Invalid expressions.';
+ };
+
+ const renderOptions = (expression, idx = 0, form) => (
+
+
+ changeCondition(selection, expression, idx, form)}
+ onBlur={() => onBlur(form, usedExpressions)}
+ options={EXPRESSION_CONDITIONS_MAP}
+ />
+
+ {renderMonitorOptions(expression, idx, form)}
+
+ onRemoveExpression(form, idx)}
+ iconType={'trash'}
+ color="danger"
+ aria-label={'Remove condition'}
+ style={{ marginTop: '4px' }}
+ />
+
+
+ );
+
+ const renderMonitorOptions = (expression, idx, form) => (
+ changeMonitor(selection, expression, idx, form)}
+ onBlur={() => onBlur(form, usedExpressions)}
+ selectedOptions={[
+ {
+ label: expression.monitor_name,
+ monitor_id: expression.monitor_id,
+ },
+ ]}
+ style={{ width: '250px' }}
+ options={(() => {
+ const differences = _.differenceBy(options, usedExpressions, 'monitor_id');
+ return [
+ {
+ monitor_id: expression.monitor_id,
+ label: expression.monitor_name,
+ },
+ ...differences.map((sel) => ({
+ monitor_id: sel.monitor_id,
+ label: sel.label,
+ })),
+ ];
+ })()}
+ />
+ );
+
+ return (
+ validate(),
+ }}
+ render={({ form }) => (
+ form.touched[`${formikFullFieldName}_value`] && !isValid(),
+ error: () => validate(),
+ style: {
+ maxWidth: 'inherit',
+ },
+ }}
+ >
+
+ {usedExpressions.map((expression, idx) => (
+
+ openPopover(idx)}
+ />
+ }
+ isOpen={expression.isOpen}
+ closePopover={() => closePopover(idx, form)}
+ panelPaddingSize="s"
+ anchorPosition="upCenter"
+ >
+ {renderOptions(expression, idx, form)}
+
+
+ ))}
+ {options.length > usedExpressions.length && (
+
+ {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions.push({
+ ...DEFAULT_NEXT_EXPRESSION,
+ });
+ setUsedExpressions(expressions);
+ }}
+ color={'primary'}
+ iconType="plusInCircleFilled"
+ aria-label={'Add one more condition'}
+ data-test-subj={'condition-add-options-btn'}
+ style={{ marginTop: '1px' }}
+ />
+
+ )}
+
+
+ )}
+ />
+ );
+};
+
+export default ExpressionBuilder;
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
new file mode 100644
index 000000000..80224d4a1
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
@@ -0,0 +1,52 @@
+import React, { useEffect, useState } from 'react';
+import * as _ from 'lodash';
+import { FormikCodeEditor } from '../../../../components/FormControls';
+
+const ExpressionEditor = ({ values, formikFieldName, formikFieldPath, isDarkMode = false }) => {
+ const [editorValue, setEditorValue] = useState('');
+ const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
+ const formikFullCodeFieldName = 'triggerConditionsCode';
+
+ useEffect(() => {
+ const code = _.get(values, formikFullFieldName, '');
+ // _.set(values, formikFullCodeFieldName, code);
+ setEditorValue(code);
+ }, [values]);
+
+ const isInvalid = (name, form) => !form.values[name]?.length;
+
+ const hasError = (name, form) => {
+ return !form.values[name]?.length && 'Invalid condition.';
+ };
+
+ return (
+ form.touched[name] && isInvalid(name, form),
+ // error: (name, form) => hasError(name, form),
+ }}
+ inputProps={{
+ // isInvalid: (name, form) => form.touched[name] && isInvalid(name, form),
+ mode: 'text',
+ width: '80%',
+ height: '300px',
+ theme: isDarkMode ? 'sense-dark' : 'github',
+ value: editorValue,
+ onChange: (code, field, form) => {
+ form.setFieldTouched(formikFullCodeFieldName, true);
+ form.setFieldValue(formikFullFieldName, code);
+ form.setFieldValue(formikFullCodeFieldName, code);
+ },
+ onBlur: (e, field, form) => form.setFieldTouched(field.name, true),
+ 'data-test-subj': 'compositeTriggerConditionEditor',
+ }}
+ />
+ );
+};
+
+export default ExpressionEditor;
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.test.ts b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionQuery.test.ts
similarity index 100%
rename from public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.test.ts
rename to public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionQuery.test.ts
diff --git a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js b/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
deleted file mode 100644
index 04280e723..000000000
--- a/public/pages/CreateTrigger/components/ExpressionQuery/ExpressionQuery.js
+++ /dev/null
@@ -1,315 +0,0 @@
-import React, { useEffect, useState, useCallback } from 'react';
-import {
- EuiFlexItem,
- EuiFlexGroup,
- EuiPopover,
- EuiComboBox,
- EuiButtonIcon,
- EuiExpression,
-} from '@elastic/eui';
-import * as _ from 'lodash';
-import { FormikFormRow, FormikInputWrapper } from '../../../../components/FormControls';
-import { FormikCodeEditor } from '../../../../components/FormControls';
-
-const ExpressionQuery = ({
- selections,
- dataTestSubj,
- value,
- defaultText,
- label,
- formikName = 'expressionQueries',
- triggerValues,
- isDarkMode = false,
-}) => {
- const DEFAULT_CONDITION = 'AND';
- const DEFAULT_NAME = defaultText ?? 'Select';
- const DEFAULT_EXPRESSION = {
- description: '',
- isOpen: false,
- monitor_id: '',
- monitor_name: DEFAULT_NAME,
- };
- const DEFAULT_NEXT_EXPRESSION = {
- ...DEFAULT_EXPRESSION,
- description: DEFAULT_CONDITION,
- };
- const EXPRESSION_CONDITIONS_MAP = [
- { description: '', label: '' },
- { description: 'AND', label: 'AND' },
- { description: 'OR', label: 'OR' },
- { description: 'NOT', label: 'NOT' },
- ];
-
- const [usedExpressions, setUsedExpressions] = useState([DEFAULT_EXPRESSION]);
- const [graphUi, setGraphUi] = useState(triggerValues.searchType === 'graph');
- const [editorValue, setEditorValue] = useState('');
-
- const getQueryTemplate = (monitor_id) => `monitor[id=${monitor_id}]`;
- const queryConditionOperator = '&&';
-
- useEffect(() => {
- if (value?.length) {
- setUsedExpressions(value);
- _.set(triggerValues, formikName, getValue(value));
- }
-
- setGraphUi(triggerValues.searchType === 'graph');
-
- if (selections?.length) {
- const editorValues = [];
- selections.map((selection) => {
- editorValues.push(getQueryTemplate(selection.monitor_id));
- });
- const script = editorValues.join(` ${queryConditionOperator} `);
- setEditorValue(script);
- _.set(triggerValues, 'triggerDefinitions[0].script.source', script);
- }
- }, [value, triggerValues.searchType, selections]);
-
- const getValue = (expressions) =>
- expressions.map((exp) => ({
- condition: _.toLower(exp.description),
- monitor_id: exp.monitor_id,
- monitor_name: exp.name,
- }));
-
- const changeMonitor = (selection, exp, idx, form) => {
- const expressions = _.cloneDeep(usedExpressions);
- let monitor = selection[0];
- if (monitor) {
- expressions[idx] = {
- ...expressions[idx],
- monitor_id: monitor.monitor_id,
- monitor_name: monitor.label,
- };
- } else {
- expressions[idx] = idx ? DEFAULT_NEXT_EXPRESSION : DEFAULT_EXPRESSION;
- }
-
- setUsedExpressions(expressions);
- onChange(form, expressions);
- };
-
- const changeCondition = (selection, exp, idx, form) => {
- const expressions = _.cloneDeep(usedExpressions);
- expressions[idx] = { ...expressions[idx], description: selection[0].description };
- setUsedExpressions(expressions);
- onChange(form, expressions);
- };
-
- const onChange = (form, expressions) => {
- form.setFieldTouched('expressionQueries', true);
- form.setFieldValue(formikName, getValue(expressions));
- };
-
- const onBlur = (form, expressions) => {
- onChange(form, expressions);
- form.setFieldError('expressionQueries', validate());
- };
-
- const openPopover = (idx = 0) => {
- const expressions = _.cloneDeep(usedExpressions);
- expressions[idx] = { ...expressions[idx], isOpen: !expressions[idx].isOpen };
- setUsedExpressions(expressions);
- };
-
- const closePopover = (idx, form) => {
- const expressions = _.cloneDeep(usedExpressions);
- expressions[idx] = { ...expressions[idx], isOpen: false };
- setUsedExpressions(expressions);
- onBlur(form, usedExpressions);
- form.setFieldTouched(`expressionQueries_${idx}`, true);
- };
-
- const onRemoveExpression = useCallback(
- (idx) => {
- const expressions = _.cloneDeep(usedExpressions);
- expressions.splice(idx, 1);
- expressions.length && (expressions[0].description = '');
-
- if (!expressions?.length) {
- expressions.push(DEFAULT_EXPRESSION);
- }
- setUsedExpressions([...expressions]);
- },
- [usedExpressions]
- );
-
- const hasInvalidExpression = () => {
- return !!usedExpressions.filter((expression) => expression.monitor_id === '')?.length;
- };
-
- const isValid = () =>
- selections.length > 1 && usedExpressions.length > 1 && !hasInvalidExpression();
-
- const validate = () => {
- if (selections.length < 2)
- return 'Trigger condition requires at least two associated monitors.';
- if (usedExpressions.length < 2)
- return 'Trigger condition requires at least two monitors selected.';
- if (hasInvalidExpression()) return 'Invalid expressions.';
- };
-
- const renderOptions = (expression, idx = 0, form) => (
-
-
- changeCondition(selection, expression, idx, form)}
- onBlur={() => onBlur(form, usedExpressions)}
- options={EXPRESSION_CONDITIONS_MAP}
- />
-
- {renderMonitorOptions(expression, idx, form)}
-
- onRemoveExpression(idx)}
- iconType={'trash'}
- color="danger"
- aria-label={'Remove condition'}
- style={{ marginTop: '4px' }}
- />
-
-
- );
-
- const renderMonitorOptions = (expression, idx, form) => (
- changeMonitor(selection, expression, idx, form)}
- onBlur={() => onBlur(form, usedExpressions)}
- selectedOptions={[
- {
- label: expression.monitor_name,
- monitor_id: expression.monitor_id,
- },
- ]}
- style={{ width: '250px' }}
- options={(() => {
- const differences = _.differenceBy(selections, usedExpressions, 'monitor_id');
- return [
- {
- monitor_id: expression.monitor_id,
- label: expression.monitor_name,
- },
- ...differences.map((sel) => ({
- monitor_id: sel.monitor_id,
- label: sel.label,
- })),
- ];
- })()}
- />
- );
-
- return (
- graphUi && validate(),
- }}
- render={({ form }) => (
- form.touched['expressionQueries'] && graphUi && !isValid(),
- error: () => graphUi && validate(),
- style: {
- maxWidth: 'inherit',
- },
- }}
- >
- {graphUi ? (
-
- {usedExpressions.map((expression, idx) => (
-
- openPopover(idx)}
- />
- }
- isOpen={expression.isOpen}
- closePopover={() => closePopover(idx, form)}
- panelPaddingSize="s"
- anchorPosition="rightDown"
- >
- {renderOptions(expression, idx, form)}
-
-
- ))}
- {selections.length > usedExpressions.length && (
-
- {
- const expressions = _.cloneDeep(usedExpressions);
- expressions.push({
- ...DEFAULT_NEXT_EXPRESSION,
- });
- setUsedExpressions(expressions);
- }}
- color={'primary'}
- iconType="plusInCircleFilled"
- aria-label={'Add one more condition'}
- data-test-subj={'condition-add-selection-btn'}
- style={{ marginTop: '1px' }}
- />
-
- )}
-
- ) : (
- {
- _.set(triggerValues, 'triggerDefinitions[0].script.source', query);
- form.setFieldValue('expressionQueries', query);
- },
- onBlur: (e, field, form) => form.setFieldTouched('expressionQueries', true),
- 'data-test-subj': 'expressionQueriesCodeEditor',
- }}
- />
- )}
-
- )}
- />
- );
-};
-
-export default ExpressionQuery;
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
index 7a4ee9f61..b34e433e3 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/constants.js
@@ -68,6 +68,17 @@ export const FORMIK_INITIAL_TRIGGER_VALUES = {
actions: undefined,
};
+export const FORMIK_COMPOSITE_INITIAL_TRIGGER_VALUES = {
+ name: '',
+ severity: '1',
+ script: {
+ lang: 'painless',
+ source: ``,
+ },
+ triggerConditions: '',
+ actions: undefined,
+};
+
export const FORMIK_INITIAL_DOC_LEVEL_SCRIPT = {
lang: FORMIK_INITIAL_TRIGGER_VALUES.script.lang,
source: '(query[name=] || query[name=]) && query[tag=]',
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
index afdc5979c..ed9fe4ed4 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/formikToTrigger.js
@@ -116,28 +116,12 @@ export function formikToDocumentLevelTriggerCondition(values, monitorUiMetadata)
}
export function formikToCompositeTriggerCondition(values) {
- const conditionMap = {
- and: '&&',
- or: '||',
- not: '!',
- '': '',
- };
-
- const triggerConditions = _.get(values, 'triggerConditions', []);
- let source = triggerConditions.reduce((query, expression) => {
- query += ` ${conditionMap[expression.condition]} monitor[id=${expression.monitor_id}]`;
- query = query.trim();
- return query;
- }, '');
-
- if (!source) {
- source = _.get(values, 'script.source', '');
- }
+ const triggerConditions = _.get(values, 'triggerConditions', '');
return {
script: {
lang: 'painless',
- source: `(${source})`,
+ source: triggerConditions,
},
};
}
@@ -314,7 +298,7 @@ export function formikToTriggerUiMetadata(values, monitorUiMetadata) {
case MONITOR_TYPE.COMPOSITE_LEVEL:
const compositeTriggersUiMetadata = {};
_.get(values, 'triggerDefinitions', []).forEach((trigger) => {
- compositeTriggersUiMetadata[trigger.name] = _.get(trigger, 'triggerConditions', []);
+ compositeTriggersUiMetadata[trigger.name] = _.get(trigger, 'triggerConditions', '');
});
return compositeTriggersUiMetadata;
}
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
index 4b45d270c..36e416c42 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
@@ -14,7 +14,7 @@ import {
ACTIONABLE_ALERTS_OPTIONS_LABELS,
NOTIFY_OPTIONS_VALUES,
} from '../../../components/Action/actions/Message';
-import { convertQueryToExpressions } from '../../DefineCompositeLevelTrigger/DefineCompositeLevelTrigger';
+import { conditionToExpressions } from '../../../components/CompositeTriggerCondition/CompositeTriggerCondition';
export function triggerToFormik(trigger, monitor) {
return _.isArray(trigger)
@@ -227,12 +227,13 @@ export function compositeTriggerToFormik(trigger, monitor) {
condition: { script },
actions,
} = trigger[TRIGGER_TYPE.COMPOSITE_LEVEL];
- // TODO this should be saved in ui_metadata, currently workflows don't save ui_metadata
- // const triggerUiMetadata = _.get(monitor, `ui_metadata.triggers[${name}]`);
- const triggerConditions = convertQueryToExpressions(
- monitor.triggers[0].chained_alert_trigger.condition.script.source,
- []
+
+ const triggerConditions = _.get(
+ monitor,
+ 'triggers[0].chained_alert_trigger.condition.script.source',
+ ''
);
+
return {
..._.cloneDeep(FORMIK_INITIAL_TRIGGER_VALUES),
id,
@@ -240,7 +241,7 @@ export function compositeTriggerToFormik(trigger, monitor) {
severity,
script,
actions: getExecutionPolicyActions(actions),
- triggerConditions: triggerConditions, //triggerUiMetadata,
+ triggerConditions: triggerConditions,
};
}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
index b5d40400a..9590925c0 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
@@ -7,25 +7,17 @@ import React, { Fragment } from 'react';
import DefineCompositeLevelTrigger from './DefineCompositeLevelTrigger';
const CompositeMonitorsAlertTrigger = ({
- edit,
- triggerArrayHelpers,
- monitor,
- monitorValues,
- triggerValues,
isDarkMode,
httpClient,
notifications,
notificationService,
plugins,
+ values,
}) => {
return (
(
);
-export const convertQueryToExpressions = (query, monitors) => {
- const conditionMap = {
- '&&': 'and',
- '||': 'or',
- '!': 'not',
- '': '',
- };
- const queryToExpressionRegex = new RegExp('(&& )?(\\|\\| )?(monitor\\[id=(.*?)\\])', 'g');
- const matcher = query.matchAll(queryToExpressionRegex);
- let match;
- let expressions = [];
- while ((match = matcher.next().value)) {
- const monitorId = match[4]?.trim();
- const monitor = monitors.filter((mon) => mon.monitor_id === monitorId);
- expressions.push({
- description: conditionMap[match[1]?.trim()] || '',
- isOpen: false,
- monitor_name: monitor[0]?.monitor_name,
- monitor_id: monitorId,
- });
- }
-
- return expressions;
-};
-
class DefineCompositeLevelTrigger extends Component {
- constructor(props) {
- super(props);
- this.state = {
- expressions: [],
- };
- }
-
- componentDidMount() {
- getMonitors(this.props.httpClient).then((monitors) => {
- const inputIds = this.props.monitorValues.inputs?.map((input) => input.monitor_id);
- if (inputIds && inputIds.length) {
- const selectedMonitors = monitors.filter(
- (monitor) => inputIds.indexOf(monitor.monitor_id) !== -1
- );
-
- const expressions = convertQueryToExpressions(
- this.props.triggerValues.triggerDefinitions[0].script.source,
- selectedMonitors
- );
-
- this.setState({
- expressions,
- });
- }
- });
- }
-
render() {
- const {
- edit,
- monitorValues,
- triggerValues,
- httpClient,
- notifications,
- notificationService,
- plugins,
- } = this.props;
-
- const fieldPath = `triggerDefinitions[0].`;
- const triggerName = _.get(triggerValues, `${fieldPath}name`, 'Trigger');
- const triggerDefinitions = _.get(triggerValues, 'triggerDefinitions', []);
- _.set(triggerValues, 'triggerDefinitions', [
+ const { values, httpClient, notifications, notificationService, plugins } = this.props;
+
+ const formikFieldPath = `triggerDefinitions[0].`;
+ const triggerName = _.get(values, `${formikFieldPath}name`, 'Trigger');
+ const triggerDefinitions = _.get(values, 'triggerDefinitions', []);
+ _.set(values, 'triggerDefinitions', [
{
- ...FORMIK_INITIAL_TRIGGER_VALUES,
+ ...FORMIK_COMPOSITE_INITIAL_TRIGGER_VALUES,
...triggerDefinitions[0],
severity: 1,
name: triggerName,
},
]);
- const triggerActions = _.get(triggerValues, 'triggerDefinitions[0].actions', []);
- const monitorList = monitorValues?.associatedMonitors
- ? monitorValues.associatedMonitors?.map((monitor) => ({
- label: monitor.label?.replaceAll(' ', '_'),
- monitor_id: monitor.value,
- }))
- : [];
+ const triggerActions = _.get(values, `${formikFieldPath}.actions`, []);
return (
-
{titleTemplate('Alert severity')}
From 5a7fe5e369a94a53964503dba092d8005fde1a26 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 23 Jun 2023 17:27:04 +0200
Subject: [PATCH 30/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../components/MonitorsList.js | 24 ++++++++++---------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index 0cd9dcb18..921935728 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -98,6 +98,11 @@ const MonitorsList = ({ values, httpClient }) => {
setFormikValues(selected, monitorIdx, form);
};
+ const onBlur = (e, field, form) => {
+ form.setFieldTouched(formikFieldName, true);
+ form.setFieldTouched(field.name, true);
+ };
+
const updateSelection = (selected) => {
const newMonitorOptions = [...options];
newMonitorOptions.forEach((mon) => {
@@ -108,10 +113,6 @@ const MonitorsList = ({ values, httpClient }) => {
};
const setFormikValues = (selected, monitorIdx, form) => {
- // form.setFieldTouched(formikFieldName, true);
- // form.setFieldTouched(`${formikFieldName}_${monitorIdx}`, true);
- // form.setFieldError(formikFieldName, validate());
-
const associatedMonitors = _.get(
values,
'associatedMonitors',
@@ -175,18 +176,19 @@ const MonitorsList = ({ values, httpClient }) => {
return (
validate(),
- }
- }
+ fieldProps={{
+ validate: () => validate(),
+ }}
render={({ field, form }) => (
form.touched[formikFieldName] && !isValid(),
+ isInvalid: () => {
+ console.log(form.touched[formikFieldName], isValid());
+ return form.touched[formikFieldName] && !isValid();
+ },
error: () => validate(),
}}
>
@@ -204,7 +206,7 @@ const MonitorsList = ({ values, httpClient }) => {
form.touched[`${formikFieldName}_${monitorIdx}`] && !selection[monitorIdx],
placeholder: 'Select a monitor',
onChange: (options, field, form) => onChange(options, monitorIdx, form),
- // onBlur: (e, field, form) => setFormikValues(monitorIdx, form),
+ onBlur: (e, field, form) => onBlur(e, field, form),
options: options,
singleSelection: { asPlainText: true },
selectedOptions: selection[monitorIdx] ? [selection[monitorIdx]] : undefined,
From b43dda71bcc123093c1b583e3b467fbb429d5632 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 23 Jun 2023 17:27:27 +0200
Subject: [PATCH 31/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../components/AssociateMonitors/components/MonitorsList.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index 921935728..4045f47a3 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -77,7 +77,7 @@ const MonitorsList = ({ values, httpClient }) => {
);
const monitorsToOptions = (monitors) =>
- monitors.map((monitor, index) => ({
+ monitors.map((monitor) => ({
label: monitor.monitor_name,
value: monitor.monitor_id,
}));
@@ -179,7 +179,7 @@ const MonitorsList = ({ values, httpClient }) => {
fieldProps={{
validate: () => validate(),
}}
- render={({ field, form }) => (
+ render={({ form }) => (
Date: Fri, 23 Jun 2023 17:33:58 +0200
Subject: [PATCH 32/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../components/AssociateMonitors/components/MonitorsList.js | 5 +----
.../containers/CreateMonitor/utils/constants.js | 1 +
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index 4045f47a3..487ac2082 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -185,10 +185,7 @@ const MonitorsList = ({ values, httpClient }) => {
form={form}
rowProps={{
label: 'Monitor',
- isInvalid: () => {
- console.log(form.touched[formikFieldName], isValid());
- return form.touched[formikFieldName] && !isValid();
- },
+ isInvalid: () => form.touched[formikFieldName] && !isValid(),
error: () => validate(),
}}
>
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
index 0675b74f5..1d150e704 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
@@ -73,6 +73,7 @@ export const FORMIK_INITIAL_VALUES = {
filters: [], // array of FORMIK_INITIAL_WHERE_EXPRESSION_VALUES
detectorId: '',
associatedMonitors: DEFAULT_ASSOCIATED_MONITORS_VALUE,
+ associatedMonitorsList: [],
};
export const FORMIK_INITIAL_AGG_VALUES = {
From c806609bc74df0ec658c723c5e96f4ff801219af Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 23 Jun 2023 17:34:13 +0200
Subject: [PATCH 33/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../CreateMonitor/containers/CreateMonitor/utils/constants.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
index 1d150e704..6e5cf9ebe 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
@@ -9,7 +9,6 @@ import {
} from '../../../components/MonitorExpressions/expressions/utils/constants';
import { MONITOR_TYPE } from '../../../../../utils/constants';
import { SUPPORTED_DOC_LEVEL_QUERY_OPERATORS } from '../../../components/DocumentLevelMonitorQueries/utils/constants';
-import { QUERY_OPERATORS } from '../../../../Dashboard/components/FindingsDashboard/findingsUtils';
export const BUCKET_COUNT = 5;
From 77c23d7b95f4a31a3404facc80c7453d7e520d2f Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 23 Jun 2023 17:42:48 +0200
Subject: [PATCH 34/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../components/AssociateMonitors/components/MonitorsList.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index 487ac2082..a90a63e8d 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -200,7 +200,9 @@ const MonitorsList = ({ values, httpClient }) => {
name={`${formikFieldName}_${monitorIdx}`}
inputProps={{
isInvalid:
- form.touched[`${formikFieldName}_${monitorIdx}`] && !selection[monitorIdx],
+ (form.touched[`${formikFieldName}_${monitorIdx}`] ||
+ form.touched[formikFieldName]) &&
+ !selection[monitorIdx],
placeholder: 'Select a monitor',
onChange: (options, field, form) => onChange(options, monitorIdx, form),
onBlur: (e, field, form) => onBlur(e, field, form),
From fa03e0fae2e5150771df2d276cca63611504ec2b Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 23 Jun 2023 19:14:41 +0200
Subject: [PATCH 35/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../containers/CreateMonitor/CreateMonitor.js | 1 +
.../CompositeTriggerCondition.js | 2 ++
.../ExpressionBuilder.js | 26 ++++++++++++++-----
.../CompositeMonitorsAlertTrigger.js | 2 ++
.../DefineCompositeLevelTrigger.js | 3 ++-
5 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index de32bec4d..049422409 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -327,6 +327,7 @@ export default class CreateMonitor extends Component {
{values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? (
{
@@ -71,6 +72,7 @@ const CompositeTriggerCondition = ({
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
index d70275b71..a94cb2543 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
@@ -43,8 +43,15 @@ export const conditionToExpressions = (condition, monitors) => {
return expressions;
};
-const ExpressionBuilder = ({ formikFieldPath = '', formikFieldName, values, httpClient }) => {
+const ExpressionBuilder = ({
+ formikFieldPath = '',
+ formikFieldName,
+ values,
+ touched,
+ httpClient,
+}) => {
const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
+ const formikFullFieldValue = _.replace(`${formikFullFieldName}_value`, /[.\[\]]/gm, '_');
const DEFAULT_CONDITION = 'AND';
const DEFAULT_NAME = 'Select associated monitor';
@@ -69,6 +76,10 @@ const ExpressionBuilder = ({ formikFieldPath = '', formikFieldName, values, http
const [options, setOptions] = useState([]);
useEffect(() => {
+ // initializing formik because these are generic fields and formik won't pick them up until fields is updated
+ _.set(touched, formikFullFieldValue, false);
+ _.set(values, formikFullFieldValue, '');
+
const monitors = _.get(values, 'monitorOptions', []);
if (monitors.length) {
setInitialValues(monitors);
@@ -148,7 +159,7 @@ const ExpressionBuilder = ({ formikFieldPath = '', formikFieldName, values, http
const onBlur = (form, expressions) => {
onChange(form, expressions);
- form.setFieldTouched(`${formikFullFieldName}_value`, true);
+ form.setFieldTouched(formikFullFieldValue, true);
};
const openPopover = (idx = 0) => {
@@ -255,16 +266,16 @@ const ExpressionBuilder = ({ formikFieldPath = '', formikFieldName, values, http
return (
validate(),
}}
render={({ form }) => (
form.touched[`${formikFullFieldName}_value`] && !isValid(),
+ isInvalid: () => form.touched[formikFullFieldValue] && !isValid(),
error: () => validate(),
style: {
maxWidth: 'inherit',
@@ -287,7 +298,10 @@ const ExpressionBuilder = ({ formikFieldPath = '', formikFieldName, values, http
description={expression.description}
value={expression.monitor_name}
isActive={!!options?.length}
- onClick={() => openPopover(idx)}
+ onClick={() => {
+ form.setFieldTouched(formikFullFieldValue, true);
+ openPopover(idx);
+ }}
/>
}
isOpen={expression.isOpen}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
index 9590925c0..390438578 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
@@ -13,11 +13,13 @@ const CompositeMonitorsAlertTrigger = ({
notificationService,
plugins,
values,
+ touched,
}) => {
return (
(
class DefineCompositeLevelTrigger extends Component {
render() {
- const { values, httpClient, notifications, notificationService, plugins } = this.props;
+ const { values, httpClient, notifications, notificationService, plugins, touched } = this.props;
const formikFieldPath = `triggerDefinitions[0].`;
const triggerName = _.get(values, `${formikFieldPath}name`, 'Trigger');
@@ -113,6 +113,7 @@ class DefineCompositeLevelTrigger extends Component {
'An alert will trigger when the following monitors generate active alerts.'
)}
values={values}
+ touched={touched}
isDarkMode={this.props.isDarkMode}
httpClient={httpClient}
/>
From 5ec5aad78e3b6cd1c1805bd915404aba7354cb95 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 23 Jun 2023 19:22:06 +0200
Subject: [PATCH 36/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../CompositeTriggerCondition.js | 27 +------------------
.../CreateTrigger/utils/triggerToFormik.js | 1 -
2 files changed, 1 insertion(+), 27 deletions(-)
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
index 4012a5e78..31155b557 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
@@ -1,34 +1,9 @@
-import React, { useEffect, useState, useCallback } from 'react';
+import React, { useEffect, useState } from 'react';
import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { FormikFormRow, FormikInputWrapper } from '../../../../components/FormControls';
import ExpressionBuilder from './ExpressionBuilder';
import ExpressionEditor from './ExpressionEditor';
-export const conditionToExpressions = (query, monitors) => {
- const conditionMap = {
- '&&': 'and',
- '||': 'or',
- '!': 'not',
- '': '',
- };
- const queryToExpressionRegex = new RegExp('( && || \\|\\| )?(monitor\\[id=(.*?)\\])', 'gm');
- const matcher = query.matchAll(queryToExpressionRegex);
- let match;
- let expressions = [];
- while ((match = matcher.next())) {
- const monitorId = match[4]?.trim();
- const monitor = monitors.filter((mon) => mon.monitor_id === monitorId);
- expressions.push({
- description: conditionMap[match[1]?.trim()] || '',
- isOpen: false,
- monitor_name: monitor[0]?.monitor_name,
- monitor_id: monitorId,
- });
- }
-
- return expressions;
-};
-
const CompositeTriggerCondition = ({
label,
formikFieldPath = '',
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
index 36e416c42..1faa58917 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
@@ -14,7 +14,6 @@ import {
ACTIONABLE_ALERTS_OPTIONS_LABELS,
NOTIFY_OPTIONS_VALUES,
} from '../../../components/Action/actions/Message';
-import { conditionToExpressions } from '../../../components/CompositeTriggerCondition/CompositeTriggerCondition';
export function triggerToFormik(trigger, monitor) {
return _.isArray(trigger)
From a9cef2ecfb796463e67b5cf6041536d401c41b97 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Fri, 23 Jun 2023 20:39:48 +0200
Subject: [PATCH 37/63] Code review #573
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors/AssociateMonitors.js | 4 +-
.../components/MonitorsEditor.js | 45 ++++++++++---------
.../components/MonitorsList.js | 2 +-
.../containers/CreateMonitor/CreateMonitor.js | 7 ++-
.../CreateMonitor/utils/constants.js | 1 +
.../WorkflowDetails/WorkflowDetails.js | 9 +++-
.../ExpressionBuilder.js | 2 +-
.../ExpressionEditor.js | 18 +++++---
8 files changed, 54 insertions(+), 34 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
index a14a59035..5a5fb7964 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
@@ -29,7 +29,7 @@ export const getMonitors = async (httpClient) => {
}
};
-const AssociateMonitors = ({ isDarkMode, values, httpClient }) => {
+const AssociateMonitors = ({ isDarkMode, values, httpClient, errors }) => {
const [graphUi, setGraphUi] = useState(false);
useEffect(() => {
@@ -50,7 +50,7 @@ const AssociateMonitors = ({ isDarkMode, values, httpClient }) => {
{graphUi ? (
) : (
-
+
)}
);
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.js
index 9d8bf16c8..38389620c 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.js
@@ -3,11 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState, useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
import { FormikCodeEditor } from '../../../../../components/FormControls';
-import { hasError, isInvalid, validateExtractionQuery } from '../../../../../utils/validate';
-const MonitorsEditor = ({ values, isDarkMode }) => {
+const MonitorsEditor = ({ values, isDarkMode, errors }) => {
const codeFieldName = 'associatedMonitorsEditor';
const formikValueName = 'associatedMonitors';
const [editorValue, setEditorValue] = useState('');
@@ -15,7 +14,7 @@ const MonitorsEditor = ({ values, isDarkMode }) => {
useEffect(() => {
try {
const code = JSON.stringify(values.associatedMonitors, null, 4);
- // _.set(values, codeFieldName, code);
+ _.set(values, codeFieldName, code);
setEditorValue(code);
} catch (e) {}
}, [values.associatedMonitors]);
@@ -34,25 +33,26 @@ const MonitorsEditor = ({ values, isDarkMode }) => {
};
const isInvalid = (name, form) => {
- if (form.touched[codeFieldName]) {
- try {
- const associatedMonitors = form.values[name];
- const json = JSON.parse(associatedMonitors);
- return !json.sequence?.delegates?.length;
- } catch (e) {
- return false;
- }
+ try {
+ const associatedMonitors = form.values[name];
+ const json = JSON.parse(associatedMonitors);
+ return !json.sequence?.delegates?.length || json.sequence?.delegates?.length < 2;
+ } catch (e) {
+ return true;
}
};
const hasError = (name, form) => {
+ const associatedMonitors = form.values[name];
+ return validate(associatedMonitors);
+ };
+
+ const validate = (value) => {
try {
- const associatedMonitors = form.values[name];
- const json = JSON.parse(associatedMonitors);
- return (
- json.sequence?.delegates?.length < 2 &&
- 'Delegates list can not be empty or have less then two associated monitors.'
- );
+ const json = JSON.parse(value);
+ if (!json.sequence?.delegates?.length || json.sequence?.delegates?.length < 2) {
+ return 'Delegates list can not be empty or have less then two associated monitors.';
+ }
} catch (e) {
return 'Invalid json.';
}
@@ -62,14 +62,17 @@ const MonitorsEditor = ({ values, isDarkMode }) => {
isInvalid(name, form),
- // error: (name, form) => hasError(name, form),
+ isInvalid: (name, form) => form.touched[codeFieldName] && isInvalid(name, form),
+ error: hasError,
}}
inputProps={{
+ isInvalid: (name, form) => isInvalid(name, form),
mode: 'json',
width: '80%',
height: '300px',
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index a90a63e8d..483e41f42 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -177,7 +177,7 @@ const MonitorsList = ({ values, httpClient }) => {
validate(),
+ validate: validate,
}}
render={({ form }) => (
-
+
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
index 6e5cf9ebe..9098f5047 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
@@ -73,6 +73,7 @@ export const FORMIK_INITIAL_VALUES = {
detectorId: '',
associatedMonitors: DEFAULT_ASSOCIATED_MONITORS_VALUE,
associatedMonitorsList: [],
+ associatedMonitorsEditor: '',
};
export const FORMIK_INITIAL_AGG_VALUES = {
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
index 1d3129279..d8c6714ca 100644
--- a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
@@ -10,7 +10,7 @@ import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonit
import { EuiSpacer } from '@elastic/eui';
import { MONITOR_TYPE, SEARCH_TYPE } from '../../../../utils/constants';
-const WorkflowDetails = ({ values, isDarkMode, httpClient }) => {
+const WorkflowDetails = ({ values, isDarkMode, httpClient, errors }) => {
const isAd = values.searchType === SEARCH_TYPE.AD;
const isComposite = values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL;
return (
@@ -29,7 +29,12 @@ const WorkflowDetails = ({ values, isDarkMode, httpClient }) => {
{isComposite && (
-
+
)}
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
index a94cb2543..3d77f5130 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
@@ -105,7 +105,7 @@ const ExpressionBuilder = ({
const condition = _.get(values, formikFullFieldName, '');
const expressions = conditionToExpressions(condition, monitors);
- setUsedExpressions(expressions.length ? expressions : [DEFAULT_EXPRESSION]);
+ setUsedExpressions(expressions?.length ? expressions : [DEFAULT_EXPRESSION]);
};
const expressionsToCondition = (expressions) => {
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
index 80224d4a1..ae69f3bef 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
@@ -5,11 +5,11 @@ import { FormikCodeEditor } from '../../../../components/FormControls';
const ExpressionEditor = ({ values, formikFieldName, formikFieldPath, isDarkMode = false }) => {
const [editorValue, setEditorValue] = useState('');
const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
- const formikFullCodeFieldName = 'triggerConditionsCode';
+ const formikFullCodeFieldName = _.replace(`${formikFullFieldName}_code`, /[.\[\]]/gm, '_');
useEffect(() => {
const code = _.get(values, formikFullFieldName, '');
- // _.set(values, formikFullCodeFieldName, code);
+ _.set(values, formikFullCodeFieldName, code);
setEditorValue(code);
}, [values]);
@@ -19,19 +19,25 @@ const ExpressionEditor = ({ values, formikFieldName, formikFieldPath, isDarkMode
return !form.values[name]?.length && 'Invalid condition.';
};
+ const validate = (value) => {
+ if (!value?.length) return 'Invalid condition.';
+ };
+
return (
form.touched[name] && isInvalid(name, form),
- // error: (name, form) => hasError(name, form),
+ isInvalid: (name, form) => form.touched[name] && isInvalid(name, form),
+ error: (name, form) => hasError(name, form),
}}
inputProps={{
- // isInvalid: (name, form) => form.touched[name] && isInvalid(name, form),
+ isInvalid: (name, form) => form.touched[name] && isInvalid(name, form),
mode: 'text',
width: '80%',
height: '300px',
From c9df028e82b6e58c9e8cf526dca70c54c5c71356 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Mon, 26 Jun 2023 15:36:41 +0200
Subject: [PATCH 38/63] Cypress tests
Signed-off-by: Jovan Cvetkovic
---
.../sample_composite_level_monitor.json | 62 +++++++++-
.../acknowledge_alerts_modal_spec.js | 1 +
.../composite_level_monitor_spec.js | 113 +++++++++++++-----
cypress/support/commands.js | 17 +--
package.json | 4 +-
.../ExpressionBuilder.js | 3 +
6 files changed, 159 insertions(+), 41 deletions(-)
diff --git a/cypress/fixtures/sample_composite_level_monitor.json b/cypress/fixtures/sample_composite_level_monitor.json
index 20a0fed18..9b1f67bf4 100644
--- a/cypress/fixtures/sample_composite_level_monitor.json
+++ b/cypress/fixtures/sample_composite_level_monitor.json
@@ -2,7 +2,7 @@
"sample_composite_monitor": {
"type": "workflow",
"schema_version": 0,
- "name": "sample_component_level_monitor",
+ "name": "sampleComponentLevelMonitor",
"workflow_type": "composite",
"enabled": true,
"enabled_time": 1686908176848,
@@ -99,7 +99,7 @@
}
},
"sample_composite_associated_monitor_1": {
- "name": "monitor_1",
+ "name": "monitorOne",
"type": "monitor",
"monitor_type": "doc_level_monitor",
"enabled": false,
@@ -155,7 +155,63 @@
]
},
"sample_composite_associated_monitor_2": {
- "name": "monitor_2",
+ "name": "monitorTwo",
+ "type": "monitor",
+ "monitor_type": "doc_level_monitor",
+ "enabled": false,
+ "schedule": {
+ "period": {
+ "unit": "MINUTES",
+ "interval": 1
+ }
+ },
+ "inputs": [
+ {
+ "doc_level_input": {
+ "description": "",
+ "indices": ["sample_index_2"],
+ "queries": [
+ {
+ "id": "monitor_2_query_1",
+ "name": "monitor_2_query_1",
+ "query": "NOT (audit_category:\"sample_text\")",
+ "tags": []
+ },
+ {
+ "id": "monitor_2_query_2",
+ "name": "monitor_2_query_2",
+ "query": "NOT (audit_node_host_name:\"sample_text\")",
+ "tags": []
+ },
+ {
+ "id": "monitor_2_query_3",
+ "name": "monitor_2_query_3",
+ "query": "NOT (audit_node_id:\"sample_text\")",
+ "tags": []
+ }
+ ]
+ }
+ }
+ ],
+ "triggers": [
+ {
+ "document_level_trigger": {
+ "id": "sample_trigger_2",
+ "name": "monitor_2_query_2",
+ "severity": "1",
+ "condition": {
+ "script": {
+ "source": "query[name=monitor_2_query_1] || query[name=monitor_2_query_2] && query[name=monitor_2_query_3]",
+ "lang": "painless"
+ }
+ },
+ "actions": []
+ }
+ }
+ ]
+ },
+ "sample_composite_associated_monitor_3": {
+ "name": "monitorThree",
"type": "monitor",
"monitor_type": "doc_level_monitor",
"enabled": false,
diff --git a/cypress/integration/acknowledge_alerts_modal_spec.js b/cypress/integration/acknowledge_alerts_modal_spec.js
index daf439b05..16e68c539 100644
--- a/cypress/integration/acknowledge_alerts_modal_spec.js
+++ b/cypress/integration/acknowledge_alerts_modal_spec.js
@@ -18,6 +18,7 @@ const TWENTY_SECONDS = 20000;
describe('AcknowledgeAlertsModal', () => {
before(() => {
// Delete any existing monitors
+ cy.deleteAllAlerts();
cy.deleteAllMonitors();
// Load sample data
diff --git a/cypress/integration/composite_level_monitor_spec.js b/cypress/integration/composite_level_monitor_spec.js
index 006c742bd..ca5e4e284 100644
--- a/cypress/integration/composite_level_monitor_spec.js
+++ b/cypress/integration/composite_level_monitor_spec.js
@@ -3,21 +3,20 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { PLUGIN_NAME } from '../support/constants';
+import { API, PLUGIN_NAME } from '../support/constants';
import sampleCompositeJson from '../fixtures/sample_composite_level_monitor.json';
+import * as _ from 'lodash';
const sample_index_1 = 'sample_index_1';
const sample_index_2 = 'sample_index_2';
const SAMPLE_VISUAL_EDITOR_MONITOR = 'sample_visual_editor_composite_level_monitor';
-const SAMPLE_COMPOSITE_LEVEL_MONITOR = 'sample_composite_level_monitor';
const clearAll = () => {
- cy.deleteAllMonitors();
cy.deleteIndexByName(sample_index_1);
cy.deleteIndexByName(sample_index_2);
- // wait until alerts index finishes writing docs
- cy.wait(1000).then(() => cy.deleteAllAlerts());
+ cy.deleteAllAlerts();
+ cy.deleteAllMonitors();
};
describe('CompositeLevelMonitor', () => {
@@ -28,25 +27,25 @@ describe('CompositeLevelMonitor', () => {
cy.createIndexByName(sample_index_1, sampleCompositeJson.sample_composite_index);
cy.createIndexByName(sample_index_2, sampleCompositeJson.sample_composite_index);
- // Create asociated monitors
+ // Create associated monitors
cy.createMonitor(sampleCompositeJson.sample_composite_associated_monitor_1);
cy.createMonitor(sampleCompositeJson.sample_composite_associated_monitor_2);
+ cy.createMonitor(sampleCompositeJson.sample_composite_associated_monitor_3);
});
beforeEach(() => {
// Set welcome screen tracking to false
localStorage.setItem('home:welcome:show', 'false');
-
- // Visit Alerting OpenSearch Dashboards
- cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/monitors`);
-
- // Common text to wait for to confirm page loaded, give up to 20 seconds for initial load
- cy.contains('Create monitor', { timeout: 20000 });
});
- let monitorId;
describe('can be created', () => {
beforeEach(() => {
+ // Visit Alerting OpenSearch Dashboards
+ cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/monitors`);
+
+ // Common text to wait for to confirm page loaded, give up to 20 seconds for initial load
+ cy.contains('Create monitor', { timeout: 20000 });
+
// Go to create monitor page
cy.contains('Create monitor').click({ force: true });
@@ -63,10 +62,10 @@ describe('CompositeLevelMonitor', () => {
// Select associated monitors
cy.get('[data-test-subj="monitors_list_0"]')
- .type(sampleCompositeJson.sample_composite_associated_monitor_1.name)
+ .type('monitorOne', { delay: 50 })
.type('{enter}');
cy.get('[data-test-subj="monitors_list_1"]')
- .type(sampleCompositeJson.sample_composite_associated_monitor_2.name)
+ .type('monitorTwo', { delay: 50 })
.type('{enter}');
// Type trigger name
@@ -76,8 +75,19 @@ describe('CompositeLevelMonitor', () => {
.type('Composite trigger');
// Add associated monitors to condition
- cy.get('[data-test-subj="condition-add-selection-btn"]').click();
- cy.get('[data-test-subj="condition-add-selection-btn"]').click();
+ cy.get('[data-test-subj="condition-add-options-btn"]').click({ force: true });
+
+ cy.get('[data-test-subj="select-expression_0"]').click({ force: true });
+ cy.wait(1000);
+ cy.get('[data-test-subj="monitors-combobox-0"]')
+ .type('monitorOne', { delay: 50 })
+ .type('{enter}');
+
+ cy.get('[data-test-subj="select-expression_1"]').click({ force: true });
+ cy.wait(1000);
+ cy.get('[data-test-subj="monitors-combobox-1"]')
+ .type('monitorTwo', { delay: 50 })
+ .type('{enter}');
// TODO: Test with Notifications plugin
// Select notification channel
@@ -89,7 +99,7 @@ describe('CompositeLevelMonitor', () => {
// Wait for monitor to be created
cy.wait('@createMonitorRequest').then((interceptor) => {
- monitorId = interceptor.response.body.resp._id;
+ const monitorId = interceptor.response.body.resp._id;
cy.contains('Loading monitors');
cy.wait('@getMonitorsRequest').then((interceptor) => {
@@ -118,7 +128,7 @@ describe('CompositeLevelMonitor', () => {
monitor1[0] && cy.executeMonitor(monitor1[0].id);
monitor2[0] && cy.executeMonitor(monitor2[0].id);
- cy.get('[role="tab"]').contains('Alerts').click();
+ cy.get('[role="tab"]').contains('Alerts').click({ force: true });
cy.get('table tbody td').contains('Composite trigger');
});
});
@@ -129,20 +139,67 @@ describe('CompositeLevelMonitor', () => {
describe('can be edited', () => {
beforeEach(() => {
- if (monitorId) {
- cy.visit(
- `${Cypress.env(
- 'opensearch_dashboards'
- )}/app/${PLUGIN_NAME}#/monitors/${monitorId}?action=update-monitor&type=workflow`
- );
- } else {
- throw new Error(`Monitor with ID: ${monitorId} not found or not created.`);
- }
+ const body = {
+ size: 200,
+ query: {
+ match_all: {},
+ },
+ };
+ cy.request({
+ method: 'GET',
+ url: `${Cypress.env('opensearch')}${API.MONITOR_BASE}/_search`,
+ failOnStatusCode: false, // In case there is no alerting config index in cluster, where the status code is 404
+ body,
+ }).then((response) => {
+ if (response.status === 200) {
+ const monitors = response.body.hits.hits;
+ const createdMonitor = _.find(
+ monitors,
+ (monitor) => monitor._source.name === SAMPLE_VISUAL_EDITOR_MONITOR
+ );
+ if (createdMonitor) {
+ cy.visit(
+ `${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/monitors/${
+ createdMonitor._id
+ }?action=update-monitor&type=workflow`
+ );
+ } else {
+ cy.log('Failed to get created monitor ', SAMPLE_VISUAL_EDITOR_MONITOR);
+ throw new Error(`Failed to get created monitor ${SAMPLE_VISUAL_EDITOR_MONITOR}`);
+ }
+ } else {
+ cy.log('Failed to get all monitors.', response);
+ }
+ });
});
it('by visual editor', () => {
// Verify edit page
cy.contains('Edit monitor', { timeout: 20000 });
+ cy.get('input[name="name"]').type('_edited');
+
+ cy.get('label').contains('Visual editor').click({ force: true });
+
+ cy.get('button').contains('Associate another monitor').click({ force: true });
+
+ cy.get('[data-test-subj="monitors_list_2"]')
+ .type('monitorThree', { delay: 50 })
+ .type('{enter}');
+
+ cy.get('[data-test-subj="condition-add-options-btn"]').click({ force: true });
+ cy.get('[data-test-subj="select-expression_2"]').click({ force: true });
+ cy.wait(1000);
+ cy.get('[data-test-subj="monitors-combobox-2"]')
+ .type('monitorThree', { delay: 50 })
+ .type('{enter}');
+
+ cy.intercept('api/alerting/workflows/*').as('updateMonitorRequest');
+ cy.get('button').contains('Update').click({ force: true });
+
+ // Wait for monitor to be created
+ cy.wait('@updateMonitorRequest').then(() => {
+ cy.get('.euiTitle--large').contains(`${SAMPLE_VISUAL_EDITOR_MONITOR}_edited`);
+ });
});
});
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 4bfcd67f0..e05711de0 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -144,15 +144,16 @@ Cypress.Commands.add('deleteAllMonitors', () => {
const monitors = response.body.hits.hits.sort((monitor) =>
monitor._source.type === 'workflow' ? -1 : 1
);
- console.log('MONITORS', monitors);
for (let i = 0; i < monitors.length; i++) {
- cy.request({
- method: 'DELETE',
- url: `${Cypress.env('opensearch')}${
- monitors[i]._source.type === 'workflow' ? API.WORKFLOW_BASE : API.MONITOR_BASE
- }/${monitors[i]._id}`,
- failOnStatusCode: false,
- });
+ if (monitors[i]._id) {
+ cy.request({
+ method: 'DELETE',
+ url: `${Cypress.env('opensearch')}${
+ monitors[i]._source.type === 'workflow' ? API.WORKFLOW_BASE : API.MONITOR_BASE
+ }/${monitors[i]._id}`,
+ failOnStatusCode: false,
+ });
+ }
}
} else {
cy.log('Failed to get all monitors.', response);
diff --git a/package.json b/package.json
index 9f46a36de..a002e7d18 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,8 @@
"test:jest:windows": "SET TZ=UTC ../../node_modules/.bin/jest --config ./test/jest.config.js",
"test:jest": "TZ=UTC ../../node_modules/.bin/jest --config ./test/jest.config.js",
"test:jest:update-snapshots": "yarn run test:jest -u",
- "cypress:open": "cypress open",
- "cypress:run": "cypress run",
+ "cypress:run:browser": "cypress open",
+ "cypress:run:ci": "cypress run",
"build": "yarn plugin-helpers build",
"plugin-helpers": "node ../../scripts/plugin_helpers",
"postbuild": "echo Renaming build artifact to [$npm_package_config_id-$npm_package_version.zip] && mv build/$npm_package_config_id*.zip build/$npm_package_config_id-$npm_package_version.zip"
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
index 3d77f5130..12d09db25 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
@@ -209,6 +209,7 @@ const ExpressionBuilder = ({
{
const differences = _.differenceBy(options, usedExpressions, 'monitor_id');
return [
@@ -302,6 +304,7 @@ const ExpressionBuilder = ({
form.setFieldTouched(formikFullFieldValue, true);
openPopover(idx);
}}
+ data-test-subj={`select-expression_${idx}`}
/>
}
isOpen={expression.isOpen}
From dcedac0fe6ae6d3d798278fb5cfa5be16e5728d1 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Mon, 26 Jun 2023 19:54:24 +0200
Subject: [PATCH 39/63] Cypress tests
Signed-off-by: Jovan Cvetkovic
---
.../AnomalyDetector.test.js.snap | 54 ++++++++++++++-----
.../__snapshots__/CreateMonitor.test.js.snap | 9 +++-
.../__snapshots__/DefineMonitor.test.js.snap | 9 +++-
.../__snapshots__/MonitorIndex.test.js.snap | 45 ++++++++++++----
.../AcknowledgeAlertsModal.test.js.snap | 9 +++-
5 files changed, 98 insertions(+), 28 deletions(-)
diff --git a/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap b/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
index d59bbb56f..12772468c 100644
--- a/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
+++ b/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
@@ -6,7 +6,13 @@ exports[`AnomalyDetectors renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -14,7 +20,6 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -72,7 +77,13 @@ exports[`AnomalyDetectors renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -80,7 +91,6 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -199,7 +209,13 @@ exports[`AnomalyDetectors renders 1`] = `
"initialValues": Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -207,7 +223,6 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -284,7 +299,13 @@ exports[`AnomalyDetectors renders 1`] = `
"values": Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -292,7 +313,6 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -417,7 +437,13 @@ exports[`AnomalyDetectors renders 1`] = `
"initialValues": Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -425,7 +451,6 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -502,7 +527,13 @@ exports[`AnomalyDetectors renders 1`] = `
"values": Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -510,7 +541,6 @@ exports[`AnomalyDetectors renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap b/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
index 32b0b9450..344bc83ca 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
@@ -14,7 +14,13 @@ exports[`CreateMonitor renders 1`] = `
"adResultIndex": undefined,
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -22,7 +28,6 @@ exports[`CreateMonitor renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
diff --git a/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap b/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
index 4d811f708..111809b22 100644
--- a/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
+++ b/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
@@ -11,7 +11,13 @@ exports[`DefineMonitor renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -19,7 +25,6 @@ exports[`DefineMonitor renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
diff --git a/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap b/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
index 09351d512..cffc953c0 100644
--- a/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
+++ b/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
@@ -6,7 +6,13 @@ exports[`MonitorIndex renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -14,7 +20,6 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -145,7 +150,13 @@ exports[`MonitorIndex renders 1`] = `
"initialValues": Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -153,7 +164,6 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -230,7 +240,13 @@ exports[`MonitorIndex renders 1`] = `
"values": Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -238,7 +254,6 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -379,7 +394,13 @@ exports[`MonitorIndex renders 1`] = `
"initialValues": Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -387,7 +408,6 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
@@ -464,7 +484,13 @@ exports[`MonitorIndex renders 1`] = `
"values": Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -472,7 +498,6 @@ exports[`MonitorIndex renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
diff --git a/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap b/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap
index 659fd3a86..85b6e9ade 100644
--- a/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap
+++ b/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap
@@ -35,7 +35,13 @@ exports[`AcknowledgeAlertsModal renders 1`] = `
Object {
"aggregationType": "count",
"aggregations": Array [],
- "associatedMonitors": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -43,7 +49,6 @@ exports[`AcknowledgeAlertsModal renders 1`] = `
"description": "",
"detectorId": "",
"disabled": false,
- "expressionQuery": null,
"fieldName": Array [],
"filters": Array [],
"frequency": "interval",
From ed1bef736c0d86ee56b39cf504f1f6364df30037 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Tue, 27 Jun 2023 11:21:29 +0200
Subject: [PATCH 40/63] Unit tests
Signed-off-by: Jovan Cvetkovic
---
.../AssociateMonitors.test.js | 22 ++
.../AssociateMonitors.test.js.snap | 71 ++++
.../components/MonitorsEditor.test.js | 22 ++
.../components/MonitorsList.test.js | 22 ++
.../__snapshots__/MonitorsEditor.test.js.snap | 49 +++
.../__snapshots__/MonitorsList.test.js.snap | 183 +++++++++
.../__snapshots__/VisualGraph.test.js.snap | 22 +-
.../WorkflowDetails/WorkflowDetails.test.js | 22 ++
.../WorkflowDetails.test.js.snap | 251 +++++++++++++
.../CompositeTriggerCondition.test.js | 30 ++
.../ExpressionBuilder.test.js | 28 ++
.../ExpressionEditor.test.js | 27 ++
.../ExpressionQuery.test.ts | 14 -
.../CompositeTriggerCondition.test.js.snap | 79 ++++
.../ExpressionBuilder.test.js.snap | 48 +++
.../ExpressionEditor.test.js.snap | 49 +++
.../CompositeMonitorsAlertTrigger.test.js | 30 ++
.../DefineCompositeLevelTrigger.test.js | 30 ++
.../NotificationConfigDialog.test.js | 28 ++
.../TriggerNotifications.test.js | 29 ++
.../TriggerNotificationsContent.test.js | 47 +++
...CompositeMonitorsAlertTrigger.test.js.snap | 308 ++++++++++++++++
.../DefineCompositeLevelTrigger.test.js.snap | 308 ++++++++++++++++
.../NotificationConfigDialog.test.js.snap | 3 +
.../TriggerNotifications.test.js.snap | 39 ++
.../TriggerNotificationsContent.test.js.snap | 348 ++++++++++++++++++
26 files changed, 2084 insertions(+), 25 deletions(-)
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.test.js
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/__snapshots__/AssociateMonitors.test.js.snap
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.test.js
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.test.js
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsEditor.test.js.snap
create mode 100644 public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsList.test.js.snap
create mode 100644 public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.test.js
create mode 100644 public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.test.js
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.test.js
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.test.js
delete mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionQuery.test.ts
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/CompositeTriggerCondition.test.js.snap
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionBuilder.test.js.snap
create mode 100644 public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionEditor.test.js.snap
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.test.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.test.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.test.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.test.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.test.js
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/CompositeMonitorsAlertTrigger.test.js.snap
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/DefineCompositeLevelTrigger.test.js.snap
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/NotificationConfigDialog.test.js.snap
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotifications.test.js.snap
create mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotificationsContent.test.js.snap
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.test.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.test.js
new file mode 100644
index 000000000..f9debfe3e
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.test.js
@@ -0,0 +1,22 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import AssociateMonitors from './AssociateMonitors';
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../containers/CreateMonitor/utils/constants';
+
+describe('AssociateMonitors', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/__snapshots__/AssociateMonitors.test.js.snap b/public/pages/CreateMonitor/components/AssociateMonitors/__snapshots__/AssociateMonitors.test.js.snap
new file mode 100644
index 000000000..6d72ed363
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/__snapshots__/AssociateMonitors.test.js.snap
@@ -0,0 +1,71 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AssociateMonitors renders 1`] = `
+Array [
+
+
+ Associate monitors
+
+
,
+ ,
+
,
+ ,
+]
+`;
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.test.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.test.js
new file mode 100644
index 000000000..1b32fcdae
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsEditor.test.js
@@ -0,0 +1,22 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../containers/CreateMonitor/utils/constants';
+import MonitorsEditor from './MonitorsEditor';
+
+describe('MonitorsEditor', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.test.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.test.js
new file mode 100644
index 000000000..4b3102d3b
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.test.js
@@ -0,0 +1,22 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../containers/CreateMonitor/utils/constants';
+import MonitorsList from './MonitorsList';
+
+describe('MonitorsList', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsEditor.test.js.snap b/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsEditor.test.js.snap
new file mode 100644
index 000000000..4726f7a90
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsEditor.test.js.snap
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MonitorsEditor renders 1`] = `
+
+`;
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsList.test.js.snap b/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsList.test.js.snap
new file mode 100644
index 000000000..8c5b8f61b
--- /dev/null
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsList.test.js.snap
@@ -0,0 +1,183 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MonitorsList renders 1`] = `
+
+`;
diff --git a/public/pages/CreateMonitor/components/VisualGraph/__snapshots__/VisualGraph.test.js.snap b/public/pages/CreateMonitor/components/VisualGraph/__snapshots__/VisualGraph.test.js.snap
index 4daf8404c..ed2250957 100644
--- a/public/pages/CreateMonitor/components/VisualGraph/__snapshots__/VisualGraph.test.js.snap
+++ b/public/pages/CreateMonitor/components/VisualGraph/__snapshots__/VisualGraph.test.js.snap
@@ -90,7 +90,7 @@ exports[`VisualGraph renders 1`] = `
text-anchor="middle"
transform="translate(0, 14)"
>
- 03 PM
+ 04 PM
- 04 PM
+ 05 PM
- 05 PM
+ 06 PM
- 06 PM
+ 07 PM
- 07 PM
+ 08 PM
- 08 PM
+ 09 PM
@@ -631,7 +631,7 @@ exports[`VisualGraph renders with bucket level monitor 1`] = `
text-anchor="middle"
transform="translate(0, 14)"
>
- 04 PM
+ 05 PM
- 05 PM
+ 06 PM
- 06 PM
+ 07 PM
- 07 PM
+ 08 PM
- 08 PM
+ 09 PM
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.test.js b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.test.js
new file mode 100644
index 000000000..6f91e7438
--- /dev/null
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.test.js
@@ -0,0 +1,22 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../CreateMonitor/utils/constants';
+import WorkflowDetails from './WorkflowDetails';
+
+describe('WorkflowDetails', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap b/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
new file mode 100644
index 000000000..5145a7795
--- /dev/null
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
@@ -0,0 +1,251 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`WorkflowDetails renders 1`] = `
+
+
+
+
+
+
+
+ Define workflow schedule
+
+
+
+
+
+
+`;
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.test.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.test.js
new file mode 100644
index 000000000..aa2660de7
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.test.js
@@ -0,0 +1,30 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import CompositeTriggerCondition from './CompositeTriggerCondition';
+import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
+
+describe('CompositeTriggerCondition', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.test.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.test.js
new file mode 100644
index 000000000..b9cefe251
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.test.js
@@ -0,0 +1,28 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
+import ExpressionBuilder from './ExpressionBuilder';
+
+describe('ExpressionBuilder', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.test.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.test.js
new file mode 100644
index 000000000..ef0bf0b0d
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.test.js
@@ -0,0 +1,27 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
+import ExpressionEditor from './ExpressionEditor';
+
+describe('ExpressionEditor', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionQuery.test.ts b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionQuery.test.ts
deleted file mode 100644
index 49e1b709c..000000000
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionQuery.test.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React from 'react';
-import { render } from '@testing-library/react';
-
-describe(' spec', () => {
- it('renders the component', () => {
- const tree = render( );
- expect(tree).toMatchSnapshot();
- });
-});
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/CompositeTriggerCondition.test.js.snap b/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/CompositeTriggerCondition.test.js.snap
new file mode 100644
index 000000000..1ab84caab
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/CompositeTriggerCondition.test.js.snap
@@ -0,0 +1,79 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`CompositeTriggerCondition renders 1`] = `
+
+`;
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionBuilder.test.js.snap b/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionBuilder.test.js.snap
new file mode 100644
index 000000000..ebb2d47f4
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionBuilder.test.js.snap
@@ -0,0 +1,48 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ExpressionBuilder renders 1`] = `
+
+`;
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionEditor.test.js.snap b/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionEditor.test.js.snap
new file mode 100644
index 000000000..0f1e38a2f
--- /dev/null
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionEditor.test.js.snap
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ExpressionEditor renders 1`] = `
+
+`;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.test.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.test.js
new file mode 100644
index 000000000..9e5a81334
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.test.js
@@ -0,0 +1,30 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
+import CompositeMonitorsAlertTrigger from './CompositeMonitorsAlertTrigger';
+
+describe('CompositeMonitorsAlertTrigger', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.test.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.test.js
new file mode 100644
index 000000000..242cb7261
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.test.js
@@ -0,0 +1,30 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
+import DefineCompositeLevelTrigger from './DefineCompositeLevelTrigger';
+
+describe('DefineCompositeLevelTrigger', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.test.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.test.js
new file mode 100644
index 000000000..49fe11d6f
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.test.js
@@ -0,0 +1,28 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
+import NotificationConfigDialog from './NotificationConfigDialog';
+
+describe('NotificationConfigDialog', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+ {}}
+ triggerValues={FORMIK_INITIAL_VALUES}
+ httpClient={{}}
+ notifications={{}}
+ actionIndex={0}
+ />
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.test.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.test.js
new file mode 100644
index 000000000..d06015c51
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.test.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
+import TriggerNotifications from './TriggerNotifications';
+
+describe('TriggerNotifications', () => {
+ test('renders', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.test.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.test.js
new file mode 100644
index 000000000..fb50e538e
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.test.js
@@ -0,0 +1,47 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { render } from 'enzyme';
+
+import { Formik } from 'formik';
+import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
+import TriggerNotifications from './TriggerNotifications';
+import TriggerNotificationsContent from './TriggerNotificationsContent';
+
+describe('TriggerNotificationsContent', () => {
+ test('renders without notifications', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+ test('renders with notifications', () => {
+ const component = (
+ {}}>
+
+
+ );
+ expect(render(component)).toMatchSnapshot();
+ });
+});
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/CompositeMonitorsAlertTrigger.test.js.snap b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/CompositeMonitorsAlertTrigger.test.js.snap
new file mode 100644
index 000000000..5b1f3ba13
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/CompositeMonitorsAlertTrigger.test.js.snap
@@ -0,0 +1,308 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`CompositeMonitorsAlertTrigger renders 1`] = `
+
+
+
+
+ Alert trigger
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+ Add notification
+
+
+
+
+
+`;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/DefineCompositeLevelTrigger.test.js.snap b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/DefineCompositeLevelTrigger.test.js.snap
new file mode 100644
index 000000000..da9752937
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/DefineCompositeLevelTrigger.test.js.snap
@@ -0,0 +1,308 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`DefineCompositeLevelTrigger renders 1`] = `
+
+
+
+
+ Alert trigger
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+ Add notification
+
+
+
+
+
+`;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/NotificationConfigDialog.test.js.snap b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/NotificationConfigDialog.test.js.snap
new file mode 100644
index 000000000..f42e2222e
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/NotificationConfigDialog.test.js.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`NotificationConfigDialog renders 1`] = `null`;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotifications.test.js.snap b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotifications.test.js.snap
new file mode 100644
index 000000000..f2e92a492
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotifications.test.js.snap
@@ -0,0 +1,39 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`TriggerNotifications renders 1`] = `
+Array [
+ ,
+
,
+ "0",
+ ,
+
+
+
+ Add notification
+
+
+ ,
+]
+`;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotificationsContent.test.js.snap b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotificationsContent.test.js.snap
new file mode 100644
index 000000000..ca94897d4
--- /dev/null
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotificationsContent.test.js.snap
@@ -0,0 +1,348 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`TriggerNotificationsContent renders 1`] = `
+
+
+
+
+
+
+
+ EuiIconMock
+
+
+ Manage channels
+
+
+
+
+
+
+`;
+
+exports[`TriggerNotificationsContent renders with notifications 1`] = `
+
+
+
+
+
+
+
+ EuiIconMock
+
+
+ Manage channels
+
+
+
+
+
+
+`;
+
+exports[`TriggerNotificationsContent renders without notifications 1`] = `
+
+
+
+
+
+
+
+ EuiIconMock
+
+
+ Manage channels
+
+
+
+
+
+
+`;
From 308b70c015e3cb1bc764520cd38b76b8d650cf80 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Tue, 27 Jun 2023 11:21:44 +0200
Subject: [PATCH 41/63] Unit tests
Signed-off-by: Jovan Cvetkovic
---
.../TriggerNotificationsContent.test.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.test.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.test.js
index fb50e538e..1a4521756 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.test.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.test.js
@@ -8,7 +8,6 @@ import { render } from 'enzyme';
import { Formik } from 'formik';
import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
-import TriggerNotifications from './TriggerNotifications';
import TriggerNotificationsContent from './TriggerNotificationsContent';
describe('TriggerNotificationsContent', () => {
From fa4f60095036ec61ce4c09fbabc835209ad93053 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Tue, 27 Jun 2023 13:24:39 +0200
Subject: [PATCH 42/63] Add visual editor callout message for advanced
conditions
Signed-off-by: Jovan Cvetkovic
---
.../MonitorDefinitionCard.js | 21 +-
.../containers/CreateMonitor/CreateMonitor.js | 197 +++++++++---------
.../CreateMonitor/utils/constants.js | 1 +
.../CreateMonitor/utils/monitorToFormik.js | 16 +-
.../MonitorDetails/MonitorDetails.js | 29 ++-
.../ExpressionBuilder.js | 4 +-
6 files changed, 153 insertions(+), 115 deletions(-)
diff --git a/public/pages/CreateMonitor/components/MonitorDefinitionCard/MonitorDefinitionCard.js b/public/pages/CreateMonitor/components/MonitorDefinitionCard/MonitorDefinitionCard.js
index 8e3ba733a..ae20fc401 100644
--- a/public/pages/CreateMonitor/components/MonitorDefinitionCard/MonitorDefinitionCard.js
+++ b/public/pages/CreateMonitor/components/MonitorDefinitionCard/MonitorDefinitionCard.js
@@ -8,12 +8,25 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText } from '@elastic/eui';
import FormikCheckableCard from '../../../../components/FormControls/FormikCheckableCard/FormikCheckableCard';
import { OS_AD_PLUGIN, MONITOR_TYPE, SEARCH_TYPE } from '../../../../utils/constants';
import { URL } from '../../../../../utils/constants';
+import _ from 'lodash';
+import { conditionToExpressions } from '../../../CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder';
const MONITOR_DEFINITION_CARD_WIDTH = '275';
-const onChangeDefinition = (e, form) => {
+const onChangeDefinition = (e, form, values) => {
const type = e.target.value;
form.setFieldValue('searchType', type, false);
+
+ let preventVisualEditor = false;
+
+ if (values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL && type === 'graph') {
+ const triggerConditions = _.get(values, 'triggerDefinitions[0].triggerConditions', '');
+ const monitors = _.get(values, 'monitorOptions', []);
+ const parsedConditions = conditionToExpressions(triggerConditions, monitors);
+ preventVisualEditor = !!triggerConditions.length && !parsedConditions.length;
+ }
+
+ form.setFieldValue('preventVisualEditor', preventVisualEditor);
};
const MonitorDefinitionCard = ({ values, plugins }) => {
@@ -52,7 +65,7 @@ const MonitorDefinitionCard = ({ values, plugins }) => {
checked: values.searchType === SEARCH_TYPE.GRAPH,
value: SEARCH_TYPE.GRAPH,
onChange: (e, field, form) => {
- onChangeDefinition(e, form);
+ onChangeDefinition(e, form, values);
},
'data-test-subj': 'visualEditorRadioCard',
}}
@@ -68,7 +81,7 @@ const MonitorDefinitionCard = ({ values, plugins }) => {
checked: values.searchType === SEARCH_TYPE.QUERY,
value: SEARCH_TYPE.QUERY,
onChange: (e, field, form) => {
- onChangeDefinition(e, form);
+ onChangeDefinition(e, form, values);
},
'data-test-subj': 'extractionQueryEditorRadioCard',
}}
@@ -85,7 +98,7 @@ const MonitorDefinitionCard = ({ values, plugins }) => {
checked: values.searchType === SEARCH_TYPE.AD,
value: SEARCH_TYPE.AD,
onChange: (e, field, form) => {
- onChangeDefinition(e, form);
+ onChangeDefinition(e, form, values);
},
'data-test-subj': 'anomalyDetectorRadioCard',
}}
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index c1c1ec0cd..876e7ff63 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -282,108 +282,115 @@ export default class CreateMonitor extends Component {
return (
- {({ values, errors, handleSubmit, isSubmitting, isValid, touched }) => (
-
-
- {edit ? 'Edit' : 'Create'} monitor
-
-
-
-
-
-
-
-
-
-
-
- {values.searchType !== SEARCH_TYPE.AD &&
- values.monitor_type !== MONITOR_TYPE.COMPOSITE_LEVEL && (
-
-
{
+ return (
+
+
+ {edit ? 'Edit' : 'Create'} monitor
+
+
+
+
+
+ {values.preventVisualEditor ? null : (
+
+
+
+
+
+
+
+ {values.searchType !== SEARCH_TYPE.AD &&
+ values.monitor_type !== MONITOR_TYPE.COMPOSITE_LEVEL && (
+
+
+
+
+ )}
+
+ {values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? (
+
+ ) : (
+
+ {(triggerArrayHelpers) => (
+
+ )}
+
+ )}
+
-
+
+
+ Cancel
+
+
+
+ {edit ? 'Update' : 'Create'}
+
+
+
+
)}
- {values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? (
-
+ notifications.toasts.addDanger({
+ title: `Failed to ${edit ? 'update' : 'create'} the monitor`,
+ text: 'Fix all highlighted error(s) before continuing.',
+ })
+ }
/>
- ) : (
-
- {(triggerArrayHelpers) => (
-
- )}
-
- )}
-
-
-
-
- Cancel
-
-
-
- {edit ? 'Update' : 'Create'}
-
-
-
-
- notifications.toasts.addDanger({
- title: `Failed to ${edit ? 'update' : 'create'} the monitor`,
- text: 'Fix all highlighted error(s) before continuing.',
- })
- }
- />
-
- )}
+
+ );
+ }}
);
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
index 9098f5047..b3399d443 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
@@ -74,6 +74,7 @@ export const FORMIK_INITIAL_VALUES = {
associatedMonitors: DEFAULT_ASSOCIATED_MONITORS_VALUE,
associatedMonitorsList: [],
associatedMonitorsEditor: '',
+ preventVisualEditor: false,
};
export const FORMIK_INITIAL_AGG_VALUES = {
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
index 945a1a3e0..b2bc43a07 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
@@ -8,11 +8,10 @@ import { FORMIK_INITIAL_DOCUMENT_LEVEL_QUERY_VALUES, FORMIK_INITIAL_VALUES } fro
import { SEARCH_TYPE, INPUTS_DETECTOR_ID, MONITOR_TYPE } from '../../../../../utils/constants';
import { OPERATORS_MAP } from '../../../components/MonitorExpressions/expressions/utils/constants';
import {
- COMPOSITE_INPUT_FIELD,
DOC_LEVEL_INPUT_FIELD,
QUERY_STRING_QUERY_OPERATORS,
} from '../../../components/DocumentLevelMonitorQueries/utils/constants';
-import { formikToCompositeInput, formikToCompositeUiMetadata } from './formikToMonitor';
+import { conditionToExpressions } from '../../../../CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder';
// Convert Monitor JSON to Formik values used in UI forms
export default function monitorToFormik(monitor) {
@@ -25,6 +24,7 @@ export default function monitorToFormik(monitor) {
schedule: { cron: { expression: cronExpression = formikValues.cronExpression, timezone } = {} },
inputs,
ui_metadata: { schedule = {}, search = {} } = {},
+ monitorOptions = [],
} = monitor;
// Default searchType to query, because if there is no ui_metadata or search then it was created through API or overwritten by API
// In that case we don't want to guess on the UI what selections a user made, so we will default to just showing the extraction query
@@ -41,8 +41,18 @@ export default function monitorToFormik(monitor) {
case MONITOR_TYPE.DOC_LEVEL:
return docLevelInputToFormik(monitor);
case MONITOR_TYPE.COMPOSITE_LEVEL:
+ const triggerConditions = _.get(
+ monitor,
+ 'triggers[0].chained_alert_trigger.condition.script.source',
+ ''
+ );
+
+ const parsedConditions = conditionToExpressions(triggerConditions, monitorOptions);
+ const preventVisualEditor = !!triggerConditions.length && !parsedConditions.length;
+
return {
associatedMonitors: _.get(monitor, 'inputs[0].composite_input', {}),
+ searchType: preventVisualEditor ? 'query' : 'graph',
};
default:
return {
@@ -65,10 +75,10 @@ export default function monitorToFormik(monitor) {
cronExpression,
/* DEFINE MONITOR */
+ searchType,
...monitorInputs(),
monitor_type,
...search,
- searchType,
fieldName: fieldName ? [{ label: fieldName }] : [],
timezone: timezone ? [{ label: timezone }] : [],
detectorId: isAD ? _.get(inputs, INPUTS_DETECTOR_ID) : undefined,
diff --git a/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js b/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
index fe766b79b..2065b7c31 100644
--- a/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
+++ b/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
@@ -3,8 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React from 'react';
-import { EuiSpacer } from '@elastic/eui';
+import React, { Fragment } from 'react';
+import { EuiSpacer, EuiCallOut } from '@elastic/eui';
import ContentPanel from '../../../../components/ContentPanel';
import FormikFieldText from '../../../../components/FormControls/FormikFieldText';
import { hasError, isInvalid, required, validateMonitorName } from '../../../../utils/validate';
@@ -41,15 +41,7 @@ function renderEmptyMessage(message) {
);
}
-const MonitorDetails = ({
- values,
- errors,
- httpClient,
- monitorToEdit,
- isAd,
- plugins,
- detectorId,
-}) => {
+const MonitorDetails = ({ values, httpClient, monitorToEdit, isAd, plugins, detectorId }) => {
const anomalyDetectorContent = isAd && renderAnomalyDetector(httpClient, values, detectorId);
const displayMonitorDefinitionCards = values.monitor_type !== MONITOR_TYPE.CLUSTER_METRICS;
return (
@@ -101,6 +93,21 @@ const MonitorDetails = ({
{anomalyDetectorContent.content}
) : null}
+
+ {values.preventVisualEditor && (
+
+
+
+
+ To view or modify all of your configurations, switch to the Extraction query editor.
+
+
+
+ )}
);
};
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
index 12d09db25..39c171d15 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
@@ -11,7 +11,7 @@ import * as _ from 'lodash';
import { FormikFormRow, FormikInputWrapper } from '../../../../components/FormControls';
import { getMonitors } from '../../../CreateMonitor/components/AssociateMonitors/AssociateMonitors';
-export const conditionToExpressions = (condition, monitors) => {
+export const conditionToExpressions = (condition = '', monitors) => {
if (!condition.length) return [];
const conditionMap = {
@@ -26,7 +26,7 @@ export const conditionToExpressions = (condition, monitors) => {
let expressions = [];
let counter = 0;
while ((match = matcher.next().value)) {
- if (counter && !match[1]) return; // Didn't find condition after the first match
+ if (counter && !match[1]) return []; // Didn't find condition after the first match
const monitorId = match[3]?.trim(); // match [3] is the monitor_id
const monitor = monitors.filter((mon) => mon.monitor_id === monitorId);
From 43c689f3e6a78a386448c97570a37ff327724fa3 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Tue, 27 Jun 2023 16:03:20 +0200
Subject: [PATCH 43/63] Add visual editor callout message for advanced
conditions
Signed-off-by: Jovan Cvetkovic
---
.../__snapshots__/VisualGraph.test.js.snap | 22 ++--
.../AnomalyDetector.test.js.snap | 6 +
.../__snapshots__/CreateMonitor.test.js.snap | 1 +
.../__snapshots__/DefineMonitor.test.js.snap | 1 +
.../__snapshots__/MonitorIndex.test.js.snap | 5 +
.../TriggerNotificationsContent.test.js.snap | 116 ------------------
.../AcknowledgeAlertsModal.test.js.snap | 1 +
7 files changed, 25 insertions(+), 127 deletions(-)
diff --git a/public/pages/CreateMonitor/components/VisualGraph/__snapshots__/VisualGraph.test.js.snap b/public/pages/CreateMonitor/components/VisualGraph/__snapshots__/VisualGraph.test.js.snap
index ed2250957..4daf8404c 100644
--- a/public/pages/CreateMonitor/components/VisualGraph/__snapshots__/VisualGraph.test.js.snap
+++ b/public/pages/CreateMonitor/components/VisualGraph/__snapshots__/VisualGraph.test.js.snap
@@ -90,7 +90,7 @@ exports[`VisualGraph renders 1`] = `
text-anchor="middle"
transform="translate(0, 14)"
>
- 04 PM
+ 03 PM
- 05 PM
+ 04 PM
- 06 PM
+ 05 PM
- 07 PM
+ 06 PM
- 08 PM
+ 07 PM
- 09 PM
+ 08 PM
@@ -631,7 +631,7 @@ exports[`VisualGraph renders with bucket level monitor 1`] = `
text-anchor="middle"
transform="translate(0, 14)"
>
- 05 PM
+ 04 PM
- 06 PM
+ 05 PM
- 07 PM
+ 06 PM
- 08 PM
+ 07 PM
- 09 PM
+ 08 PM
diff --git a/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap b/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
index 12772468c..dd1a89d69 100644
--- a/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
+++ b/public/pages/CreateMonitor/containers/AnomalyDetectors/__tests__/__snapshots__/AnomalyDetector.test.js.snap
@@ -43,6 +43,7 @@ exports[`AnomalyDetectors renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -114,6 +115,7 @@ exports[`AnomalyDetectors renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -246,6 +248,7 @@ exports[`AnomalyDetectors renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -336,6 +339,7 @@ exports[`AnomalyDetectors renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -474,6 +478,7 @@ exports[`AnomalyDetectors renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -564,6 +569,7 @@ exports[`AnomalyDetectors renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap b/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
index 344bc83ca..6400e4709 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/__snapshots__/CreateMonitor.test.js.snap
@@ -51,6 +51,7 @@ exports[`CreateMonitor renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
diff --git a/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap b/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
index 111809b22..eb666d8ad 100644
--- a/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
+++ b/public/pages/CreateMonitor/containers/DefineMonitor/__snapshots__/DefineMonitor.test.js.snap
@@ -48,6 +48,7 @@ exports[`DefineMonitor renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
diff --git a/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap b/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
index cffc953c0..2c0d6ed4f 100644
--- a/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
+++ b/public/pages/CreateMonitor/containers/MonitorIndex/__snapshots__/MonitorIndex.test.js.snap
@@ -43,6 +43,7 @@ exports[`MonitorIndex renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -187,6 +188,7 @@ exports[`MonitorIndex renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -277,6 +279,7 @@ exports[`MonitorIndex renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -431,6 +434,7 @@ exports[`MonitorIndex renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
@@ -521,6 +525,7 @@ exports[`MonitorIndex renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotificationsContent.test.js.snap b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotificationsContent.test.js.snap
index ca94897d4..7def4a5d7 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotificationsContent.test.js.snap
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/TriggerNotificationsContent.test.js.snap
@@ -1,121 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`TriggerNotificationsContent renders 1`] = `
-
-
-
-
-
-
-
- EuiIconMock
-
-
- Manage channels
-
-
-
-
-
-
-`;
-
exports[`TriggerNotificationsContent renders with notifications 1`] = `
Date: Tue, 27 Jun 2023 16:45:05 +0200
Subject: [PATCH 44/63] Add visual editor callout message for advanced
conditions
Signed-off-by: Jovan Cvetkovic
---
.../components/AssociateMonitors/components/MonitorsList.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index 483e41f42..b1b173597 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -53,8 +53,10 @@ const MonitorsList = ({ values, httpClient }) => {
'associatedMonitors',
DEFAULT_ASSOCIATED_MONITORS_VALUE
);
+
const selected = {};
- associatedMonitors.sequence.delegates.forEach((monitor, index) => {
+ const delegates = _.sortBy(associatedMonitors.sequence.delegates, ['order']);
+ delegates.forEach((monitor, index) => {
const filteredOption = options.filter((option) => option.monitor_id === monitor.monitor_id);
selected[index] = {
label: filteredOption[0]?.monitor_name || '',
From bd403f37faf26e825ac6c0e8e635419a59a91fb6 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Tue, 27 Jun 2023 17:38:34 +0200
Subject: [PATCH 45/63] auto add cinditions when associated monitor is selected
Signed-off-by: Jovan Cvetkovic
---
.../components/MonitorsList.js | 18 +++++++++------
.../containers/CreateMonitor/CreateMonitor.js | 1 +
.../CompositeTriggerCondition.js | 2 ++
.../ExpressionBuilder.js | 23 ++++++++++++++++---
.../CompositeMonitorsAlertTrigger.js | 2 ++
.../DefineCompositeLevelTrigger.js | 4 +++-
6 files changed, 39 insertions(+), 11 deletions(-)
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index b1b173597..173b98bc0 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -68,8 +68,8 @@ const MonitorsList = ({ values, httpClient }) => {
return selected;
};
- const generateFields = (selected) =>
- _.reduce(
+ const generateFields = (selected) => {
+ return _.reduce(
Object.keys(selected).length > 1 ? Object.keys(selected) : [0, 1],
(result, value, key) => {
result.push(key);
@@ -77,6 +77,7 @@ const MonitorsList = ({ values, httpClient }) => {
},
[]
);
+ };
const monitorsToOptions = (monitors) =>
monitors.map((monitor) => ({
@@ -95,9 +96,8 @@ const MonitorsList = ({ values, httpClient }) => {
}
setSelection(selected);
- updateSelection(selected);
-
setFormikValues(selected, monitorIdx, form);
+ updateSelection(selected);
};
const onBlur = (e, field, form) => {
@@ -153,6 +153,8 @@ const MonitorsList = ({ values, httpClient }) => {
let nextIndex = Math.max(...fields) + 1;
const newMonitorFields = [...fields, nextIndex];
setFields(newMonitorFields);
+
+ updateSelection(selection);
};
const onRemoveMonitor = (monitorIdx, idx, form) => {
@@ -164,9 +166,8 @@ const MonitorsList = ({ values, httpClient }) => {
newMonitorFields.splice(idx, 1);
setFields(newMonitorFields);
- updateSelection(selected);
-
setFormikValues(selected, monitorIdx, form);
+ updateSelection(selected);
};
const isValid = () => Object.keys(selection).length > 1;
@@ -266,7 +267,10 @@ const MonitorsList = ({ values, httpClient }) => {
))}
- onAddMonitor()} disabled={fields.length >= 10}>
+ onAddMonitor()}
+ disabled={fields.length >= 10 || fields.length >= options.length}
+ >
Associate another monitor
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index 876e7ff63..bef688047 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -334,6 +334,7 @@ export default class CreateMonitor extends Component {
{values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? (
{
const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
const [graphUi, setGraphUi] = useState(values.searchType === 'graph');
@@ -45,6 +46,7 @@ const CompositeTriggerCondition = ({
{graphUi ? (
{
const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
const formikFullFieldValue = _.replace(`${formikFullFieldName}_value`, /[.\[\]]/gm, '_');
@@ -77,8 +78,8 @@ const ExpressionBuilder = ({
useEffect(() => {
// initializing formik because these are generic fields and formik won't pick them up until fields is updated
- _.set(touched, formikFullFieldValue, false);
- _.set(values, formikFullFieldValue, '');
+ !_.get(touched, formikFullFieldValue) && _.set(touched, formikFullFieldValue, false);
+ !_.get(values, formikFullFieldValue) && _.set(values, formikFullFieldValue, '');
const monitors = _.get(values, 'monitorOptions', []);
if (monitors.length) {
@@ -104,7 +105,23 @@ const ExpressionBuilder = ({
setOptions(monitorOptions);
const condition = _.get(values, formikFullFieldName, '');
- const expressions = conditionToExpressions(condition, monitors);
+
+ let expressions = conditionToExpressions(condition, monitors);
+ console.log('EDIT', edit, _.get(touched, formikFullFieldValue, false));
+ if (!edit && !_.get(touched, formikFullFieldValue, false)) {
+ expressions = [];
+ monitorOptions.forEach((monitor, index) => {
+ expressions.push({
+ description: index ? 'AND' : '',
+ monitor_id: monitor.monitor_id,
+ monitor_name: monitor.label,
+ });
+ });
+ }
+
+ console.log('associatedMonitors', associatedMonitors);
+ console.log('monitorOptions', monitorOptions);
+ console.log('expressions', expressions);
setUsedExpressions(expressions?.length ? expressions : [DEFAULT_EXPRESSION]);
};
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
index 390438578..160bc51b6 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
@@ -7,6 +7,7 @@ import React, { Fragment } from 'react';
import DefineCompositeLevelTrigger from './DefineCompositeLevelTrigger';
const CompositeMonitorsAlertTrigger = ({
+ edit,
isDarkMode,
httpClient,
notifications,
@@ -18,6 +19,7 @@ const CompositeMonitorsAlertTrigger = ({
return (
(
class DefineCompositeLevelTrigger extends Component {
render() {
- const { values, httpClient, notifications, notificationService, plugins, touched } = this.props;
+ const { values, httpClient, notifications, notificationService, plugins, touched, edit } =
+ this.props;
const formikFieldPath = `triggerDefinitions[0].`;
const triggerName = _.get(values, `${formikFieldPath}name`, 'Trigger');
@@ -106,6 +107,7 @@ class DefineCompositeLevelTrigger extends Component {
Date: Tue, 27 Jun 2023 13:25:00 -0700
Subject: [PATCH 46/63] getting associated monitors count
Signed-off-by: Amardeepsingh Siglani
---
.../CreateMonitor/utils/monitorQueryParams.js | 2 --
.../containers/Monitors/utils/tableUtils.js | 5 ++++
server/services/MonitorService.js | 29 +++++++++++++++++--
3 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorQueryParams.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorQueryParams.js
index c696e2eb2..2dfc2bb00 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorQueryParams.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorQueryParams.js
@@ -3,8 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { SEARCH_TYPE } from '../../../../../utils/constants';
-
export const initializeFromQueryParams = (queryParams) => {
return {
searchType: queryParams.searchType || undefined,
diff --git a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
index ad2ae9248..bb58968ce 100644
--- a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
+++ b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
@@ -98,4 +98,9 @@ export const columns = [
sortable: true,
truncateText: false,
},
+ {
+ field: 'associatedCompositeMonitorCnt',
+ name: 'Associated composite monitors',
+ truncateText: false,
+ },
];
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 1869c5324..27e5ac092 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -92,7 +92,7 @@ export default class MonitorService {
const ifPrimaryTerm = _.get(getResponse, '_primary_term', null);
if (monitor) {
const { callAsCurrentUser } = this.esDriver.asScoped(req);
- const searchResponse = await callAsCurrentUser('alerting.getMonitors', {
+ const aggsParams = {
index: INDEX.ALL_ALERTS,
body: {
size: 0,
@@ -119,7 +119,8 @@ export default class MonitorService {
},
},
},
- });
+ };
+ const searchResponse = await callAsCurrentUser('alerting.getMonitors', aggsParams);
const dayCount = _.get(searchResponse, 'aggregations.24_hour_count.buckets.0.doc_count', 0);
const activeBuckets = _.get(searchResponse, 'aggregations.active_count.buckets', []);
const activeCount = activeBuckets.reduce(
@@ -263,6 +264,20 @@ export default class MonitorService {
should,
},
},
+ aggregations: {
+ associated_composite_monitors: {
+ nested: {
+ path: 'workflow.inputs.composite_input.sequence.delegates',
+ },
+ aggs: {
+ monitor_ids: {
+ terms: {
+ field: 'workflow.inputs.composite_input.sequence.delegates.monitor_id',
+ },
+ },
+ },
+ },
+ },
},
};
@@ -283,6 +298,14 @@ export default class MonitorService {
}, {});
const monitorMap = new Map(monitorKeyValueTuples);
const monitorIds = [...monitorMap.keys()];
+ const associatedCompositeMonitorCountMap = {};
+ _.get(
+ getResponse,
+ 'aggregations.associated_composite_monitors.monitor_ids.buckets',
+ []
+ ).forEach(({ key, doc_count }) => {
+ associatedCompositeMonitorCountMap[key] = doc_count;
+ });
const aggsOrderData = {};
const aggsSorts = {
@@ -368,6 +391,7 @@ export default class MonitorService {
active,
errors,
currentTime: Date.now(),
+ associatedCompositeMonitorCnt: associatedCompositeMonitorCountMap[id] || 0,
};
}
);
@@ -381,6 +405,7 @@ export default class MonitorService {
errors: 0,
latestAlert: '--',
currentTime: Date.now(),
+ associatedCompositeMonitorCnt: associatedCompositeMonitorCountMap[monitor.id] || 0,
}));
let results = _.orderBy(buckets.concat(unusedMonitors), [sortField], [sortDirection]);
From 5f689d57386a82c68a2b3b65519f9a699bb8425a Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Wed, 28 Jun 2023 00:26:04 -0700
Subject: [PATCH 47/63] ux updates
Signed-off-by: Amardeepsingh Siglani
---
public/app.js | 1 +
public/app.scss | 6 +
public/components/Breadcrumbs/Breadcrumbs.js | 5 +-
.../DeleteModal/DeleteMonitorModal.tsx | 31 ++--
.../AlertsDashboardFlyoutComponent.js | 29 ++++
public/components/Flyout/flyouts/index.js | 2 +
.../TriggerNotificationsContent.js | 3 +-
.../ChainedAlertDetails.tsx | 141 ++++++++++++++++++
.../ChainedAlertDetailsFlyout.tsx | 48 ++++++
.../pages/Dashboard/containers/Dashboard.js | 105 ++++++++-----
public/pages/Dashboard/utils/tableUtils.js | 24 +++
.../MonitorOverview/MonitorOverview.scss | 15 ++
.../MonitorOverview/utils/getOverviewStats.js | 31 +++-
.../containers/MonitorDetails.js | 28 ++--
.../MonitorHistory/MonitorHistory.js | 3 +-
.../containers/Triggers/Triggers.js | 19 ++-
.../Monitors/containers/Monitors/Monitors.js | 15 +-
server/clusters/alerting/alertingPlugin.js | 13 ++
server/routes/alerts.js | 1 +
server/routes/monitors.js | 15 ++
server/services/AlertService.js | 17 ++-
server/services/MonitorService.js | 38 ++++-
22 files changed, 509 insertions(+), 81 deletions(-)
create mode 100644 public/app.scss
create mode 100644 public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
create mode 100644 public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout.tsx
create mode 100644 public/pages/MonitorDetails/components/MonitorOverview/MonitorOverview.scss
diff --git a/public/app.js b/public/app.js
index 3f0049057..dfc85f6c3 100644
--- a/public/app.js
+++ b/public/app.js
@@ -10,6 +10,7 @@ import { HashRouter as Router, Route } from 'react-router-dom';
import 'react-vis/dist/style.css';
// TODO: review the CSS style and migrate the necessary style to SASS, as Less is not supported in OpenSearch Dashboards "new platform" anymore
// import './less/main.less';
+import './app.scss';
import Main from './pages/Main';
import { CoreContext } from './utils/CoreContext';
import { ServicesContext, NotificationService } from './services';
diff --git a/public/app.scss b/public/app.scss
new file mode 100644
index 000000000..78fa72311
--- /dev/null
+++ b/public/app.scss
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+@import "./pages/MonitorDetails/components/MonitorOverview/MonitorOverview.scss";
\ No newline at end of file
diff --git a/public/components/Breadcrumbs/Breadcrumbs.js b/public/components/Breadcrumbs/Breadcrumbs.js
index 1fa9d6ebd..4bc968b40 100644
--- a/public/components/Breadcrumbs/Breadcrumbs.js
+++ b/public/components/Breadcrumbs/Breadcrumbs.js
@@ -106,7 +106,7 @@ export async function getBreadcrumb(route, routeState, httpClient) {
// This condition is true for any auto generated 20 character long,
// URL-safe, base64-encoded document ID by opensearch
if (RegExp(/^[0-9a-z_-]{20}$/i).test(base)) {
- const { action, type } = queryString.parse(`?${queryParams}`);
+ const { action, type, monitorType } = queryString.parse(`?${queryParams}`);
switch (action) {
case DESTINATION_ACTIONS.UPDATE_DESTINATION:
const destinationName = _.get(routeState, 'destinationToEdit.name', base);
@@ -119,7 +119,8 @@ export async function getBreadcrumb(route, routeState, httpClient) {
// TODO::Everything else is considered as monitor, we should break this.
let monitorName = base;
try {
- const searchPool = type === 'workflow' ? 'workflows' : 'monitors';
+ const searchPool =
+ type === 'workflow' || monitorType === 'composite' ? 'workflows' : 'monitors';
const response = await httpClient.get(`../api/alerting/${searchPool}/${base}`);
if (response.ok) {
monitorName = response.resp.name;
diff --git a/public/components/DeleteModal/DeleteMonitorModal.tsx b/public/components/DeleteModal/DeleteMonitorModal.tsx
index 7b12c4436..fdf96f62e 100644
--- a/public/components/DeleteModal/DeleteMonitorModal.tsx
+++ b/public/components/DeleteModal/DeleteMonitorModal.tsx
@@ -3,16 +3,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { ChangeEvent, Component } from 'react';
+import React, { Component } from 'react';
import {
EuiConfirmModal,
EuiOverlayMask
} from '@elastic/eui';
interface DeleteModalProps {
- monitorNames: string[];
- onClickDelete: (event?: any) => void;
- closeDeleteModal: (event?: any) => void;
+ monitors: any[];
+ onClickDelete: () => void;
+ closeDeleteModal: () => void;
}
export const DEFAULT_DELETION_TEXT = 'delete';
@@ -20,15 +20,22 @@ export const DEFAULT_DELETION_TEXT = 'delete';
export default class DeleteMonitorModal extends Component {
render() {
const {
- monitorNames,
+ monitors,
closeDeleteModal,
onClickDelete
} = this.props;
+ const monitorNames = monitors.map(monitor => monitor.name);
let warningHeading = `Delete monitor ${monitorNames[0]}?`;
let warningBody: React.ReactNode = 'This action cannot be undone.';
+ let allowDelete = true;
- if (monitorNames.length > 1) {
+ if (monitors.length === 1 && monitors[0].associatedCompositeMonitorCnt > 0) {
+ warningHeading = `Unable to delete ${monitorNames[0]}`;
+ warningBody = `The monitor ${monitorNames[0]} is currently associated with composite monitors. Unlink from the composite monitors before deleting this monitor.`;
+ allowDelete = false;
+ }
+ else if (monitorNames.length > 1) {
warningHeading = `Delete ${monitorNames.length} monitors?`;
warningBody = (
<>
@@ -44,14 +51,16 @@ export default class DeleteMonitorModal extends Component {
{}}
onConfirm={() => {
- onClickDelete();
+ if (allowDelete) {
+ onClickDelete();
+ }
closeDeleteModal();
}}
- cancelButtonText={'Cancel'}
- confirmButtonText={'Delete'}
- buttonColor={'danger'}
+ cancelButtonText={allowDelete ? 'Cancel' : undefined}
+ confirmButtonText={allowDelete ? 'Delete' : 'Close'}
+ buttonColor={allowDelete ? 'danger' : 'primary'}
defaultFocusedButton="confirm"
>
{warningBody}
diff --git a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
index a283e0cbd..10956fbcc 100644
--- a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
+++ b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
@@ -17,6 +17,8 @@ import {
EuiTab,
EuiTabs,
EuiText,
+ EuiToolTip,
+ EuiButtonIcon,
} from '@elastic/eui';
import { getTime } from '../../../../pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats';
import { PLUGIN_NAME } from '../../../../../utils/constants';
@@ -334,6 +336,32 @@ export default class AlertsDashboardFlyoutComponent extends Component {
getAlertsFindingColumn(httpClient, history, location, notifications)
);
break;
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ columns = _.cloneDeep(queryColumns);
+ columns.push({
+ name: 'Actions',
+ sortable: false,
+ actions: [
+ {
+ render: (alert) => (
+
+ {
+ this.props.openChainedAlertsFlyout?.({
+ alert,
+ closeFlyout: this.props.closeFlyout,
+ });
+ }}
+ />
+
+ ),
+ },
+ ],
+ });
+ break;
default:
columns = queryColumns;
break;
@@ -421,6 +449,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
sorting={sorting}
isSelectable={selectable}
selection={selection}
+ hasActions={true}
onChange={this.onTableChange}
noItemsMessage={loading ? 'Loading alerts...' : 'No alerts.'}
data-test-subj={`alertsDashboardFlyout_table_${trigger_name}`}
diff --git a/public/components/Flyout/flyouts/index.js b/public/components/Flyout/flyouts/index.js
index 90b7aff9f..305b12d57 100644
--- a/public/components/Flyout/flyouts/index.js
+++ b/public/components/Flyout/flyouts/index.js
@@ -7,12 +7,14 @@ import message from './message';
import messageFrequency from './messageFrequency';
import triggerCondition from './triggerCondition';
import alertsDashboard from './alertsDashboard';
+import { chainedAlertDetailsFlyout } from '../../../pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout';
const Flyouts = {
messageFrequency,
message,
triggerCondition,
alertsDashboard,
+ chainedAlertDetailsFlyout,
};
export default Flyouts;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
index b437f4404..a943d3654 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
@@ -8,7 +8,7 @@ import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiSpacer } from '
import { FormikComboBox } from '../../../../components/FormControls';
import NotificationConfigDialog from './NotificationConfigDialog';
import _ from 'lodash';
-import { FORMIK_INITIAL_ACTION_VALUES } from '../../utils/constants';
+import { FORMIK_INITIAL_ACTION_VALUES, MANAGE_CHANNELS_PATH } from '../../utils/constants';
import { NOTIFY_OPTIONS_VALUES } from '../../components/Action/actions/Message';
import { required } from '../../../../utils/validate';
@@ -95,6 +95,7 @@ const TriggerNotificationsContent = ({
iconType={'popout'}
style={{ marginTop: '22px' }}
disabled={!hasNotifications}
+ onClick={() => window.open(httpClient.basePath.prepend(MANAGE_CHANNELS_PATH))}
>
Manage channels
diff --git a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
new file mode 100644
index 000000000..bc91db41a
--- /dev/null
+++ b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
@@ -0,0 +1,141 @@
+/*
+* Copyright OpenSearch Contributors
+* SPDX-License-Identifier: Apache-2.0
+*/
+
+import React, { useState } from 'react';
+import {
+ EuiFlexGrid,
+ EuiTitle,
+ EuiSpacer,
+ EuiBasicTableColumn,
+ EuiButtonIcon,
+ EuiPanel,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiText,
+ EuiInMemoryTable
+} from '@elastic/eui';
+import OverviewStat from '../../../MonitorDetails/components/OverviewStat';
+import { DEFAULT_EMPTY_DATA } from '../../../../utils/constants';
+import { associatedAlertsTableColumns, renderTime } from '../../utils/tableUtils';
+import _ from 'lodash';
+
+export const ChainedAlertDetails = ({ alert }) => {
+ const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{
+ [key: string]: JSX.Element;
+ }>({});
+
+ const toggleCorrelationDetails = (item: any) => {
+ const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap };
+ if (itemIdToExpandedRowMapValues[item.id]) {
+ delete itemIdToExpandedRowMapValues[item.id];
+ } else {
+ itemIdToExpandedRowMapValues[item.id] = (
+
+
+
+ State
+
+
+
+ {typeof item.state !== 'string' ? DEFAULT_EMPTY_DATA : _.capitalize(item.state.toLowerCase())}
+
+
+
+
+
+
+ End time
+
+
+
+ {item.end_time}
+
+
+
+
+
+
+ Time acknowledged
+
+
+
+ {item.acknowledged_time || DEFAULT_EMPTY_DATA}
+
+
+
+
+ );
+ }
+
+ setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues);
+ };
+
+ const overviewItems = [
+ {
+ header: 'Trigger name',
+ value: alert.trigger_name || DEFAULT_EMPTY_DATA
+ },
+ {
+ header: 'Alert start time',
+ value: renderTime(alert.start_time) || DEFAULT_EMPTY_DATA
+ },
+ {
+ header: 'State',
+ value: typeof alert.state !== 'string' ? DEFAULT_EMPTY_DATA : _.capitalize(alert.state.toLowerCase())
+ },
+ {
+ header: 'Severity',
+ value: alert.severity
+ }
+ ];
+
+ const dummyItems = [
+ {
+ id: '3m4k_4gBao9NSj-nooCV',
+ start_time: 1687907771029,
+ end_time: 1687907771029,
+ severity: "1",
+ state: 'ACTIVE',
+ trigger_name: 'Sample trigger',
+ acknowledged_time: undefined
+ }
+ ];
+
+ const actions: any[] = [
+ {
+ render: (item: any) => (
+ toggleCorrelationDetails(item)}
+ aria-label={itemIdToExpandedRowMap[item.id] ? 'Collapse' : 'Expand'}
+ iconType={itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'}
+ />
+ ),
+ },
+ ];
+
+ return (
+ <>
+
+ {overviewItems.map((props) => (
+
+ ))}
+
+
+
+ Alerts from associated monitors
+
+
+ []}
+ items={dummyItems}
+ itemId='id'
+ itemIdToExpandedRowMap={itemIdToExpandedRowMap}
+ hasActions={true}
+ isExpandable={true}
+ pagination={true}
+ />
+ >
+ )
+}
\ No newline at end of file
diff --git a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout.tsx b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout.tsx
new file mode 100644
index 000000000..39db515b2
--- /dev/null
+++ b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout.tsx
@@ -0,0 +1,48 @@
+/*
+* Copyright OpenSearch Contributors
+* SPDX-License-Identifier: Apache-2.0
+*/
+
+import React from 'react';
+import {
+ EuiTitle,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiButtonIcon,
+} from '@elastic/eui';
+import { ChainedAlertDetails } from './ChainedAlertDetails';
+
+export const chainedAlertDetailsFlyout = ({ closeFlyout, alert }) => {
+ return {
+ flyoutProps: {
+ 'aria-labelledby': 'alertsDashboardFlyout',
+ size: 'm',
+ hideCloseButton: true,
+ },
+ headerProps: { hasBorder: true },
+ header: (
+
+
+
+ {`Alerts details`}
+
+
+
+
+
+
+ ),
+ footerProps: { style: { backgroundColor: '#F5F7FA' } },
+ body: (
+
+ ),
+ }
+}
\ No newline at end of file
diff --git a/public/pages/Dashboard/containers/Dashboard.js b/public/pages/Dashboard/containers/Dashboard.js
index 1cfd0da07..a6575ffd9 100644
--- a/public/pages/Dashboard/containers/Dashboard.js
+++ b/public/pages/Dashboard/containers/Dashboard.js
@@ -6,7 +6,14 @@
import React, { Component } from 'react';
import _ from 'lodash';
import queryString from 'query-string';
-import { EuiBasicTable, EuiButton, EuiHorizontalRule, EuiIcon } from '@elastic/eui';
+import {
+ EuiBasicTable,
+ EuiButton,
+ EuiHorizontalRule,
+ EuiIcon,
+ EuiToolTip,
+ EuiButtonIcon,
+} from '@elastic/eui';
import ContentPanel from '../../../components/ContentPanel';
import DashboardEmptyPrompt from '../components/DashboardEmptyPrompt';
import DashboardControls from '../components/DashboardControls';
@@ -30,6 +37,7 @@ import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../Monitors/containers/Monitors/ut
import { MAX_ALERT_COUNT } from '../utils/constants';
import AcknowledgeAlertsModal from '../components/AcknowledgeAlertsModal';
import { getAlertsFindingColumn } from '../components/FindingsDashboard/findingsUtils';
+import { chainedAlertDetailsFlyout } from '../components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout';
export default class Dashboard extends Component {
constructor(props) {
@@ -37,15 +45,8 @@ export default class Dashboard extends Component {
const { location, perAlertView } = props;
- const {
- alertState,
- from,
- search,
- severityLevel,
- size,
- sortDirection,
- sortField,
- } = getURLQueryParams(location);
+ const { alertState, from, search, severityLevel, size, sortDirection, sortField } =
+ getURLQueryParams(location);
this.state = {
alerts: [],
@@ -74,16 +75,8 @@ export default class Dashboard extends Component {
};
componentDidMount() {
- const {
- alertState,
- page,
- search,
- severityLevel,
- size,
- sortDirection,
- sortField,
- monitorIds,
- } = this.state;
+ const { alertState, page, search, severityLevel, size, sortDirection, sortField, monitorIds } =
+ this.state;
this.getAlerts(
page * size,
size,
@@ -134,7 +127,9 @@ export default class Dashboard extends Component {
severityLevel,
alertState,
monitorIds,
+ monitorType: this.props.monitorType,
};
+
const queryParamsString = queryString.stringify(params);
location.search;
const { httpClient, history, notifications, perAlertView } = this.props;
@@ -226,16 +221,8 @@ export default class Dashboard extends Component {
);
this.setState({ selectedItems: [] });
- const {
- page,
- size,
- search,
- sortField,
- sortDirection,
- severityLevel,
- alertState,
- monitorIds,
- } = this.state;
+ const { page, size, search, sortField, sortDirection, severityLevel, alertState, monitorIds } =
+ this.state;
this.getAlerts(
page * size,
size,
@@ -280,6 +267,26 @@ export default class Dashboard extends Component {
if (!_.isEmpty(payload)) {
this.props.setFlyout({
type: 'alertsDashboard',
+ payload: {
+ ...payload,
+ openChainedAlertsFlyout: this.openChainedAlertsFlyout,
+ closeFlyout: this.closeFlyout,
+ },
+ });
+ }
+ };
+
+ closeFlyout = () => {
+ const { setFlyout } = this.props;
+ if (typeof setFlyout === 'function') setFlyout(null);
+ this.setState({ flyoutIsOpen: false });
+ };
+
+ openChainedAlertsFlyout = (payload) => {
+ this.setState({ flyoutIsOpen: true });
+ if (!_.isEmpty(payload)) {
+ this.props.setFlyout({
+ type: 'chainedAlertDetailsFlyout',
payload: { ...payload },
});
}
@@ -292,16 +299,8 @@ export default class Dashboard extends Component {
};
refreshDashboard = () => {
- const {
- page,
- size,
- search,
- sortField,
- sortDirection,
- severityLevel,
- alertState,
- monitorIds,
- } = this.state;
+ const { page, size, search, sortField, sortDirection, severityLevel, alertState, monitorIds } =
+ this.state;
this.getAlerts(
page * size,
size,
@@ -400,6 +399,32 @@ export default class Dashboard extends Component {
)
);
break;
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ columnType = _.cloneDeep(queryColumns);
+ columnType.push({
+ name: 'Actions',
+ sortable: false,
+ actions: [
+ {
+ render: (alert) => (
+
+ {
+ this.openChainedAlertsFlyout({
+ alert,
+ closeFlyout: this.closeFlyout,
+ });
+ }}
+ />
+
+ ),
+ },
+ ],
+ });
+ break;
default:
columnType = queryColumns;
break;
diff --git a/public/pages/Dashboard/utils/tableUtils.js b/public/pages/Dashboard/utils/tableUtils.js
index 76fcf8a76..335510622 100644
--- a/public/pages/Dashboard/utils/tableUtils.js
+++ b/public/pages/Dashboard/utils/tableUtils.js
@@ -212,3 +212,27 @@ export const alertColumns = (
),
},
];
+
+export const associatedAlertsTableColumns = [
+ {
+ field: 'start_time',
+ name: 'Alert start time',
+ sortable: true,
+ truncateText: false,
+ render: renderTime,
+ dataType: 'date',
+ },
+ {
+ field: 'severity',
+ name: 'Severity',
+ sortable: false,
+ truncateText: false,
+ },
+ {
+ field: 'trigger_name',
+ name: 'Trigger name',
+ sortable: true,
+ truncateText: true,
+ textOnly: true,
+ },
+];
diff --git a/public/pages/MonitorDetails/components/MonitorOverview/MonitorOverview.scss b/public/pages/MonitorDetails/components/MonitorOverview/MonitorOverview.scss
new file mode 100644
index 000000000..19356b332
--- /dev/null
+++ b/public/pages/MonitorDetails/components/MonitorOverview/MonitorOverview.scss
@@ -0,0 +1,15 @@
+/*
+* Copyright OpenSearch Contributors
+* SPDX-License-Identifier: Apache-2.0
+*/
+
+.associated-comp-monitor-link .euiLink__externalIcon {
+ display: none;
+}
+
+.associated-alerts-table-details-row {
+ .associated-alerts-table-details-row-value {
+ font-weight: 600;
+ color: $euiColorDarkestShade;
+ }
+}
\ No newline at end of file
diff --git a/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js b/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
index d9844b262..7ddae5432 100644
--- a/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
+++ b/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
@@ -16,6 +16,7 @@ import {
} from '../../../../../utils/constants';
import { API_TYPES } from '../../../../CreateMonitor/components/ClusterMetricsMonitor/utils/clusterMetricsMonitorConstants';
import { getApiType } from '../../../../CreateMonitor/components/ClusterMetricsMonitor/utils/clusterMetricsMonitorHelpers';
+import { PLUGIN_NAME } from '../../../../../../utils/constants';
// TODO: used in multiple places, move into helper
export function getTime(time) {
@@ -85,7 +86,7 @@ export default function getOverviewStats(
]
: [];
const monitorLevelType = _.get(monitor, 'ui_metadata.monitor_type', 'query_level_monitor');
- return [
+ const overviewStats = [
{
header: 'Monitor type',
value: getMonitorLevelType(monitorLevelType),
@@ -126,4 +127,32 @@ export default function getOverviewStats(
value: monitor.user && monitor.user.name ? monitor.user.name : '-',
},
];
+
+ if (monitor.associated_workflows) {
+ overviewStats.push({
+ header: 'Associated composite monitors',
+ value:
+ monitor.associated_workflows.length > 0 ? (
+ <>
+ {monitor.associated_workflows.map((id, idx) => {
+ return (
+
+ {id}
+ {`${idx < monitor.associated_workflows.length - 1 ? ', ' : ''}`}
+
+ );
+ })}
+ >
+ ) : (
+ '-'
+ ),
+ });
+ }
+
+ return overviewStats;
}
diff --git a/public/pages/MonitorDetails/containers/MonitorDetails.js b/public/pages/MonitorDetails/containers/MonitorDetails.js
index adc7bb3f0..225866423 100644
--- a/public/pages/MonitorDetails/containers/MonitorDetails.js
+++ b/public/pages/MonitorDetails/containers/MonitorDetails.js
@@ -73,7 +73,10 @@ export default class MonitorDetails extends Component {
}
isWorkflow = () => {
- return new URLSearchParams(this.props.location.search).get('type') === 'workflow';
+ const searchParams = new URLSearchParams(this.props.location.search);
+ return (
+ searchParams.get('type') === 'workflow' || searchParams.get('monitorType') === 'composite'
+ );
};
componentDidMount() {
@@ -316,11 +319,13 @@ export default class MonitorDetails extends Component {
};
renderTableTabs = () => {
- const { tabId } = this.state;
- const tabs = [
- { ...TABLE_TAB_IDS.ALERTS, content: this.renderAlertsTable() },
- { ...TABLE_TAB_IDS.FINDINGS, content: this.renderFindingsTable() },
- ];
+ const { tabId, monitor } = this.state;
+ const tabs = [{ ...TABLE_TAB_IDS.ALERTS, content: this.renderAlertsTable() }];
+
+ if (monitor.monitor_type !== MONITOR_TYPE.COMPOSITE_LEVEL) {
+ tabs.push({ ...TABLE_TAB_IDS.FINDINGS, content: this.renderFindingsTable() });
+ }
+
return tabs.map((tab, index) => (
{this.renderNoTriggersCallOut()}
@@ -458,11 +465,12 @@ export default class MonitorDetails extends Component {
{displayTableTabs ? (
- {this.renderTableTabs()}
+ {monitor.monitor_type !== MONITOR_TYPE.COMPOSITE_LEVEL ? (
+ {this.renderTableTabs()}
+ ) : null}
{this.state.tabContent}
- ) : // this.renderAlertsTable()
- null}
+ ) : null}
{isJsonModalOpen && (
diff --git a/public/pages/MonitorDetails/containers/MonitorHistory/MonitorHistory.js b/public/pages/MonitorDetails/containers/MonitorHistory/MonitorHistory.js
index df28cb33e..2cd44a876 100644
--- a/public/pages/MonitorDetails/containers/MonitorHistory/MonitorHistory.js
+++ b/public/pages/MonitorDetails/containers/MonitorHistory/MonitorHistory.js
@@ -230,13 +230,14 @@ class MonitorHistory extends PureComponent {
isLoading: true,
});
const { timeSeriesWindow } = this.state;
- const { httpClient, triggers, monitorId, notifications } = this.props;
+ const { httpClient, triggers, monitorId, notifications, monitorType } = this.props;
try {
const params = {
size: HistoryConstants.MAX_DOC_COUNT_FOR_ALERTS,
sortField: 'start_time',
sortDirection: 'asc',
monitorIds: monitorId,
+ monitorType,
};
const resp = await httpClient.get('../api/alerting/alerts', { query: params });
diff --git a/public/pages/MonitorDetails/containers/Triggers/Triggers.js b/public/pages/MonitorDetails/containers/Triggers/Triggers.js
index 3109cd19a..214048d29 100644
--- a/public/pages/MonitorDetails/containers/Triggers/Triggers.js
+++ b/public/pages/MonitorDetails/containers/Triggers/Triggers.js
@@ -100,14 +100,6 @@ export default class Triggers extends Component {
truncateText: true,
width: '15%',
},
- {
- name: 'Condition -- (formatting pending...)',
- truncateText: true,
- render: (item) => {
- return item.condition.script.source;
- },
- width: '50%',
- },
{
field: 'actions',
name: 'Number of actions',
@@ -125,6 +117,17 @@ export default class Triggers extends Component {
},
];
+ if (monitor.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL) {
+ columns.splice(1, 0, {
+ name: 'Condition -- (formatting pending...)',
+ truncateText: true,
+ render: (item) => {
+ return item.condition.script.source;
+ },
+ width: '50%',
+ });
+ }
+
const sorting = { sort: { field, direction } };
const items = getUnwrappedTriggers(monitor);
diff --git a/public/pages/Monitors/containers/Monitors/Monitors.js b/public/pages/Monitors/containers/Monitors/Monitors.js
index c9c8926d8..6088178ac 100644
--- a/public/pages/Monitors/containers/Monitors/Monitors.js
+++ b/public/pages/Monitors/containers/Monitors/Monitors.js
@@ -220,11 +220,14 @@ export default class Monitors extends Component {
deleteMonitor(item) {
const { httpClient, notifications } = this.props;
const { id, version } = item;
+ const poolType = item.item_type === 'composite' ? 'workflows' : 'monitors';
return httpClient
- .delete(`../api/alerting/monitors/${id}`, { query: { version } })
+ .delete(`../api/alerting/${poolType}/${id}`, { query: { version } })
.then((resp) => {
if (!resp.ok) {
backendErrorNotification(notifications, 'delete', 'monitor', resp.resp);
+ } else {
+ notifications.toasts.addSuccess(`Monitor deleted successfully.`);
}
return resp;
})
@@ -378,6 +381,10 @@ export default class Monitors extends Component {
return `${item.id}-${item.currentTime}`;
}
+ isDeleteNotSupported = (items) => {
+ return items.length > 1 && items.some((item) => item.associatedCompositeMonitorCnt > 0);
+ };
+
render() {
const {
alerts,
@@ -421,7 +428,9 @@ export default class Monitors extends Component {
actions={
{this.state.monitorItemsToDelete && (
item.name)}
+ monitors={this.state.monitorItemsToDelete}
closeDeleteModal={() => this.setState({ monitorItemsToDelete: undefined })}
onClickDelete={() => this.deleteMonitors(this.state.monitorItemsToDelete)}
/>
diff --git a/server/clusters/alerting/alertingPlugin.js b/server/clusters/alerting/alertingPlugin.js
index 743705bb3..36779e788 100644
--- a/server/clusters/alerting/alertingPlugin.js
+++ b/server/clusters/alerting/alertingPlugin.js
@@ -81,6 +81,19 @@ export default function alertingPlugin(Client, config, components) {
method: 'DELETE',
});
+ alerting.deleteWorkflow = ca({
+ url: {
+ fmt: `${WORKFLOW_BASE_API}/<%=workflowId%>`,
+ req: {
+ workflowId: {
+ type: 'string',
+ required: true,
+ },
+ },
+ },
+ method: 'DELETE',
+ });
+
// TODO DRAFT: May need to add 'refresh' assignment here again.
alerting.updateMonitor = ca({
url: {
diff --git a/server/routes/alerts.js b/server/routes/alerts.js
index 15b7da2ac..c8711bfc7 100644
--- a/server/routes/alerts.js
+++ b/server/routes/alerts.js
@@ -21,6 +21,7 @@ export default function (services, router) {
severityLevel: schema.maybe(schema.string()),
alertState: schema.maybe(schema.string()),
monitorIds: schema.maybe(schema.string()),
+ monitorType: schema.maybe(schema.string()),
}),
},
},
diff --git a/server/routes/monitors.js b/server/routes/monitors.js
index 4d46437bc..dcf98cee8 100644
--- a/server/routes/monitors.js
+++ b/server/routes/monitors.js
@@ -141,6 +141,21 @@ export default function (services, router) {
monitorService.deleteMonitor
);
+ router.delete(
+ {
+ path: '/api/alerting/workflows/{id}',
+ validate: {
+ params: schema.object({
+ id: schema.string(),
+ }),
+ query: schema.object({
+ version: schema.number(),
+ }),
+ },
+ },
+ monitorService.deleteWorkflow
+ );
+
router.post(
{
path: '/api/alerting/monitors/{id}/_acknowledge/alerts',
diff --git a/server/services/AlertService.js b/server/services/AlertService.js
index 6f00b2589..e7478beac 100644
--- a/server/services/AlertService.js
+++ b/server/services/AlertService.js
@@ -29,8 +29,12 @@ export default class AlertService {
severityLevel = 'ALL',
alertState = 'ALL',
monitorIds = [],
+ monitorType = 'monitor',
} = req.query;
+ console.log('get alerts req query from frontend');
+ console.log(JSON.stringify(req.query));
+
var params;
switch (sortField) {
case GET_ALERTS_SORT_FILTERS.MONITOR_NAME:
@@ -73,13 +77,17 @@ export default class AlertService {
params.alertState = alertState;
params.searchString = search;
if (search.trim()) params.searchString = `*${search.trim().split(' ').join('* *')}*`;
- if (monitorIds.length > 0)
- params.monitorId = !Array.isArray(monitorIds) ? monitorIds : monitorIds[0];
+ if (monitorIds.length > 0) {
+ const idField = monitorType === 'composite' ? 'workflowIds' : 'monitorId';
+ params[idField] = !Array.isArray(monitorIds) ? monitorIds : monitorIds[0];
+ }
const { callAsCurrentUser } = this.esDriver.asScoped(req);
try {
+ console.log('Get Alerts params ****');
+ console.log(monitorType);
+ console.log(JSON.stringify(params));
const resp = await callAsCurrentUser('alerting.getAlerts', params);
- console.log(params);
const alerts = resp.alerts.map((hit) => {
const alert = hit;
const id = hit.alert_id;
@@ -88,6 +96,9 @@ export default class AlertService {
});
const totalAlerts = resp.totalAlerts;
+ console.log('Get alerts response *****');
+ console.log(JSON.stringify(alerts));
+
return res.ok({
body: {
ok: true,
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 27e5ac092..3c5a7ac1e 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -79,6 +79,30 @@ export default class MonitorService {
}
};
+ deleteWorkflow = async (context, req, res) => {
+ try {
+ const { id } = req.params;
+ const params = { workflowId: id };
+ const { callAsCurrentUser } = await this.esDriver.asScoped(req);
+ const response = await callAsCurrentUser('alerting.deleteWorkflow', params);
+ console.log('delete workflow response ^*^*^*^*^*');
+ console.log(JSON.stringify(response));
+ return res.ok({
+ body: {
+ ok: response.result === 'deleted' || response.result === undefined,
+ },
+ });
+ } catch (err) {
+ console.error('Alerting - MonitorService - deleteWorkflow:', err);
+ return res.ok({
+ body: {
+ ok: false,
+ resp: err.message,
+ },
+ });
+ }
+ };
+
getMonitor = async (context, req, res) => {
console.log('****** GET MONITOR *****');
try {
@@ -86,10 +110,13 @@ export default class MonitorService {
const params = { monitorId: id };
const { callAsCurrentUser } = await this.esDriver.asScoped(req);
const getResponse = await callAsCurrentUser('alerting.getMonitor', params);
- const monitor = _.get(getResponse, 'monitor', null);
+ console.log('Get monitor complete response ^^^^^^^^^');
+ console.log(JSON.stringify(getResponse));
+ let monitor = _.get(getResponse, 'monitor', null);
const version = _.get(getResponse, '_version', null);
const ifSeqNo = _.get(getResponse, '_seq_no', null);
const ifPrimaryTerm = _.get(getResponse, '_primary_term', null);
+ const associated_workflows = _.get(getResponse, 'associated_workflows', null);
if (monitor) {
const { callAsCurrentUser } = this.esDriver.asScoped(req);
const aggsParams = {
@@ -127,6 +154,12 @@ export default class MonitorService {
(acc, curr) => (curr.key === 'ACTIVE' ? curr.doc_count : acc),
0
);
+ if (associated_workflows) {
+ monitor = {
+ ...monitor,
+ associated_workflows,
+ };
+ }
return res.ok({
body: { ok: true, resp: monitor, activeCount, dayCount, version, ifSeqNo, ifPrimaryTerm },
});
@@ -497,6 +530,9 @@ export default class MonitorService {
const { query, index, size } = req.body;
const params = { index, size, body: query };
+ console.log('Search monitors ******* ');
+ console.log(JSON.stringify(params));
+
const { callAsCurrentUser } = await this.esDriver.asScoped(req);
const results = await callAsCurrentUser('alerting.getMonitors', params);
return res.ok({
From 06584f6db2dfcbc3c8a4aa346ebcd6f06f4c4358 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Wed, 28 Jun 2023 14:02:15 +0200
Subject: [PATCH 48/63] Adds multiple triggers
Signed-off-by: Jovan Cvetkovic
---
.../MonitorDefinitionCard.js | 13 +++-
.../containers/CreateMonitor/CreateMonitor.js | 51 +++++--------
.../CompositeTriggerCondition.js | 9 ++-
.../ExpressionBuilder.js | 33 +++++----
.../ExpressionEditor.js | 16 ++++-
.../ConfigureTriggers/ConfigureTriggers.js | 34 +++++++++
.../CompositeMonitorsAlertTrigger.js | 34 ---------
.../CompositeMonitorsAlertTrigger.test.js | 30 --------
.../DefineCompositeLevelTrigger.js | 71 ++++++++++++-------
yarn.lock | 2 +-
10 files changed, 148 insertions(+), 145 deletions(-)
delete mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
delete mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.test.js
diff --git a/public/pages/CreateMonitor/components/MonitorDefinitionCard/MonitorDefinitionCard.js b/public/pages/CreateMonitor/components/MonitorDefinitionCard/MonitorDefinitionCard.js
index ae20fc401..b58dda8d0 100644
--- a/public/pages/CreateMonitor/components/MonitorDefinitionCard/MonitorDefinitionCard.js
+++ b/public/pages/CreateMonitor/components/MonitorDefinitionCard/MonitorDefinitionCard.js
@@ -20,10 +20,17 @@ const onChangeDefinition = (e, form, values) => {
let preventVisualEditor = false;
if (values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL && type === 'graph') {
- const triggerConditions = _.get(values, 'triggerDefinitions[0].triggerConditions', '');
+ const triggerDefinitions = _.get(values, 'triggerDefinitions', []);
const monitors = _.get(values, 'monitorOptions', []);
- const parsedConditions = conditionToExpressions(triggerConditions, monitors);
- preventVisualEditor = !!triggerConditions.length && !parsedConditions.length;
+ for (let trigger of triggerDefinitions) {
+ const triggerConditions = trigger.triggerConditions || '';
+ const parsedConditions = conditionToExpressions(triggerConditions, monitors);
+
+ if (!!triggerConditions.length && !parsedConditions.length) {
+ preventVisualEditor = true;
+ break;
+ }
+ }
}
form.setFieldValue('preventVisualEditor', preventVisualEditor);
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index bef688047..15d43df05 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -33,7 +33,6 @@ import {
import { triggerToFormik } from '../../../CreateTrigger/containers/CreateTrigger/utils/triggerToFormik';
import { TRIGGER_TYPE } from '../../../CreateTrigger/containers/CreateTrigger/utils/constants';
import WorkflowDetails from '../WorkflowDetails/WorkflowDetails';
-import CompositeMonitorsAlertTrigger from '../../../CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger';
export default class CreateMonitor extends Component {
static defaultProps = {
@@ -332,37 +331,25 @@ export default class CreateMonitor extends Component {
)}
- {values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL ? (
-
- ) : (
-
- {(triggerArrayHelpers) => (
-
- )}
-
- )}
+
+ {(triggerArrayHelpers) => (
+
+ )}
+
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
index e0898e328..398e77e73 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
@@ -13,6 +13,7 @@ const CompositeTriggerCondition = ({
isDarkMode = false,
httpClient,
edit,
+ triggerIndex,
}) => {
const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
const [graphUi, setGraphUi] = useState(values.searchType === 'graph');
@@ -25,13 +26,13 @@ const CompositeTriggerCondition = ({
const validate = () => {};
return (
graphUi && validate(),
}}
render={({ form }) => (
-
+
{graphUi ? (
) : (
{
const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
const formikFullFieldValue = _.replace(`${formikFullFieldName}_value`, /[.\[\]]/gm, '_');
+ const expressionNamePrefix = `expressionQueries_${triggerIndex}`;
const DEFAULT_CONDITION = 'AND';
const DEFAULT_NAME = 'Select associated monitor';
@@ -107,8 +109,7 @@ const ExpressionBuilder = ({
const condition = _.get(values, formikFullFieldName, '');
let expressions = conditionToExpressions(condition, monitors);
- console.log('EDIT', edit, _.get(touched, formikFullFieldValue, false));
- if (!edit && !_.get(touched, formikFullFieldValue, false)) {
+ if (!edit && !_.get(touched, formikFullFieldValue, false) && !triggerIndex) {
expressions = [];
monitorOptions.forEach((monitor, index) => {
expressions.push({
@@ -117,11 +118,10 @@ const ExpressionBuilder = ({
monitor_name: monitor.label,
});
});
+
+ _.set(values, formikFullFieldName, expressionsToCondition(expressions));
}
- console.log('associatedMonitors', associatedMonitors);
- console.log('monitorOptions', monitorOptions);
- console.log('expressions', expressions);
setUsedExpressions(expressions?.length ? expressions : [DEFAULT_EXPRESSION]);
};
@@ -190,7 +190,7 @@ const ExpressionBuilder = ({
expressions[idx] = { ...expressions[idx], isOpen: false };
setUsedExpressions(expressions);
onBlur(form, expressions);
- form.setFieldTouched(`expressionQueries_${idx}`, true);
+ form.setFieldTouched(`${expressionNamePrefix}_${idx}`, true);
};
const onRemoveExpression = useCallback(
@@ -221,12 +221,15 @@ const ExpressionBuilder = ({
};
const renderOptions = (expression, idx = 0, form) => (
-
+
{renderMonitorOptions(expression, idx, form)}
onRemoveExpression(form, idx)}
iconType={'trash'}
color="danger"
@@ -266,7 +269,7 @@ const ExpressionBuilder = ({
},
]}
style={{ width: '250px' }}
- data-test-subj={`monitors-combobox-${idx}`}
+ data-test-subj={`monitors-combobox-${triggerIndex}-${idx}`}
options={(() => {
const differences = _.differenceBy(options, usedExpressions, 'monitor_id');
return [
@@ -307,12 +310,14 @@ const ExpressionBuilder = ({
className={'expressionQueries'}
>
{usedExpressions.map((expression, idx) => (
-
+
}
isOpen={expression.isOpen}
@@ -346,7 +351,7 @@ const ExpressionBuilder = ({
color={'primary'}
iconType="plusInCircleFilled"
aria-label={'Add one more condition'}
- data-test-subj={'condition-add-options-btn'}
+ data-test-subj={`condition-add-options-btn_${triggerIndex}`}
style={{ marginTop: '1px' }}
/>
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
index ae69f3bef..0d80b8ac5 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionEditor.js
@@ -2,10 +2,20 @@ import React, { useEffect, useState } from 'react';
import * as _ from 'lodash';
import { FormikCodeEditor } from '../../../../components/FormControls';
-const ExpressionEditor = ({ values, formikFieldName, formikFieldPath, isDarkMode = false }) => {
+const ExpressionEditor = ({
+ values,
+ formikFieldName,
+ formikFieldPath,
+ isDarkMode = false,
+ triggerIndex,
+}) => {
const [editorValue, setEditorValue] = useState('');
const formikFullFieldName = `${formikFieldPath}${formikFieldName}`;
- const formikFullCodeFieldName = _.replace(`${formikFullFieldName}_code`, /[.\[\]]/gm, '_');
+ const formikFullCodeFieldName = _.replace(
+ `${formikFullFieldName}_${triggerIndex}_code`,
+ /[.\[\]]/gm,
+ '_'
+ );
useEffect(() => {
const code = _.get(values, formikFullFieldName, '');
@@ -49,7 +59,7 @@ const ExpressionEditor = ({ values, formikFieldName, formikFieldPath, isDarkMode
form.setFieldValue(formikFullCodeFieldName, code);
},
onBlur: (e, field, form) => form.setFieldTouched(field.name, true),
- 'data-test-subj': 'compositeTriggerConditionEditor',
+ 'data-test-subj': `compositeTriggerConditionEditor_${triggerIndex}`,
}}
/>
);
diff --git a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
index 06d98fc39..86fad90ba 100644
--- a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
+++ b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
@@ -24,6 +24,7 @@ import {
} from '../../../CreateMonitor/components/ClusterMetricsMonitor/utils/clusterMetricsMonitorHelpers';
import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
import { getDefaultScript } from '../../utils/helper';
+import DefineCompositeLevelTrigger from '../DefineCompositeLevelTrigger';
class ConfigureTriggers extends React.Component {
constructor(props) {
@@ -292,6 +293,37 @@ class ConfigureTriggers extends React.Component {
);
};
+ renderCompositeLevelTrigger = (triggerArrayHelpers, index) => {
+ const {
+ edit,
+ monitor,
+ monitorValues,
+ setFlyout,
+ triggers,
+ triggerValues,
+ isDarkMode,
+ httpClient,
+ notifications,
+ notificationService,
+ plugins,
+ touched,
+ } = this.props;
+ const { dataTypes, executeResponse } = this.state;
+ return (
+
+ );
+ };
+
renderTriggers = (triggerArrayHelpers) => {
const { monitorValues, triggerValues } = this.props;
const { triggerEmptyPrompt } = this.state;
@@ -303,6 +335,8 @@ class ConfigureTriggers extends React.Component {
return this.renderDefineBucketLevelTrigger(arrayHelpers, index);
case MONITOR_TYPE.DOC_LEVEL:
return this.renderDefineDocumentLevelTrigger(arrayHelpers, index);
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return this.renderCompositeLevelTrigger(arrayHelpers, index);
default:
return this.renderDefineTrigger(arrayHelpers, index);
}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
deleted file mode 100644
index 160bc51b6..000000000
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React, { Fragment } from 'react';
-import DefineCompositeLevelTrigger from './DefineCompositeLevelTrigger';
-
-const CompositeMonitorsAlertTrigger = ({
- edit,
- isDarkMode,
- httpClient,
- notifications,
- notificationService,
- plugins,
- values,
- touched,
-}) => {
- return (
-
-
-
- );
-};
-export default CompositeMonitorsAlertTrigger;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.test.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.test.js
deleted file mode 100644
index 9e5a81334..000000000
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/CompositeMonitorsAlertTrigger.test.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import React from 'react';
-import { render } from 'enzyme';
-
-import { Formik } from 'formik';
-import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants';
-import CompositeMonitorsAlertTrigger from './CompositeMonitorsAlertTrigger';
-
-describe('CompositeMonitorsAlertTrigger', () => {
- test('renders', () => {
- const component = (
- {}}>
-
-
- );
- expect(render(component)).toMatchSnapshot();
- });
-});
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index dd35e6ec3..2e7c69456 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -6,10 +6,10 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
-import { EuiSpacer, EuiText } from '@elastic/eui';
+import { EuiSpacer, EuiText, EuiTitle, EuiAccordion, EuiButton } from '@elastic/eui';
import { FormikFieldText, FormikSelect } from '../../../../components/FormControls';
import { hasError, isInvalid } from '../../../../utils/validate';
-import { SEVERITY_OPTIONS } from '../../utils/constants';
+import { DEFAULT_TRIGGER_NAME, SEVERITY_OPTIONS } from '../../utils/constants';
import CompositeTriggerCondition from '../../components/CompositeTriggerCondition/CompositeTriggerCondition';
import TriggerNotifications from './TriggerNotifications';
import ContentPanel from '../../../../components/ContentPanel';
@@ -58,32 +58,52 @@ export const titleTemplate = (title, subTitle) => (
class DefineCompositeLevelTrigger extends Component {
render() {
- const { values, httpClient, notifications, notificationService, plugins, touched, edit } =
- this.props;
-
- const formikFieldPath = `triggerDefinitions[0].`;
+ const {
+ values,
+ httpClient,
+ notifications,
+ notificationService,
+ plugins,
+ touched,
+ edit,
+ triggerIndex,
+ } = this.props;
+
+ const formikFieldPath = `triggerDefinitions[${triggerIndex}].`;
const triggerName = _.get(values, `${formikFieldPath}name`, 'Trigger');
const triggerDefinitions = _.get(values, 'triggerDefinitions', []);
- _.set(values, 'triggerDefinitions', [
- {
- ...FORMIK_COMPOSITE_INITIAL_TRIGGER_VALUES,
- ...triggerDefinitions[0],
- severity: 1,
- name: triggerName,
- },
- ]);
- const triggerActions = _.get(values, `${formikFieldPath}.actions`, []);
+ !triggerDefinitions.length &&
+ _.set(values, 'triggerDefinitions', [
+ {
+ ...FORMIK_COMPOSITE_INITIAL_TRIGGER_VALUES,
+ ...triggerDefinitions[triggerIndex],
+ severity: 1,
+ name: triggerName,
+ },
+ ]);
+ const triggerActions = _.get(values, `${formikFieldPath}actions`, []);
return (
-
+ {_.isEmpty(triggerName) ? DEFAULT_TRIGGER_NAME : triggerName}
+
+ }
+ initialIsOpen={edit ? false : triggerIndex === 0}
+ extraAction={
+ {
+ triggerArrayHelpers.remove(triggerIndex);
+ }}
+ size={'s'}
+ >
+ Remove trigger
+
+ }
+ style={{ paddingBottom: '15px', paddingTop: '10px' }}
>
@@ -107,6 +127,7 @@ class DefineCompositeLevelTrigger extends Component {
-
+
);
}
}
diff --git a/yarn.lock b/yarn.lock
index d35b800d1..b2156cb96 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2784,7 +2784,7 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
-lint-staged@^10.0.0:
+lint-staged@^10.2.0:
version "10.5.4"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.4.tgz#cd153b5f0987d2371fc1d2847a409a2fe705b665"
integrity sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg==
From 335b87c26881151764605fa9c735a610235bf1e2 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Wed, 28 Jun 2023 14:03:27 +0200
Subject: [PATCH 49/63] Adds multiple triggers
Signed-off-by: Jovan Cvetkovic
---
.../CompositeTriggerCondition/ExpressionBuilder.js | 2 +-
.../containers/ConfigureTriggers/ConfigureTriggers.js | 5 -----
.../DefineCompositeLevelTrigger.js | 1 -
3 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
index 060e2f7a2..7756e4503 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
@@ -97,7 +97,7 @@ const ExpressionBuilder = ({
const setInitialValues = (monitors) => {
const monitorOptions = [];
const associatedMonitors = _.get(values, 'associatedMonitors', {});
- associatedMonitors.sequence.delegates.forEach((monitor, index) => {
+ associatedMonitors.sequence.delegates.forEach((monitor) => {
const filteredOption = monitors.filter((option) => option.monitor_id === monitor.monitor_id);
monitorOptions.push({
label: filteredOption[0]?.monitor_name || '',
diff --git a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
index 86fad90ba..04e34a8fa 100644
--- a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
+++ b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
@@ -296,11 +296,7 @@ class ConfigureTriggers extends React.Component {
renderCompositeLevelTrigger = (triggerArrayHelpers, index) => {
const {
edit,
- monitor,
monitorValues,
- setFlyout,
- triggers,
- triggerValues,
isDarkMode,
httpClient,
notifications,
@@ -308,7 +304,6 @@ class ConfigureTriggers extends React.Component {
plugins,
touched,
} = this.props;
- const { dataTypes, executeResponse } = this.state;
return (
Date: Wed, 28 Jun 2023 14:54:00 +0200
Subject: [PATCH 50/63] Adds multiple triggers
Signed-off-by: Jovan Cvetkovic
---
.../DefineCompositeLevelTrigger.js | 2 ++
.../NotificationConfigDialog.js | 20 +++++++++++--------
.../TriggerNotifications.js | 10 +++++++---
.../TriggerNotificationsContent.js | 8 ++++++--
4 files changed, 27 insertions(+), 13 deletions(-)
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index dcb1bd535..25ac0d192 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -154,6 +154,8 @@ class DefineCompositeLevelTrigger extends Component {
{
- const triggerIndex = 0;
const monitor = formikToMonitor(triggerValues);
delete monitor.monitor_type;
const context = getTriggerContext({}, monitor, triggerValues, 0);
const initialActionValues = _.cloneDeep(FORMIK_INITIAL_ACTION_VALUES);
- let action = _.get(triggerValues, `triggerDefinitions[0].actions[${actionIndex}]`, {
+ let action = _.get(triggerValues, `${formikFieldPath}actions[${actionIndex}]`, {
...initialActionValues,
});
- const fieldPath = 'triggerDefinitions[0]';
const [initialValues, setInitialValues] = useState({});
useEffect(() => {
setInitialValues({
- [`action${actionIndex}`]: _.get(triggerValues, `${fieldPath}actions.${actionIndex}`, ''),
+ [`action${actionIndex}`]: _.get(
+ triggerValues,
+ `${formikFieldPath}actions.${actionIndex}`,
+ ''
+ ),
});
}, []);
@@ -58,8 +62,8 @@ const NotificationConfigDialog = ({
testTrigger = {
...testTrigger,
- name: _.get(triggerValues, 'triggerDefinitions[0].name', ''),
- severity: _.get(triggerValues, 'triggerDefinitions[0].severity', ''),
+ name: _.get(triggerValues, `${formikFieldPath}name`, ''),
+ severity: _.get(triggerValues, `${formikFieldPath}severity`, ''),
};
const action = _.get(testTrigger, `${TRIGGER_TYPE.COMPOSITE_LEVEL}.actions[${index}]`);
const condition = {
@@ -98,7 +102,7 @@ const NotificationConfigDialog = ({
const clearConfig = () => {
_.set(
triggerValues,
- `${fieldPath}actions.${actionIndex}`,
+ `${formikFieldPath}actions.${actionIndex}`,
initialValues[`action${actionIndex}`]
);
closeModal();
@@ -117,7 +121,7 @@ const NotificationConfigDialog = ({
{
const [actions, setActions] = useState([]);
const [options, setOptions] = useState([]);
@@ -93,7 +95,7 @@ const TriggerNotifications = ({
const onRemoveNotification = (idx) => {
const newActions = [...actions];
newActions.splice(idx, 1);
- _.set(triggerValues, 'triggerDefinitions[0].actions', newActions);
+ _.set(triggerValues, `${formikFieldPath}actions`, newActions);
setActions(newActions);
};
@@ -105,8 +107,8 @@ const TriggerNotifications = ({
actions.map((action, actionIndex) => (
{`Notification ${actionIndex + 1}`}}
paddingSize={'s'}
@@ -126,10 +128,12 @@ const TriggerNotifications = ({
action={action}
options={options}
actionIndex={actionIndex}
+ triggerIndex={triggerIndex}
notifications={notifications}
triggerValues={triggerValues}
httpClient={httpClient}
hasNotifications={plugins.indexOf(OS_NOTIFICATION_PLUGIN) !== -1}
+ formikFieldPath={formikFieldPath}
/>
))}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
index a943d3654..b299918e1 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
@@ -16,10 +16,12 @@ const TriggerNotificationsContent = ({
action,
options,
actionIndex,
+ triggerIndex,
triggerValues,
httpClient,
notifications,
hasNotifications,
+ formikFieldPath,
}) => {
const [selected, setSelected] = useState([]);
const [isModalVisible, setIsModalVisible] = useState(false);
@@ -40,7 +42,7 @@ const TriggerNotificationsContent = ({
setSelected(selectedOptions);
const initialActionValues = _.cloneDeep(FORMIK_INITIAL_ACTION_VALUES);
- _.set(triggerValues, `triggerDefinitions[0].actions[${actionIndex}]`, {
+ _.set(triggerValues, `${formikFieldPath}actions[${actionIndex}]`, {
...initialActionValues,
destination_id: selectedOptions[0]?.value,
name: selectedOptions[0]?.label,
@@ -72,7 +74,7 @@ const TriggerNotificationsContent = ({
}}
>
)}
From 0f560a64b8a02824e3201b201971d2c4f5a29606 Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Wed, 28 Jun 2023 16:17:25 +0200
Subject: [PATCH 51/63] Adds multiple triggers
Signed-off-by: Jovan Cvetkovic
---
.../containers/CreateTrigger/utils/triggerToFormik.js | 2 +-
.../TriggerNotificationsContent.js | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
index 1faa58917..04991d2b8 100644
--- a/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
+++ b/public/pages/CreateTrigger/containers/CreateTrigger/utils/triggerToFormik.js
@@ -239,7 +239,7 @@ export function compositeTriggerToFormik(trigger, monitor) {
name,
severity,
script,
- actions: getExecutionPolicyActions(actions),
+ actions,
triggerConditions: triggerConditions,
};
}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
index b299918e1..839bced55 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotificationsContent.js
@@ -27,11 +27,11 @@ const TriggerNotificationsContent = ({
const [isModalVisible, setIsModalVisible] = useState(false);
useEffect(() => {
- action?.config_id &&
+ action?.destination_id &&
setSelected([
{
label: action.name,
- value: action.config_id,
+ value: action.destination_id,
type: action.config_type,
description: action.description,
},
From cdf168f19c7cfb6a82c6f973e46a921c482a5cbf Mon Sep 17 00:00:00 2001
From: Jovan Cvetkovic
Date: Wed, 28 Jun 2023 20:01:30 +0200
Subject: [PATCH 52/63] Adds multiple triggers
Signed-off-by: Jovan Cvetkovic
---
.../containers/ConfigureTriggers/ConfigureTriggers.js | 1 +
.../DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js | 1 +
2 files changed, 2 insertions(+)
diff --git a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
index 04e34a8fa..f5adc5b2d 100644
--- a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
+++ b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
@@ -306,6 +306,7 @@ class ConfigureTriggers extends React.Component {
} = this.props;
return (
Date: Thu, 29 Jun 2023 17:46:14 -0700
Subject: [PATCH 53/63] integrated all apis; monitor deletion added
Signed-off-by: Amardeepsingh Siglani
---
.../AlertsDashboardFlyoutComponent.js | 21 ++++-
.../AssociateMonitors/AssociateMonitors.js | 11 ++-
.../ExpressionBuilder.js | 10 ++-
.../AcknowledgeAlertsModal.js | 83 +++----------------
.../ChainedAlertDetails.tsx | 26 ++----
.../ChainedAlertDetailsFlyout.tsx | 19 ++++-
.../pages/Dashboard/containers/Dashboard.js | 45 ++++++----
public/pages/Dashboard/utils/helpers.js | 8 +-
public/pages/Dashboard/utils/tableUtils.js | 5 +-
.../MonitorOverview/utils/getOverviewStats.js | 4 +-
.../containers/MonitorDetails.js | 26 +++++-
.../Monitors/containers/Monitors/Monitors.js | 27 ++----
public/utils/constants.js | 6 ++
public/utils/helpers.js | 16 ++++
server/clusters/alerting/alertingPlugin.js | 31 +++++++
server/routes/alerts.js | 13 +++
server/routes/monitors.js | 13 +++
server/services/AlertService.js | 35 +++++++-
server/services/MonitorService.js | 51 +++++++++++-
19 files changed, 302 insertions(+), 148 deletions(-)
diff --git a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
index 10956fbcc..27b904514 100644
--- a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
+++ b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
@@ -62,7 +62,10 @@ export default class AlertsDashboardFlyoutComponent extends Component {
super(props);
const { location, monitors, monitor_id } = this.props;
const monitor = _.get(_.find(monitors, { _id: monitor_id }), '_source');
- const monitorType = _.get(monitor, 'monitor_type', MONITOR_TYPE.QUERY_LEVEL);
+ let monitorType = _.get(monitor, 'monitor_type', undefined);
+ if (!monitorType) {
+ monitorType = _.get(monitor, 'workflow_type', MONITOR_TYPE.QUERY_LEVEL);
+ }
const { alertState, from, search, severityLevel, size, sortDirection, sortField } =
getURLQueryParams(location);
@@ -162,8 +165,16 @@ export default class AlertsDashboardFlyoutComponent extends Component {
getAlerts = async () => {
this.setState({ loading: true, tabContent: undefined });
- const { from, search, sortField, sortDirection, severityLevel, alertState, monitorIds } =
- this.state;
+ const {
+ from,
+ search,
+ sortField,
+ sortDirection,
+ severityLevel,
+ alertState,
+ monitorIds,
+ monitorType,
+ } = this.state;
const { httpClient, history, notifications, triggerID } = this.props;
@@ -176,6 +187,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
severityLevel,
alertState,
monitorIds,
+ monitorType,
};
const queryParamsString = queryString.stringify(params);
@@ -284,6 +296,8 @@ export default class AlertsDashboardFlyoutComponent extends Component {
return TRIGGER_TYPE.BUCKET_LEVEL;
case MONITOR_TYPE.DOC_LEVEL:
return TRIGGER_TYPE.DOC_LEVEL;
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return TRIGGER_TYPE.COMPOSITE_LEVEL;
default:
return TRIGGER_TYPE.QUERY_LEVEL;
}
@@ -353,6 +367,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
this.props.openChainedAlertsFlyout?.({
alert,
closeFlyout: this.props.closeFlyout,
+ httpClient,
});
}}
/>
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
index 5a5fb7964..801ab46de 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js
@@ -7,6 +7,7 @@ import React, { Fragment, useState, useEffect } from 'react';
import { EuiSpacer, EuiText } from '@elastic/eui';
import MonitorsList from './components/MonitorsList';
import MonitorsEditor from './components/MonitorsEditor';
+import { monitorTypesForComposition } from '../../../../utils/constants';
export const getMonitors = async (httpClient) => {
const response = await httpClient.get('../api/alerting/monitors', {
@@ -21,8 +22,14 @@ export const getMonitors = async (httpClient) => {
});
if (response.ok) {
- const { monitors, totalMonitors } = response;
- return monitors.map((monitor) => ({ monitor_id: monitor.id, monitor_name: monitor.name }));
+ const { monitors } = response;
+ return monitors
+ .filter(
+ (monitor) =>
+ monitor.monitor?.type === 'monitor' &&
+ monitorTypesForComposition.has(monitor.monitor?.monitor_type)
+ )
+ .map((monitor) => ({ monitor_id: monitor.id, monitor_name: monitor.name }));
} else {
console.log('error getting monitors:', response);
return [];
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
index 7756e4503..469618db2 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
@@ -77,6 +77,7 @@ const ExpressionBuilder = ({
const [usedExpressions, setUsedExpressions] = useState([DEFAULT_EXPRESSION]);
const [options, setOptions] = useState([]);
+ const triggerConditions = _.get(values, formikFullFieldName, '');
useEffect(() => {
// initializing formik because these are generic fields and formik won't pick them up until fields is updated
@@ -92,7 +93,7 @@ const ExpressionBuilder = ({
setInitialValues(monitors);
});
}
- }, [values.associatedMonitors?.sequence?.delegates, values[formikFullFieldName]]);
+ }, [values.associatedMonitors?.sequence?.delegates, triggerConditions]);
const setInitialValues = (monitors) => {
const monitorOptions = [];
@@ -109,7 +110,12 @@ const ExpressionBuilder = ({
const condition = _.get(values, formikFullFieldName, '');
let expressions = conditionToExpressions(condition, monitors);
- if (!edit && !_.get(touched, formikFullFieldValue, false) && !triggerIndex) {
+ if (
+ !edit &&
+ !_.get(touched, formikFullFieldValue, false) &&
+ triggerIndex === 0 &&
+ expressions.length === 0
+ ) {
expressions = [];
monitorOptions.forEach((monitor, index) => {
expressions.push({
diff --git a/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.js b/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.js
index 18324b7c8..991f2983a 100644
--- a/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.js
+++ b/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.js
@@ -53,15 +53,8 @@ export default class AcknowledgeAlertsModal extends Component {
super(props);
const { location, monitor_id } = this.props;
- const {
- alertState,
- from,
- search,
- severityLevel,
- size,
- sortDirection,
- sortField,
- } = getURLQueryParams(location);
+ const { alertState, from, search, severityLevel, size, sortDirection, sortField } =
+ getURLQueryParams(location);
this.state = {
alerts: [],
@@ -82,16 +75,8 @@ export default class AcknowledgeAlertsModal extends Component {
}
componentDidMount() {
- const {
- alertState,
- page,
- search,
- severityLevel,
- size,
- sortDirection,
- sortField,
- monitorIds,
- } = this.state;
+ const { alertState, page, search, severityLevel, size, sortDirection, sortField, monitorIds } =
+ this.state;
this.getAlerts(
page * size,
size,
@@ -133,15 +118,8 @@ export default class AcknowledgeAlertsModal extends Component {
getAlerts = async () => {
this.setState({ ...this.state, loading: true });
- const {
- from,
- search,
- sortField,
- sortDirection,
- severityLevel,
- alertState,
- monitorIds,
- } = this.state;
+ const { from, search, sortField, sortDirection, severityLevel, alertState, monitorIds } =
+ this.state;
const { httpClient, history, notifications, triggerId } = this.props;
@@ -184,41 +162,10 @@ export default class AcknowledgeAlertsModal extends Component {
if (!selectedItems.length) return;
- const selectedAlerts = filterActiveAlerts(selectedItems);
-
- const monitorAlerts = selectedAlerts.reduce((monitorAlerts, alert) => {
- const { id, monitor_id: monitorId } = alert;
- if (monitorAlerts[monitorId]) monitorAlerts[monitorId].push(id);
- else monitorAlerts[monitorId] = [id];
- return monitorAlerts;
- }, {});
-
- Object.entries(monitorAlerts).map(([monitorId, alerts]) =>
- httpClient
- .post(`../api/alerting/monitors/${monitorId}/_acknowledge/alerts`, {
- body: JSON.stringify({ alerts }),
- })
- .then((resp) => {
- if (!resp.ok) {
- backendErrorNotification(notifications, 'acknowledge', 'alert', resp.resp);
- } else {
- const successfulCount = _.get(resp, 'resp.success', []).length;
- displayAcknowledgedAlertsToast(notifications, successfulCount);
- }
- })
- .catch((error) => error)
- );
+ await this.props.acknowledgeAlerts(selectedItems);
- const {
- page,
- size,
- search,
- sortField,
- sortDirection,
- severityLevel,
- alertState,
- monitorIds,
- } = this.state;
+ const { page, size, search, sortField, sortDirection, severityLevel, alertState, monitorIds } =
+ this.state;
await this.getAlerts(
page * size,
size,
@@ -274,15 +221,8 @@ export default class AcknowledgeAlertsModal extends Component {
};
render() {
- const {
- httpClient,
- location,
- history,
- monitor,
- notifications,
- onClose,
- triggerName,
- } = this.props;
+ const { httpClient, location, history, monitor, notifications, onClose, triggerName } =
+ this.props;
const detectorId = _.get(monitor, MONITOR_INPUT_DETECTOR_ID);
const groupBy = _.get(monitor, MONITOR_GROUP_BY);
const monitorType = _.get(monitor, 'monitor_type', MONITOR_TYPE.QUERY_LEVEL);
@@ -481,4 +421,5 @@ AcknowledgeAlertsModal.propTypes = {
triggerId: PropTypes.string.isRequired,
triggerName: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
+ acknowledgeAlerts: PropTypes.func.isRequired,
};
diff --git a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
index bc91db41a..852be1416 100644
--- a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
+++ b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import {
EuiFlexGrid,
EuiTitle,
@@ -21,7 +21,7 @@ import { DEFAULT_EMPTY_DATA } from '../../../../utils/constants';
import { associatedAlertsTableColumns, renderTime } from '../../utils/tableUtils';
import _ from 'lodash';
-export const ChainedAlertDetails = ({ alert }) => {
+export const ChainedAlertDetails = ({ alert, associatedAlerts }) => {
const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{
[key: string]: JSX.Element;
}>({});
@@ -50,7 +50,7 @@ export const ChainedAlertDetails = ({ alert }) => {
- {item.end_time}
+ {item.end_time || DEFAULT_EMPTY_DATA}
@@ -91,18 +91,6 @@ export const ChainedAlertDetails = ({ alert }) => {
}
];
- const dummyItems = [
- {
- id: '3m4k_4gBao9NSj-nooCV',
- start_time: 1687907771029,
- end_time: 1687907771029,
- severity: "1",
- state: 'ACTIVE',
- trigger_name: 'Sample trigger',
- acknowledged_time: undefined
- }
- ];
-
const actions: any[] = [
{
render: (item: any) => (
@@ -112,6 +100,7 @@ export const ChainedAlertDetails = ({ alert }) => {
iconType={itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'}
/>
),
+ width: '50px'
},
];
@@ -123,18 +112,19 @@ export const ChainedAlertDetails = ({ alert }) => {
))}
-
- Alerts from associated monitors
+
+ Alerts from associated monitors
[]}
- items={dummyItems}
+ items={associatedAlerts}
itemId='id'
itemIdToExpandedRowMap={itemIdToExpandedRowMap}
hasActions={true}
isExpandable={true}
pagination={true}
+ sorting={true}
/>
>
)
diff --git a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout.tsx b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout.tsx
index 39db515b2..8e7f16c5f 100644
--- a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout.tsx
+++ b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout.tsx
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import {
EuiTitle,
EuiFlexGroup,
@@ -12,7 +12,18 @@ import {
} from '@elastic/eui';
import { ChainedAlertDetails } from './ChainedAlertDetails';
-export const chainedAlertDetailsFlyout = ({ closeFlyout, alert }) => {
+export const chainedAlertDetailsFlyout = ({ closeFlyout, alert, httpClient }) => {
+ const [associatedAlerts, setAssociatedAlerts] = useState([]);
+
+ useEffect(() => {
+ httpClient.get('../api/alerting/workflows/alerts', { query: { workflowIds: alert.workflow_id, getAssociatedAlerts: true }})
+ .then((response: any) => {
+ if (response.ok) {
+ setAssociatedAlerts(response.resp.associatedAlerts);
+ }
+ })
+ }, []);
+
return {
flyoutProps: {
'aria-labelledby': 'alertsDashboardFlyout',
@@ -27,7 +38,7 @@ export const chainedAlertDetailsFlyout = ({ closeFlyout, alert }) => {
className="eui-textTruncate"
size={'m'}
>
- {`Alerts details`}
+ {`Alert details`}
@@ -42,7 +53,7 @@ export const chainedAlertDetailsFlyout = ({ closeFlyout, alert }) => {
),
footerProps: { style: { backgroundColor: '#F5F7FA' } },
body: (
-
+
),
}
}
\ No newline at end of file
diff --git a/public/pages/Dashboard/containers/Dashboard.js b/public/pages/Dashboard/containers/Dashboard.js
index a6575ffd9..c5ca89024 100644
--- a/public/pages/Dashboard/containers/Dashboard.js
+++ b/public/pages/Dashboard/containers/Dashboard.js
@@ -37,7 +37,6 @@ import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../Monitors/containers/Monitors/ut
import { MAX_ALERT_COUNT } from '../utils/constants';
import AcknowledgeAlertsModal from '../components/AcknowledgeAlertsModal';
import { getAlertsFindingColumn } from '../components/FindingsDashboard/findingsUtils';
-import { chainedAlertDetailsFlyout } from '../components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout';
export default class Dashboard extends Component {
constructor(props) {
@@ -161,7 +160,9 @@ export default class Dashboard extends Component {
const { httpClient } = this.props;
const { alertsByTriggers } = this.state;
this.setState({ ...this.state, loadingMonitors: true });
- const monitorIds = alertsByTriggers.map((alert) => alert.monitor_id);
+ const monitorIds = Array.from(
+ new Set(alertsByTriggers.map((alert) => alert.monitor_id).filter((monitorId) => !!monitorId))
+ );
let monitors;
try {
const params = {
@@ -187,26 +188,25 @@ export default class Dashboard extends Component {
this.setState({ ...this.state, loadingMonitors: false, monitors: monitors });
}
- // TODO: exists in both Dashboard and Monitors, should be moved to redux when implemented
- acknowledgeAlert = async () => {
- const { selectedItems } = this.state;
- const { httpClient, notifications, perAlertView } = this.props;
-
- if (!selectedItems.length) return;
-
- let selectedAlerts = perAlertView ? selectedItems : _.get(selectedItems, '0.alerts', []);
- selectedAlerts = filterActiveAlerts(selectedAlerts);
+ acknowledgeAlerts = async (alerts) => {
+ const { httpClient, notifications } = this.props;
+ const selectedAlerts = filterActiveAlerts(alerts);
const monitorAlerts = selectedAlerts.reduce((monitorAlerts, alert) => {
- const { id, monitor_id: monitorId } = alert;
- if (monitorAlerts[monitorId]) monitorAlerts[monitorId].push(id);
- else monitorAlerts[monitorId] = [id];
+ const id = alert.id;
+ const monitorId = alert.workflow_id || alert.monitor_id;
+ if (monitorAlerts[monitorId]) monitorAlerts[monitorId].alerts.push(id);
+ else
+ monitorAlerts[monitorId] = {
+ alerts: [id],
+ poolType: !!alert.workflow_id ? 'workflows' : 'monitors',
+ };
return monitorAlerts;
}, {});
- Object.entries(monitorAlerts).map(([monitorId, alerts]) =>
+ Object.entries(monitorAlerts).map(([monitorId, { alerts, poolType }]) =>
httpClient
- .post(`../api/alerting/monitors/${monitorId}/_acknowledge/alerts`, {
+ .post(`../api/alerting/${poolType}/${monitorId}/_acknowledge/alerts`, {
body: JSON.stringify({ alerts }),
})
.then((resp) => {
@@ -219,6 +219,17 @@ export default class Dashboard extends Component {
})
.catch((error) => error)
);
+ };
+
+ // TODO: exists in both Dashboard and Monitors, should be moved to redux when implemented
+ acknowledgeAlert = async () => {
+ const { selectedItems } = this.state;
+ const { perAlertView } = this.props;
+
+ if (!selectedItems.length) return;
+
+ let selectedAlerts = perAlertView ? selectedItems : _.get(selectedItems, '0.alerts', []);
+ await this.acknowledgeAlerts(selectedAlerts);
this.setState({ selectedItems: [] });
const { page, size, search, sortField, sortDirection, severityLevel, alertState, monitorIds } =
@@ -338,6 +349,7 @@ export default class Dashboard extends Component {
onClose={this.closeModal}
triggerId={triggerID}
triggerName={trigger_name}
+ acknowledgeAlerts={this.acknowledgeAlerts}
/>
);
};
@@ -416,6 +428,7 @@ export default class Dashboard extends Component {
this.openChainedAlertsFlyout({
alert,
closeFlyout: this.closeFlyout,
+ httpClient,
});
}}
/>
diff --git a/public/pages/Dashboard/utils/helpers.js b/public/pages/Dashboard/utils/helpers.js
index b8c260e4b..643ee3a41 100644
--- a/public/pages/Dashboard/utils/helpers.js
+++ b/public/pages/Dashboard/utils/helpers.js
@@ -33,6 +33,9 @@ export function addFirstAlert(firstAlert) {
last_notification_time,
monitor_name,
monitor_id,
+ workflow_id,
+ workflow_name,
+ alert_source,
} = firstAlert;
let newAlertList = _.cloneDeep(EMPTY_ALERT_LIST);
newAlertList[state]++;
@@ -45,8 +48,9 @@ export function addFirstAlert(firstAlert) {
severity,
start_time,
last_notification_time,
- monitor_name,
- monitor_id,
+ monitor_name: monitor_name || workflow_name,
+ monitor_id: monitor_id || workflow_id,
+ alert_source,
};
}
diff --git a/public/pages/Dashboard/utils/tableUtils.js b/public/pages/Dashboard/utils/tableUtils.js
index 335510622..dd445b337 100644
--- a/public/pages/Dashboard/utils/tableUtils.js
+++ b/public/pages/Dashboard/utils/tableUtils.js
@@ -208,7 +208,9 @@ export const alertColumns = (
truncateText: true,
textOnly: true,
render: (name, alert) => (
- {name}
+
+ {name}
+
),
},
];
@@ -227,6 +229,7 @@ export const associatedAlertsTableColumns = [
name: 'Severity',
sortable: false,
truncateText: false,
+ width: '100px',
},
{
field: 'trigger_name',
diff --git a/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js b/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
index 7ddae5432..7044acebc 100644
--- a/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
+++ b/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
@@ -134,7 +134,7 @@ export default function getOverviewStats(
value:
monitor.associated_workflows.length > 0 ? (
<>
- {monitor.associated_workflows.map((id, idx) => {
+ {monitor.associated_workflows.map(({ id, name }, idx) => {
return (
- {id}
+ {name}
{`${idx < monitor.associated_workflows.length - 1 ? ', ' : ''}`}
);
diff --git a/public/pages/MonitorDetails/containers/MonitorDetails.js b/public/pages/MonitorDetails/containers/MonitorDetails.js
index 225866423..88576bac9 100644
--- a/public/pages/MonitorDetails/containers/MonitorDetails.js
+++ b/public/pages/MonitorDetails/containers/MonitorDetails.js
@@ -40,12 +40,13 @@ import {
TRIGGER_ACTIONS,
} from '../../../utils/constants';
import { migrateTriggerMetadata } from './utils/helpers';
-import { backendErrorNotification } from '../../../utils/helpers';
+import { backendErrorNotification, deleteMonitor } from '../../../utils/helpers';
import { getUnwrappedTriggers } from './Triggers/Triggers';
import { formikToMonitor } from '../../CreateMonitor/containers/CreateMonitor/utils/formikToMonitor';
import monitorToFormik from '../../CreateMonitor/containers/CreateMonitor/utils/monitorToFormik';
import FindingsDashboard from '../../Dashboard/containers/FindingsDashboard';
import { TABLE_TAB_IDS } from '../../Dashboard/components/FindingsDashboard/findingsUtils';
+import DeleteMonitorModal from '../../../components/DeleteModal/DeleteMonitorModal';
export default class MonitorDetails extends Component {
constructor(props) {
@@ -69,6 +70,7 @@ export default class MonitorDetails extends Component {
},
isJsonModalOpen: false,
tabId: TABLE_TAB_IDS.ALERTS.id,
+ showDeleteModal: false,
};
}
@@ -266,6 +268,15 @@ export default class MonitorDetails extends Component {
return { ...formikToMonitor(monitorValues), triggers };
};
+ onDeleteClick = () => {
+ this.setState({ showDeleteModal: true });
+ };
+
+ deleteMonitor = async () => {
+ await deleteMonitor(this.state.monitor, this.props.httpClient, this.props.notifications);
+ this.props.history.push('/monitors');
+ };
+
renderAlertsTable = () => {
const { monitor, editMonitor } = this.state;
const {
@@ -353,6 +364,7 @@ export default class MonitorDetails extends Component {
loading,
editMonitor,
isJsonModalOpen,
+ showDeleteModal,
} = this.state;
const {
location,
@@ -432,6 +444,11 @@ export default class MonitorDetails extends Component {
Export as JSON
+
+
+ Delete
+
+
)}
+ {showDeleteModal && (
+ this.setState({ showDeleteModal: false })}
+ onClickDelete={() => this.deleteMonitor()}
+ />
+ )}
);
}
diff --git a/public/pages/Monitors/containers/Monitors/Monitors.js b/public/pages/Monitors/containers/Monitors/Monitors.js
index 6088178ac..cbe2cefc6 100644
--- a/public/pages/Monitors/containers/Monitors/Monitors.js
+++ b/public/pages/Monitors/containers/Monitors/Monitors.js
@@ -16,7 +16,7 @@ import { DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_QUERY_PARAMS } from './utils/constan
import { getURLQueryParams } from './utils/helpers';
import { columns as staticColumns } from './utils/tableUtils';
import { MONITOR_ACTIONS, MONITOR_TYPE } from '../../../../utils/constants';
-import { backendErrorNotification } from '../../../../utils/helpers';
+import { backendErrorNotification, deleteMonitor } from '../../../../utils/helpers';
import { displayAcknowledgedAlertsToast } from '../../../Dashboard/utils/helpers';
import DeleteMonitorModal from '../../../../components/DeleteModal/DeleteMonitorModal';
@@ -55,7 +55,6 @@ export default class Monitors extends Component {
this.onSelectionChange = this.onSelectionChange.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
this.updateMonitor = this.updateMonitor.bind(this);
- this.deleteMonitor = this.deleteMonitor.bind(this);
this.updateMonitors = this.updateMonitors.bind(this);
this.deleteMonitors = this.deleteMonitors.bind(this);
this.onClickAcknowledge = this.onClickAcknowledge.bind(this);
@@ -217,23 +216,6 @@ export default class Monitors extends Component {
.catch((err) => err);
}
- deleteMonitor(item) {
- const { httpClient, notifications } = this.props;
- const { id, version } = item;
- const poolType = item.item_type === 'composite' ? 'workflows' : 'monitors';
- return httpClient
- .delete(`../api/alerting/${poolType}/${id}`, { query: { version } })
- .then((resp) => {
- if (!resp.ok) {
- backendErrorNotification(notifications, 'delete', 'monitor', resp.resp);
- } else {
- notifications.toasts.addSuccess(`Monitor deleted successfully.`);
- }
- return resp;
- })
- .catch((err) => err);
- }
-
updateMonitors(items, update) {
const arrayOfPromises = items.map((item) =>
this.updateMonitor(item, update).catch((error) => error)
@@ -247,8 +229,11 @@ export default class Monitors extends Component {
});
}
- deleteMonitors(items) {
- const arrayOfPromises = items.map((item) => this.deleteMonitor(item).catch((error) => error));
+ async deleteMonitors(items) {
+ const { httpClient, notifications } = this.props;
+ const arrayOfPromises = items.map((item) =>
+ deleteMonitor(item, httpClient, notifications).catch((error) => error)
+ );
return Promise.all(arrayOfPromises).then((values) => {
// TODO: Show which values failed, succeeded, etc.
diff --git a/public/utils/constants.js b/public/utils/constants.js
index cfe51489c..84e19950a 100644
--- a/public/utils/constants.js
+++ b/public/utils/constants.js
@@ -96,3 +96,9 @@ export const PREVIEW_ERROR_TYPE = {
NO_ENABLED_FEATURES: 2,
SPARSE_DATA: 3,
};
+
+export const monitorTypesForComposition = new Set([
+ MONITOR_TYPE.BUCKET_LEVEL,
+ MONITOR_TYPE.DOC_LEVEL,
+ MONITOR_TYPE.QUERY_LEVEL,
+]);
diff --git a/public/utils/helpers.js b/public/utils/helpers.js
index b4fcb58a1..ed4a2d86c 100644
--- a/public/utils/helpers.js
+++ b/public/utils/helpers.js
@@ -46,3 +46,19 @@ export const inputLimitText = (
);
};
+
+export async function deleteMonitor(monitor, httpClient, notifications) {
+ const { id, version } = monitor;
+ const poolType = monitor.item_type === 'composite' ? 'workflows' : 'monitors';
+ return httpClient
+ .delete(`../api/alerting/${poolType}/${id}`, { query: { version } })
+ .then((resp) => {
+ if (!resp.ok) {
+ backendErrorNotification(notifications, 'delete', 'monitor', resp.resp);
+ } else {
+ notifications.toasts.addSuccess(`Monitor deleted successfully.`);
+ }
+ return resp;
+ })
+ .catch((err) => err);
+}
diff --git a/server/clusters/alerting/alertingPlugin.js b/server/clusters/alerting/alertingPlugin.js
index 36779e788..6a6ea8456 100644
--- a/server/clusters/alerting/alertingPlugin.js
+++ b/server/clusters/alerting/alertingPlugin.js
@@ -146,6 +146,20 @@ export default function alertingPlugin(Client, config, components) {
method: 'POST',
});
+ alerting.acknowledgeChainedAlerts = ca({
+ url: {
+ fmt: `${WORKFLOW_BASE_API}/<%=workflowId%>/_acknowledge/alerts`,
+ req: {
+ workflowId: {
+ type: 'string',
+ required: true,
+ },
+ },
+ },
+ needBody: true,
+ method: 'POST',
+ });
+
alerting.getAlerts = ca({
url: {
fmt: `${MONITOR_BASE_API}/alerts`,
@@ -357,4 +371,21 @@ export default function alertingPlugin(Client, config, components) {
},
method: 'DELETE',
});
+
+ alerting.getWorkflowAlerts = ca({
+ url: {
+ fmt: `${WORKFLOW_BASE_API}/alerts?workflowIds=<%=workflowIds%>&getAssociatedAlerts=<%=getAssociatedAlerts%>`,
+ req: {
+ workflowIds: {
+ type: 'string',
+ required: true,
+ },
+ getAssociatedAlerts: {
+ type: 'boolean',
+ required: true,
+ },
+ },
+ },
+ method: 'GET',
+ });
}
diff --git a/server/routes/alerts.js b/server/routes/alerts.js
index c8711bfc7..c55463a98 100644
--- a/server/routes/alerts.js
+++ b/server/routes/alerts.js
@@ -27,4 +27,17 @@ export default function (services, router) {
},
alertService.getAlerts
);
+
+ router.get(
+ {
+ path: '/api/alerting/workflows/alerts',
+ validate: {
+ query: schema.object({
+ workflowIds: schema.string(),
+ getAssociatedAlerts: schema.boolean(),
+ }),
+ },
+ },
+ alertService.getWorkflowAlerts
+ );
}
diff --git a/server/routes/monitors.js b/server/routes/monitors.js
index dcf98cee8..f73203fa7 100644
--- a/server/routes/monitors.js
+++ b/server/routes/monitors.js
@@ -168,4 +168,17 @@ export default function (services, router) {
},
monitorService.acknowledgeAlerts
);
+
+ router.post(
+ {
+ path: '/api/alerting/workflows/{id}/_acknowledge/alerts',
+ validate: {
+ params: schema.object({
+ id: schema.string(),
+ }),
+ body: schema.any(),
+ },
+ },
+ monitorService.acknowledgeChainedAlerts
+ );
}
diff --git a/server/services/AlertService.js b/server/services/AlertService.js
index e7478beac..7daf50b5f 100644
--- a/server/services/AlertService.js
+++ b/server/services/AlertService.js
@@ -92,7 +92,12 @@ export default class AlertService {
const alert = hit;
const id = hit.alert_id;
const version = hit.alert_version;
- return { id, ...alert, version };
+ return {
+ id,
+ ...alert,
+ version,
+ alert_source: !!alert.workflow_id ? 'workflow' : 'monitor',
+ };
});
const totalAlerts = resp.totalAlerts;
@@ -116,4 +121,32 @@ export default class AlertService {
});
}
};
+
+ getWorkflowAlerts = async (context, req, res) => {
+ console.log('****** GET WORKFLOW ALERTS *****');
+ console.log('get alerts req query from frontend');
+ console.log(JSON.stringify(req.query));
+
+ const { callAsCurrentUser } = this.esDriver.asScoped(req);
+ try {
+ const resp = await callAsCurrentUser('alerting.getWorkflowAlerts', req.query);
+ console.log('Get workflow alerts response *****');
+ console.log(JSON.stringify(resp));
+
+ return res.ok({
+ body: {
+ ok: true,
+ resp,
+ },
+ });
+ } catch (err) {
+ console.log(err.message);
+ return res.ok({
+ body: {
+ ok: false,
+ err: err.message,
+ },
+ });
+ }
+ };
}
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 3c5a7ac1e..831095705 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -63,9 +63,11 @@ export default class MonitorService {
const params = { monitorId: id };
const { callAsCurrentUser } = await this.esDriver.asScoped(req);
const response = await callAsCurrentUser('alerting.deleteMonitor', params);
+ console.log('Delete monitor response');
+ console.log(JSON.stringify(response));
return res.ok({
body: {
- ok: response.result === 'deleted',
+ ok: response.result === 'deleted' || response.result === undefined,
},
});
} catch (err) {
@@ -160,6 +162,12 @@ export default class MonitorService {
associated_workflows,
};
}
+ monitor = {
+ ...monitor,
+ item_type: monitor.workflow_type || monitor.monitor_type,
+ id,
+ version,
+ };
return res.ok({
body: { ok: true, resp: monitor, activeCount, dayCount, version, ifSeqNo, ifPrimaryTerm },
});
@@ -188,16 +196,22 @@ export default class MonitorService {
const params = { monitorId: id };
const { callAsCurrentUser } = await this.esDriver.asScoped(req);
const getResponse = await callAsCurrentUser('alerting.getWorkflow', params);
- const monitor = _.get(getResponse, 'workflow', null);
+ let workflow = _.get(getResponse, 'workflow', null);
const version = _.get(getResponse, '_version', null);
const ifSeqNo = _.get(getResponse, '_seq_no', null);
const ifPrimaryTerm = _.get(getResponse, '_primary_term', null);
- monitor.monitor_type = monitor.workflow_type;
+ workflow.monitor_type = workflow.workflow_type;
+ workflow = {
+ ...workflow,
+ item_type: workflow.workflow_type,
+ id,
+ version,
+ };
return res.ok({
body: {
ok: true,
- resp: monitor,
+ resp: workflow,
activeCount: 0,
dayCount: 0,
version,
@@ -498,6 +512,35 @@ export default class MonitorService {
}
};
+ acknowledgeChainedAlerts = async (context, req, res) => {
+ try {
+ const { id } = req.params;
+ const params = {
+ workflowId: id,
+ body: req.body,
+ };
+ const { callAsCurrentUser } = this.esDriver.asScoped(req);
+ const acknowledgeResponse = await callAsCurrentUser(
+ 'alerting.acknowledgeChainedAlerts',
+ params
+ );
+ return res.ok({
+ body: {
+ ok: !acknowledgeResponse.failed.length,
+ resp: acknowledgeResponse,
+ },
+ });
+ } catch (err) {
+ console.error('Alerting - MonitorService - acknowledgeChainedAlerts:', err);
+ return res.ok({
+ body: {
+ ok: false,
+ resp: err.message,
+ },
+ });
+ }
+ };
+
executeMonitor = async (context, req, res) => {
try {
const { dryrun = 'true' } = req.query;
From f5265fcd44b10ce5c446bd3ffaf8a309e09a4867 Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Thu, 29 Jun 2023 18:14:46 -0700
Subject: [PATCH 54/63] fixed delete modal
Signed-off-by: Amardeepsingh Siglani
---
.../DeleteModal/DeleteMonitorModal.tsx | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/public/components/DeleteModal/DeleteMonitorModal.tsx b/public/components/DeleteModal/DeleteMonitorModal.tsx
index fdf96f62e..019d339b6 100644
--- a/public/components/DeleteModal/DeleteMonitorModal.tsx
+++ b/public/components/DeleteModal/DeleteMonitorModal.tsx
@@ -6,8 +6,10 @@
import React, { Component } from 'react';
import {
EuiConfirmModal,
+ EuiLink,
EuiOverlayMask
} from '@elastic/eui';
+import { PLUGIN_NAME } from '../../../utils/constants';
interface DeleteModalProps {
monitors: any[];
@@ -30,9 +32,19 @@ export default class DeleteMonitorModal extends Component {
let warningBody: React.ReactNode = 'This action cannot be undone.';
let allowDelete = true;
- if (monitors.length === 1 && monitors[0].associatedCompositeMonitorCnt > 0) {
+ if (monitors.length === 1 && (monitors[0].associatedCompositeMonitorCnt > 0 || monitors[0].associated_workflows?.length > 0)) {
warningHeading = `Unable to delete ${monitorNames[0]}`;
- warningBody = `The monitor ${monitorNames[0]} is currently associated with composite monitors. Unlink from the composite monitors before deleting this monitor.`;
+ warningBody = (
+ <>
+ {`The monitor ${monitorNames[0]} is currently associated with composite monitors. Unlink from the composite monitors before deleting this monitor.`}
+ { monitors[0].associated_workflows?.length > 0 ?
+
+ {monitors[0].associated_workflows.map(({ id, name }) => {name} )}
+
+ : null
+ }
+ >
+ )
allowDelete = false;
}
else if (monitorNames.length > 1) {
@@ -51,7 +63,7 @@ export default class DeleteMonitorModal extends Component {
{}}
+ onCancel={closeDeleteModal}
onConfirm={() => {
if (allowDelete) {
onClickDelete();
From 3f291486da25fd38270adba0e4f029ec1647ac7d Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Thu, 6 Jul 2023 21:55:50 -0700
Subject: [PATCH 55/63] fixed ux issues from bug bash
Signed-off-by: Amardeepsingh Siglani
---
.../DeleteModal/DeleteMonitorModal.tsx | 126 ++++++++++--------
.../components/MonitorsList.js | 2 +-
.../MonitorDefinitionCard.js | 2 +-
.../components/MonitorType/MonitorType.js | 4 +-
.../CreateMonitor/utils/monitorToFormik.js | 3 +-
.../AddTriggerButton/AddTriggerButton.js | 12 +-
.../ExpressionBuilder.js | 74 +++++-----
.../TriggerEmptyPrompt/TriggerEmptyPrompt.js | 12 +-
.../ConfigureTriggers/ConfigureTriggers.js | 19 ++-
.../NotificationConfigDialog.js | 2 +-
.../MonitorOverview/utils/getOverviewStats.js | 7 +-
.../containers/MonitorDetails.js | 2 +-
.../Monitors/containers/Monitors/Monitors.js | 80 +++++++----
.../containers/Monitors/utils/tableUtils.js | 3 +-
14 files changed, 221 insertions(+), 127 deletions(-)
diff --git a/public/components/DeleteModal/DeleteMonitorModal.tsx b/public/components/DeleteModal/DeleteMonitorModal.tsx
index 019d339b6..afac7ed43 100644
--- a/public/components/DeleteModal/DeleteMonitorModal.tsx
+++ b/public/components/DeleteModal/DeleteMonitorModal.tsx
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { Component } from 'react';
+import React, { Component, useEffect, useState } from 'react';
import {
EuiConfirmModal,
EuiLink,
@@ -13,71 +13,83 @@ import { PLUGIN_NAME } from '../../../utils/constants';
interface DeleteModalProps {
monitors: any[];
+ httpClient?: any;
onClickDelete: () => void;
closeDeleteModal: () => void;
}
export const DEFAULT_DELETION_TEXT = 'delete';
-export default class DeleteMonitorModal extends Component {
- render() {
- const {
- monitors,
- closeDeleteModal,
- onClickDelete
- } = this.props;
+export const DeleteMonitorModal = ({
+ monitors,
+ httpClient,
+ closeDeleteModal,
+ onClickDelete
+ }: DeleteModalProps) => {
+ const [associatedWorkflows, setAssociatedWorkflows] = useState(undefined);
+ const monitorNames = monitors.map(monitor => monitor.name);
+ let warningHeading = `Delete monitor ${monitorNames[0]}?`;
+ let warningBody: React.ReactNode = 'This action cannot be undone.';
+ let allowDelete = true;
- const monitorNames = monitors.map(monitor => monitor.name);
- let warningHeading = `Delete monitor ${monitorNames[0]}?`;
- let warningBody: React.ReactNode = 'This action cannot be undone.';
- let allowDelete = true;
-
- if (monitors.length === 1 && (monitors[0].associatedCompositeMonitorCnt > 0 || monitors[0].associated_workflows?.length > 0)) {
- warningHeading = `Unable to delete ${monitorNames[0]}`;
- warningBody = (
- <>
- {`The monitor ${monitorNames[0]} is currently associated with composite monitors. Unlink from the composite monitors before deleting this monitor.`}
- { monitors[0].associated_workflows?.length > 0 ?
-
- {monitors[0].associated_workflows.map(({ id, name }) => {name} )}
-
- : null
- }
- >
- )
- allowDelete = false;
+ if (monitors.length === 1 && monitors[0].associatedCompositeMonitorCnt > 0) {
+ if (monitors[0].associated_workflows) {
+ setAssociatedWorkflows(monitors[0].associated_workflows);
}
- else if (monitorNames.length > 1) {
- warningHeading = `Delete ${monitorNames.length} monitors?`;
- warningBody = (
- <>
- {`The following monitors will be permanently deleted. ${warningBody}`}
-
- {monitorNames.map((name, idx) => {name} )}
-
- >
- )
+ else {
+ httpClient?.get(`../api/alerting/monitors/${monitors[0].id}`)
+ .then((res: any) => {
+ setAssociatedWorkflows(res.resp.associated_workflows);
+ })
+ .catch((err :any) => {
+ console.error('err', err);
+ });
}
- return (
-
- {
- if (allowDelete) {
- onClickDelete();
- }
- closeDeleteModal();
- }}
- cancelButtonText={allowDelete ? 'Cancel' : undefined}
- confirmButtonText={allowDelete ? 'Delete' : 'Close'}
- buttonColor={allowDelete ? 'danger' : 'primary'}
- defaultFocusedButton="confirm"
- >
- {warningBody}
-
-
- );
+ warningHeading = `Unable to delete ${monitorNames[0]}`;
+ warningBody = (
+ <>
+ {`The monitor ${monitorNames[0]} is currently associated with composite monitors. Unlink from the composite monitors before deleting this monitor.`}
+ { associatedWorkflows ?
+
+ {associatedWorkflows.map(({ id, name }) => {name} )}
+
+ : null
+ }
+ >
+ )
+ allowDelete = false;
+ }
+ else if (monitorNames.length > 1) {
+ warningHeading = `Delete ${monitorNames.length} monitors?`;
+ warningBody = (
+ <>
+ {`The following monitors will be permanently deleted. ${warningBody}`}
+
+ {monitorNames.map((name, idx) => {name} )}
+
+ >
+ )
}
+
+ return (
+
+ {
+ if (allowDelete) {
+ onClickDelete();
+ }
+ closeDeleteModal();
+ }}
+ cancelButtonText={allowDelete ? 'Cancel' : undefined}
+ confirmButtonText={allowDelete ? 'Delete' : 'Close'}
+ buttonColor={allowDelete ? 'danger' : 'primary'}
+ defaultFocusedButton="confirm"
+ >
+ {warningBody}
+
+
+ );
}
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
index 173b98bc0..5ff6d3172 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/MonitorsList.js
@@ -230,7 +230,7 @@ const MonitorsList = ({ values, httpClient }) => {
bottom: '24px',
}}
>
-
+
{
const triggerConditions = trigger.triggerConditions || '';
const parsedConditions = conditionToExpressions(triggerConditions, monitors);
- if (!!triggerConditions.length && !parsedConditions.length) {
+ if (triggerConditions !== '()' && !!triggerConditions.length && !parsedConditions.length) {
preventVisualEditor = true;
break;
}
diff --git a/public/pages/CreateMonitor/components/MonitorType/MonitorType.js b/public/pages/CreateMonitor/components/MonitorType/MonitorType.js
index ebb45aac0..9da6e8c2d 100644
--- a/public/pages/CreateMonitor/components/MonitorType/MonitorType.js
+++ b/public/pages/CreateMonitor/components/MonitorType/MonitorType.js
@@ -68,8 +68,8 @@ const documentLevelDescription = // TODO DRAFT: confirm wording
const compositeLevelDescription = (
- Composite monitors allow you to monitor the states of existing monitors and to reduce alert
- noise.
+ Composite monitors chain the outputs of different monitor types and focus trigger conditions to
+ reduce alert noise.
);
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
index b2bc43a07..fb518ad5c 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/monitorToFormik.js
@@ -48,7 +48,8 @@ export default function monitorToFormik(monitor) {
);
const parsedConditions = conditionToExpressions(triggerConditions, monitorOptions);
- const preventVisualEditor = !!triggerConditions.length && !parsedConditions.length;
+ const preventVisualEditor =
+ !!triggerConditions.length && triggerConditions !== '()' && !parsedConditions.length;
return {
associatedMonitors: _.get(monitor, 'inputs[0].composite_input', {}),
diff --git a/public/pages/CreateTrigger/components/AddTriggerButton/AddTriggerButton.js b/public/pages/CreateTrigger/components/AddTriggerButton/AddTriggerButton.js
index 856399fa2..7ffc9b8e0 100644
--- a/public/pages/CreateTrigger/components/AddTriggerButton/AddTriggerButton.js
+++ b/public/pages/CreateTrigger/components/AddTriggerButton/AddTriggerButton.js
@@ -6,18 +6,26 @@
import React from 'react';
import _ from 'lodash';
import { EuiButton } from '@elastic/eui';
-import { FORMIK_INITIAL_TRIGGER_VALUES } from '../../containers/CreateTrigger/utils/constants';
+import {
+ FORMIK_COMPOSITE_INITIAL_TRIGGER_VALUES,
+ FORMIK_INITIAL_TRIGGER_VALUES,
+} from '../../containers/CreateTrigger/utils/constants';
+import { MONITOR_TYPE } from '../../../../utils/constants';
const AddTriggerButton = ({
arrayHelpers,
disabled,
+ monitorType,
script = FORMIK_INITIAL_TRIGGER_VALUES.script,
}) => {
const buttonText =
_.get(arrayHelpers, 'form.values.triggerDefinitions', []).length === 0
? 'Add trigger'
: 'Add another trigger';
- const values = _.cloneDeep({ ...FORMIK_INITIAL_TRIGGER_VALUES, script });
+ const values =
+ monitorType === MONITOR_TYPE.COMPOSITE_LEVEL
+ ? _.cloneDeep(FORMIK_COMPOSITE_INITIAL_TRIGGER_VALUES)
+ : _.cloneDeep({ ...FORMIK_INITIAL_TRIGGER_VALUES, script });
return (
{
'||': 'OR',
'!': 'NOT',
'': '',
+ '&& !': 'AND_NOT',
+ '|| !': 'OR_NOT',
};
- const queryToExpressionRegex = new RegExp(/( && || \|\| )?(monitor\[id=(.*?)\])/, 'gm');
+ const queryToExpressionRegex = new RegExp(
+ /( && || \|\| || && \!|| \|\| \!)?(monitor\[id=(.*?)\])/,
+ 'gm'
+ );
const matcher = condition.matchAll(queryToExpressionRegex);
let match;
let expressions = [];
@@ -68,11 +74,12 @@ const ExpressionBuilder = ({
...DEFAULT_EXPRESSION,
description: DEFAULT_CONDITION,
};
+ const FIRST_EXPRESSION_CONDITIONS_MAP = [{ description: 'NOT', label: 'NOT' }];
const EXPRESSION_CONDITIONS_MAP = [
- { description: '', label: '' },
{ description: 'AND', label: 'AND' },
{ description: 'OR', label: 'OR' },
- { description: 'NOT', label: 'NOT' },
+ { description: 'AND_NOT', label: 'AND NOT' },
+ { description: 'OR_NOT', label: 'OR NOT' },
];
const [usedExpressions, setUsedExpressions] = useState([DEFAULT_EXPRESSION]);
@@ -133,15 +140,17 @@ const ExpressionBuilder = ({
const expressionsToCondition = (expressions) => {
const conditionMap = {
- AND: '&&',
- OR: '||',
+ AND: '&& ',
+ OR: '|| ',
NOT: '!',
'': '',
+ AND_NOT: '&& !',
+ OR_NOT: '|| !',
};
const condition = expressions.reduce((query, expression) => {
if (expression?.monitor_id) {
- query += ` ${conditionMap[expression.description]} monitor[id=${expression.monitor_id}]`;
+ query += ` ${conditionMap[expression.description]}monitor[id=${expression.monitor_id}]`;
query = query.trim();
}
return query;
@@ -245,19 +254,22 @@ const ExpressionBuilder = ({
]}
onChange={(selection) => changeCondition(selection, expression, idx, form)}
onBlur={() => onBlur(form, usedExpressions)}
- options={EXPRESSION_CONDITIONS_MAP}
+ options={idx === 0 ? FIRST_EXPRESSION_CONDITIONS_MAP : EXPRESSION_CONDITIONS_MAP}
+ autoFocus={false}
/>
{renderMonitorOptions(expression, idx, form)}
- onRemoveExpression(form, idx)}
- iconType={'trash'}
- color="danger"
- aria-label={'Remove condition'}
- style={{ marginTop: '4px' }}
- />
+
+ onRemoveExpression(form, idx)}
+ iconType={'trash'}
+ color="danger"
+ aria-label={'Remove condition'}
+ style={{ marginTop: '4px' }}
+ />
+
);
@@ -327,7 +339,7 @@ const ExpressionBuilder = ({
aria-label={'Add condition expression'}
description={expression.description}
value={expression.monitor_name}
- isActive={!!options?.length}
+ isActive={expression.isOpen}
onClick={() => {
form.setFieldTouched(formikFullFieldValue, true);
openPopover(idx);
@@ -346,20 +358,22 @@ const ExpressionBuilder = ({
))}
{options.length > usedExpressions.length && (
- {
- const expressions = _.cloneDeep(usedExpressions);
- expressions.push({
- ...DEFAULT_NEXT_EXPRESSION,
- });
- setUsedExpressions(expressions);
- }}
- color={'primary'}
- iconType="plusInCircleFilled"
- aria-label={'Add one more condition'}
- data-test-subj={`condition-add-options-btn_${triggerIndex}`}
- style={{ marginTop: '1px' }}
- />
+
+ {
+ const expressions = _.cloneDeep(usedExpressions);
+ expressions.push({
+ ...DEFAULT_NEXT_EXPRESSION,
+ });
+ setUsedExpressions(expressions);
+ }}
+ color={'primary'}
+ iconType="plusInCircleFilled"
+ aria-label={'Add one more condition'}
+ data-test-subj={`condition-add-options-btn_${triggerIndex}`}
+ style={{ marginTop: '1px' }}
+ />
+
)}
diff --git a/public/pages/CreateTrigger/components/TriggerEmptyPrompt/TriggerEmptyPrompt.js b/public/pages/CreateTrigger/components/TriggerEmptyPrompt/TriggerEmptyPrompt.js
index 2fdaff087..651c1d504 100644
--- a/public/pages/CreateTrigger/components/TriggerEmptyPrompt/TriggerEmptyPrompt.js
+++ b/public/pages/CreateTrigger/components/TriggerEmptyPrompt/TriggerEmptyPrompt.js
@@ -8,11 +8,15 @@ import { EuiEmptyPrompt, EuiText } from '@elastic/eui';
import AddTriggerButton from '../AddTriggerButton';
import { FORMIK_INITIAL_TRIGGER_VALUES } from '../../containers/CreateTrigger/utils/constants';
-const addTriggerButton = (arrayHelpers, script) => (
-
+const addTriggerButton = (arrayHelpers, monitorType, script) => (
+
);
-const TriggerEmptyPrompt = ({ arrayHelpers, script = FORMIK_INITIAL_TRIGGER_VALUES.script }) => (
+const TriggerEmptyPrompt = ({
+ arrayHelpers,
+ monitorType,
+ script = FORMIK_INITIAL_TRIGGER_VALUES.script,
+}) => (
Add a trigger to define conditions and actions.
}
- actions={addTriggerButton(arrayHelpers, script)}
+ actions={addTriggerButton(arrayHelpers, monitorType, script)}
/>
);
diff --git a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
index f5adc5b2d..a633a04ae 100644
--- a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
+++ b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
@@ -69,7 +69,22 @@ class ConfigureTriggers extends React.Component {
'monitorValues.uri.api_type',
FORMIK_INITIAL_VALUES.uri.api_type
);
- if (prevSearchType !== currSearchType || prevApiType !== currApiType) {
+ const prevMonitorType = _.get(
+ prevProps,
+ 'monitorValues.monitor_type',
+ FORMIK_INITIAL_VALUES.monitor_type
+ );
+ const currMonitorType = _.get(
+ this.props,
+ 'monitorValues.monitor_type',
+ FORMIK_INITIAL_VALUES.monitor_type
+ );
+ const updateTriggerControls =
+ prevSearchType !== currSearchType ||
+ prevApiType !== currApiType ||
+ prevMonitorType !== currMonitorType;
+
+ if (updateTriggerControls) {
this.setState({ addTriggerButton: this.prepareAddTriggerButton() });
this.setState({ triggerEmptyPrompt: this.prepareTriggerEmptyPrompt() });
}
@@ -102,6 +117,7 @@ class ConfigureTriggers extends React.Component {
);
@@ -112,6 +128,7 @@ class ConfigureTriggers extends React.Component {
return (
);
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
index 8fc6d0139..8e365a86a 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/NotificationConfigDialog.js
@@ -132,7 +132,7 @@ const NotificationConfigDialog = ({
/>
- clearConfig()}>Close
+ clearConfig()}>Cancel
closeModal()} fill>
Update
diff --git a/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js b/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
index 7044acebc..2c19d89e1 100644
--- a/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
+++ b/public/pages/MonitorDetails/components/MonitorOverview/utils/getOverviewStats.js
@@ -51,6 +51,8 @@ function getMonitorLevelType(monitorType) {
return 'Per cluster metrics monitor';
case MONITOR_TYPE.DOC_LEVEL:
return 'Per document monitor';
+ case MONITOR_TYPE.COMPOSITE_LEVEL:
+ return 'Composite monitor';
default:
// TODO: May be valuable to implement a toast that displays in this case.
console.log('Unexpected monitor type:', monitorType);
@@ -85,7 +87,10 @@ export default function getOverviewStats(
},
]
: [];
- const monitorLevelType = _.get(monitor, 'ui_metadata.monitor_type', 'query_level_monitor');
+ let monitorLevelType = _.get(monitor, 'monitor_type', undefined);
+ if (!monitorLevelType) {
+ monitorLevelType = _.get(monitor, 'ui_metadata.monitor_type', 'query_level_monitor');
+ }
const overviewStats = [
{
header: 'Monitor type',
diff --git a/public/pages/MonitorDetails/containers/MonitorDetails.js b/public/pages/MonitorDetails/containers/MonitorDetails.js
index 88576bac9..9e37660b6 100644
--- a/public/pages/MonitorDetails/containers/MonitorDetails.js
+++ b/public/pages/MonitorDetails/containers/MonitorDetails.js
@@ -46,7 +46,7 @@ import { formikToMonitor } from '../../CreateMonitor/containers/CreateMonitor/ut
import monitorToFormik from '../../CreateMonitor/containers/CreateMonitor/utils/monitorToFormik';
import FindingsDashboard from '../../Dashboard/containers/FindingsDashboard';
import { TABLE_TAB_IDS } from '../../Dashboard/components/FindingsDashboard/findingsUtils';
-import DeleteMonitorModal from '../../../components/DeleteModal/DeleteMonitorModal';
+import { DeleteMonitorModal } from '../../../components/DeleteModal/DeleteMonitorModal';
export default class MonitorDetails extends Component {
constructor(props) {
diff --git a/public/pages/Monitors/containers/Monitors/Monitors.js b/public/pages/Monitors/containers/Monitors/Monitors.js
index cbe2cefc6..7049f8d83 100644
--- a/public/pages/Monitors/containers/Monitors/Monitors.js
+++ b/public/pages/Monitors/containers/Monitors/Monitors.js
@@ -18,7 +18,7 @@ import { columns as staticColumns } from './utils/tableUtils';
import { MONITOR_ACTIONS, MONITOR_TYPE } from '../../../../utils/constants';
import { backendErrorNotification, deleteMonitor } from '../../../../utils/helpers';
import { displayAcknowledgedAlertsToast } from '../../../Dashboard/utils/helpers';
-import DeleteMonitorModal from '../../../../components/DeleteModal/DeleteMonitorModal';
+import { DeleteMonitorModal } from '../../../../components/DeleteModal/DeleteMonitorModal';
const MAX_MONITOR_COUNT = 1000;
@@ -216,7 +216,7 @@ export default class Monitors extends Component {
.catch((err) => err);
}
- updateMonitors(items, update) {
+ async updateMonitors(items, update) {
const arrayOfPromises = items.map((item) =>
this.updateMonitor(item, update).catch((error) => error)
);
@@ -251,16 +251,18 @@ export default class Monitors extends Component {
const { httpClient, notifications } = this.props;
const monitorAlerts = alerts.reduce((monitorAlerts, alert) => {
- const { id, monitor_id: monitorId } = alert;
- if (monitorAlerts[monitorId]) monitorAlerts[monitorId].push(id);
- else monitorAlerts[monitorId] = [id];
+ const { id, monitor_id, workflow_id, alert_source } = alert;
+ const monitorId = workflow_id || monitor_id;
+ if (monitorAlerts[monitorId]) monitorAlerts[monitorId].ids.push(id);
+ else monitorAlerts[monitorId] = { ids: [id], alert_source };
return monitorAlerts;
}, {});
- const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) =>
+ const promises = Object.entries(monitorAlerts).map(([monitorId, { ids, alert_source }]) => {
+ const poolType = alert_source === 'workflow' ? 'workflows' : 'monitors';
httpClient
- .post(`../api/alerting/monitors/${monitorId}/_acknowledge/alerts`, {
- body: JSON.stringify({ alerts }),
+ .post(`../api/alerting/${poolType}/${monitorId}/_acknowledge/alerts`, {
+ body: JSON.stringify({ alerts: ids }),
})
.then((resp) => {
if (!resp.ok) {
@@ -270,8 +272,8 @@ export default class Monitors extends Component {
displayAcknowledgedAlertsToast(notifications, successfulCount);
}
})
- .catch((error) => error)
- );
+ .catch((error) => error);
+ });
const values = await Promise.all(promises);
// TODO: Show which values failed, succeeded, etc.
@@ -322,8 +324,13 @@ export default class Monitors extends Component {
}
async getActiveAlerts(selectedItems) {
- const monitorIds = selectedItems.map((monitor) => monitor.id);
- if (!monitorIds.length) return;
+ const monitorIds = selectedItems
+ .filter((item) => item.item_type !== MONITOR_TYPE.COMPOSITE_LEVEL)
+ .map((monitor) => monitor.id);
+ const workflowIds = selectedItems
+ .filter((item) => item.item_type === MONITOR_TYPE.COMPOSITE_LEVEL)
+ .map((monitor) => monitor.id);
+ if (!monitorIds.length && !workflowIds.length) return;
// TODO: Limiting to 100.. otherwise could be bringing back large amount of alerts that all need to be acknowledged 1 by 1, handle case when there are more than 100 on UI
const params = {
from: 0,
@@ -335,20 +342,43 @@ export default class Monitors extends Component {
};
const { httpClient, notifications } = this.props;
+ let allAlerts = [];
+ let totalAlertsCount = 0;
- const response = await httpClient.get('../api/alerting/alerts', { query: params });
+ if (monitorIds.length > 0) {
+ const monitorAlertsResponse = await httpClient.get('../api/alerting/alerts', {
+ query: params,
+ });
+ if (!monitorAlertsResponse.ok) {
+ console.error(monitorAlertsResponse);
+ backendErrorNotification(notifications, 'get', 'alerts', monitorAlertsResponse.err);
+ } else {
+ const { alerts, totalAlerts } = monitorAlertsResponse;
+ allAlerts = allAlerts.concat(alerts);
+ totalAlertsCount += totalAlerts;
+ }
+ }
- if (response.ok) {
- const { alerts, totalAlerts } = response;
- this.setState({
- alerts,
- totalAlerts,
- showAcknowledgeModal: true,
+ if (workflowIds.length > 0) {
+ const chainedAlertsResponse = await httpClient.get('../api/alerting/alerts', {
+ query: { ...params, monitorIds: workflowIds, monitorType: MONITOR_TYPE.COMPOSITE_LEVEL },
});
- } else {
- console.error(response);
- backendErrorNotification(notifications, 'get', 'alerts', response.err);
+
+ if (!chainedAlertsResponse.ok) {
+ console.error(chainedAlertsResponse);
+ backendErrorNotification(notifications, 'get', 'alerts', chainedAlertsResponse.err);
+ } else {
+ const { alerts, totalAlerts } = chainedAlertsResponse;
+ allAlerts = allAlerts.concat(alerts);
+ totalAlertsCount += totalAlerts;
+ }
}
+
+ this.setState({
+ alerts: allAlerts,
+ totalAlerts: totalAlertsCount,
+ showAcknowledgeModal: true,
+ });
}
onClickCancel() {
@@ -385,6 +415,7 @@ export default class Monitors extends Component {
totalAlerts,
totalMonitors,
loadingMonitors,
+ monitorItemsToDelete,
} = this.state;
const filterIsApplied = !!search || monitorState !== DEFAULT_QUERY_PARAMS.state;
@@ -473,9 +504,10 @@ export default class Monitors extends Component {
sorting={sorting}
/>
- {this.state.monitorItemsToDelete && (
+ {monitorItemsToDelete && (
this.setState({ monitorItemsToDelete: undefined })}
onClickDelete={() => this.deleteMonitors(this.state.monitorItemsToDelete)}
/>
diff --git a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
index bb58968ce..9bdb3f72f 100644
--- a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
+++ b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
@@ -6,7 +6,7 @@
import React from 'react';
import { EuiLink } from '@elastic/eui';
import moment from 'moment';
-import { DEFAULT_EMPTY_DATA } from '../../../../../utils/constants';
+import { DEFAULT_EMPTY_DATA, MONITOR_TYPE } from '../../../../../utils/constants';
import { PLUGIN_NAME } from '../../../../../../utils/constants';
import { getItemLevelType } from './helpers';
@@ -102,5 +102,6 @@ export const columns = [
field: 'associatedCompositeMonitorCnt',
name: 'Associated composite monitors',
truncateText: false,
+ render: (count, item) => (item.item_type === MONITOR_TYPE.COMPOSITE_LEVEL ? '–' : count),
},
];
From 5d5e23658cd5988ad2cf504e0c6e1e67813644f9 Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Mon, 10 Jul 2023 18:13:08 -0700
Subject: [PATCH 56/63] addressed UX feedback
Signed-off-by: Amardeepsingh Siglani
---
.../AlertsDashboardFlyoutComponent.js | 6 +-
public/components/Flyout/flyouts/index.js | 2 -
.../DefineCompositeLevelTrigger.js | 146 +++++++++---------
.../TriggerNotifications.js | 55 ++++---
.../ChainedAlertDetails.tsx | 2 +-
.../ChainedAlertDetailsFlyout.tsx | 70 +++++----
.../pages/Dashboard/containers/Dashboard.js | 120 +++++++-------
public/pages/Dashboard/utils/tableUtils.js | 15 +-
.../containers/MonitorDetails.js | 1 +
.../containers/Triggers/Triggers.js | 89 ++++++++++-
10 files changed, 299 insertions(+), 207 deletions(-)
diff --git a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
index 27b904514..7c902d97e 100644
--- a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
+++ b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js
@@ -364,11 +364,7 @@ export default class AlertsDashboardFlyoutComponent extends Component {
data-test-subj={`view-details-icon`}
iconType={'inspect'}
onClick={() => {
- this.props.openChainedAlertsFlyout?.({
- alert,
- closeFlyout: this.props.closeFlyout,
- httpClient,
- });
+ this.props.openChainedAlertsFlyout?.(alert);
}}
/>
diff --git a/public/components/Flyout/flyouts/index.js b/public/components/Flyout/flyouts/index.js
index 305b12d57..90b7aff9f 100644
--- a/public/components/Flyout/flyouts/index.js
+++ b/public/components/Flyout/flyouts/index.js
@@ -7,14 +7,12 @@ import message from './message';
import messageFrequency from './messageFrequency';
import triggerCondition from './triggerCondition';
import alertsDashboard from './alertsDashboard';
-import { chainedAlertDetailsFlyout } from '../../../pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout';
const Flyouts = {
messageFrequency,
message,
triggerCondition,
alertsDashboard,
- chainedAlertDetailsFlyout,
};
export default Flyouts;
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
index 3dd3de291..8d8051866 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/DefineCompositeLevelTrigger.js
@@ -14,8 +14,20 @@ import CompositeTriggerCondition from '../../components/CompositeTriggerConditio
import TriggerNotifications from './TriggerNotifications';
import { FORMIK_COMPOSITE_INITIAL_TRIGGER_VALUES } from '../CreateTrigger/utils/constants';
+export const titleTemplate = (title, subTitle) => (
+
+ {title}
+ {subTitle && (
+
+ {subTitle}
+
+ )}
+
+
+);
+
const defaultRowProps = {
- label: 'Trigger name',
+ label: titleTemplate('Trigger name'),
style: { paddingLeft: '10px' },
isInvalid,
error: hasError,
@@ -43,18 +55,6 @@ const propTypes = {
isDarkMode: PropTypes.bool.isRequired,
};
-export const titleTemplate = (title, subTitle) => (
-
- {title}
- {subTitle && (
-
- {subTitle}
-
- )}
-
-
-);
-
class DefineCompositeLevelTrigger extends Component {
render() {
const {
@@ -105,65 +105,67 @@ class DefineCompositeLevelTrigger extends Component {
}
style={{ paddingBottom: '15px', paddingTop: '10px' }}
>
-
-
-
-
-
-
-
-
-
-
- {titleTemplate('Alert severity')}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {titleTemplate('Alert severity')}
+
+
+
+
+
+
);
}
diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
index c37128993..5e6305b98 100644
--- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
+++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js
@@ -103,17 +103,16 @@ const TriggerNotifications = ({
{titleTemplate('Notifications')}
- {actions.length &&
- actions.map((action, actionIndex) => (
- {`Notification ${actionIndex + 1}`}}
- paddingSize={'s'}
- extraAction={
- actions.length > 1 && (
+ {actions.length
+ ? actions.map((action, actionIndex) => (
+ {`Notification ${actionIndex + 1}`}}
+ paddingSize={'s'}
+ extraAction={
onRemoveNotification(actionIndex)}
size={'s'}
/>
- )
- }
- >
-
-
- ))}
-
+ }
+ >
+
+
+ ))
+ : null}
+ {actions.length ? : null}
onAddNotification()}>Add notification
);
diff --git a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
index 852be1416..f50108e75 100644
--- a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
+++ b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx
@@ -113,7 +113,7 @@ export const ChainedAlertDetails = ({ alert, associatedAlerts }) => {
- Alerts from associated monitors
+ Delegate monitor alerts
{
+export const ChainedAlertDetailsFlyout = ({ closeFlyout, alert, httpClient }) => {
const [associatedAlerts, setAssociatedAlerts] = useState([]);
useEffect(() => {
@@ -24,36 +27,37 @@ export const chainedAlertDetailsFlyout = ({ closeFlyout, alert, httpClient }) =>
})
}, []);
- return {
- flyoutProps: {
- 'aria-labelledby': 'alertsDashboardFlyout',
- size: 'm',
- hideCloseButton: true,
- },
- headerProps: { hasBorder: true },
- header: (
-
-
-
- {`Alert details`}
-
-
-
-
-
-
- ),
- footerProps: { style: { backgroundColor: '#F5F7FA' } },
- body: (
-
- ),
- }
+ return (
+
+
+
+
+
+ {`Alert details`}
+
+
+
+
+
+
+
+
+
+
+
+ )
}
\ No newline at end of file
diff --git a/public/pages/Dashboard/containers/Dashboard.js b/public/pages/Dashboard/containers/Dashboard.js
index c5ca89024..874b1729b 100644
--- a/public/pages/Dashboard/containers/Dashboard.js
+++ b/public/pages/Dashboard/containers/Dashboard.js
@@ -13,6 +13,8 @@ import {
EuiIcon,
EuiToolTip,
EuiButtonIcon,
+ EuiFlyout,
+ EuiFlyoutHeader,
} from '@elastic/eui';
import ContentPanel from '../../../components/ContentPanel';
import DashboardEmptyPrompt from '../components/DashboardEmptyPrompt';
@@ -37,6 +39,7 @@ import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../Monitors/containers/Monitors/ut
import { MAX_ALERT_COUNT } from '../utils/constants';
import AcknowledgeAlertsModal from '../components/AcknowledgeAlertsModal';
import { getAlertsFindingColumn } from '../components/FindingsDashboard/findingsUtils';
+import { ChainedAlertDetailsFlyout } from '../components/ChainedAlertDetailsFlyout/ChainedAlertDetailsFlyout';
export default class Dashboard extends Component {
constructor(props) {
@@ -65,6 +68,7 @@ export default class Dashboard extends Component {
sortField,
totalAlerts: 0,
totalTriggers: 0,
+ chainedAlert: undefined,
};
}
@@ -287,20 +291,12 @@ export default class Dashboard extends Component {
}
};
- closeFlyout = () => {
- const { setFlyout } = this.props;
- if (typeof setFlyout === 'function') setFlyout(null);
- this.setState({ flyoutIsOpen: false });
+ openChainedAlertsFlyout = (chainedAlert) => {
+ this.setState({ chainedAlert });
};
- openChainedAlertsFlyout = (payload) => {
- this.setState({ flyoutIsOpen: true });
- if (!_.isEmpty(payload)) {
- this.props.setFlyout({
- type: 'chainedAlertDetailsFlyout',
- payload: { ...payload },
- });
- }
+ closeChainedAlertsFlyout = () => {
+ this.setState({ chainedAlert: undefined });
};
closeFlyout = () => {
@@ -359,6 +355,7 @@ export default class Dashboard extends Component {
alerts,
alertsByTriggers,
alertState,
+ chainedAlert,
flyoutIsOpen,
loadingMonitors,
monitors,
@@ -425,11 +422,7 @@ export default class Dashboard extends Component {
data-test-subj={`view-details-icon`}
iconType={'inspect'}
onClick={() => {
- this.openChainedAlertsFlyout({
- alert,
- closeFlyout: this.closeFlyout,
- httpClient,
- });
+ this.openChainedAlertsFlyout(alert);
}}
/>
@@ -538,48 +531,57 @@ export default class Dashboard extends Component {
};
return (
-
-
-
-
-
- }
- data-test-subj={'alertsDashboard_table'}
- />
-
- {this.state.showAlertsModal && this.renderModal()}
-
+ <>
+ {chainedAlert && (
+
+ )}
+
+
+
+
+
+ }
+ data-test-subj={'alertsDashboard_table'}
+ />
+
+ {this.state.showAlertsModal && this.renderModal()}
+
+ >
);
}
}
diff --git a/public/pages/Dashboard/utils/tableUtils.js b/public/pages/Dashboard/utils/tableUtils.js
index dd445b337..f60d37a33 100644
--- a/public/pages/Dashboard/utils/tableUtils.js
+++ b/public/pages/Dashboard/utils/tableUtils.js
@@ -5,7 +5,7 @@
import React from 'react';
import _ from 'lodash';
-import { EuiLink } from '@elastic/eui';
+import { EuiLink, EuiToolTip } from '@elastic/eui';
import moment from 'moment';
import { ALERT_STATE, DEFAULT_EMPTY_DATA } from '../../../utils/constants';
import { PLUGIN_NAME } from '../../../../utils/constants';
@@ -231,6 +231,19 @@ export const associatedAlertsTableColumns = [
truncateText: false,
width: '100px',
},
+ {
+ field: 'monitor_name',
+ name: 'Delegate monitor',
+ sortable: false,
+ truncateText: true,
+ render: (monitorName) => {
+ return (
+
+ {monitorName}
+
+ );
+ },
+ },
{
field: 'trigger_name',
name: 'Trigger name',
diff --git a/public/pages/MonitorDetails/containers/MonitorDetails.js b/public/pages/MonitorDetails/containers/MonitorDetails.js
index 9e37660b6..6badd6d3d 100644
--- a/public/pages/MonitorDetails/containers/MonitorDetails.js
+++ b/public/pages/MonitorDetails/containers/MonitorDetails.js
@@ -462,6 +462,7 @@ export default class MonitorDetails extends Component {
{
+ const conditionMap = {
+ AND: 'AND ',
+ OR: 'OR ',
+ NOT: 'NOT',
+ '': '',
+ AND_NOT: 'AND NOT',
+ OR_NOT: 'OR NOT',
+ };
+
+ const condition = expressions.reduce((query, expression) => {
+ if (expression?.monitor_name) {
+ query += ` ${conditionMap[expression.description]} ${expression.monitor_name}`;
+ query = query.trim();
+ }
+ return query;
+ }, '');
+
+ return condition;
+};
+
export default class Triggers extends Component {
constructor(props) {
super(props);
@@ -41,12 +63,31 @@ export default class Triggers extends Component {
tableKey: uuidv4(),
direction: 'asc',
selectedItems: [],
+ items: [],
+ triggerConditionsById: {},
};
this.onDelete = this.onDelete.bind(this);
this.onEdit = this.onEdit.bind(this);
this.onSelectionChange = this.onSelectionChange.bind(this);
this.onTableChange = this.onTableChange.bind(this);
+ this.monitorsById = {};
+ }
+
+ async componentDidMount() {
+ const { monitor } = this.props;
+ const triggers = getUnwrappedTriggers(monitor);
+
+ if (monitor.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL) {
+ for (const trigger of triggers) {
+ const { condition, id } = trigger;
+ await this.initializeTriggerConditionsWithMonitorNames(condition.script.source, id);
+ }
+
+ this.setState({ items: triggers });
+ } else {
+ this.setState({ items: triggers });
+ }
}
componentWillReceiveProps(nextProps, nextContext) {
@@ -59,6 +100,43 @@ export default class Triggers extends Component {
}
}
+ async getMonitor(id) {
+ return this.props.httpClient
+ .get(`../api/alerting/monitors/${id}`)
+ .then((res) => {
+ return res.resp;
+ })
+ .catch((err) => {
+ console.error('err', err);
+ return undefined;
+ });
+ }
+
+ async updateMonitorNameInExpression(expression) {
+ const { monitor_id } = expression;
+ if (!this.monitorsById[monitor_id]) {
+ const monitor = await this.getMonitor(monitor_id);
+ if (!monitor) return;
+
+ this.monitorsById[monitor_id] = monitor;
+ expression.monitor_name = monitor.name;
+ }
+ }
+
+ async initializeTriggerConditionsWithMonitorNames(condition, triggerId) {
+ const expressions = conditionToExpressions(condition, []);
+ for (const expression of expressions) {
+ await this.updateMonitorNameInExpression(expression);
+ }
+
+ this.setState({
+ triggerConditionsById: {
+ ...this.state.triggerConditionsById,
+ [triggerId]: expressionsToCondition(expressions),
+ },
+ });
+ }
+
onDelete() {
const { selectedItems } = this.state;
const { updateMonitor, monitor } = this.props;
@@ -88,7 +166,7 @@ export default class Triggers extends Component {
}
render() {
- const { direction, field, tableKey } = this.state;
+ const { direction, field, tableKey, items } = this.state;
const { monitor } = this.props;
const numOfTriggers = _.get(monitor, 'triggers', []).length;
@@ -113,23 +191,21 @@ export default class Triggers extends Component {
name: 'Severity',
sortable: true,
truncateText: false,
- width: '20%',
+ width: '10%',
},
];
if (monitor.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL) {
columns.splice(1, 0, {
- name: 'Condition -- (formatting pending...)',
- truncateText: true,
+ name: 'Condition',
render: (item) => {
- return item.condition.script.source;
+ return this.state.triggerConditionsById[item.id] ?? '–';
},
width: '50%',
});
}
const sorting = { sort: { field, direction } };
- const items = getUnwrappedTriggers(monitor);
return (
Date: Mon, 10 Jul 2023 21:34:39 -0700
Subject: [PATCH 57/63] fixed monitor service
Signed-off-by: Amardeepsingh Siglani
---
server/services/MonitorService.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/services/MonitorService.js b/server/services/MonitorService.js
index 82a5b5f7e..5513b7f66 100644
--- a/server/services/MonitorService.js
+++ b/server/services/MonitorService.js
@@ -350,7 +350,7 @@ export default class MonitorService {
_source,
} = result;
const monitor = _source.monitor ? _source.monitor : _source;
- monitor[item_type] = monitor.workflow_type || monitor.monitor_type;
+ monitor['item_type'] = monitor.workflow_type || monitor.monitor_type;
const { name, enabled } = monitor;
return [id, { id, version, ifSeqNo, ifPrimaryTerm, name, enabled, monitor }];
}, {});
From 531f0c10fa51c4532f24dae9a83118d4914946c4 Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Mon, 10 Jul 2023 22:27:22 -0700
Subject: [PATCH 58/63] fixed build error; schedule
Signed-off-by: Amardeepsingh Siglani
---
.../containers/CreateMonitor/CreateMonitor.js | 23 +++++++++++--------
.../MonitorDetails/MonitorDetails.js | 5 +++-
.../containers/MonitorDetails.js | 5 ++++
3 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
index 6abd1d916..328d377a2 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/CreateMonitor.js
@@ -17,7 +17,7 @@ import {
import DefineMonitor from '../DefineMonitor';
import { FORMIK_INITIAL_VALUES } from './utils/constants';
import { formikToMonitor } from './utils/formikToMonitor';
-import { SEARCH_TYPE } from '../../../../utils/constants';
+import { MONITOR_TYPE, SEARCH_TYPE } from '../../../../utils/constants';
import { SubmitErrorHandler } from '../../../../utils/SubmitErrorHandler';
import MonitorDetails from '../MonitorDetails';
import ConfigureTriggers from '../../../CreateTrigger/containers/ConfigureTriggers';
@@ -140,6 +140,8 @@ export default class CreateMonitor extends Component {
{({ values, errors, handleSubmit, isSubmitting, isValid, touched }) => {
+ const isComposite = values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL;
+
return (
@@ -161,14 +163,17 @@ export default class CreateMonitor extends Component {
{values.preventVisualEditor ? null : (
-
-
-
+ {isComposite ? (
+ <>
+
+
+ >
+ ) : null}
diff --git a/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js b/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
index d6111d57d..04d1b10fd 100644
--- a/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
+++ b/public/pages/CreateMonitor/containers/MonitorDetails/MonitorDetails.js
@@ -12,6 +12,7 @@ import MonitorDefinitionCard from '../../components/MonitorDefinitionCard';
import MonitorType from '../../components/MonitorType';
import AnomalyDetectors from '../AnomalyDetectors/AnomalyDetectors';
import { MONITOR_TYPE } from '../../../../utils/constants';
+import Schedule from '../../components/Schedule';
const renderAnomalyDetector = ({ httpClient, values, detectorId, flyoutMode }) => ({
actions: [],
@@ -124,7 +125,9 @@ const MonitorDetails = ({
)}
{!flyoutMode && }
-
+ {values.monitor_type !== MONITOR_TYPE.COMPOSITE_LEVEL ? (
+
+ ) : null}
);
};
diff --git a/public/pages/MonitorDetails/containers/MonitorDetails.js b/public/pages/MonitorDetails/containers/MonitorDetails.js
index 6badd6d3d..2a9752b53 100644
--- a/public/pages/MonitorDetails/containers/MonitorDetails.js
+++ b/public/pages/MonitorDetails/containers/MonitorDetails.js
@@ -75,6 +75,11 @@ export default class MonitorDetails extends Component {
}
isWorkflow = () => {
+ const { monitor } = this.state;
+ if (monitor && monitor.workflow_type) {
+ return true;
+ }
+
const searchParams = new URLSearchParams(this.props.location.search);
return (
searchParams.get('type') === 'workflow' || searchParams.get('monitorType') === 'composite'
From 9cde458a868b41548c10ad11536d4188028c7052 Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Mon, 10 Jul 2023 22:59:00 -0700
Subject: [PATCH 59/63] updated snapshots
Signed-off-by: Amardeepsingh Siglani
---
.../__snapshots__/CreateNew.test.js.snap | 54 --
.../AddAlertingMonitor.test.js.snap | 8 +
...lertsDashboardFlyoutComponent.test.js.snap | 1 +
.../__snapshots__/MonitorsList.test.js.snap | 3 +-
.../__snapshots__/MonitorType.test.js.snap | 2 +-
.../WorkflowDetails.test.js.snap | 6 +-
.../CompositeTriggerCondition.test.js.snap | 14 +-
.../ExpressionBuilder.test.js.snap | 2 +-
.../ExpressionEditor.test.js.snap | 6 +-
...CompositeMonitorsAlertTrigger.test.js.snap | 308 -----------
.../DefineCompositeLevelTrigger.test.js.snap | 504 +++++++++---------
.../TriggerNotifications.test.js.snap | 10 +-
.../TriggerNotificationsContent.test.js.snap | 16 +-
.../__snapshots__/Triggers.test.js.snap | 6 +-
.../containers/Monitors/Monitors.test.js | 29 +-
.../__snapshots__/Monitors.test.js.snap | 344 ++++++------
public/utils/helpers.test.js | 24 +-
17 files changed, 508 insertions(+), 829 deletions(-)
delete mode 100644 public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/CreateNew/__snapshots__/CreateNew.test.js.snap
delete mode 100644 public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/__snapshots__/CompositeMonitorsAlertTrigger.test.js.snap
diff --git a/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/CreateNew/__snapshots__/CreateNew.test.js.snap b/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/CreateNew/__snapshots__/CreateNew.test.js.snap
deleted file mode 100644
index 9488e4705..000000000
--- a/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/CreateNew/__snapshots__/CreateNew.test.js.snap
+++ /dev/null
@@ -1,54 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`CreateNew renders 1`] = `
-
-`;
diff --git a/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/__snapshots__/AddAlertingMonitor.test.js.snap b/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/__snapshots__/AddAlertingMonitor.test.js.snap
index 48c907c9f..4d1dfa1b6 100644
--- a/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/__snapshots__/AddAlertingMonitor.test.js.snap
+++ b/public/components/FeatureAnywhereContextMenu/AddAlertingMonitor/__snapshots__/AddAlertingMonitor.test.js.snap
@@ -10,6 +10,13 @@ exports[`AddAlertingMonitor renders 1`] = `
"adResultIndex": undefined,
"aggregationType": "count",
"aggregations": Array [],
+ "associatedMonitors": Object {
+ "sequence": Object {
+ "delegates": Array [],
+ },
+ },
+ "associatedMonitorsEditor": "",
+ "associatedMonitorsList": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"cronExpression": "0 */1 * * *",
@@ -40,6 +47,7 @@ exports[`AddAlertingMonitor renders 1`] = `
"interval": 1,
"unit": "MINUTES",
},
+ "preventVisualEditor": false,
"queries": Array [],
"query": "{
\\"size\\": 0,
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 097ed241d..a0549545e 100644
--- a/public/components/Flyout/flyouts/components/__snapshots__/AlertsDashboardFlyoutComponent.test.js.snap
+++ b/public/components/Flyout/flyouts/components/__snapshots__/AlertsDashboardFlyoutComponent.test.js.snap
@@ -228,6 +228,7 @@ exports[`AlertsDashboardFlyoutComponent renders 1`] = `
]
}
data-test-subj="alertsDashboardFlyout_table_undefined"
+ hasActions={true}
isSelectable={true}
itemId={[Function]}
items={Array []}
diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsList.test.js.snap b/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsList.test.js.snap
index 8c5b8f61b..a0d5f7b21 100644
--- a/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsList.test.js.snap
+++ b/public/pages/CreateMonitor/components/AssociateMonitors/components/__snapshots__/MonitorsList.test.js.snap
@@ -156,7 +156,8 @@ exports[`MonitorsList renders 1`] = `
class="euiSpacer euiSpacer--m"
/>
- Composite monitors allow you to monitor the states of existing monitors and to reduce alert noise.
+ Composite monitors chain the outputs of different monitor types and focus trigger conditions to reduce alert noise.
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap b/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
index 5145a7795..a17ae565e 100644
--- a/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
@@ -49,7 +49,7 @@ exports[`WorkflowDetails renders 1`] = `
style="margin-bottom:0px"
>
- Define workflow schedule
+ Schedule
- Minutes
+ Minute(s)
Trigger label
@@ -21,22 +21,22 @@ exports[`CompositeTriggerCondition renders 1`] = `
>
diff --git a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
index b3399d443..9e35830be 100644
--- a/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
+++ b/public/pages/CreateMonitor/containers/CreateMonitor/utils/constants.js
@@ -22,14 +22,15 @@ export const FORMIK_INITIAL_WHERE_EXPRESSION_VALUES = {
fieldRangeEnd: undefined,
};
+/** Sample delegate
+ * {
+ order: 1,
+ monitor_id: '{{m1}}',
+ }
+ */
export const DEFAULT_ASSOCIATED_MONITORS_VALUE = {
sequence: {
- delegates: [
- /*{
- order: 1,
- monitor_id: '{{m1}}',
- }*/
- ],
+ delegates: [],
},
};
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
index d8c6714ca..cb4bea732 100644
--- a/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/WorkflowDetails.js
@@ -3,16 +3,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { Fragment } from 'react';
+import React from 'react';
import ContentPanel from '../../../../components/ContentPanel';
import Schedule from '../../components/Schedule';
import AssociateMonitors from '../../components/AssociateMonitors/AssociateMonitors';
import { EuiSpacer } from '@elastic/eui';
-import { MONITOR_TYPE, SEARCH_TYPE } from '../../../../utils/constants';
const WorkflowDetails = ({ values, isDarkMode, httpClient, errors }) => {
- const isAd = values.searchType === SEARCH_TYPE.AD;
- const isComposite = values.monitor_type === MONITOR_TYPE.COMPOSITE_LEVEL;
return (
{
paddingTop: '20px',
}}
>
-
+
- {isComposite && (
-
-
-
-
- )}
+
+
);
};
diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap b/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
index a17ae565e..5c36ca0fb 100644
--- a/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
+++ b/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap
@@ -246,6 +246,90 @@ exports[`WorkflowDetails renders 1`] = `
+
+
+
+ Delegate monitors
+
+
+
+
+
`;
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
index 398e77e73..38d07f799 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/CompositeTriggerCondition.js
@@ -22,22 +22,15 @@ const CompositeTriggerCondition = ({
setGraphUi(values.searchType === 'graph');
}, [values.searchType]);
- const isValid = () => true;
- const validate = () => {};
return (
graphUi && validate(),
- }}
render={({ form }) => (
form.touched[formikFullFieldName] && !isValid(),
- error: () => validate(),
style: {
maxWidth: 'inherit',
},
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
index 4d8f6a4e5..da0c9209b 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/ExpressionBuilder.js
@@ -63,7 +63,7 @@ const ExpressionBuilder = ({
const expressionNamePrefix = `expressionQueries_${triggerIndex}`;
const DEFAULT_CONDITION = 'AND';
- const DEFAULT_NAME = 'Select associated monitor';
+ const DEFAULT_NAME = 'Select delegate monitor';
const DEFAULT_EXPRESSION = {
description: '',
isOpen: false,
diff --git a/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionBuilder.test.js.snap b/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionBuilder.test.js.snap
index d230197fe..71961f70a 100644
--- a/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionBuilder.test.js.snap
+++ b/public/pages/CreateTrigger/components/CompositeTriggerCondition/__snapshots__/ExpressionBuilder.test.js.snap
@@ -36,7 +36,7 @@ exports[`ExpressionBuilder renders 1`] = `
- Select associated monitor
+ Select delegate monitor
diff --git a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
index 8e9e22539..e7bc19a43 100644
--- a/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
+++ b/public/pages/CreateTrigger/containers/ConfigureTriggers/ConfigureTriggers.js
@@ -435,11 +435,17 @@ class ConfigureTriggers extends React.Component {
const displayAddTriggerButton = numOfTriggers > 0;
const disableAddTriggerButton = numOfTriggers >= MAX_TRIGGERS;
const monitorType = monitorValues.monitor_type;
+ const isComposite = monitorType === MONITOR_TYPE.COMPOSITE_LEVEL;
return (
- An alert will trigger when the following monitors generate active alerts.
+ When selected monitors meet the specified conditions for alert generation, the composite monitor triggers its own alert.
0 ? (
<>
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 30e301c37..4bebe8cbc 100644
--- a/public/pages/Monitors/containers/Monitors/__snapshots__/Monitors.test.js.snap
+++ b/public/pages/Monitors/containers/Monitors/__snapshots__/Monitors.test.js.snap
@@ -110,7 +110,7 @@ exports[`Monitors renders 1`] = `
},
Object {
"field": "associatedCompositeMonitorCnt",
- "name": "Associated composite monitors",
+ "name": "Associations with composite monitors",
"render": [Function],
"truncateText": false,
},
diff --git a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
index d568da579..eba228a5c 100644
--- a/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
+++ b/public/pages/Monitors/containers/Monitors/utils/tableUtils.js
@@ -104,7 +104,7 @@ export const columns = [
},
{
field: 'associatedCompositeMonitorCnt',
- name: 'Associated composite monitors',
+ name: 'Associations with composite monitors',
truncateText: false,
render: (count, item) => (item.item_type === MONITOR_TYPE.COMPOSITE_LEVEL ? '–' : count),
},
diff --git a/server/services/AlertService.js b/server/services/AlertService.js
index 7daf50b5f..5ae8af828 100644
--- a/server/services/AlertService.js
+++ b/server/services/AlertService.js
@@ -19,7 +19,6 @@ export default class AlertService {
}
getAlerts = async (context, req, res) => {
- console.log('****** GET ALERTS *****');
const {
from = 0,
size = 20,
@@ -32,9 +31,6 @@ export default class AlertService {
monitorType = 'monitor',
} = req.query;
- console.log('get alerts req query from frontend');
- console.log(JSON.stringify(req.query));
-
var params;
switch (sortField) {
case GET_ALERTS_SORT_FILTERS.MONITOR_NAME:
@@ -84,9 +80,6 @@ export default class AlertService {
const { callAsCurrentUser } = this.esDriver.asScoped(req);
try {
- console.log('Get Alerts params ****');
- console.log(monitorType);
- console.log(JSON.stringify(params));
const resp = await callAsCurrentUser('alerting.getAlerts', params);
const alerts = resp.alerts.map((hit) => {
const alert = hit;
@@ -101,9 +94,6 @@ export default class AlertService {
});
const totalAlerts = resp.totalAlerts;
- console.log('Get alerts response *****');
- console.log(JSON.stringify(alerts));
-
return res.ok({
body: {
ok: true,
@@ -112,7 +102,7 @@ export default class AlertService {
},
});
} catch (err) {
- console.log(err.message);
+ console.error(err.message);
return res.ok({
body: {
ok: false,
@@ -123,15 +113,9 @@ export default class AlertService {
};
getWorkflowAlerts = async (context, req, res) => {
- console.log('****** GET WORKFLOW ALERTS *****');
- console.log('get alerts req query from frontend');
- console.log(JSON.stringify(req.query));
-
const { callAsCurrentUser } = this.esDriver.asScoped(req);
try {
const resp = await callAsCurrentUser('alerting.getWorkflowAlerts', req.query);
- console.log('Get workflow alerts response *****');
- console.log(JSON.stringify(resp));
return res.ok({
body: {
@@ -140,7 +124,7 @@ export default class AlertService {
},
});
} catch (err) {
- console.log(err.message);
+ console.error(err.message);
return res.ok({
body: {
ok: false,
From a49281390a2992980c9ba3d4117d2edad660e0e4 Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Tue, 11 Jul 2023 15:27:31 -0700
Subject: [PATCH 61/63] fixed cypress tests
Signed-off-by: Amardeepsingh Siglani
---
cypress/integration/query_level_monitor_spec.js | 2 ++
.../MonitorDetails/containers/Triggers/Triggers.js | 12 +++++++++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/cypress/integration/query_level_monitor_spec.js b/cypress/integration/query_level_monitor_spec.js
index 05404f696..8995c29d5 100644
--- a/cypress/integration/query_level_monitor_spec.js
+++ b/cypress/integration/query_level_monitor_spec.js
@@ -244,6 +244,8 @@ describe('Query-Level Monitors', () => {
// Click the Delete button
cy.contains('Delete').click({ force: true });
+ cy.wait(1000);
+ cy.get('[data-test-subj="confirmModalConfirmButton"]').click({ force: true });
// Confirm we can see an empty monitor list
cy.contains('There are no existing monitors');
diff --git a/public/pages/MonitorDetails/containers/Triggers/Triggers.js b/public/pages/MonitorDetails/containers/Triggers/Triggers.js
index 143f7745b..a2ba3d0da 100644
--- a/public/pages/MonitorDetails/containers/Triggers/Triggers.js
+++ b/public/pages/MonitorDetails/containers/Triggers/Triggers.js
@@ -74,7 +74,17 @@ export default class Triggers extends Component {
this.monitorsById = {};
}
- async componentDidMount() {
+ componentDidMount() {
+ this.updateMonitorState();
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.monitor !== prevProps.monitor) {
+ this.updateMonitorState();
+ }
+ }
+
+ async updateMonitorState() {
const { monitor } = this.props;
const triggers = getUnwrappedTriggers(monitor);
From 2c363ece0f6ecff28848574c253615c7b6db7676 Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Tue, 11 Jul 2023 15:46:09 -0700
Subject: [PATCH 62/63] updated cypress tests
Signed-off-by: Amardeepsingh Siglani
---
.../composite_level_monitor_spec.js | 51 ++-----------------
1 file changed, 5 insertions(+), 46 deletions(-)
diff --git a/cypress/integration/composite_level_monitor_spec.js b/cypress/integration/composite_level_monitor_spec.js
index d82e78b73..78402e74b 100644
--- a/cypress/integration/composite_level_monitor_spec.js
+++ b/cypress/integration/composite_level_monitor_spec.js
@@ -55,12 +55,13 @@ describe('CompositeLevelMonitor', () => {
it('by visual editor', () => {
// Select visual editor for method of definition
+ // cy.intercept('GET', '/api/notifications/get_configs?*', channelResponse);
cy.get('[data-test-subj="visualEditorRadioCard"]').click({ force: true });
// Wait for input to load and then type in the monitor name
cy.get('input[name="name"]').type(SAMPLE_VISUAL_EDITOR_MONITOR);
- // Select delegate monitors
+ // Select associated monitors
cy.get('[data-test-subj="monitors_list_0"]')
.type('monitorOne', { delay: 50 })
.type('{enter}');
@@ -68,30 +69,17 @@ describe('CompositeLevelMonitor', () => {
.type('monitorTwo', { delay: 50 })
.type('{enter}');
+ cy.get('button').contains('Add trigger').click({ force: true });
+
// Type trigger name
cy.get('[data-test-subj="composite-trigger-name"]')
.type('{selectall}')
.type('{backspace}')
.type('Composite trigger');
- // Add associated monitors to condition
- cy.get('[data-test-subj="condition-add-options-btn"]').click({ force: true });
-
- cy.get('[data-test-subj="select-expression_0"]').click({ force: true });
- cy.wait(1000);
- cy.get('[data-test-subj="monitors-combobox-0"]')
- .type('monitorOne', { delay: 50 })
- .type('{enter}');
-
- cy.get('[data-test-subj="select-expression_1"]').click({ force: true });
- cy.wait(1000);
- cy.get('[data-test-subj="monitors-combobox-1"]')
- .type('monitorTwo', { delay: 50 })
- .type('{enter}');
-
// TODO: Test with Notifications plugin
// Select notification channel
- // cy.get('[title="Notification 1"]').type('Channel name');
+ // cy.get('[name="channel_name_0_0"]').find('input').type('Slack QA').type('{enter}');
cy.intercept('api/alerting/workflows').as('createMonitorRequest');
cy.intercept(`api/alerting/monitors?*`).as('getMonitorsRequest');
@@ -172,35 +160,6 @@ describe('CompositeLevelMonitor', () => {
}
});
});
-
- it('by visual editor', () => {
- // Verify edit page
- cy.contains('Edit monitor', { timeout: 20000 });
- cy.get('input[name="name"]').type('_edited');
-
- cy.get('label').contains('Visual editor').click({ force: true });
-
- cy.get('button').contains('Associate another monitor').click({ force: true });
-
- cy.get('[data-test-subj="monitors_list_2"]')
- .type('monitorThree', { delay: 50 })
- .type('{enter}');
-
- cy.get('[data-test-subj="condition-add-options-btn"]').click({ force: true });
- cy.get('[data-test-subj="select-expression_2"]').click({ force: true });
- cy.wait(1000);
- cy.get('[data-test-subj="monitors-combobox-2"]')
- .type('monitorThree', { delay: 50 })
- .type('{enter}');
-
- cy.intercept('api/alerting/workflows/*').as('updateMonitorRequest');
- cy.get('button').contains('Update').click({ force: true });
-
- // Wait for monitor to be created
- cy.wait('@updateMonitorRequest').then(() => {
- cy.get('.euiTitle--large').contains(`${SAMPLE_VISUAL_EDITOR_MONITOR}_edited`);
- });
- });
});
after(() => clearAll());
From e2204d5bf6240116042923c15e294c5c056ac4ab Mon Sep 17 00:00:00 2001
From: Amardeepsingh Siglani
Date: Tue, 11 Jul 2023 16:40:57 -0700
Subject: [PATCH 63/63] removed commented code
Signed-off-by: Amardeepsingh Siglani
---
cypress/integration/composite_level_monitor_spec.js | 5 -----
1 file changed, 5 deletions(-)
diff --git a/cypress/integration/composite_level_monitor_spec.js b/cypress/integration/composite_level_monitor_spec.js
index 78402e74b..84a8a45b1 100644
--- a/cypress/integration/composite_level_monitor_spec.js
+++ b/cypress/integration/composite_level_monitor_spec.js
@@ -55,7 +55,6 @@ describe('CompositeLevelMonitor', () => {
it('by visual editor', () => {
// Select visual editor for method of definition
- // cy.intercept('GET', '/api/notifications/get_configs?*', channelResponse);
cy.get('[data-test-subj="visualEditorRadioCard"]').click({ force: true });
// Wait for input to load and then type in the monitor name
@@ -77,10 +76,6 @@ describe('CompositeLevelMonitor', () => {
.type('{backspace}')
.type('Composite trigger');
- // TODO: Test with Notifications plugin
- // Select notification channel
- // cy.get('[name="channel_name_0_0"]').find('input').type('Slack QA').type('{enter}');
-
cy.intercept('api/alerting/workflows').as('createMonitorRequest');
cy.intercept(`api/alerting/monitors?*`).as('getMonitorsRequest');
cy.get('button').contains('Create').click({ force: true });