From 188ddd8c2252b876450cb1d528c74b760a84ae24 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 9 Nov 2020 13:45:15 +0000 Subject: [PATCH 01/30] extracted index-threshold apis into triggers actions ui --- .eslintrc.js | 9 +- x-pack/plugins/stack_alerts/kibana.json | 2 +- .../alert_types/geo_threshold/alert_type.ts | 6 +- .../geo_threshold/es_query_builder.ts | 2 +- .../geo_threshold/geo_threshold.ts | 4 +- .../server/alert_types/geo_threshold/index.ts | 9 +- .../geo_threshold/tests/alert_type.test.ts | 6 +- .../stack_alerts/server/alert_types/index.ts | 8 +- .../alert_types/index_threshold/README.md | 230 +----------------- .../index_threshold/alert_type.test.ts | 10 +- .../alert_types/index_threshold/alert_type.ts | 19 +- .../index_threshold/alert_type_params.test.ts | 186 +++++++++++++- .../index_threshold/alert_type_params.ts | 5 +- .../alert_types/index_threshold/index.ts | 24 +- x-pack/plugins/stack_alerts/server/index.ts | 2 - .../stack_alerts/server/plugin.test.ts | 29 --- x-pack/plugins/stack_alerts/server/plugin.ts | 31 +-- x-pack/plugins/stack_alerts/server/types.ts | 17 +- .../common/data}/index.ts | 0 .../triggers_actions_ui/common/index.ts | 7 + .../plugins/triggers_actions_ui/kibana.json | 2 +- .../public/common/lib/index_threshold_api.ts | 10 +- .../triggers_actions_ui/server/data/README.md | 228 +++++++++++++++++ .../triggers_actions_ui/server/data/index.ts | 40 +++ .../server/data}/lib/core_query_types.test.ts | 0 .../server/data}/lib/core_query_types.ts | 0 .../server/data}/lib/date_range_info.test.ts | 0 .../server/data}/lib/date_range_info.ts | 2 +- .../server/data/lib/index.ts | 12 + .../data}/lib/time_series_query.test.ts | 21 +- .../server/data}/lib/time_series_query.ts | 5 +- .../data}/lib/time_series_types.test.ts | 0 .../server/data}/lib/time_series_types.ts | 8 +- .../server/data}/routes/fields.ts | 12 +- .../server/data}/routes/index.ts | 15 +- .../server/data}/routes/indices.ts | 18 +- .../server/data}/routes/time_series_query.ts | 22 +- .../triggers_actions_ui/server/index.ts | 19 +- .../triggers_actions_ui/server/plugin.ts | 39 +++ 39 files changed, 646 insertions(+), 413 deletions(-) rename x-pack/plugins/{stack_alerts/common/alert_types/index_threshold => triggers_actions_ui/common/data}/index.ts (100%) create mode 100644 x-pack/plugins/triggers_actions_ui/common/index.ts create mode 100644 x-pack/plugins/triggers_actions_ui/server/data/README.md create mode 100644 x-pack/plugins/triggers_actions_ui/server/data/index.ts rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/lib/core_query_types.test.ts (100%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/lib/core_query_types.ts (100%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/lib/date_range_info.test.ts (100%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/lib/date_range_info.ts (98%) create mode 100644 x-pack/plugins/triggers_actions_ui/server/data/lib/index.ts rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/lib/time_series_query.test.ts (58%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/lib/time_series_query.ts (96%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/lib/time_series_types.test.ts (100%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/lib/time_series_types.ts (95%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/routes/fields.ts (90%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/routes/index.ts (55%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/routes/indices.ts (85%) rename x-pack/plugins/{stack_alerts/server/alert_types/index_threshold => triggers_actions_ui/server/data}/routes/time_series_query.ts (58%) create mode 100644 x-pack/plugins/triggers_actions_ui/server/plugin.ts diff --git a/.eslintrc.js b/.eslintrc.js index 5c2a2817eae53..d5629f1d37470 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1031,12 +1031,19 @@ module.exports = { * Alerting Services overrides */ { - // typescript only for front and back end + // typescript for front and back end files: ['x-pack/plugins/{alerts,stack_alerts,actions,task_manager,event_log}/**/*.{ts,tsx}'], rules: { '@typescript-eslint/no-explicit-any': 'error', }, }, + { + // typescript only for back end + files: ['x-pack/plugins/triggers_actions_ui/server/**/*.ts'], + rules: { + '@typescript-eslint/no-explicit-any': 'error', + }, + }, /** * Lens overrides diff --git a/x-pack/plugins/stack_alerts/kibana.json b/x-pack/plugins/stack_alerts/kibana.json index b26114577c430..dfaa2b61123c4 100644 --- a/x-pack/plugins/stack_alerts/kibana.json +++ b/x-pack/plugins/stack_alerts/kibana.json @@ -3,7 +3,7 @@ "server": true, "version": "8.0.0", "kibanaVersion": "kibana", - "requiredPlugins": ["alerts", "features"], + "requiredPlugins": ["alerts", "features", "triggersActionsUi"], "configPath": ["xpack", "stack_alerts"], "ui": false } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts index 360a6eb169573..9fc46fe2f2586 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; -import { Service } from '../../types'; +import { Logger } from 'src/core/server'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { getGeoThresholdExecutor } from './geo_threshold'; import { @@ -173,7 +173,7 @@ export interface GeoThresholdParams { } export function getAlertType( - service: Omit + logger: Logger ): { defaultActionGroupId: string; actionGroups: ActionGroup[]; @@ -222,7 +222,7 @@ export function getAlertType( name: alertTypeName, actionGroups: [{ id: ActionGroupId, name: actionGroupName }], defaultActionGroupId: ActionGroupId, - executor: getGeoThresholdExecutor(service), + executor: getGeoThresholdExecutor(logger), producer: STACK_ALERTS_FEATURE_ID, validate: { params: ParamsSchema, diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/es_query_builder.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/es_query_builder.ts index c4238e62ff261..97be51b2a6256 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/es_query_builder.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/es_query_builder.ts @@ -6,7 +6,7 @@ import { ILegacyScopedClusterClient } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; -import { Logger } from '../../types'; +import { Logger } from 'src/core/server'; export const OTHER_CATEGORY = 'other'; // Consider dynamically obtaining from config? diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts index f30dea151ece8..394ee8d606abe 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts @@ -6,10 +6,10 @@ import _ from 'lodash'; import { SearchResponse } from 'elasticsearch'; +import { Logger } from 'src/core/server'; import { executeEsQueryFactory, getShapesFilters, OTHER_CATEGORY } from './es_query_builder'; import { AlertServices, AlertTypeState } from '../../../../alerts/server'; import { ActionGroupId, GEO_THRESHOLD_ID, GeoThresholdParams } from './alert_type'; -import { Logger } from '../../types'; interface LatestEntityLocation { location: number[]; @@ -169,7 +169,7 @@ function getOffsetTime(delayOffsetWithUnits: string, oldTime: Date): Date { return adjustedDate; } -export const getGeoThresholdExecutor = ({ logger: log }: { logger: Logger }) => +export const getGeoThresholdExecutor = (log: Logger) => async function ({ previousStartedAt, startedAt, diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/index.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/index.ts index d57f219bb8f9a..2fa2bed9d8419 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/index.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/index.ts @@ -4,15 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Service, AlertingSetup } from '../../types'; +import { Logger } from 'src/core/server'; +import { AlertingSetup } from '../../types'; import { getAlertType } from './alert_type'; interface RegisterParams { - service: Omit; + logger: Logger; alerts: AlertingSetup; } export function register(params: RegisterParams) { - const { service, alerts } = params; - alerts.registerType(getAlertType(service)); + const { logger, alerts } = params; + alerts.registerType(getAlertType(logger)); } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/alert_type.test.ts index 5cf113f519a5a..49b56b5571b44 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/alert_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/tests/alert_type.test.ts @@ -8,11 +8,9 @@ import { loggingSystemMock } from '../../../../../../../src/core/server/mocks'; import { getAlertType, GeoThresholdParams } from '../alert_type'; describe('alertType', () => { - const service = { - logger: loggingSystemMock.create().get(), - }; + const logger = loggingSystemMock.create().get(); - const alertType = getAlertType(service); + const alertType = getAlertType(logger); it('alert type creation structure is the expected value', async () => { expect(alertType.id).toBe('.geo-threshold'); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index.ts b/x-pack/plugins/stack_alerts/server/alert_types/index.ts index dd9f1488092f4..461358d1296e2 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index.ts @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Service, IRouter, AlertingSetup } from '../types'; +import { Logger } from 'src/core/server'; +import { AlertingSetup, StackAlertsStartDeps } from '../types'; import { register as registerIndexThreshold } from './index_threshold'; import { register as registerGeoThreshold } from './geo_threshold'; interface RegisterAlertTypesParams { - service: Service; - router: IRouter; + logger: Logger; + data: Promise; alerts: AlertingSetup; - baseRoute: string; } export function registerBuiltInAlertTypes(params: RegisterAlertTypesParams) { diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/README.md b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/README.md index 0ff01ddfb49c7..9b0eb23950cc3 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/README.md +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/README.md @@ -13,10 +13,7 @@ is exceeded. ## alertType `.index-threshold` -The alertType parameters are specified in -[`lib/core_query_types.ts`][it-core-query] -and -[`alert_type_params.ts`][it-alert-params]. +The alertType parameters are specified in [`alert_type_params.ts`][it-alert-params]. The alertType has a single actionGroup, `'threshold met'`. The `context` object provided to actions is specified in @@ -123,227 +120,6 @@ server log [17:32:10.060] [warning][actions][actions][plugins] \ [now-iso]: https://github.com/pmuellr/now-iso -## http endpoints +## Data Apis via the TriggersActionsUi plugin and its http endpoints -The following endpoints are provided for this alert type: - -- `POST /api/stack_alerts/index_threshold/_indices` -- `POST /api/stack_alerts/index_threshold/_fields` -- `POST /api/stack_alerts/index_threshold/_time_series_query` - -### `POST .../_indices` - -This HTTP endpoint is provided for the alerting ui to list the available -"index names" for the user to select to use with the alert. This API also -returns aliases which match the supplied pattern. - -The request body is expected to be a JSON object in the following form, where the -`pattern` value may include comma-separated names and wildcards. - -```js -{ - pattern: "index-name-pattern" -} -``` - -The response body is a JSON object in the following form, where each element -of the `indices` array is the name of an index or alias. The number of elements -returned is limited, as this API is intended to be used to help narrow down -index names to use with the alert, and not support pagination, etc. - -```js -{ - indices: ["index-name-1", "alias-name-1", ...] -} -``` - -### `POST .../_fields` - -This HTTP endpoint is provided for the alerting ui to list the available -fields for the user to select to use with the alert. - -The request body is expected to be a JSON object in the following form, where the -`indexPatterns` array elements may include comma-separated names and wildcards. - -```js -{ - indexPatterns: ["index-pattern-1", "index-pattern-2"] -} -``` - -The response body is a JSON object in the following form, where each element -fields array is a field object. - -```js -{ - fields: [fieldObject1, fieldObject2, ...] -} -``` - -A field object is the following shape: - -```typescript -{ - name: string, // field name - type: string, // field type - eg 'keyword', 'date', 'long', etc - normalizedType: string, // for numeric types, this will be 'number' - aggregatable: true, // value from elasticsearch field capabilities - searchable: true, // value from elasticsearch field capabilities -} -``` - -### `POST .../_time_series_query` - -This HTTP endpoint is provided to return the values the alertType would calculate, -over a series of time. It is intended to be used in the alerting UI to -provide a "preview" of the alert during creation/editing based on recent data, -and could be used to show a "simulation" of the the alert over an arbitrary -range of time. - -The endpoint is `POST /api/stack_alerts/index_threshold/_time_series_query`. -The request and response bodies are specifed in -[`lib/core_query_types.ts`][it-core-query] -and -[`lib/time_series_types.ts`][it-timeSeries-types]. -The request body is very similar to the alertType's parameters. - -### example - -Continuing with the example above, here's a query to get the values calculated -for the last 10 seconds. -This example uses [now-iso][] to generate iso date strings. - -```console -curl -k "https://elastic:changeme@localhost:5601/api/stack_alerts/index_threshold/_time_series_query" \ - -H "kbn-xsrf: foo" -H "content-type: application/json" -d "{ - \"index\": \"es-hb-sim\", - \"timeField\": \"@timestamp\", - \"aggType\": \"avg\", - \"aggField\": \"summary.up\", - \"groupBy\": \"top\", - \"termSize\": 100, - \"termField\": \"monitor.name.keyword\", - \"interval\": \"1s\", - \"dateStart\": \"`now-iso -10s`\", - \"dateEnd\": \"`now-iso`\", - \"timeWindowSize\": 5, - \"timeWindowUnit\": \"s\" -}" -``` - -``` -{ - "results": [ - { - "group": "host-A", - "metrics": [ - [ "2020-02-26T15:10:40.000Z", 0 ], - [ "2020-02-26T15:10:41.000Z", 0 ], - [ "2020-02-26T15:10:42.000Z", 0 ], - [ "2020-02-26T15:10:43.000Z", 0 ], - [ "2020-02-26T15:10:44.000Z", 0 ], - [ "2020-02-26T15:10:45.000Z", 0 ], - [ "2020-02-26T15:10:46.000Z", 0 ], - [ "2020-02-26T15:10:47.000Z", 0 ], - [ "2020-02-26T15:10:48.000Z", 0 ], - [ "2020-02-26T15:10:49.000Z", 0 ], - [ "2020-02-26T15:10:50.000Z", 0 ] - ] - } - ] -} -``` - -To get the current value of the calculated metric, you can leave off the date: - -``` -curl -k "https://elastic:changeme@localhost:5601/api/stack_alerts/index_threshold/_time_series_query" \ - -H "kbn-xsrf: foo" -H "content-type: application/json" -d '{ - "index": "es-hb-sim", - "timeField": "@timestamp", - "aggType": "avg", - "aggField": "summary.up", - "groupBy": "top", - "termField": "monitor.name.keyword", - "termSize": 100, - "interval": "1s", - "timeWindowSize": 5, - "timeWindowUnit": "s" -}' -``` - -``` -{ - "results": [ - { - "group": "host-A", - "metrics": [ - [ "2020-02-26T15:23:36.635Z", 0 ] - ] - } - ] -} -``` - -[it-timeSeries-types]: lib/time_series_types.ts - -## service functions - -A single service function is available that provides the functionality -of the http endpoint `POST /api/stack_alerts/index_threshold/_time_series_query`, -but as an API for Kibana plugins. The function is available as -`alertingService.indexThreshold.timeSeriesQuery()` - -The parameters and return value for the function are the same as for the HTTP -request, though some additional parameters are required (logger, callCluster, -etc). - -## notes on the timeSeriesQuery API / http endpoint - -This API provides additional parameters beyond what the alertType itself uses: - -- `dateStart` -- `dateEnd` -- `interval` - -The `dateStart` and `dateEnd` parameters are ISO date strings. - -The `interval` parameter is intended to model the `interval` the alert is -currently using, and uses the same `1s`, `2m`, `3h`, etc format. Over the -supplied date range, a time-series data point will be calculated every -`interval` duration. - -So the number of time-series points in the output of the API should be: - -``` -( dateStart - dateEnd ) / interval -``` - -Example: - -``` -dateStart: '2020-01-01T00:00:00' -dateEnd: '2020-01-02T00:00:00' -interval: '1h' -``` - -The date range is 1 day === 24 hours. The interval is 1 hour. So there should -be ~24 time series points in the output. - -For preview purposes: - -- The `termSize` parameter should be used to help cut -down on the amount of work ES does, and keep the generated graphs a little -simpler. Probably something like `10`. - -- For queries with long date ranges, you probably don't want to use the -`interval` the alert is set to, as the `interval` used in the query, as this -could result in a lot of time-series points being generated, which is both -costly in ES, and may result in noisy graphs. - -- The `timeWindow*` parameters should be the same as what the alert is using, -especially for the `count` and `sum` aggregation types. Those aggregations -don't scale the same way the others do, when the window changes. Even for -the other aggregations, changing the window could result in dramatically -different values being generated - `avg` will be more "average-y", `min` -and `max` will be a little stickier. \ No newline at end of file +The Index Threshold Alert Type is backed by Apis exposed by the [TriggersActionsUi plugin](../../../../triggers_actions_ui/README.md). diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts index 2f0cf3cbbcd16..9d253c0d2845f 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts @@ -9,14 +9,12 @@ import { getAlertType } from './alert_type'; import { Params } from './alert_type_params'; describe('alertType', () => { - const service = { - indexThreshold: { - timeSeriesQuery: jest.fn(), - }, - logger: loggingSystemMock.create().get(), + const logger = loggingSystemMock.create().get(); + const data = { + timeSeriesQuery: jest.fn(), }; - const alertType = getAlertType(service); + const alertType = getAlertType(logger, Promise.resolve(data)); it('alert type creation structure is the expected value', async () => { expect(alertType.id).toBe('.index-threshold'); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts index 2a1ed429b7fe1..fab7203142608 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts @@ -5,23 +5,26 @@ */ import { i18n } from '@kbn/i18n'; -import { AlertType, AlertExecutorOptions } from '../../types'; +import { Logger } from 'src/core/server'; +import { AlertType, AlertExecutorOptions, StackAlertsStartDeps } from '../../types'; import { Params, ParamsSchema } from './alert_type_params'; import { ActionContext, BaseActionContext, addMessages } from './action_context'; -import { TimeSeriesQuery } from './lib/time_series_query'; -import { Service } from '../../types'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; +import { + CoreQueryParamsSchemaProperties, + TimeSeriesQuery, +} from '../../../../triggers_actions_ui/server'; export const ID = '.index-threshold'; -import { CoreQueryParamsSchemaProperties } from './lib/core_query_types'; const ActionGroupId = 'threshold met'; const ComparatorFns = getComparatorFns(); export const ComparatorFnNames = new Set(ComparatorFns.keys()); -export function getAlertType(service: Service): AlertType { - const { logger } = service; - +export function getAlertType( + logger: Logger, + data: Promise +): AlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.indexThreshold.alertTypeTitle', { defaultMessage: 'Index threshold', }); @@ -144,7 +147,7 @@ export function getAlertType(service: Service): AlertType> = { index: 'index-name', @@ -71,3 +71,185 @@ describe('alertType Params validate()', () => { return ParamsSchema.validate(params); } }); + +export function runTests(schema: ObjectType, defaultTypeParams: Record): void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let params: any; + + const CoreDefaultParams: Writable> = { + index: 'index-name', + timeField: 'time-field', + aggType: 'count', + groupBy: 'all', + timeWindowSize: 5, + timeWindowUnit: 'm', + }; + + describe('coreQueryTypes', () => { + beforeEach(() => { + params = { ...CoreDefaultParams, ...defaultTypeParams }; + }); + + it('succeeds with minimal properties', async () => { + expect(validate()).toBeTruthy(); + }); + + it('succeeds with maximal properties', async () => { + params.aggType = 'avg'; + params.aggField = 'agg-field'; + params.groupBy = 'top'; + params.termField = 'group-field'; + params.termSize = 200; + expect(validate()).toBeTruthy(); + + params.index = ['index-name-1', 'index-name-2']; + params.aggType = 'avg'; + params.aggField = 'agg-field'; + params.groupBy = 'top'; + params.termField = 'group-field'; + params.termSize = 200; + expect(validate()).toBeTruthy(); + }); + + it('fails for invalid index', async () => { + delete params.index; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[index]: expected at least one defined value but got [undefined]"` + ); + + params.index = 42; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot(` +"[index]: types that failed validation: +- [index.0]: expected value of type [string] but got [number] +- [index.1]: expected value of type [array] but got [number]" +`); + + params.index = ''; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot(` +"[index]: types that failed validation: +- [index.0]: value has length [0] but it must have a minimum length of [1]. +- [index.1]: could not parse array value from json input" +`); + + params.index = ['', 'a']; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot(` +"[index]: types that failed validation: +- [index.0]: expected value of type [string] but got [Array] +- [index.1.0]: value has length [0] but it must have a minimum length of [1]." +`); + }); + + it('fails for invalid timeField', async () => { + delete params.timeField; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[timeField]: expected value of type [string] but got [undefined]"` + ); + + params.timeField = 42; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[timeField]: expected value of type [string] but got [number]"` + ); + + params.timeField = ''; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[timeField]: value has length [0] but it must have a minimum length of [1]."` + ); + }); + + it('fails for invalid aggType', async () => { + params.aggType = 42; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[aggType]: expected value of type [string] but got [number]"` + ); + + params.aggType = '-not-a-valid-aggType-'; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[aggType]: invalid aggType: \\"-not-a-valid-aggType-\\""` + ); + }); + + it('fails for invalid aggField', async () => { + params.aggField = 42; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[aggField]: expected value of type [string] but got [number]"` + ); + + params.aggField = ''; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[aggField]: value has length [0] but it must have a minimum length of [1]."` + ); + }); + + it('fails for invalid termField', async () => { + params.groupBy = 'top'; + params.termField = 42; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[termField]: expected value of type [string] but got [number]"` + ); + + params.termField = ''; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[termField]: value has length [0] but it must have a minimum length of [1]."` + ); + }); + + it('fails for invalid termSize', async () => { + params.groupBy = 'top'; + params.termField = 'fee'; + params.termSize = 'foo'; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[termSize]: expected value of type [number] but got [string]"` + ); + + params.termSize = MAX_GROUPS + 1; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[termSize]: must be less than or equal to 1000"` + ); + + params.termSize = 0; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[termSize]: Value must be equal to or greater than [1]."` + ); + }); + + it('fails for invalid timeWindowSize', async () => { + params.timeWindowSize = 'foo'; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[timeWindowSize]: expected value of type [number] but got [string]"` + ); + + params.timeWindowSize = 0; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[timeWindowSize]: Value must be equal to or greater than [1]."` + ); + }); + + it('fails for invalid timeWindowUnit', async () => { + params.timeWindowUnit = 42; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[timeWindowUnit]: expected value of type [string] but got [number]"` + ); + + params.timeWindowUnit = 'x'; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[timeWindowUnit]: invalid timeWindowUnit: \\"x\\""` + ); + }); + + it('fails for invalid aggType/aggField', async () => { + params.aggType = 'avg'; + delete params.aggField; + expect(onValidate()).toThrowErrorMatchingInlineSnapshot( + `"[aggField]: must have a value when [aggType] is \\"avg\\""` + ); + }); + }); + + function onValidate(): () => void { + return () => validate(); + } + + function validate(): unknown { + return schema.validate(params); + } +} diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts index 34d74fa98f959..b51545770dd7b 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts @@ -7,7 +7,10 @@ import { i18n } from '@kbn/i18n'; import { schema, TypeOf } from '@kbn/config-schema'; import { ComparatorFnNames, getInvalidComparatorMessage } from './alert_type'; -import { CoreQueryParamsSchemaProperties, validateCoreQueryBody } from './lib/core_query_types'; +import { + CoreQueryParamsSchemaProperties, + validateCoreQueryBody, +} from '../../../../triggers_actions_ui/server'; // alert type parameters diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/index.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/index.ts index 9787ece323c59..a075b0d614cbb 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/index.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/index.ts @@ -4,34 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Service, AlertingSetup, IRouter } from '../../types'; -import { timeSeriesQuery } from './lib/time_series_query'; +import { Logger } from 'src/core/server'; +import { AlertingSetup, StackAlertsStartDeps } from '../../types'; import { getAlertType } from './alert_type'; -import { registerRoutes } from './routes'; // future enhancement: make these configurable? export const MAX_INTERVALS = 1000; export const MAX_GROUPS = 1000; export const DEFAULT_GROUPS = 100; -export function getService() { - return { - timeSeriesQuery, - }; -} - interface RegisterParams { - service: Service; - router: IRouter; + logger: Logger; + data: Promise; alerts: AlertingSetup; - baseRoute: string; } export function register(params: RegisterParams) { - const { service, router, alerts, baseRoute } = params; - - alerts.registerType(getAlertType(service)); - - const baseBuiltInRoute = `${baseRoute}/index_threshold`; - registerRoutes({ service, router, baseRoute: baseBuiltInRoute }); + const { logger, data, alerts } = params; + alerts.registerType(getAlertType(logger, data)); } diff --git a/x-pack/plugins/stack_alerts/server/index.ts b/x-pack/plugins/stack_alerts/server/index.ts index 108393c0d1469..bfc0a73819286 100644 --- a/x-pack/plugins/stack_alerts/server/index.ts +++ b/x-pack/plugins/stack_alerts/server/index.ts @@ -14,5 +14,3 @@ export const plugin = (ctx: PluginInitializerContext) => new AlertingBuiltinsPlu export const config = { schema: configSchema, }; - -export { IService } from './types'; diff --git a/x-pack/plugins/stack_alerts/server/plugin.test.ts b/x-pack/plugins/stack_alerts/server/plugin.test.ts index 3e2a919be0f13..71972707852fe 100644 --- a/x-pack/plugins/stack_alerts/server/plugin.test.ts +++ b/x-pack/plugins/stack_alerts/server/plugin.test.ts @@ -69,34 +69,5 @@ describe('AlertingBuiltins Plugin', () => { expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith(BUILT_IN_ALERTS_FEATURE); }); - - it('should return a service in the expected shape', async () => { - const alertingSetup = alertsMock.createSetup(); - const featuresSetup = featuresPluginMock.createSetup(); - const service = await plugin.setup(coreSetup, { - alerts: alertingSetup, - features: featuresSetup, - }); - - expect(typeof service.indexThreshold.timeSeriesQuery).toBe('function'); - }); - }); - - describe('start()', () => { - let context: ReturnType; - let plugin: AlertingBuiltinsPlugin; - let coreStart: ReturnType; - - beforeEach(() => { - context = coreMock.createPluginInitializerContext(); - plugin = new AlertingBuiltinsPlugin(context); - coreStart = coreMock.createStart(); - }); - - it('should return a service in the expected shape', async () => { - const service = await plugin.start(coreStart); - - expect(typeof service.indexThreshold.timeSeriesQuery).toBe('function'); - }); }); }); diff --git a/x-pack/plugins/stack_alerts/server/plugin.ts b/x-pack/plugins/stack_alerts/server/plugin.ts index f250bbc70fb80..66ac9e455e8b6 100644 --- a/x-pack/plugins/stack_alerts/server/plugin.ts +++ b/x-pack/plugins/stack_alerts/server/plugin.ts @@ -4,40 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Plugin, Logger, CoreSetup, CoreStart, PluginInitializerContext } from 'src/core/server'; +import { Plugin, Logger, CoreSetup, PluginInitializerContext } from 'src/core/server'; -import { Service, IService, StackAlertsDeps } from './types'; -import { getService as getServiceIndexThreshold } from './alert_types/index_threshold'; +import { StackAlertsDeps, StackAlertsStartDeps } from './types'; import { registerBuiltInAlertTypes } from './alert_types'; import { BUILT_IN_ALERTS_FEATURE } from './feature'; -export class AlertingBuiltinsPlugin implements Plugin { +export class AlertingBuiltinsPlugin + implements Plugin { private readonly logger: Logger; - private readonly service: Service; constructor(ctx: PluginInitializerContext) { this.logger = ctx.logger.get(); - this.service = { - indexThreshold: getServiceIndexThreshold(), - logger: this.logger, - }; } - public async setup(core: CoreSetup, { alerts, features }: StackAlertsDeps): Promise { + public async setup( + core: CoreSetup, + { alerts, features }: StackAlertsDeps + ): Promise { features.registerKibanaFeature(BUILT_IN_ALERTS_FEATURE); registerBuiltInAlertTypes({ - service: this.service, - router: core.http.createRouter(), + logger: this.logger, + data: core + .getStartServices() + .then(async ([, { triggersActionsUi }]) => triggersActionsUi.data), alerts, - baseRoute: '/api/stack_alerts', }); - return this.service; - } - - public async start(core: CoreStart): Promise { - return this.service; } + public async start(): Promise {} public async stop(): Promise {} } diff --git a/x-pack/plugins/stack_alerts/server/types.ts b/x-pack/plugins/stack_alerts/server/types.ts index d0eb8aa768915..e37596e8ff970 100644 --- a/x-pack/plugins/stack_alerts/server/types.ts +++ b/x-pack/plugins/stack_alerts/server/types.ts @@ -4,11 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger, LegacyScopedClusterClient } from '../../../../src/core/server'; +import { PluginStartContract as TriggersActionsUiStartContract } from '../../triggers_actions_ui/server'; import { PluginSetupContract as AlertingSetup } from '../../alerts/server'; -import { getService as getServiceIndexThreshold } from './alert_types/index_threshold'; - -export { Logger, IRouter } from '../../../../src/core/server'; export { PluginSetupContract as AlertingSetup, @@ -23,14 +20,6 @@ export interface StackAlertsDeps { features: FeaturesPluginSetup; } -// external service exposed through plugin setup/start -export interface IService { - indexThreshold: ReturnType; -} - -// version of service for internal use -export interface Service extends IService { - logger: Logger; +export interface StackAlertsStartDeps { + triggersActionsUi: TriggersActionsUiStartContract; } - -export type CallCluster = LegacyScopedClusterClient['callAsCurrentUser']; diff --git a/x-pack/plugins/stack_alerts/common/alert_types/index_threshold/index.ts b/x-pack/plugins/triggers_actions_ui/common/data/index.ts similarity index 100% rename from x-pack/plugins/stack_alerts/common/alert_types/index_threshold/index.ts rename to x-pack/plugins/triggers_actions_ui/common/data/index.ts diff --git a/x-pack/plugins/triggers_actions_ui/common/index.ts b/x-pack/plugins/triggers_actions_ui/common/index.ts new file mode 100644 index 0000000000000..5775cc2454a7e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/common/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './data'; diff --git a/x-pack/plugins/triggers_actions_ui/kibana.json b/x-pack/plugins/triggers_actions_ui/kibana.json index 72e1f0be5f7f4..2b279a1c1bc45 100644 --- a/x-pack/plugins/triggers_actions_ui/kibana.json +++ b/x-pack/plugins/triggers_actions_ui/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "server": true, "ui": true, - "optionalPlugins": ["alerts", "stackAlerts", "features", "home"], + "optionalPlugins": ["alerts", "features", "home"], "requiredPlugins": ["management", "charts", "data", "kibanaReact"], "configPath": ["xpack", "trigger_actions_ui"], "extraPublicDirs": ["public/common", "public/common/constants"], diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts index 11d273fcb7a42..264f046bf88fd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import { HttpSetup } from 'kibana/public'; -import { TimeSeriesResult } from '../../../../stack_alerts/common/alert_types/index_threshold'; +import { TimeSeriesResult } from '../../../common'; -const INDEX_THRESHOLD_API_ROOT = '/api/stack_alerts/index_threshold'; +const INDEX_THRESHOLD_DATA_API_ROOT = '/api/triggers_actions_ui/data'; export async function getMatchingIndicesForThresholdAlertType({ pattern, @@ -21,7 +21,7 @@ export async function getMatchingIndicesForThresholdAlertType({ if (!pattern.endsWith('*')) { pattern = `${pattern}*`; } - const { indices } = await http.post(`${INDEX_THRESHOLD_API_ROOT}/_indices`, { + const { indices } = await http.post(`${INDEX_THRESHOLD_DATA_API_ROOT}/_indices`, { body: JSON.stringify({ pattern }), }); return indices; @@ -34,7 +34,7 @@ export async function getThresholdAlertTypeFields({ indexes: string[]; http: HttpSetup; }): Promise> { - const { fields } = await http.post(`${INDEX_THRESHOLD_API_ROOT}/_fields`, { + const { fields } = await http.post(`${INDEX_THRESHOLD_DATA_API_ROOT}/_fields`, { body: JSON.stringify({ indexPatterns: indexes }), }); return fields; @@ -85,7 +85,7 @@ export async function getThresholdAlertVisualizationData({ interval: visualizeOptions.interval, }; - return await http.post(`${INDEX_THRESHOLD_API_ROOT}/_time_series_query`, { + return await http.post(`${INDEX_THRESHOLD_DATA_API_ROOT}/_time_series_query`, { body: JSON.stringify(timeSeriesQueryParams), }); } diff --git a/x-pack/plugins/triggers_actions_ui/server/data/README.md b/x-pack/plugins/triggers_actions_ui/server/data/README.md new file mode 100644 index 0000000000000..78577f0783008 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/server/data/README.md @@ -0,0 +1,228 @@ +# Data Apis + +The TriggersActionsUi plugin's Data Apis back the functionality needed by the Index Threshold Stack Alert. + +## http endpoints + +The following endpoints are provided for this alert type: + +- `POST /api/triggers_actions_ui/data/_indices` +- `POST /api/triggers_actions_ui/data/_fields` +- `POST /api/triggers_actions_ui/data/_time_series_query` + +### `POST .../_indices` + +This HTTP endpoint is provided for the alerting ui to list the available +"index names" for the user to select to use with the alert. This API also +returns aliases which match the supplied pattern. + +The request body is expected to be a JSON object in the following form, where the +`pattern` value may include comma-separated names and wildcards. + +```js +{ + pattern: "index-name-pattern" +} +``` + +The response body is a JSON object in the following form, where each element +of the `indices` array is the name of an index or alias. The number of elements +returned is limited, as this API is intended to be used to help narrow down +index names to use with the alert, and not support pagination, etc. + +```js +{ + indices: ["index-name-1", "alias-name-1", ...] +} +``` + +### `POST .../_fields` + +This HTTP endpoint is provided for the alerting ui to list the available +fields for the user to select to use with the alert. + +The request body is expected to be a JSON object in the following form, where the +`indexPatterns` array elements may include comma-separated names and wildcards. + +```js +{ + indexPatterns: ["index-pattern-1", "index-pattern-2"] +} +``` + +The response body is a JSON object in the following form, where each element +fields array is a field object. + +```js +{ + fields: [fieldObject1, fieldObject2, ...] +} +``` + +A field object is the following shape: + +```typescript +{ + name: string, // field name + type: string, // field type - eg 'keyword', 'date', 'long', etc + normalizedType: string, // for numeric types, this will be 'number' + aggregatable: true, // value from elasticsearch field capabilities + searchable: true, // value from elasticsearch field capabilities +} +``` + +### `POST .../_time_series_query` + +This HTTP endpoint is provided to return the values the alertType would calculate, +over a series of time. It is intended to be used in the alerting UI to +provide a "preview" of the alert during creation/editing based on recent data, +and could be used to show a "simulation" of the the alert over an arbitrary +range of time. + +The endpoint is `POST /api/triggers_actions_ui/data/_time_series_query`. +The request and response bodies are specifed in +[`lib/core_query_types.ts`][it-core-query] +and +[`lib/time_series_types.ts`][it-timeSeries-types]. +The request body is very similar to the alertType's parameters. + +### example + +Continuing with the example above, here's a query to get the values calculated +for the last 10 seconds. +This example uses [now-iso][] to generate iso date strings. + +```console +curl -k "https://elastic:changeme@localhost:5601/api/triggers_actions_ui/data/_time_series_query" \ + -H "kbn-xsrf: foo" -H "content-type: application/json" -d "{ + \"index\": \"es-hb-sim\", + \"timeField\": \"@timestamp\", + \"aggType\": \"avg\", + \"aggField\": \"summary.up\", + \"groupBy\": \"top\", + \"termSize\": 100, + \"termField\": \"monitor.name.keyword\", + \"interval\": \"1s\", + \"dateStart\": \"`now-iso -10s`\", + \"dateEnd\": \"`now-iso`\", + \"timeWindowSize\": 5, + \"timeWindowUnit\": \"s\" +}" +``` + +``` +{ + "results": [ + { + "group": "host-A", + "metrics": [ + [ "2020-02-26T15:10:40.000Z", 0 ], + [ "2020-02-26T15:10:41.000Z", 0 ], + [ "2020-02-26T15:10:42.000Z", 0 ], + [ "2020-02-26T15:10:43.000Z", 0 ], + [ "2020-02-26T15:10:44.000Z", 0 ], + [ "2020-02-26T15:10:45.000Z", 0 ], + [ "2020-02-26T15:10:46.000Z", 0 ], + [ "2020-02-26T15:10:47.000Z", 0 ], + [ "2020-02-26T15:10:48.000Z", 0 ], + [ "2020-02-26T15:10:49.000Z", 0 ], + [ "2020-02-26T15:10:50.000Z", 0 ] + ] + } + ] +} +``` + +To get the current value of the calculated metric, you can leave off the date: + +``` +curl -k "https://elastic:changeme@localhost:5601/api/triggers_actions_ui/data/_time_series_query" \ + -H "kbn-xsrf: foo" -H "content-type: application/json" -d '{ + "index": "es-hb-sim", + "timeField": "@timestamp", + "aggType": "avg", + "aggField": "summary.up", + "groupBy": "top", + "termField": "monitor.name.keyword", + "termSize": 100, + "interval": "1s", + "timeWindowSize": 5, + "timeWindowUnit": "s" +}' +``` + +``` +{ + "results": [ + { + "group": "host-A", + "metrics": [ + [ "2020-02-26T15:23:36.635Z", 0 ] + ] + } + ] +} +``` + +[it-timeSeries-types]: lib/time_series_types.ts + +## service functions + +A single service function is available that provides the functionality +of the http endpoint `POST /api/triggers_actions_ui/data/_time_series_query`, +but as an API for Kibana plugins. The function is available as +`triggersActionsUi.data.timeSeriesQuery()` on the plugin's _Start_ contract + +The parameters and return value for the function are the same as for the HTTP +request, though some additional parameters are required (logger, callCluster, +etc). + +## notes on the timeSeriesQuery API / http endpoint + +This API provides additional parameters beyond what the alertType itself uses: + +- `dateStart` +- `dateEnd` +- `interval` + +The `dateStart` and `dateEnd` parameters are ISO date strings. + +The `interval` parameter is intended to model the `interval` the alert is +currently using, and uses the same `1s`, `2m`, `3h`, etc format. Over the +supplied date range, a time-series data point will be calculated every +`interval` duration. + +So the number of time-series points in the output of the API should be: + +``` +( dateStart - dateEnd ) / interval +``` + +Example: + +``` +dateStart: '2020-01-01T00:00:00' +dateEnd: '2020-01-02T00:00:00' +interval: '1h' +``` + +The date range is 1 day === 24 hours. The interval is 1 hour. So there should +be ~24 time series points in the output. + +For preview purposes: + +- The `termSize` parameter should be used to help cut +down on the amount of work ES does, and keep the generated graphs a little +simpler. Probably something like `10`. + +- For queries with long date ranges, you probably don't want to use the +`interval` the alert is set to, as the `interval` used in the query, as this +could result in a lot of time-series points being generated, which is both +costly in ES, and may result in noisy graphs. + +- The `timeWindow*` parameters should be the same as what the alert is using, +especially for the `count` and `sum` aggregation types. Those aggregations +don't scale the same way the others do, when the window changes. Even for +the other aggregations, changing the window could result in dramatically +different values being generated - `avg` will be more "average-y", `min` +and `max` will be a little stickier. \ No newline at end of file diff --git a/x-pack/plugins/triggers_actions_ui/server/data/index.ts b/x-pack/plugins/triggers_actions_ui/server/data/index.ts new file mode 100644 index 0000000000000..6ee2b4bb8a5fe --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/server/data/index.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Logger, IRouter } from '../../../../../src/core/server'; +import { timeSeriesQuery } from './lib/time_series_query'; +import { registerRoutes } from './routes'; + +export { + TimeSeriesQuery, + CoreQueryParams, + CoreQueryParamsSchemaProperties, + validateCoreQueryBody, +} from './lib'; + +// future enhancement: make these configurable? +export const MAX_INTERVALS = 1000; +export const MAX_GROUPS = 1000; +export const DEFAULT_GROUPS = 100; + +export function getService() { + return { + timeSeriesQuery, + }; +} + +interface RegisterParams { + logger: Logger; + router: IRouter; + data: ReturnType; + baseRoute: string; +} + +export function register(params: RegisterParams) { + const { logger, router, data, baseRoute } = params; + const baseBuiltInRoute = `${baseRoute}/data`; + registerRoutes({ logger, router, data, baseRoute: baseBuiltInRoute }); +} diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/core_query_types.test.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/core_query_types.test.ts similarity index 100% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/core_query_types.test.ts rename to x-pack/plugins/triggers_actions_ui/server/data/lib/core_query_types.test.ts diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/core_query_types.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/core_query_types.ts similarity index 100% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/core_query_types.ts rename to x-pack/plugins/triggers_actions_ui/server/data/lib/core_query_types.ts diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/date_range_info.test.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.test.ts similarity index 100% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/date_range_info.test.ts rename to x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.test.ts diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/date_range_info.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.ts similarity index 98% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/date_range_info.ts rename to x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.ts index 34f276d08706b..63ae421b1467e 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/date_range_info.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { times } from 'lodash'; -import { parseDuration } from '../../../../../alerts/server'; +import { parseDuration } from '../../../../alerts/server'; import { MAX_INTERVALS } from '../index'; // dates as numbers are epoch millis diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/index.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/index.ts new file mode 100644 index 0000000000000..096a928249fd5 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { TimeSeriesQuery } from './time_series_query'; +export { + CoreQueryParams, + CoreQueryParamsSchemaProperties, + validateCoreQueryBody, +} from './core_query_types'; diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_query.test.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts similarity index 58% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_query.test.ts rename to x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts index 0565a8634fc71..f1234249a257f 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_query.test.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts @@ -6,12 +6,8 @@ // test error conditions of calling timeSeriesQuery - postive results tested in FT -import { loggingSystemMock } from '../../../../../../../src/core/server/mocks'; -import { coreMock } from '../../../../../../../src/core/server/mocks'; -import { AlertingBuiltinsPlugin } from '../../../plugin'; -import { TimeSeriesQueryParameters, TimeSeriesResult, TimeSeriesQuery } from './time_series_query'; - -type TimeSeriesQueryFn = (query: TimeSeriesQueryParameters) => Promise; +import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { TimeSeriesQueryParameters, TimeSeriesQuery, timeSeriesQuery } from './time_series_query'; const DefaultQueryParams: TimeSeriesQuery = { index: 'index-name', @@ -32,16 +28,7 @@ describe('timeSeriesQuery', () => { let params: TimeSeriesQueryParameters; const mockCallCluster = jest.fn(); - let timeSeriesQueryFn: TimeSeriesQueryFn; - beforeEach(async () => { - // rather than use the function from an import, retrieve it from the plugin - const context = coreMock.createPluginInitializerContext(); - const plugin = new AlertingBuiltinsPlugin(context); - const coreStart = coreMock.createStart(); - const service = await plugin.start(coreStart); - timeSeriesQueryFn = service.indexThreshold.timeSeriesQuery; - mockCallCluster.mockReset(); params = { logger: loggingSystemMock.create().get(), @@ -52,14 +39,14 @@ describe('timeSeriesQuery', () => { it('fails as expected when the callCluster call fails', async () => { mockCallCluster.mockRejectedValue(new Error('woopsie')); - expect(timeSeriesQueryFn(params)).rejects.toThrowErrorMatchingInlineSnapshot( + expect(timeSeriesQuery(params)).rejects.toThrowErrorMatchingInlineSnapshot( `"error running search"` ); }); it('fails as expected when the query params are invalid', async () => { params.query = { ...params.query, dateStart: 'x' }; - expect(timeSeriesQueryFn(params)).rejects.toThrowErrorMatchingInlineSnapshot( + expect(timeSeriesQuery(params)).rejects.toThrowErrorMatchingInlineSnapshot( `"invalid date format for dateStart: \\"x\\""` ); }); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_query.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts similarity index 96% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_query.ts rename to x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts index 9c4133be6f483..29a996bbb5ef6 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_query.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts @@ -5,16 +5,17 @@ */ import { SearchResponse } from 'elasticsearch'; +import { Logger } from 'kibana/server'; +import { LegacyScopedClusterClient } from '../../../../../../src/core/server'; import { DEFAULT_GROUPS } from '../index'; import { getDateRangeInfo } from './date_range_info'; -import { Logger, CallCluster } from '../../../types'; import { TimeSeriesQuery, TimeSeriesResult, TimeSeriesResultRow } from './time_series_types'; export { TimeSeriesQuery, TimeSeriesResult } from './time_series_types'; export interface TimeSeriesQueryParameters { logger: Logger; - callCluster: CallCluster; + callCluster: LegacyScopedClusterClient['callAsCurrentUser']; query: TimeSeriesQuery; } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_types.test.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.test.ts similarity index 100% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_types.test.ts rename to x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.test.ts diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_types.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts similarity index 95% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_types.ts rename to x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts index a8b35c34c596f..1e681472016d0 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_types.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { schema, TypeOf } from '@kbn/config-schema'; -import { parseDuration } from '../../../../../alerts/server'; +import { parseDuration } from '../../../../alerts/server'; import { MAX_INTERVALS } from '../index'; import { CoreQueryParamsSchemaProperties, validateCoreQueryBody } from './core_query_types'; import { @@ -18,11 +18,7 @@ import { getDateStartAfterDateEndErrorMessage, } from './date_range_info'; -export { - TimeSeriesResult, - TimeSeriesResultRow, - MetricResult, -} from '../../../../common/alert_types/index_threshold'; +export { TimeSeriesResult, TimeSeriesResultRow, MetricResult } from '../../../common/data'; // The parameters here are very similar to the alert parameters. // Missing are `comparator` and `threshold`, which aren't needed to generate diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/fields.ts b/x-pack/plugins/triggers_actions_ui/server/data/routes/fields.ts similarity index 90% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/fields.ts rename to x-pack/plugins/triggers_actions_ui/server/data/routes/fields.ts index ea1e17002c4a5..17a2b2929f0cf 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/fields.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/routes/fields.ts @@ -16,7 +16,7 @@ import { KibanaResponseFactory, ILegacyScopedClusterClient, } from 'kibana/server'; -import { Service } from '../../../types'; +import { Logger } from '../../../../../../src/core/server'; const bodySchema = schema.object({ indexPatterns: schema.arrayOf(schema.string()), @@ -24,9 +24,9 @@ const bodySchema = schema.object({ type RequestBody = TypeOf; -export function createFieldsRoute(service: Service, router: IRouter, baseRoute: string) { +export function createFieldsRoute(logger: Logger, router: IRouter, baseRoute: string) { const path = `${baseRoute}/_fields`; - service.logger.debug(`registering indexThreshold route POST ${path}`); + logger.debug(`registering indexThreshold route POST ${path}`); router.post( { path, @@ -41,7 +41,7 @@ export function createFieldsRoute(service: Service, router: IRouter, baseRoute: req: KibanaRequest, res: KibanaResponseFactory ): Promise { - service.logger.debug(`route ${path} request: ${JSON.stringify(req.body)}`); + logger.debug(`route ${path} request: ${JSON.stringify(req.body)}`); let rawFields: RawFields; @@ -54,7 +54,7 @@ export function createFieldsRoute(service: Service, router: IRouter, baseRoute: rawFields = await getRawFields(ctx.core.elasticsearch.legacy.client, req.body.indexPatterns); } catch (err) { const indexPatterns = req.body.indexPatterns.join(','); - service.logger.warn( + logger.warn( `route ${path} error getting fields from pattern "${indexPatterns}": ${err.message}` ); return res.ok({ body: { fields: [] } }); @@ -62,7 +62,7 @@ export function createFieldsRoute(service: Service, router: IRouter, baseRoute: const result = { fields: getFieldsFromRawFields(rawFields) }; - service.logger.debug(`route ${path} response: ${JSON.stringify(result)}`); + logger.debug(`route ${path} response: ${JSON.stringify(result)}`); return res.ok({ body: result }); } } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/index.ts b/x-pack/plugins/triggers_actions_ui/server/data/routes/index.ts similarity index 55% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/index.ts rename to x-pack/plugins/triggers_actions_ui/server/data/routes/index.ts index 8410e48dd46d9..664b78cabb560 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/index.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/routes/index.ts @@ -4,19 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Service, IRouter } from '../../../types'; +import { Logger } from '../../../../../../src/core/server'; import { createTimeSeriesQueryRoute } from './time_series_query'; import { createFieldsRoute } from './fields'; import { createIndicesRoute } from './indices'; +import { IRouter } from '../../../../../../src/core/server'; +import { getService } from '..'; interface RegisterRoutesParams { - service: Service; + logger: Logger; router: IRouter; + data: ReturnType; baseRoute: string; } export function registerRoutes(params: RegisterRoutesParams) { - const { service, router, baseRoute } = params; - createTimeSeriesQueryRoute(service, router, baseRoute); - createFieldsRoute(service, router, baseRoute); - createIndicesRoute(service, router, baseRoute); + const { logger, router, baseRoute, data } = params; + createTimeSeriesQueryRoute(logger, data.timeSeriesQuery, router, baseRoute); + createFieldsRoute(logger, router, baseRoute); + createIndicesRoute(logger, router, baseRoute); } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/indices.ts b/x-pack/plugins/triggers_actions_ui/server/data/routes/indices.ts similarity index 85% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/indices.ts rename to x-pack/plugins/triggers_actions_ui/server/data/routes/indices.ts index c94705829ec60..9b84ce5ac0bcc 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/indices.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/routes/indices.ts @@ -19,7 +19,7 @@ import { ILegacyScopedClusterClient, } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; -import { Service } from '../../../types'; +import { Logger } from '../../../../../../src/core/server'; const bodySchema = schema.object({ pattern: schema.string(), @@ -27,9 +27,9 @@ const bodySchema = schema.object({ type RequestBody = TypeOf; -export function createIndicesRoute(service: Service, router: IRouter, baseRoute: string) { +export function createIndicesRoute(logger: Logger, router: IRouter, baseRoute: string) { const path = `${baseRoute}/_indices`; - service.logger.debug(`registering indexThreshold route POST ${path}`); + logger.debug(`registering indexThreshold route POST ${path}`); router.post( { path, @@ -45,7 +45,7 @@ export function createIndicesRoute(service: Service, router: IRouter, baseRoute: res: KibanaResponseFactory ): Promise { const pattern = req.body.pattern; - service.logger.debug(`route ${path} request: ${JSON.stringify(req.body)}`); + logger.debug(`route ${path} request: ${JSON.stringify(req.body)}`); if (pattern.trim() === '') { return res.ok({ body: { indices: [] } }); @@ -55,23 +55,19 @@ export function createIndicesRoute(service: Service, router: IRouter, baseRoute: try { aliases = await getAliasesFromPattern(ctx.core.elasticsearch.legacy.client, pattern); } catch (err) { - service.logger.warn( - `route ${path} error getting aliases from pattern "${pattern}": ${err.message}` - ); + logger.warn(`route ${path} error getting aliases from pattern "${pattern}": ${err.message}`); } let indices: string[] = []; try { indices = await getIndicesFromPattern(ctx.core.elasticsearch.legacy.client, pattern); } catch (err) { - service.logger.warn( - `route ${path} error getting indices from pattern "${pattern}": ${err.message}` - ); + logger.warn(`route ${path} error getting indices from pattern "${pattern}": ${err.message}`); } const result = { indices: uniqueCombined(aliases, indices, MAX_INDICES) }; - service.logger.debug(`route ${path} response: ${JSON.stringify(result)}`); + logger.debug(`route ${path} response: ${JSON.stringify(result)}`); return res.ok({ body: result }); } } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/time_series_query.ts b/x-pack/plugins/triggers_actions_ui/server/data/routes/time_series_query.ts similarity index 58% rename from x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/time_series_query.ts rename to x-pack/plugins/triggers_actions_ui/server/data/routes/time_series_query.ts index 9af01dc766a99..805bd7d4004c2 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/routes/time_series_query.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/routes/time_series_query.ts @@ -11,14 +11,20 @@ import { IKibanaResponse, KibanaResponseFactory, } from 'kibana/server'; +import { Logger } from '../../../../../../src/core/server'; +import { TimeSeriesQueryParameters } from '../lib/time_series_query'; -import { Service } from '../../../types'; -import { TimeSeriesQuery, TimeSeriesQuerySchema } from '../lib/time_series_types'; +import { TimeSeriesQuery, TimeSeriesQuerySchema, TimeSeriesResult } from '../lib/time_series_types'; export { TimeSeriesQuery, TimeSeriesResult } from '../lib/time_series_types'; -export function createTimeSeriesQueryRoute(service: Service, router: IRouter, baseRoute: string) { +export function createTimeSeriesQueryRoute( + logger: Logger, + timeSeriesQuery: (params: TimeSeriesQueryParameters) => Promise, + router: IRouter, + baseRoute: string +) { const path = `${baseRoute}/_time_series_query`; - service.logger.debug(`registering indexThreshold route POST ${path}`); + logger.debug(`registering indexThreshold route POST ${path}`); router.post( { path, @@ -33,15 +39,15 @@ export function createTimeSeriesQueryRoute(service: Service, router: IRouter, ba req: KibanaRequest, res: KibanaResponseFactory ): Promise { - service.logger.debug(`route ${path} request: ${JSON.stringify(req.body)}`); + logger.debug(`route ${path} request: ${JSON.stringify(req.body)}`); - const result = await service.indexThreshold.timeSeriesQuery({ - logger: service.logger, + const result = await timeSeriesQuery({ + logger, callCluster: ctx.core.elasticsearch.legacy.client.callAsCurrentUser, query: req.body, }); - service.logger.debug(`route ${path} response: ${JSON.stringify(result)}`); + logger.debug(`route ${path} response: ${JSON.stringify(result)}`); return res.ok({ body: result }); } } diff --git a/x-pack/plugins/triggers_actions_ui/server/index.ts b/x-pack/plugins/triggers_actions_ui/server/index.ts index c12572f4ea7e9..abd61f2bd3541 100644 --- a/x-pack/plugins/triggers_actions_ui/server/index.ts +++ b/x-pack/plugins/triggers_actions_ui/server/index.ts @@ -4,8 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginConfigDescriptor } from 'kibana/server'; +import { PluginConfigDescriptor, PluginInitializerContext } from 'kibana/server'; import { configSchema, ConfigSchema } from '../config'; +import { TriggersActionsPlugin } from './plugin'; + +export { PluginStartContract } from './plugin'; +export { + TimeSeriesQuery, + CoreQueryParams, + CoreQueryParamsSchemaProperties, + validateCoreQueryBody, + MAX_INTERVALS, + MAX_GROUPS, + DEFAULT_GROUPS, +} from './data'; export const config: PluginConfigDescriptor = { exposeToBrowser: { @@ -14,7 +26,4 @@ export const config: PluginConfigDescriptor = { schema: configSchema, }; -export const plugin = () => ({ - setup() {}, - start() {}, -}); +export const plugin = (ctx: PluginInitializerContext) => new TriggersActionsPlugin(ctx); diff --git a/x-pack/plugins/triggers_actions_ui/server/plugin.ts b/x-pack/plugins/triggers_actions_ui/server/plugin.ts new file mode 100644 index 0000000000000..c0d29341e217b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/server/plugin.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Logger, Plugin, CoreSetup, PluginInitializerContext } from 'src/core/server'; +import { getService, register as registerDataService } from './data'; + +export interface PluginStartContract { + data: ReturnType; +} + +export class TriggersActionsPlugin implements Plugin { + private readonly logger: Logger; + private readonly data: PluginStartContract['data']; + + constructor(ctx: PluginInitializerContext) { + this.logger = ctx.logger.get(); + this.data = getService(); + } + + public async setup(core: CoreSetup): Promise { + registerDataService({ + logger: this.logger, + data: this.data, + router: core.http.createRouter(), + baseRoute: '/api/triggers_actions_ui', + }); + } + + public async start(): Promise { + return { + data: this.data, + }; + } + + public async stop(): Promise {} +} From d6459b16bfb37146c488575ddf477737e2b74ba2 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 9 Nov 2020 14:38:16 +0000 Subject: [PATCH 02/30] fixed i18n --- .../translations/translations/ja-JP.json | 14 ++-- .../translations/translations/zh-CN.json | 14 ++-- .../server/data/lib/core_query_types.ts | 67 ++++++++++++------- .../time_series_query_endpoint.ts | 2 +- 4 files changed, 56 insertions(+), 41 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e7784846598e4..7444745d41c1f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -19567,24 +19567,24 @@ "xpack.stackAlerts.indexThreshold.actionVariableContextThresholdLabel": "しきい値として使用する値の配列。「between」と「notBetween」には2つの値が必要です。その他は1つの値が必要です。", "xpack.stackAlerts.indexThreshold.actionVariableContextTitleLabel": "アラートの事前構成タイトル。", "xpack.stackAlerts.indexThreshold.actionVariableContextValueLabel": "しきい値を超えた値。", - "xpack.stackAlerts.indexThreshold.aggTypeRequiredErrorMessage": "[aggType]が「{aggType}」のときには[aggField]に値が必要です", + "xpack.triggersActionsUI.data.coreQueryParams.aggTypeRequiredErrorMessage": "[aggType]が「{aggType}」のときには[aggField]に値が必要です", "xpack.stackAlerts.indexThreshold.alertTypeContextMessageDescription": "アラート{name}グループ{group}値{value}が{date}に{window}にわたってしきい値{function}を超えました", "xpack.stackAlerts.indexThreshold.alertTypeContextSubjectTitle": "アラート{name}グループ{group}がしきい値を超えました", "xpack.stackAlerts.indexThreshold.alertTypeTitle": "インデックスしきい値", "xpack.stackAlerts.indexThreshold.dateStartGTdateEndErrorMessage": "[dateStart]が[dateEnd]よりも大です", "xpack.stackAlerts.indexThreshold.formattedFieldErrorMessage": "{fieldName}の無効な{formatName}形式:「{fieldValue}」", "xpack.stackAlerts.indexThreshold.intervalRequiredErrorMessage": "[interval]: [dateStart]が[dateEnd]と等しくない場合に指定する必要があります", - "xpack.stackAlerts.indexThreshold.invalidAggTypeErrorMessage": "無効な aggType:「{aggType}」", + "xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage": "無効な aggType:「{aggType}」", "xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage": "無効なthresholdComparatorが指定されました: {comparator}", "xpack.stackAlerts.indexThreshold.invalidDateErrorMessage": "無効な日付{date}", "xpack.stackAlerts.indexThreshold.invalidDurationErrorMessage": "無効な期間:「{duration}」", - "xpack.stackAlerts.indexThreshold.invalidGroupByErrorMessage": "無効なgroupBy:「{groupBy}」", - "xpack.stackAlerts.indexThreshold.invalidTermSizeMaximumErrorMessage": "[termSize]: {maxGroups}以下でなければなりません。", + "xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage": "無効なgroupBy:「{groupBy}」", + "xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize]: {maxGroups}以下でなければなりません。", "xpack.stackAlerts.indexThreshold.invalidThreshold2ErrorMessage": "[threshold]: 「{thresholdComparator}」比較子の場合には2つの要素が必要です", - "xpack.stackAlerts.indexThreshold.invalidTimeWindowUnitsErrorMessage": "無効なtimeWindowUnit:「{timeWindowUnit}」", + "xpack.triggersActionsUI.data.coreQueryParams.invalidTimeWindowUnitsErrorMessage": "無効なtimeWindowUnit:「{timeWindowUnit}」", "xpack.stackAlerts.indexThreshold.maxIntervalsErrorMessage": "間隔{intervals}の計算値が{maxIntervals}よりも大です", - "xpack.stackAlerts.indexThreshold.termFieldRequiredErrorMessage": "[termField]: [groupBy]がトップのときにはtermFieldが必要です", - "xpack.stackAlerts.indexThreshold.termSizeRequiredErrorMessage": "[termSize]: [groupBy]がトップのときにはtermSizeが必要です", + "xpack.triggersActionsUI.data.coreQueryParams.termFieldRequiredErrorMessage": "[termField]: [groupBy]がトップのときにはtermFieldが必要です", + "xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage": "[termSize]: [groupBy]がトップのときにはtermSizeが必要です", "xpack.transform.actionDeleteTransform.bulkDeleteDestinationIndexTitle": "ディスティネーションインデックスの削除", "xpack.transform.actionDeleteTransform.bulkDeleteDestIndexPatternTitle": "ディスティネーションインデックスパターンの削除", "xpack.transform.actionDeleteTransform.deleteDestinationIndexTitle": "ディスティネーションインデックス{destinationIndex}の削除", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f3cd662bacba7..5e6d48f9e8de4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -19586,24 +19586,24 @@ "xpack.stackAlerts.indexThreshold.actionVariableContextThresholdLabel": "用作阈值的值数组;“between”和“notBetween”需要两个值,其他则需要一个值。", "xpack.stackAlerts.indexThreshold.actionVariableContextTitleLabel": "告警的预构造标题。", "xpack.stackAlerts.indexThreshold.actionVariableContextValueLabel": "超过阈值的值。", - "xpack.stackAlerts.indexThreshold.aggTypeRequiredErrorMessage": "[aggField]:当 [aggType] 为“{aggType}”时必须有值", + "xpack.triggersActionsUI.data.coreQueryParams.aggTypeRequiredErrorMessage": "[aggField]:当 [aggType] 为“{aggType}”时必须有值", "xpack.stackAlerts.indexThreshold.alertTypeContextMessageDescription": "告警 {name} 组 {group} 值 {value} 在 {window} 于 {date}超过了阈值 {function}", "xpack.stackAlerts.indexThreshold.alertTypeContextSubjectTitle": "告警 {name} 组 {group} 超过了阈值", "xpack.stackAlerts.indexThreshold.alertTypeTitle": "索引阈值", "xpack.stackAlerts.indexThreshold.dateStartGTdateEndErrorMessage": "[dateStart]:晚于 [dateEnd]", "xpack.stackAlerts.indexThreshold.formattedFieldErrorMessage": "{fieldName} 的 {formatName} 格式无效:“{fieldValue}”", "xpack.stackAlerts.indexThreshold.intervalRequiredErrorMessage": "[interval]:如果 [dateStart] 不等于 [dateEnd],则必须指定", - "xpack.stackAlerts.indexThreshold.invalidAggTypeErrorMessage": "aggType 无效:“{aggType}”", + "xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage": "aggType 无效:“{aggType}”", "xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage": "指定的 thresholdComparator 无效:{comparator}", "xpack.stackAlerts.indexThreshold.invalidDateErrorMessage": "日期 {date} 无效", "xpack.stackAlerts.indexThreshold.invalidDurationErrorMessage": "持续时间无效:“{duration}”", - "xpack.stackAlerts.indexThreshold.invalidGroupByErrorMessage": "groupBy 无效:“{groupBy}”", - "xpack.stackAlerts.indexThreshold.invalidTermSizeMaximumErrorMessage": "[termSize]:必须小于或等于 {maxGroups}", + "xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage": "groupBy 无效:“{groupBy}”", + "xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize]:必须小于或等于 {maxGroups}", "xpack.stackAlerts.indexThreshold.invalidThreshold2ErrorMessage": "[threshold]:对于“{thresholdComparator}”比较运算符,必须包含两个元素", - "xpack.stackAlerts.indexThreshold.invalidTimeWindowUnitsErrorMessage": "timeWindowUnit 无效:“{timeWindowUnit}”", + "xpack.triggersActionsUI.data.coreQueryParams.invalidTimeWindowUnitsErrorMessage": "timeWindowUnit 无效:“{timeWindowUnit}”", "xpack.stackAlerts.indexThreshold.maxIntervalsErrorMessage": "时间间隔 {intervals} 的计算数目大于最大值 {maxIntervals}", - "xpack.stackAlerts.indexThreshold.termFieldRequiredErrorMessage": "[termField]:[groupBy] 为 top 时,termField 为必需", - "xpack.stackAlerts.indexThreshold.termSizeRequiredErrorMessage": "[termSize]:[groupBy] 为 top 时,termSize 为必需", + "xpack.triggersActionsUI.data.coreQueryParams.termFieldRequiredErrorMessage": "[termField]:[groupBy] 为 top 时,termField 为必需", + "xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage": "[termSize]:[groupBy] 为 top 时,termSize 为必需", "xpack.transform.actionDeleteTransform.bulkDeleteDestinationIndexTitle": "删除目标索引", "xpack.transform.actionDeleteTransform.bulkDeleteDestIndexPatternTitle": "删除目标索引模式", "xpack.transform.actionDeleteTransform.deleteDestinationIndexTitle": "删除目标索引 {destinationIndex}", diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/core_query_types.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/core_query_types.ts index d96f555eceff4..bc7d0c352756e 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/core_query_types.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/core_query_types.ts @@ -51,33 +51,45 @@ export function validateCoreQueryBody(anyParams: unknown): string | undefined { termSize, }: CoreQueryParams = anyParams as CoreQueryParams; if (aggType !== 'count' && !aggField) { - return i18n.translate('xpack.stackAlerts.indexThreshold.aggTypeRequiredErrorMessage', { - defaultMessage: '[aggField]: must have a value when [aggType] is "{aggType}"', - values: { - aggType, - }, - }); + return i18n.translate( + 'xpack.triggersActionsUI.data.coreQueryParams.aggTypeRequiredErrorMessage', + { + defaultMessage: '[aggField]: must have a value when [aggType] is "{aggType}"', + values: { + aggType, + }, + } + ); } // check grouping if (groupBy === 'top') { if (termField == null) { - return i18n.translate('xpack.stackAlerts.indexThreshold.termFieldRequiredErrorMessage', { - defaultMessage: '[termField]: termField required when [groupBy] is top', - }); + return i18n.translate( + 'xpack.triggersActionsUI.data.coreQueryParams.termFieldRequiredErrorMessage', + { + defaultMessage: '[termField]: termField required when [groupBy] is top', + } + ); } if (termSize == null) { - return i18n.translate('xpack.stackAlerts.indexThreshold.termSizeRequiredErrorMessage', { - defaultMessage: '[termSize]: termSize required when [groupBy] is top', - }); + return i18n.translate( + 'xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage', + { + defaultMessage: '[termSize]: termSize required when [groupBy] is top', + } + ); } if (termSize > MAX_GROUPS) { - return i18n.translate('xpack.stackAlerts.indexThreshold.invalidTermSizeMaximumErrorMessage', { - defaultMessage: '[termSize]: must be less than or equal to {maxGroups}', - values: { - maxGroups: MAX_GROUPS, - }, - }); + return i18n.translate( + 'xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage', + { + defaultMessage: '[termSize]: must be less than or equal to {maxGroups}', + values: { + maxGroups: MAX_GROUPS, + }, + } + ); } } } @@ -89,7 +101,7 @@ function validateAggType(aggType: string): string | undefined { return; } - return i18n.translate('xpack.stackAlerts.indexThreshold.invalidAggTypeErrorMessage', { + return i18n.translate('xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage', { defaultMessage: 'invalid aggType: "{aggType}"', values: { aggType, @@ -102,7 +114,7 @@ export function validateGroupBy(groupBy: string): string | undefined { return; } - return i18n.translate('xpack.stackAlerts.indexThreshold.invalidGroupByErrorMessage', { + return i18n.translate('xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage', { defaultMessage: 'invalid groupBy: "{groupBy}"', values: { groupBy, @@ -117,10 +129,13 @@ export function validateTimeWindowUnits(timeWindowUnit: string): string | undefi return; } - return i18n.translate('xpack.stackAlerts.indexThreshold.invalidTimeWindowUnitsErrorMessage', { - defaultMessage: 'invalid timeWindowUnit: "{timeWindowUnit}"', - values: { - timeWindowUnit, - }, - }); + return i18n.translate( + 'xpack.triggersActionsUI.data.coreQueryParams.invalidTimeWindowUnitsErrorMessage', + { + defaultMessage: 'invalid timeWindowUnit: "{timeWindowUnit}"', + values: { + timeWindowUnit, + }, + } + ); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts index 70dbb860e08d6..e9d20fa7791df 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import { Spaces } from '../../../../scenarios'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ESTestIndexTool, ES_TEST_INDEX_NAME, getUrlPrefix } from '../../../../../common/lib'; -import { TimeSeriesQuery } from '../../../../../../../plugins/stack_alerts/server/alert_types/index_threshold/lib/time_series_query'; +import { TimeSeriesQuery } from '../../../../../../../plugins/triggers_actions_ui/server'; import { createEsDocuments } from './create_test_data'; From e02056e6de8bc11ee4aacdf2afc4cabab00f1c97 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 9 Nov 2020 18:56:43 +0000 Subject: [PATCH 03/30] moved Threshold UIs into Stack Alerts --- .../stack_alerts/{server => common}/config.ts | 1 + x-pack/plugins/stack_alerts/common/index.ts | 2 +- x-pack/plugins/stack_alerts/kibana.json | 4 +- .../alert_types}/geo_threshold/index.ts | 3 +- .../expressions/boundary_index_expression.tsx | 7 ++- .../expressions/entity_by_expression.tsx | 4 +- .../expressions/entity_index_expression.tsx | 7 ++- .../geo_threshold/query_builder/index.tsx | 14 +++--- .../expression_with_popover.tsx | 10 ++--- .../geo_index_pattern_select.tsx | 5 ++- .../util_components/single_field_select.tsx | 2 +- .../alert_types}/geo_threshold/types.ts | 0 .../geo_threshold/validation.test.ts | 0 .../alert_types}/geo_threshold/validation.ts | 2 +- .../public/alert_types}/index.ts | 15 +++---- .../alert_types}/threshold/expression.scss | 0 .../alert_types}/threshold/expression.tsx | 31 +++++++------ .../public/alert_types}/threshold/index.ts | 3 +- .../threshold/index_threshold_api.ts | 45 +++++++++++++++++++ .../public/alert_types}/threshold/types.ts | 0 .../alert_types}/threshold/validation.test.ts | 0 .../alert_types}/threshold/validation.ts | 4 +- .../alert_types}/threshold/visualization.tsx | 30 ++++++++----- x-pack/plugins/stack_alerts/public/index.ts | 10 +++++ x-pack/plugins/stack_alerts/public/plugin.tsx | 35 +++++++++++++++ x-pack/plugins/stack_alerts/server/index.ts | 19 +++++--- .../translations/translations/ja-JP.json | 10 ++--- .../translations/translations/zh-CN.json | 10 ++--- .../plugins/triggers_actions_ui/kibana.json | 2 +- .../public/common/index.ts | 4 ++ .../public/common/index_controls/index.ts | 4 +- .../public/common/lib/index.ts | 6 +++ .../public/common/lib/index_threshold_api.ts | 44 +++++------------- .../triggers_actions_ui/public/index.ts | 7 +-- .../triggers_actions_ui/public/plugin.ts | 23 +--------- .../server/data/lib/date_range_info.ts | 13 +++--- .../server/data/lib/time_series_types.ts | 24 ++++++---- .../index_threshold/fields_endpoint.ts | 2 +- .../index_threshold/indices_endpoint.ts | 2 +- .../time_series_query_endpoint.ts | 2 +- 40 files changed, 245 insertions(+), 161 deletions(-) rename x-pack/plugins/stack_alerts/{server => common}/config.ts (83%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/index.ts (90%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/query_builder/expressions/boundary_index_expression.tsx (94%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/query_builder/expressions/entity_by_expression.tsx (93%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/query_builder/expressions/entity_index_expression.tsx (93%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/query_builder/index.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/query_builder/util_components/expression_with_popover.tsx (92%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/query_builder/util_components/single_field_select.tsx (96%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/types.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/validation.test.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/geo_threshold/validation.ts (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/index.ts (57%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/threshold/expression.scss (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/threshold/expression.tsx (94%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/threshold/index.ts (90%) create mode 100644 x-pack/plugins/stack_alerts/public/alert_types/threshold/index_threshold_api.ts rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/threshold/types.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/threshold/validation.test.ts (100%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/threshold/validation.ts (97%) rename x-pack/plugins/{triggers_actions_ui/public/application/components/builtin_alert_types => stack_alerts/public/alert_types}/threshold/visualization.tsx (91%) create mode 100644 x-pack/plugins/stack_alerts/public/index.ts create mode 100644 x-pack/plugins/stack_alerts/public/plugin.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/lib/index.ts diff --git a/x-pack/plugins/stack_alerts/server/config.ts b/x-pack/plugins/stack_alerts/common/config.ts similarity index 83% rename from x-pack/plugins/stack_alerts/server/config.ts rename to x-pack/plugins/stack_alerts/common/config.ts index 8a13aedd5fdd8..300b73a65bd47 100644 --- a/x-pack/plugins/stack_alerts/server/config.ts +++ b/x-pack/plugins/stack_alerts/common/config.ts @@ -8,6 +8,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), + enableGeoTrackingThresholdAlert: schema.maybe(schema.boolean({ defaultValue: false })), }); export type Config = TypeOf; diff --git a/x-pack/plugins/stack_alerts/common/index.ts b/x-pack/plugins/stack_alerts/common/index.ts index 79dd18d321f07..a75625d0641aa 100644 --- a/x-pack/plugins/stack_alerts/common/index.ts +++ b/x-pack/plugins/stack_alerts/common/index.ts @@ -3,5 +3,5 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +export * from './config'; export const STACK_ALERTS_FEATURE_ID = 'stackAlerts'; diff --git a/x-pack/plugins/stack_alerts/kibana.json b/x-pack/plugins/stack_alerts/kibana.json index dfaa2b61123c4..b7405c38d1611 100644 --- a/x-pack/plugins/stack_alerts/kibana.json +++ b/x-pack/plugins/stack_alerts/kibana.json @@ -3,7 +3,7 @@ "server": true, "version": "8.0.0", "kibanaVersion": "kibana", - "requiredPlugins": ["alerts", "features", "triggersActionsUi"], + "requiredPlugins": ["alerts", "features", "triggersActionsUi", "kibanaReact"], "configPath": ["xpack", "stack_alerts"], - "ui": false + "ui": true } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts similarity index 90% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/index.ts rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts index 00d9ebbbbc066..e36440f371997 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts @@ -5,10 +5,9 @@ */ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { AlertTypeModel } from '../../../../types'; import { validateExpression } from './validation'; import { GeoThresholdAlertParams } from './types'; -import { AlertsContextValue } from '../../../context/alerts_context'; +import { AlertTypeModel, AlertsContextValue } from '../../../../triggers_actions_ui/public'; export function getAlertType(): AlertTypeModel { return { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx similarity index 94% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx index 497e053a4ed60..4990971bfe2b2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx @@ -7,14 +7,13 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { IErrorObject } from '../../../../../../types'; +import { IErrorObject, AlertsContextValue } from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_SHAPE_TYPES, GeoThresholdAlertParams } from '../../types'; -import { AlertsContextValue } from '../../../../../context/alerts_context'; import { GeoIndexPatternSelect } from '../util_components/geo_index_pattern_select'; import { SingleFieldSelect } from '../util_components/single_field_select'; import { ExpressionWithPopover } from '../util_components/expression_with_popover'; -import { IFieldType } from '../../../../../../../../../../src/plugins/data/common/index_patterns/fields'; -import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/common/index_patterns'; +import { IFieldType } from '../../../../../../../../src/plugins/data/common/index_patterns/fields'; +import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; interface Props { alertParams: GeoThresholdAlertParams; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx similarity index 93% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx index f355d25796b7c..230cbac2131f2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx @@ -8,10 +8,10 @@ import React, { FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import _ from 'lodash'; -import { IErrorObject } from '../../../../../../types'; +import { IErrorObject } from '../../../../../../triggers_actions_ui/public'; import { SingleFieldSelect } from '../util_components/single_field_select'; import { ExpressionWithPopover } from '../util_components/expression_with_popover'; -import { IFieldType } from '../../../../../../../../../../src/plugins/data/common/index_patterns/fields'; +import { IFieldType } from '../../../../../../../../src/plugins/data/common/index_patterns/fields'; interface Props { errors: IErrorObject; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx similarity index 93% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx index 506530c171cd4..fc20ee7888f57 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx @@ -8,14 +8,13 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { IErrorObject } from '../../../../../../types'; +import { IErrorObject, AlertsContextValue } from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_FIELD_TYPES } from '../../types'; -import { AlertsContextValue } from '../../../../../context/alerts_context'; import { GeoIndexPatternSelect } from '../util_components/geo_index_pattern_select'; import { SingleFieldSelect } from '../util_components/single_field_select'; import { ExpressionWithPopover } from '../util_components/expression_with_popover'; -import { IFieldType } from '../../../../../../../../../../src/plugins/data/common/index_patterns/fields'; -import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/common/index_patterns'; +import { IFieldType } from '../../../../../../../../src/plugins/data/common/index_patterns/fields'; +import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns'; interface Props { dateField: string; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/index.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/index.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx index ccc2ddd9c01ca..410cea28f52e8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/index.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx @@ -19,15 +19,17 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { AlertTypeParamsExpressionProps } from '../../../../../types'; +import { + AlertTypeParamsExpressionProps, + getTimeOptions, + AlertsContextValue, +} from '../../../../../triggers_actions_ui/public'; import { GeoThresholdAlertParams, TrackingEvent } from '../types'; -import { AlertsContextValue } from '../../../../context/alerts_context'; import { ExpressionWithPopover } from './util_components/expression_with_popover'; import { EntityIndexExpression } from './expressions/entity_index_expression'; import { EntityByExpression } from './expressions/entity_by_expression'; import { BoundaryIndexExpression } from './expressions/boundary_index_expression'; -import { IIndexPattern } from '../../../../../../../../../src/plugins/data/common/index_patterns'; -import { getTimeOptions } from '../../../../../common/lib/get_time_options'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; const DEFAULT_VALUES = { TRACKING_EVENT: '', @@ -45,8 +47,8 @@ const DEFAULT_VALUES = { }; const conditionOptions = Object.keys(TrackingEvent).map((key) => ({ - text: (TrackingEvent as any)[key], - value: (TrackingEvent as any)[key], + text: TrackingEvent[key as TrackingEvent], + value: TrackingEvent[key as TrackingEvent], })); const labelForDelayOffset = ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx similarity index 92% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx index 7e1cae51f1411..74dad07f2b229 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React, { ReactNode, useState } from 'react'; import { EuiButtonIcon, EuiExpression, @@ -22,10 +22,10 @@ export const ExpressionWithPopover: ({ value, isInvalid, }: { - popoverContent: any; - expressionDescription: any; - defaultValue?: any; - value?: any; + popoverContent: ReactNode; + expressionDescription: ReactNode; + defaultValue?: ReactNode; + value?: ReactNode; isInvalid?: boolean; }) => JSX.Element = ({ popoverContent, expressionDescription, defaultValue, value, isInvalid }) => { const [popoverOpen, setPopoverOpen] = useState(false); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx index 42995dfb1b9d6..0e5817b38a442 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component } from 'react'; +import React, { Component, ReactNode } from 'react'; import { EuiCallOut, EuiFormRow, EuiLink, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -14,6 +14,7 @@ import { HttpSetup } from 'kibana/public'; interface Props { onChange: (indexPattern: IndexPattern) => void; value: string | undefined; + // eslint-disable-next-line @typescript-eslint/no-explicit-any IndexPatternSelectComponent: any; indexPatternService: IndexPatternsContract | undefined; http: HttpSetup; @@ -39,7 +40,7 @@ export class GeoIndexPatternSelect extends Component { this._isMounted = true; } - _onIndexPatternSelect = async (indexPatternId: any) => { + _onIndexPatternSelect = async (indexPatternId: string) => { if (!indexPatternId || indexPatternId.length === 0 || !this.props.indexPatternService) { return; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/single_field_select.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/single_field_select.tsx similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/single_field_select.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/single_field_select.tsx index 30389b31ce8c8..ef6e6f6f5e18f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/query_builder/util_components/single_field_select.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/single_field_select.tsx @@ -14,7 +14,7 @@ import { EuiFlexItem, } from '@elastic/eui'; import { IFieldType } from 'src/plugins/data/public'; -import { FieldIcon } from '../../../../../../../../../../src/plugins/kibana_react/public'; +import { FieldIcon } from '../../../../../../../../src/plugins/kibana_react/public'; function fieldsToOptions(fields?: IFieldType[]): Array> { if (!fields) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/types.ts rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/validation.test.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.test.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/validation.test.ts rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.test.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/validation.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.ts similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/validation.ts rename to x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.ts index 078a88d9e8415..8931b759bbc6e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/geo_threshold/validation.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { ValidationResult } from '../../../../types'; +import { ValidationResult } from '../../../../triggers_actions_ui/public'; import { GeoThresholdAlertParams } from './types'; export const validateExpression = (alertParams: GeoThresholdAlertParams): ValidationResult => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/index.ts similarity index 57% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts rename to x-pack/plugins/stack_alerts/public/alert_types/index.ts index 4b2860dcf9b72..61cf7193fedb7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/index.ts @@ -6,18 +6,17 @@ import { getAlertType as getGeoThresholdAlertType } from './geo_threshold'; import { getAlertType as getThresholdAlertType } from './threshold'; -import { TypeRegistry } from '../../type_registry'; -import { AlertTypeModel } from '../../../types'; -import { TriggersActionsUiConfigType } from '../../../plugin'; +import { Config } from '../../common'; +import { TriggersAndActionsUIPublicPluginSetup } from '../../../triggers_actions_ui/public'; -export function registerBuiltInAlertTypes({ +export function registerAlertTypes({ alertTypeRegistry, - triggerActionsUiConfig, + config, }: { - alertTypeRegistry: TypeRegistry; - triggerActionsUiConfig: TriggersActionsUiConfigType; + alertTypeRegistry: TriggersAndActionsUIPublicPluginSetup['alertTypeRegistry']; + config: Config; }) { - if (triggerActionsUiConfig.enableGeoTrackingThresholdAlert) { + if (config.enableGeoTrackingThresholdAlert) { alertTypeRegistry.register(getGeoThresholdAlertType()); } alertTypeRegistry.register(getThresholdAlertType()); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.scss b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.scss similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.scss rename to x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.scss diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx similarity index 94% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx index e309d97b57f34..e240ad9cb6f37 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx @@ -29,21 +29,20 @@ import { getIndexPatterns, getIndexOptions, getFields, -} from '../../../../common/index_controls'; -import { COMPARATORS, builtInComparators } from '../../../../common/constants'; -import { getTimeFieldOptions } from '../../../../common/lib/get_time_options'; -import { ThresholdVisualization } from './visualization'; -import { WhenExpression } from '../../../../common'; -import { + COMPARATORS, + builtInComparators, + getTimeFieldOptions, OfExpression, ThresholdExpression, ForLastExpression, GroupByExpression, -} from '../../../../common'; -import { builtInAggregationTypes } from '../../../../common/constants'; + WhenExpression, + builtInAggregationTypes, + AlertTypeParamsExpressionProps, + AlertsContextValue, +} from '../../../../triggers_actions_ui/public'; +import { ThresholdVisualization } from './visualization'; import { IndexThresholdAlertParams } from './types'; -import { AlertTypeParamsExpressionProps } from '../../../../types'; -import { AlertsContextValue } from '../../../context/alerts_context'; import './expression.scss'; const DEFAULT_VALUES = { @@ -89,7 +88,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent>([]); + const [esFields, setEsFields] = useState([]); const [indexOptions, setIndexOptions] = useState([]); const [timeFieldOptions, setTimeFieldOptions] = useState([firstFieldOption]); const [isIndiciesLoading, setIsIndiciesLoading] = useState(false); @@ -98,7 +97,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent expressionFieldsWithValidation.includes(errorKey) && errors[errorKey].length >= 1 && - (alertParams as { [key: string]: any })[errorKey] !== undefined + alertParams[errorKey as keyof IndexThresholdAlertParams] !== undefined ); const canShowVizualization = !!Object.keys(errors).find( @@ -126,7 +125,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent 0) { const currentEsFields = await getFields(http, index); - const timeFields = getTimeFieldOptions(currentEsFields as any); + const timeFields = getTimeFieldOptions(currentEsFields); setEsFields(currentEsFields); setTimeFieldOptions([firstFieldOption, ...timeFields]); @@ -211,7 +210,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent + onChangeWindowSize={(selectedWindowSize: number | undefined) => setAlertParams('timeWindowSize', selectedWindowSize) } - onChangeWindowUnit={(selectedWindowUnit: any) => + onChangeWindowUnit={(selectedWindowUnit: string) => setAlertParams('timeWindowUnit', selectedWindowUnit) } /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts similarity index 90% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/index.ts rename to x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts index d105e2c510bcd..51c5f9e1d8878 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts @@ -6,10 +6,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { AlertTypeModel } from '../../../../types'; import { validateExpression } from './validation'; import { IndexThresholdAlertParams } from './types'; -import { AlertsContextValue } from '../../../context/alerts_context'; +import { AlertTypeModel, AlertsContextValue } from '../../../../triggers_actions_ui/public'; export function getAlertType(): AlertTypeModel { return { diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/index_threshold_api.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/index_threshold_api.ts new file mode 100644 index 0000000000000..ec531b26fc8c6 --- /dev/null +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/index_threshold_api.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { HttpSetup } from 'kibana/public'; +import { TimeSeriesResult } from '../../../../triggers_actions_ui/common'; +import { IndexThresholdAlertParams } from './types'; + +const INDEX_THRESHOLD_DATA_API_ROOT = '/api/triggers_actions_ui/data'; + +export interface GetThresholdAlertVisualizationDataParams { + model: IndexThresholdAlertParams; + visualizeOptions: { + rangeFrom: string; + rangeTo: string; + interval: string; + }; + http: HttpSetup; +} + +export async function getThresholdAlertVisualizationData({ + model, + visualizeOptions, + http, +}: GetThresholdAlertVisualizationDataParams): Promise { + const timeSeriesQueryParams = { + index: model.index, + timeField: model.timeField, + aggType: model.aggType, + aggField: model.aggField, + groupBy: model.groupBy, + termField: model.termField, + termSize: model.termSize, + timeWindowSize: model.timeWindowSize, + timeWindowUnit: model.timeWindowUnit, + dateStart: new Date(visualizeOptions.rangeFrom).toISOString(), + dateEnd: new Date(visualizeOptions.rangeTo).toISOString(), + interval: visualizeOptions.interval, + }; + + return await http.post(`${INDEX_THRESHOLD_DATA_API_ROOT}/_time_series_query`, { + body: JSON.stringify(timeSeriesQueryParams), + }); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts rename to x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/validation.test.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/validation.test.ts rename to x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.test.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/validation.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.ts similarity index 97% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/validation.ts rename to x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.ts index 3912b2fffae1e..888ed54d2aeaa 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/validation.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { ValidationResult } from '../../../../types'; import { IndexThresholdAlertParams } from './types'; import { + ValidationResult, builtInGroupByTypes, builtInAggregationTypes, builtInComparators, -} from '../../../../common/constants'; +} from '../../../../triggers_actions_ui/public'; export const validateExpression = (alertParams: IndexThresholdAlertParams): ValidationResult => { const { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx similarity index 91% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx rename to x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx index a282fa08e8f38..4bdd7409cc33b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx @@ -29,11 +29,17 @@ import { EuiLoadingSpinner, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getThresholdAlertVisualizationData } from '../../../../common/lib/index_threshold_api'; -import { AggregationType, Comparator } from '../../../../common/types'; -import { AlertsContextValue } from '../../../context/alerts_context'; +import { + getThresholdAlertVisualizationData, + GetThresholdAlertVisualizationDataParams, +} from './index_threshold_api'; +import { + AlertsContextValue, + AggregationType, + Comparator, +} from '../../../../triggers_actions_ui/public'; import { IndexThresholdAlertParams } from './types'; -import { parseDuration } from '../../../../../../alerts/common/parse_duration'; +import { parseDuration } from '../../../../alerts/common/parse_duration'; const customTheme = () => { return { @@ -125,7 +131,7 @@ export const ThresholdVisualization: React.FunctionComponent = ({ const { http, toastNotifications, charts, uiSettings, dataFieldsFormats } = alertsContext; const [loadingState, setLoadingState] = useState(null); - const [error, setError] = useState(undefined); + const [error, setError] = useState(undefined); const [visualizationData, setVisualizationData] = useState>(); const [startVisualizationAt, setStartVisualizationAt] = useState(new Date()); @@ -239,7 +245,7 @@ export const ThresholdVisualization: React.FunctionComponent = ({ const alertVisualizationDataKeys = Object.keys(visualizationData); const timezone = getTimezone(uiSettings); const actualThreshold = getThreshold(); - let maxY = actualThreshold[actualThreshold.length - 1] as any; + let maxY = actualThreshold[actualThreshold.length - 1]; (Object.values(visualizationData) as number[][][]).forEach((data) => { data.forEach(([, y]) => { @@ -288,14 +294,14 @@ export const ThresholdVisualization: React.FunctionComponent = ({ /> ); })} - {actualThreshold.map((_value: any, i: number) => { - const specId = i === 0 ? 'threshold' : `threshold${i}`; + {actualThreshold.map((_value: number, thresholdIndex: number) => { + const specId = thresholdIndex === 0 ? 'threshold' : `threshold${thresholdIndex}`; return ( ); })} @@ -325,7 +331,11 @@ export const ThresholdVisualization: React.FunctionComponent = ({ }; // convert the data from the visualization API into something easier to digest with charts -async function getVisualizationData(model: any, visualizeOptions: any, http: HttpSetup) { +async function getVisualizationData( + model: IndexThresholdAlertParams, + visualizeOptions: GetThresholdAlertVisualizationDataParams['visualizeOptions'], + http: HttpSetup +) { const vizData = await getThresholdAlertVisualizationData({ model, visualizeOptions, diff --git a/x-pack/plugins/stack_alerts/public/index.ts b/x-pack/plugins/stack_alerts/public/index.ts new file mode 100644 index 0000000000000..2f84a5949f111 --- /dev/null +++ b/x-pack/plugins/stack_alerts/public/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext } from 'src/core/public'; +import { StackAlertsPublicPlugin } from './plugin'; + +export const plugin = (ctx: PluginInitializerContext) => new StackAlertsPublicPlugin(ctx); diff --git a/x-pack/plugins/stack_alerts/public/plugin.tsx b/x-pack/plugins/stack_alerts/public/plugin.tsx new file mode 100644 index 0000000000000..63176e7b30277 --- /dev/null +++ b/x-pack/plugins/stack_alerts/public/plugin.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public'; +import { TriggersAndActionsUIPublicPluginSetup } from '../../../plugins/triggers_actions_ui/public'; +import { registerAlertTypes } from './alert_types'; +import { Config } from '../common'; + +export type Setup = void; +export type Start = void; + +export interface StackAlertsPublicSetupDeps { + triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; +} + +export class StackAlertsPublicPlugin implements Plugin { + private initializerContext: PluginInitializerContext; + + constructor(initializerContext: PluginInitializerContext) { + this.initializerContext = initializerContext; + } + + public setup(core: CoreSetup, { triggersActionsUi }: StackAlertsPublicSetupDeps) { + registerAlertTypes({ + alertTypeRegistry: triggersActionsUi.alertTypeRegistry, + config: this.initializerContext.config.get(), + }); + } + + public start() {} + public stop() {} +} diff --git a/x-pack/plugins/stack_alerts/server/index.ts b/x-pack/plugins/stack_alerts/server/index.ts index bfc0a73819286..adb617558e6f4 100644 --- a/x-pack/plugins/stack_alerts/server/index.ts +++ b/x-pack/plugins/stack_alerts/server/index.ts @@ -4,13 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from 'src/core/server'; +import { PluginConfigDescriptor, PluginInitializerContext } from 'src/core/server'; import { AlertingBuiltinsPlugin } from './plugin'; -import { configSchema } from './config'; +import { configSchema, Config } from '../common/config'; export { ID as INDEX_THRESHOLD_ID } from './alert_types/index_threshold/alert_type'; -export const plugin = (ctx: PluginInitializerContext) => new AlertingBuiltinsPlugin(ctx); - -export const config = { +export const config: PluginConfigDescriptor = { + exposeToBrowser: { + enableGeoTrackingThresholdAlert: true, + }, schema: configSchema, + deprecations: ({ renameFromRoot }) => [ + renameFromRoot( + 'xpack.triggers_actions_ui.enableGeoTrackingThresholdAlert', + 'xpack.stack_alerts.enableGeoTrackingThresholdAlert' + ), + ], }; + +export const plugin = (ctx: PluginInitializerContext) => new AlertingBuiltinsPlugin(ctx); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 7444745d41c1f..c35a848f87f26 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -19571,18 +19571,18 @@ "xpack.stackAlerts.indexThreshold.alertTypeContextMessageDescription": "アラート{name}グループ{group}値{value}が{date}に{window}にわたってしきい値{function}を超えました", "xpack.stackAlerts.indexThreshold.alertTypeContextSubjectTitle": "アラート{name}グループ{group}がしきい値を超えました", "xpack.stackAlerts.indexThreshold.alertTypeTitle": "インデックスしきい値", - "xpack.stackAlerts.indexThreshold.dateStartGTdateEndErrorMessage": "[dateStart]が[dateEnd]よりも大です", - "xpack.stackAlerts.indexThreshold.formattedFieldErrorMessage": "{fieldName}の無効な{formatName}形式:「{fieldValue}」", - "xpack.stackAlerts.indexThreshold.intervalRequiredErrorMessage": "[interval]: [dateStart]が[dateEnd]と等しくない場合に指定する必要があります", + "xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage": "[dateStart]が[dateEnd]よりも大です", + "xpack.triggersActionsUI.data.coreQueryParams.formattedFieldErrorMessage": "{fieldName}の無効な{formatName}形式:「{fieldValue}」", + "xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval]: [dateStart]が[dateEnd]と等しくない場合に指定する必要があります", "xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage": "無効な aggType:「{aggType}」", "xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage": "無効なthresholdComparatorが指定されました: {comparator}", "xpack.stackAlerts.indexThreshold.invalidDateErrorMessage": "無効な日付{date}", - "xpack.stackAlerts.indexThreshold.invalidDurationErrorMessage": "無効な期間:「{duration}」", + "xpack.triggersActionsUI.data.coreQueryParams.invalidDurationErrorMessage": "無効な期間:「{duration}」", "xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage": "無効なgroupBy:「{groupBy}」", "xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize]: {maxGroups}以下でなければなりません。", "xpack.stackAlerts.indexThreshold.invalidThreshold2ErrorMessage": "[threshold]: 「{thresholdComparator}」比較子の場合には2つの要素が必要です", "xpack.triggersActionsUI.data.coreQueryParams.invalidTimeWindowUnitsErrorMessage": "無効なtimeWindowUnit:「{timeWindowUnit}」", - "xpack.stackAlerts.indexThreshold.maxIntervalsErrorMessage": "間隔{intervals}の計算値が{maxIntervals}よりも大です", + "xpack.triggersActionsUI.data.coreQueryParams.maxIntervalsErrorMessage": "間隔{intervals}の計算値が{maxIntervals}よりも大です", "xpack.triggersActionsUI.data.coreQueryParams.termFieldRequiredErrorMessage": "[termField]: [groupBy]がトップのときにはtermFieldが必要です", "xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage": "[termSize]: [groupBy]がトップのときにはtermSizeが必要です", "xpack.transform.actionDeleteTransform.bulkDeleteDestinationIndexTitle": "ディスティネーションインデックスの削除", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5e6d48f9e8de4..e2c81ad91b249 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -19590,18 +19590,18 @@ "xpack.stackAlerts.indexThreshold.alertTypeContextMessageDescription": "告警 {name} 组 {group} 值 {value} 在 {window} 于 {date}超过了阈值 {function}", "xpack.stackAlerts.indexThreshold.alertTypeContextSubjectTitle": "告警 {name} 组 {group} 超过了阈值", "xpack.stackAlerts.indexThreshold.alertTypeTitle": "索引阈值", - "xpack.stackAlerts.indexThreshold.dateStartGTdateEndErrorMessage": "[dateStart]:晚于 [dateEnd]", - "xpack.stackAlerts.indexThreshold.formattedFieldErrorMessage": "{fieldName} 的 {formatName} 格式无效:“{fieldValue}”", - "xpack.stackAlerts.indexThreshold.intervalRequiredErrorMessage": "[interval]:如果 [dateStart] 不等于 [dateEnd],则必须指定", + "xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage": "[dateStart]:晚于 [dateEnd]", + "xpack.triggersActionsUI.data.coreQueryParams.formattedFieldErrorMessage": "{fieldName} 的 {formatName} 格式无效:“{fieldValue}”", + "xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval]:如果 [dateStart] 不等于 [dateEnd],则必须指定", "xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage": "aggType 无效:“{aggType}”", "xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage": "指定的 thresholdComparator 无效:{comparator}", "xpack.stackAlerts.indexThreshold.invalidDateErrorMessage": "日期 {date} 无效", - "xpack.stackAlerts.indexThreshold.invalidDurationErrorMessage": "持续时间无效:“{duration}”", + "xpack.triggersActionsUI.data.coreQueryParams.invalidDurationErrorMessage": "持续时间无效:“{duration}”", "xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage": "groupBy 无效:“{groupBy}”", "xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize]:必须小于或等于 {maxGroups}", "xpack.stackAlerts.indexThreshold.invalidThreshold2ErrorMessage": "[threshold]:对于“{thresholdComparator}”比较运算符,必须包含两个元素", "xpack.triggersActionsUI.data.coreQueryParams.invalidTimeWindowUnitsErrorMessage": "timeWindowUnit 无效:“{timeWindowUnit}”", - "xpack.stackAlerts.indexThreshold.maxIntervalsErrorMessage": "时间间隔 {intervals} 的计算数目大于最大值 {maxIntervals}", + "xpack.triggersActionsUI.data.coreQueryParams.maxIntervalsErrorMessage": "时间间隔 {intervals} 的计算数目大于最大值 {maxIntervals}", "xpack.triggersActionsUI.data.coreQueryParams.termFieldRequiredErrorMessage": "[termField]:[groupBy] 为 top 时,termField 为必需", "xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage": "[termSize]:[groupBy] 为 top 时,termSize 为必需", "xpack.transform.actionDeleteTransform.bulkDeleteDestinationIndexTitle": "删除目标索引", diff --git a/x-pack/plugins/triggers_actions_ui/kibana.json b/x-pack/plugins/triggers_actions_ui/kibana.json index 2b279a1c1bc45..9d79ab9232bf3 100644 --- a/x-pack/plugins/triggers_actions_ui/kibana.json +++ b/x-pack/plugins/triggers_actions_ui/kibana.json @@ -4,7 +4,7 @@ "server": true, "ui": true, "optionalPlugins": ["alerts", "features", "home"], - "requiredPlugins": ["management", "charts", "data", "kibanaReact"], + "requiredPlugins": ["management", "charts", "data"], "configPath": ["xpack", "trigger_actions_ui"], "extraPublicDirs": ["public/common", "public/common/constants"], "requiredBundles": ["home", "alerts", "esUiShared"] diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index.ts index 32b323334654e..86c33a373753f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/index.ts @@ -5,6 +5,10 @@ */ export * from './expression_items'; +export * from './constants'; +export * from './index_controls'; +export * from './lib'; +export * from './types'; export { connectorConfiguration as ServiceNowConnectorConfiguration } from '../application/components/builtin_action_types/servicenow/config'; export { connectorConfiguration as JiraConnectorConfiguration } from '../application/components/builtin_action_types/jira/config'; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts index da332aa326ccf..89dea734f1b62 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { loadIndexPatterns, getMatchingIndicesForThresholdAlertType, - getThresholdAlertTypeFields, + getESIndexFields, getSavedObjectsClient, } from '../lib/index_threshold_api'; @@ -85,7 +85,7 @@ export const getIndexOptions = async ( }; export const getFields = async (http: HttpSetup, indexes: string[]) => { - return await getThresholdAlertTypeFields({ indexes, http }); + return await getESIndexFields({ indexes, http }); }; export const firstFieldOption = { diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/index.ts new file mode 100644 index 0000000000000..7671e239f8fff --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export { getTimeFieldOptions, getTimeOptions } from './get_time_options'; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts index 264f046bf88fd..a105f72b31c3b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import { HttpSetup } from 'kibana/public'; -import { TimeSeriesResult } from '../../../common'; const INDEX_THRESHOLD_DATA_API_ROOT = '/api/triggers_actions_ui/data'; @@ -27,13 +26,21 @@ export async function getMatchingIndicesForThresholdAlertType({ return indices; } -export async function getThresholdAlertTypeFields({ +export async function getESIndexFields({ indexes, http, }: { indexes: string[]; http: HttpSetup; -}): Promise> { +}): Promise< + Array<{ + name: string; + type: string; + normalizedType: string; + searchable: boolean; + aggregatable: boolean; + }> +> { const { fields } = await http.post(`${INDEX_THRESHOLD_DATA_API_ROOT}/_fields`, { body: JSON.stringify({ indexPatterns: indexes }), }); @@ -58,34 +65,3 @@ export const loadIndexPatterns = async () => { }); return savedObjects; }; - -interface GetThresholdAlertVisualizationDataParams { - model: any; - visualizeOptions: any; - http: HttpSetup; -} - -export async function getThresholdAlertVisualizationData({ - model, - visualizeOptions, - http, -}: GetThresholdAlertVisualizationDataParams): Promise { - const timeSeriesQueryParams = { - index: model.index, - timeField: model.timeField, - aggType: model.aggType, - aggField: model.aggField, - groupBy: model.groupBy, - termField: model.termField, - termSize: model.termSize, - timeWindowSize: model.timeWindowSize, - timeWindowUnit: model.timeWindowUnit, - dateStart: new Date(visualizeOptions.rangeFrom).toISOString(), - dateEnd: new Date(visualizeOptions.rangeTo).toISOString(), - interval: visualizeOptions.interval, - }; - - return await http.post(`${INDEX_THRESHOLD_DATA_API_ROOT}/_time_series_query`, { - body: JSON.stringify(timeSeriesQueryParams), - }); -} diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index a28b10683c28f..3794112e1d502 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from 'src/core/public'; import { Plugin } from './plugin'; export { AlertsContextProvider, AlertsContextValue } from './application/context/alerts_context'; @@ -22,15 +21,17 @@ export { ValidationResult, ActionVariable, ActionConnector, + IErrorObject, } from './types'; export { ConnectorAddFlyout, ConnectorEditFlyout, } from './application/sections/action_connector_form'; export { loadActionTypes } from './application/lib/action_connector_api'; +export * from './common'; -export function plugin(ctx: PluginInitializerContext) { - return new Plugin(ctx); +export function plugin() { + return new Plugin(); } export { Plugin }; diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index b22be6ef9b2f6..95943fcbf8469 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -4,17 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - CoreSetup, - CoreStart, - Plugin as CorePlugin, - PluginInitializerContext, -} from 'src/core/public'; +import { CoreSetup, CoreStart, Plugin as CorePlugin } from 'src/core/public'; import { i18n } from '@kbn/i18n'; import { FeaturesPluginStart } from '../../features/public'; import { registerBuiltInActionTypes } from './application/components/builtin_action_types'; -import { registerBuiltInAlertTypes } from './application/components/builtin_alert_types'; import { ActionTypeModel, AlertTypeModel } from './types'; import { TypeRegistry } from './application/type_registry'; import { @@ -29,10 +23,6 @@ import { ChartsPluginStart } from '../../../../src/plugins/charts/public'; import { PluginStartContract as AlertingStart } from '../../alerts/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; -export interface TriggersActionsUiConfigType { - enableGeoTrackingThresholdAlert: boolean; -} - export interface TriggersAndActionsUIPublicPluginSetup { actionTypeRegistry: TypeRegistry; alertTypeRegistry: TypeRegistry; @@ -66,14 +56,10 @@ export class Plugin > { private actionTypeRegistry: TypeRegistry; private alertTypeRegistry: TypeRegistry; - private initializerContext: PluginInitializerContext; - constructor(initializerContext: PluginInitializerContext) { + constructor() { this.actionTypeRegistry = new TypeRegistry(); - this.alertTypeRegistry = new TypeRegistry(); - - this.initializerContext = initializerContext; } public setup(core: CoreSetup, plugins: PluginsSetup): TriggersAndActionsUIPublicPluginSetup { @@ -143,11 +129,6 @@ export class Plugin actionTypeRegistry: this.actionTypeRegistry, }); - registerBuiltInAlertTypes({ - alertTypeRegistry: this.alertTypeRegistry, - triggerActionsUiConfig: this.initializerContext.config.get(), - }); - return { actionTypeRegistry: this.actionTypeRegistry, alertTypeRegistry: this.alertTypeRegistry, diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.ts index 63ae421b1467e..da125ba7ea29d 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/date_range_info.ts @@ -100,7 +100,7 @@ function getDuration(durationS: string, field: string): number { } function getParseErrorMessage(formatName: string, fieldName: string, fieldValue: string) { - return i18n.translate('xpack.stackAlerts.indexThreshold.formattedFieldErrorMessage', { + return i18n.translate('xpack.triggersActionsUI.data.coreQueryParams.formattedFieldErrorMessage', { defaultMessage: 'invalid {formatName} format for {fieldName}: "{fieldValue}"', values: { formatName, @@ -111,7 +111,7 @@ function getParseErrorMessage(formatName: string, fieldName: string, fieldValue: } export function getTooManyIntervalsErrorMessage(intervals: number, maxIntervals: number) { - return i18n.translate('xpack.stackAlerts.indexThreshold.maxIntervalsErrorMessage', { + return i18n.translate('xpack.triggersActionsUI.data.coreQueryParams.maxIntervalsErrorMessage', { defaultMessage: 'calculated number of intervals {intervals} is greater than maximum {maxIntervals}', values: { @@ -122,7 +122,10 @@ export function getTooManyIntervalsErrorMessage(intervals: number, maxIntervals: } export function getDateStartAfterDateEndErrorMessage(): string { - return i18n.translate('xpack.stackAlerts.indexThreshold.dateStartGTdateEndErrorMessage', { - defaultMessage: '[dateStart]: is greater than [dateEnd]', - }); + return i18n.translate( + 'xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage', + { + defaultMessage: '[dateStart]: is greater than [dateEnd]', + } + ); } diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts index 1e681472016d0..10a003603eb78 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts @@ -62,9 +62,12 @@ function validateBody(anyParams: unknown): string | undefined { } if (epochStart !== epochEnd && !interval) { - return i18n.translate('xpack.stackAlerts.indexThreshold.intervalRequiredErrorMessage', { - defaultMessage: '[interval]: must be specified if [dateStart] does not equal [dateEnd]', - }); + return i18n.translate( + 'xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage', + { + defaultMessage: '[interval]: must be specified if [dateStart] does not equal [dateEnd]', + } + ); } if (interval) { @@ -93,11 +96,14 @@ export function validateDuration(duration: string): string | undefined { try { parseDuration(duration); } catch (err) { - return i18n.translate('xpack.stackAlerts.indexThreshold.invalidDurationErrorMessage', { - defaultMessage: 'invalid duration: "{duration}"', - values: { - duration, - }, - }); + return i18n.translate( + 'xpack.triggersActionsUI.data.coreQueryParams.invalidDurationErrorMessage', + { + defaultMessage: 'invalid duration: "{duration}"', + values: { + duration, + }, + } + ); } } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/fields_endpoint.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/fields_endpoint.ts index 76ff41aac5397..881be83236be5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/fields_endpoint.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/fields_endpoint.ts @@ -10,7 +10,7 @@ import { Spaces } from '../../../../scenarios'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ESTestIndexTool, ES_TEST_INDEX_NAME, getUrlPrefix } from '../../../../../common/lib'; -const API_URI = 'api/stack_alerts/index_threshold/_fields'; +const API_URI = 'api/triggers_actions_ui/data/_fields'; // eslint-disable-next-line import/no-default-export export default function fieldsEndpointTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/indices_endpoint.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/indices_endpoint.ts index ba2b71e7134b6..7d89e2701d628 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/indices_endpoint.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/indices_endpoint.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; import { ESTestIndexTool, ES_TEST_INDEX_NAME, getUrlPrefix } from '../../../../../common/lib'; import { createEsDocuments } from './create_test_data'; -const API_URI = 'api/stack_alerts/index_threshold/_indices'; +const API_URI = 'api/triggers_actions_ui/data/_indices'; // eslint-disable-next-line import/no-default-export export default function indicesEndpointTests({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts index e9d20fa7791df..334f898232bbc 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/time_series_query_endpoint.ts @@ -13,7 +13,7 @@ import { TimeSeriesQuery } from '../../../../../../../plugins/triggers_actions_u import { createEsDocuments } from './create_test_data'; -const INDEX_THRESHOLD_TIME_SERIES_QUERY_URL = 'api/stack_alerts/index_threshold/_time_series_query'; +const INDEX_THRESHOLD_TIME_SERIES_QUERY_URL = 'api/triggers_actions_ui/data/_time_series_query'; const START_DATE_MM_DD_HH_MM_SS_MS = '01-01T00:00:00.000Z'; const START_DATE = `2020-${START_DATE_MM_DD_HH_MM_SS_MS}`; From c7ae42d11b662acc50942b60a29c8d9e1c6f90db Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 10:02:19 +0000 Subject: [PATCH 04/30] removed unused imports --- .../query_builder/util_components/geo_index_pattern_select.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx index 0e5817b38a442..0ad0f56a5cdd1 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, ReactNode } from 'react'; +import React, { Component } from 'react'; import { EuiCallOut, EuiFormRow, EuiLink, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; From 56b286df70a1f3a43c3445a65cd2a70e6999ad48 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 10:11:27 +0000 Subject: [PATCH 05/30] fixed i18n in geo --- .../public/alert_types/geo_threshold/index.ts | 4 ++-- .../expressions/boundary_index_expression.tsx | 10 ++++---- .../expressions/entity_by_expression.tsx | 4 ++-- .../expressions/entity_index_expression.tsx | 17 ++++++-------- .../geo_threshold/query_builder/index.tsx | 23 ++++++++----------- .../geo_index_pattern_select.tsx | 23 ++++++++----------- .../alert_types/geo_threshold/validation.ts | 16 ++++++------- 7 files changed, 44 insertions(+), 53 deletions(-) diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts index e36440f371997..35f5648de40f3 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/index.ts @@ -12,10 +12,10 @@ import { AlertTypeModel, AlertsContextValue } from '../../../../triggers_actions export function getAlertType(): AlertTypeModel { return { id: '.geo-threshold', - name: i18n.translate('xpack.triggersActionsUI.geoThreshold.name.trackingThreshold', { + name: i18n.translate('xpack.stackAlerts.geoThreshold.name.trackingThreshold', { defaultMessage: 'Tracking threshold', }), - description: i18n.translate('xpack.triggersActionsUI.geoThreshold.descriptionText', { + description: i18n.translate('xpack.stackAlerts.geoThreshold.descriptionText', { defaultMessage: 'Alert when an entity enters or leaves a geo boundary.', }), iconClass: 'globe', diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx index 4990971bfe2b2..55dfc82bdbdb8 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/boundary_index_expression.tsx @@ -116,12 +116,12 @@ export const BoundaryIndexExpression: FunctionComponent = ({ = ({ = ({ defaultValue={'Select an index pattern and geo shape field'} value={boundaryIndexPattern.title} popoverContent={indexPopover} - expressionDescription={i18n.translate('xpack.triggersActionsUI.geoThreshold.indexLabel', { + expressionDescription={i18n.translate('xpack.stackAlerts.geoThreshold.indexLabel', { defaultMessage: 'index', })} /> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx index 230cbac2131f2..f519ad882802c 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_by_expression.tsx @@ -59,7 +59,7 @@ export const EntityByExpression: FunctionComponent = ({ = ({ value={entity} defaultValue={'Select entity field'} popoverContent={indexPopover} - expressionDescription={i18n.translate('xpack.triggersActionsUI.geoThreshold.entityByLabel', { + expressionDescription={i18n.translate('xpack.stackAlerts.geoThreshold.entityByLabel', { defaultMessage: 'by', })} /> diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx index fc20ee7888f57..e5e43210d1e6b 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx @@ -104,13 +104,13 @@ export const EntityIndexExpression: FunctionComponent = ({ fullWidth label={ } > = ({ = ({ value={indexPattern.title} defaultValue={'Select an index pattern and geo shape/point field'} popoverContent={indexPopover} - expressionDescription={i18n.translate( - 'xpack.triggersActionsUI.geoThreshold.entityIndexLabel', - { - defaultMessage: 'index', - } - )} + expressionDescription={i18n.translate('xpack.stackAlerts.geoThreshold.entityIndexLabel', { + defaultMessage: 'index', + })} /> ); }; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx index 410cea28f52e8..f138c08c0f993 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/index.tsx @@ -54,13 +54,13 @@ const conditionOptions = Object.keys(TrackingEvent).map((key) => ({ const labelForDelayOffset = ( <> {' '} @@ -127,7 +127,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent
@@ -223,7 +223,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent
@@ -253,7 +253,7 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent
@@ -282,19 +282,16 @@ export const GeoThresholdAlertTypeExpression: React.FunctionComponent
} - expressionDescription={i18n.translate( - 'xpack.triggersActionsUI.geoThreshold.whenEntityLabel', - { - defaultMessage: 'when entity', - } - )} + expressionDescription={i18n.translate('xpack.stackAlerts.geoThreshold.whenEntityLabel', { + defaultMessage: 'when entity', + })} />
diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx index 0ad0f56a5cdd1..a552d6d998c7e 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/geo_index_pattern_select.tsx @@ -71,42 +71,39 @@ export class GeoIndexPatternSelect extends Component { return ( <>

@@ -124,7 +121,7 @@ export class GeoIndexPatternSelect extends Component { {this._renderNoIndexPatternWarning()} @@ -134,7 +131,7 @@ export class GeoIndexPatternSelect extends Component { indexPatternId={this.props.value} onChange={this._onIndexPatternSelect} placeholder={i18n.translate( - 'xpack.triggersActionsUI.geoThreshold.indexPatternSelectPlaceholder', + 'xpack.stackAlerts.geoThreshold.indexPatternSelectPlaceholder', { defaultMessage: 'Select index pattern', } diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.ts index 8931b759bbc6e..7a511f681ecaa 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/validation.ts @@ -35,7 +35,7 @@ export const validateExpression = (alertParams: GeoThresholdAlertParams): Valida if (!index) { errors.index.push( - i18n.translate('xpack.triggersActionsUI.geoThreshold.error.requiredIndexTitleText', { + i18n.translate('xpack.stackAlerts.geoThreshold.error.requiredIndexTitleText', { defaultMessage: 'Index pattern is required.', }) ); @@ -43,7 +43,7 @@ export const validateExpression = (alertParams: GeoThresholdAlertParams): Valida if (!geoField) { errors.geoField.push( - i18n.translate('xpack.triggersActionsUI.geoThreshold.error.requiredGeoFieldText', { + i18n.translate('xpack.stackAlerts.geoThreshold.error.requiredGeoFieldText', { defaultMessage: 'Geo field is required.', }) ); @@ -51,7 +51,7 @@ export const validateExpression = (alertParams: GeoThresholdAlertParams): Valida if (!entity) { errors.entity.push( - i18n.translate('xpack.triggersActionsUI.geoThreshold.error.requiredEntityText', { + i18n.translate('xpack.stackAlerts.geoThreshold.error.requiredEntityText', { defaultMessage: 'Entity is required.', }) ); @@ -59,7 +59,7 @@ export const validateExpression = (alertParams: GeoThresholdAlertParams): Valida if (!dateField) { errors.dateField.push( - i18n.translate('xpack.triggersActionsUI.geoThreshold.error.requiredDateFieldText', { + i18n.translate('xpack.stackAlerts.geoThreshold.error.requiredDateFieldText', { defaultMessage: 'Date field is required.', }) ); @@ -67,7 +67,7 @@ export const validateExpression = (alertParams: GeoThresholdAlertParams): Valida if (!trackingEvent) { errors.trackingEvent.push( - i18n.translate('xpack.triggersActionsUI.geoThreshold.error.requiredTrackingEventText', { + i18n.translate('xpack.stackAlerts.geoThreshold.error.requiredTrackingEventText', { defaultMessage: 'Tracking event is required.', }) ); @@ -75,7 +75,7 @@ export const validateExpression = (alertParams: GeoThresholdAlertParams): Valida if (!boundaryType) { errors.boundaryType.push( - i18n.translate('xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryTypeText', { + i18n.translate('xpack.stackAlerts.geoThreshold.error.requiredBoundaryTypeText', { defaultMessage: 'Boundary type is required.', }) ); @@ -83,7 +83,7 @@ export const validateExpression = (alertParams: GeoThresholdAlertParams): Valida if (!boundaryIndexTitle) { errors.boundaryIndexTitle.push( - i18n.translate('xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryIndexTitleText', { + i18n.translate('xpack.stackAlerts.geoThreshold.error.requiredBoundaryIndexTitleText', { defaultMessage: 'Boundary index pattern title is required.', }) ); @@ -91,7 +91,7 @@ export const validateExpression = (alertParams: GeoThresholdAlertParams): Valida if (!boundaryGeoField) { errors.boundaryGeoField.push( - i18n.translate('xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryGeoFieldText', { + i18n.translate('xpack.stackAlerts.geoThreshold.error.requiredBoundaryGeoFieldText', { defaultMessage: 'Boundary geo field is required.', }) ); From f3ca392cd799251551b7f022e54fde5dad2c41c8 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 10:29:48 +0000 Subject: [PATCH 06/30] renamed index apis --- .../plugins/triggers_actions_ui/public/application/boot.tsx | 2 +- .../public/common/index_controls/index.ts | 6 +++--- .../{index_threshold_api.ts => indices_and_field_apis.ts} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename x-pack/plugins/triggers_actions_ui/public/common/lib/{index_threshold_api.ts => indices_and_field_apis.ts} (96%) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx index 80f9ac532d1c9..fc12e01e2a131 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx @@ -9,7 +9,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { SavedObjectsClientContract } from 'src/core/public'; import { App, AppDeps } from './app'; -import { setSavedObjectsClient } from '../common/lib/index_threshold_api'; +import { setSavedObjectsClient } from '../common/lib/indices_and_field_apis'; interface BootDeps extends AppDeps { element: HTMLElement; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts index 89dea734f1b62..87f217cbceca0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts @@ -9,10 +9,10 @@ import { HttpSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { loadIndexPatterns, - getMatchingIndicesForThresholdAlertType, + getMatchingIndices, getESIndexFields, getSavedObjectsClient, -} from '../lib/index_threshold_api'; +} from '../lib/indices_and_field_apis'; export interface IOption { label: string; @@ -39,7 +39,7 @@ export const getIndexOptions = async ( return options; } - const matchingIndices = (await getMatchingIndicesForThresholdAlertType({ + const matchingIndices = (await getMatchingIndices({ pattern, http, })) as string[]; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/indices_and_field_apis.ts similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts rename to x-pack/plugins/triggers_actions_ui/public/common/lib/indices_and_field_apis.ts index a105f72b31c3b..14bf417585ede 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/indices_and_field_apis.ts @@ -7,7 +7,7 @@ import { HttpSetup } from 'kibana/public'; const INDEX_THRESHOLD_DATA_API_ROOT = '/api/triggers_actions_ui/data'; -export async function getMatchingIndicesForThresholdAlertType({ +export async function getMatchingIndices({ pattern, http, }: { From b6df73bac191163ac647e04b1d8f0f472cc5d440 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 10:31:33 +0000 Subject: [PATCH 07/30] renamed index apis to data apis --- .../plugins/triggers_actions_ui/public/application/boot.tsx | 2 +- .../public/common/index_controls/index.ts | 2 +- .../common/lib/{indices_and_field_apis.ts => data_apis.ts} | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename x-pack/plugins/triggers_actions_ui/public/common/lib/{indices_and_field_apis.ts => data_apis.ts} (85%) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx index fc12e01e2a131..bb46fd02a98a9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx @@ -9,7 +9,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { SavedObjectsClientContract } from 'src/core/public'; import { App, AppDeps } from './app'; -import { setSavedObjectsClient } from '../common/lib/indices_and_field_apis'; +import { setSavedObjectsClient } from '../common/lib/data_apis'; interface BootDeps extends AppDeps { element: HTMLElement; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts index 87f217cbceca0..54b69705637d4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts @@ -12,7 +12,7 @@ import { getMatchingIndices, getESIndexFields, getSavedObjectsClient, -} from '../lib/indices_and_field_apis'; +} from '../lib/data_apis'; export interface IOption { label: string; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/lib/indices_and_field_apis.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts similarity index 85% rename from x-pack/plugins/triggers_actions_ui/public/common/lib/indices_and_field_apis.ts rename to x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts index 14bf417585ede..573d306ae5550 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/lib/indices_and_field_apis.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/data_apis.ts @@ -5,7 +5,7 @@ */ import { HttpSetup } from 'kibana/public'; -const INDEX_THRESHOLD_DATA_API_ROOT = '/api/triggers_actions_ui/data'; +const DATA_API_ROOT = '/api/triggers_actions_ui/data'; export async function getMatchingIndices({ pattern, @@ -20,7 +20,7 @@ export async function getMatchingIndices({ if (!pattern.endsWith('*')) { pattern = `${pattern}*`; } - const { indices } = await http.post(`${INDEX_THRESHOLD_DATA_API_ROOT}/_indices`, { + const { indices } = await http.post(`${DATA_API_ROOT}/_indices`, { body: JSON.stringify({ pattern }), }); return indices; @@ -41,7 +41,7 @@ export async function getESIndexFields({ aggregatable: boolean; }> > { - const { fields } = await http.post(`${INDEX_THRESHOLD_DATA_API_ROOT}/_fields`, { + const { fields } = await http.post(`${DATA_API_ROOT}/_fields`, { body: JSON.stringify({ indexPatterns: indexes }), }); return fields; From 72f0816968bfc7681b7d10028ef7914cc21d7254 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 11:25:24 +0000 Subject: [PATCH 08/30] fixed i18n --- .../expression_with_popover.tsx | 2 +- .../alert_types/threshold/expression.tsx | 34 +++++------- .../public/alert_types/threshold/index.ts | 6 +-- .../alert_types/threshold/validation.ts | 18 +++---- .../alert_types/threshold/visualization.tsx | 10 ++-- .../translations/translations/ja-JP.json | 52 +++++++++---------- .../translations/translations/zh-CN.json | 52 +++++++++---------- x-pack/plugins/triggers_actions_ui/README.md | 2 +- .../public/common/index_controls/index.ts | 9 ++-- 9 files changed, 91 insertions(+), 94 deletions(-) diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx index 74dad07f2b229..a83667cfd92c6 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/util_components/expression_with_popover.tsx @@ -61,7 +61,7 @@ export const ExpressionWithPopover: ({ iconType="cross" color="danger" aria-label={i18n.translate( - 'xpack.triggersActionsUI.sections.alertAdd.geoThreshold.closePopoverLabel', + 'xpack.stackAlerts.geoThreshold.ui.expressionPopover.closePopoverLabel', { defaultMessage: 'Close', } diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx index e240ad9cb6f37..92cb8c9055bde 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx @@ -105,7 +105,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent } @@ -166,7 +166,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent } @@ -232,7 +232,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent } @@ -283,7 +283,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent

@@ -295,12 +295,9 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent 0 ? renderIndices(index) : firstFieldOption.text} isActive={indexPopoverOpen} onClick={() => { @@ -320,12 +317,9 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent - {i18n.translate( - 'xpack.triggersActionsUI.sections.alertAdd.threshold.indexButtonLabel', - { - defaultMessage: 'index', - } - )} + {i18n.translate('xpack.stackAlerts.threshold.ui.alertParams.indexButtonLabel', { + defaultMessage: 'index', + })}
@@ -426,7 +420,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts index da36167cd78c0..b7923a3013613 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/index.ts @@ -12,10 +12,10 @@ import { AlertTypeModel, AlertsContextValue } from '../../../../triggers_actions export function getAlertType(): AlertTypeModel { return { id: '.index-threshold', - name: i18n.translate('xpack.triggersActionsUI.indexThresholdAlert.nameText', { + name: i18n.translate('xpack.stackAlerts.threshold.ui.alertType.nameText', { defaultMessage: 'Index threshold', }), - description: i18n.translate('xpack.triggersActionsUI.indexThresholdAlert.descriptionText', { + description: i18n.translate('xpack.stackAlerts.threshold.ui.alertType.descriptionText', { defaultMessage: 'Alert when an aggregated query meets the threshold.', }), iconClass: 'alert', @@ -25,7 +25,7 @@ export function getAlertType(): AlertTypeModel import('./expression')), validate: validateExpression, defaultActionMessage: i18n.translate( - 'xpack.triggersActionsUI.components.builtinAlertTypes.threshold.alertDefaultActionMessage', + 'xpack.stackAlerts.threshold.ui.alertType.defaultActionMessage', { defaultMessage: `alert \\{\\{alertName\\}\\} group \\{\\{context.group\\}\\} value \\{\\{context.value\\}\\} exceeded threshold \\{\\{context.function\\}\\} over \\{\\{params.timeWindowSize\\}\\}\\{\\{params.timeWindowUnit\\}\\} on \\{\\{context.date\\}\\}`, } diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.ts index 888ed54d2aeaa..4bbf80906377b 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/validation.ts @@ -39,21 +39,21 @@ export const validateExpression = (alertParams: IndexThresholdAlertParams): Vali validationResult.errors = errors; if (!index || index.length === 0) { errors.index.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.requiredIndexText', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.requiredIndexText', { defaultMessage: 'Index is required.', }) ); } if (!timeField) { errors.timeField.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.requiredTimeFieldText', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.requiredTimeFieldText', { defaultMessage: 'Time field is required.', }) ); } if (aggType && builtInAggregationTypes[aggType].fieldRequired && !aggField) { errors.aggField.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.requiredAggFieldText', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.requiredAggFieldText', { defaultMessage: 'Aggregation field is required.', }) ); @@ -65,7 +65,7 @@ export const validateExpression = (alertParams: IndexThresholdAlertParams): Vali !termSize ) { errors.termSize.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.requiredTermSizedText', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.requiredTermSizedText', { defaultMessage: 'Term size is required.', }) ); @@ -77,21 +77,21 @@ export const validateExpression = (alertParams: IndexThresholdAlertParams): Vali !termField ) { errors.termField.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.requiredtTermFieldText', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.requiredTermFieldText', { defaultMessage: 'Term field is required.', }) ); } if (!timeWindowSize) { errors.timeWindowSize.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.requiredTimeWindowSizeText', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.requiredTimeWindowSizeText', { defaultMessage: 'Time window size is required.', }) ); } if (!threshold || threshold.length === 0 || threshold[0] === undefined) { errors.threshold0.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.requiredThreshold0Text', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.requiredThreshold0Text', { defaultMessage: 'Threshold0 is required.', }) ); @@ -104,14 +104,14 @@ export const validateExpression = (alertParams: IndexThresholdAlertParams): Vali (threshold && threshold.length < builtInComparators[thresholdComparator!].requiredValues)) ) { errors.threshold1.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.requiredThreshold1Text', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.requiredThreshold1Text', { defaultMessage: 'Threshold1 is required.', }) ); } if (threshold && threshold.length === 2 && threshold[0] > threshold[1]) { errors.threshold1.push( - i18n.translate('xpack.triggersActionsUI.sections.addAlert.error.greaterThenThreshold0Text', { + i18n.translate('xpack.stackAlerts.threshold.ui.validation.error.greaterThenThreshold0Text', { defaultMessage: 'Threshold1 should be > Threshold0.', }) ); diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx index 4bdd7409cc33b..6145aa3671a7f 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/visualization.tsx @@ -156,7 +156,7 @@ export const ThresholdVisualization: React.FunctionComponent = ({ if (toastNotifications) { toastNotifications.addDanger({ title: i18n.translate( - 'xpack.triggersActionsUI.sections.alertAdd.unableToLoadVisualizationMessage', + 'xpack.stackAlerts.threshold.ui.visualization.unableToLoadVisualizationMessage', { defaultMessage: 'Unable to load visualization' } ), }); @@ -205,7 +205,7 @@ export const ThresholdVisualization: React.FunctionComponent = ({ body={ @@ -221,7 +221,7 @@ export const ThresholdVisualization: React.FunctionComponent = ({ @@ -311,14 +311,14 @@ export const ThresholdVisualization: React.FunctionComponent = ({ size="s" title={ } color="warning" > diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9613d8be8d4ed..06459bff92bc0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20183,15 +20183,15 @@ "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderValueText": "値が必要です。", "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText": "メソッドが必要です", "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredPasswordText": "パスワードが必要です。", - "xpack.triggersActionsUI.sections.addAlert.error.greaterThenThreshold0Text": "しきい値 1 はしきい値 0 よりも大きい値にしてください。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredAggFieldText": "集約フィールドが必要です。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredIndexText": "インデックスが必要です。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredTermSizedText": "用語サイズが必要です。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredThreshold0Text": "しきい値 0 が必要です。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredThreshold1Text": "しきい値 1 が必要です。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredTimeFieldText": "時間フィールドが必要です。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredTimeWindowSizeText": "時間ウィンドウサイズが必要です。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredtTermFieldText": "用語フィールドが必要です。", + "xpack.stackAlerts.threshold.ui.validation.error.greaterThenThreshold0Text": "しきい値 1 はしきい値 0 よりも大きい値にしてください。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredAggFieldText": "集約フィールドが必要です。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredIndexText": "インデックスが必要です。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredTermSizedText": "用語サイズが必要です。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredThreshold0Text": "しきい値 0 が必要です。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredThreshold1Text": "しきい値 1 が必要です。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredTimeFieldText": "時間フィールドが必要です。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredTimeWindowSizeText": "時間ウィンドウサイズが必要です。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredTermFieldText": "用語フィールドが必要です。", "xpack.triggersActionsUI.sections.addConnectorForm.flyoutTitle": "{actionTypeName} コネクタ", "xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "コネクターを選択", "xpack.triggersActionsUI.sections.addConnectorForm.updateErrorNotificationText": "コネクターを作成できません。", @@ -20200,27 +20200,27 @@ "xpack.triggersActionsUI.sections.addModalConnectorForm.flyoutTitle": "{actionTypeName} コネクター", "xpack.triggersActionsUI.sections.addModalConnectorForm.saveButtonLabel": "保存", "xpack.triggersActionsUI.sections.addModalConnectorForm.updateSuccessNotificationText": "「{connectorName}」を作成しました", - "xpack.triggersActionsUI.sections.alertAdd.conditionPrompt": "条件を定義してください", - "xpack.triggersActionsUI.sections.alertAdd.errorLoadingAlertVisualizationTitle": "アラートビジュアライゼーションを読み込めません", + "xpack.stackAlerts.threshold.ui.conditionPrompt": "条件を定義してください", + "xpack.stackAlerts.threshold.ui.visualization.errorLoadingAlertVisualizationTitle": "アラートビジュアライゼーションを読み込めません", "xpack.triggersActionsUI.sections.alertAdd.flyoutTitle": "アラートの作成", - "xpack.triggersActionsUI.sections.alertAdd.geoThreshold.closePopoverLabel": "閉じる", - "xpack.triggersActionsUI.sections.alertAdd.loadingAlertVisualizationDescription": "アラートビジュアライゼーションを読み込み中...", + "xpack.stackAlerts.geoThreshold.ui.expressionPopover.closePopoverLabel": "閉じる", + "xpack.stackAlerts.threshold.ui.visualization.loadingAlertVisualizationDescription": "アラートビジュアライゼーションを読み込み中...", "xpack.triggersActionsUI.sections.alertAdd.operationName": "作成", - "xpack.triggersActionsUI.sections.alertAdd.previewAlertVisualizationDescription": "プレビューを生成するための式を完成します。", + "xpack.stackAlerts.threshold.ui.previewAlertVisualizationDescription": "プレビューを生成するための式を完成します。", "xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "アラートを作成できません。", "xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "「{alertName}」 を保存しました", - "xpack.triggersActionsUI.sections.alertAdd.selectIndex": "インデックスを選択してください", - "xpack.triggersActionsUI.sections.alertAdd.threshold.closeIndexPopoverLabel": "閉じる", - "xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。", - "xpack.triggersActionsUI.sections.alertAdd.threshold.howToBroadenSearchQueryDescription": "* で検索クエリの範囲を広げます。", - "xpack.triggersActionsUI.sections.alertAdd.threshold.indexButtonLabel": "インデックス", - "xpack.triggersActionsUI.sections.alertAdd.threshold.indexLabel": "インデックス", - "xpack.triggersActionsUI.sections.alertAdd.threshold.indicesToQueryLabel": "クエリを実行するインデックス", - "xpack.triggersActionsUI.sections.alertAdd.threshold.timeFieldLabel": "時間フィールド", - "xpack.triggersActionsUI.sections.alertAdd.threshold.timeFieldOptionLabel": "フィールドを選択", - "xpack.triggersActionsUI.sections.alertAdd.thresholdPreviewChart.dataDoesNotExistTextMessage": "時間範囲とフィルターが正しいことを確認してください。", - "xpack.triggersActionsUI.sections.alertAdd.thresholdPreviewChart.noDataTitle": "このクエリに一致するデータはありません", - "xpack.triggersActionsUI.sections.alertAdd.unableToLoadVisualizationMessage": "ビジュアライゼーションを読み込めません", + "xpack.stackAlerts.threshold.ui.selectIndex": "インデックスを選択してください", + "xpack.stackAlerts.threshold.ui.alertParams.closeIndexPopoverLabel": "閉じる", + "xpack.stackAlerts.threshold.ui.alertParams.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。", + "xpack.stackAlerts.threshold.ui.alertParams.howToBroadenSearchQueryDescription": "* で検索クエリの範囲を広げます。", + "xpack.stackAlerts.threshold.ui.alertParams.indexButtonLabel": "インデックス", + "xpack.stackAlerts.threshold.ui.alertParams.indexLabel": "インデックス", + "xpack.stackAlerts.threshold.ui.alertParams.indicesToQueryLabel": "クエリを実行するインデックス", + "xpack.stackAlerts.threshold.ui.alertParams.timeFieldLabel": "時間フィールド", + "xpack.triggersActionsUI.sections.alertAdd.indexControls.timeFieldOptionLabel": "フィールドを選択", + "xpack.stackAlerts.threshold.ui.visualization.thresholdPreviewChart.dataDoesNotExistTextMessage": "時間範囲とフィルターが正しいことを確認してください。", + "xpack.stackAlerts.threshold.ui.visualization.thresholdPreviewChart.noDataTitle": "このクエリに一致するデータはありません", + "xpack.stackAlerts.threshold.ui.visualization.unableToLoadVisualizationMessage": "ビジュアライゼーションを読み込めません", "xpack.triggersActionsUI.sections.alertDetails.alertInstances.disabledAlert": "このアラートは無効になっていて再表示できません。[↑ を無効にする]を切り替えてアクティブにします。", "xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.columns.duration": "期間", "xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.columns.instance": "インスタンス", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 02635dababea9..33a17f291d290 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20203,15 +20203,15 @@ "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderValueText": "“值”必填。", "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText": "“方法”必填", "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredPasswordText": "“密码”必填。", - "xpack.triggersActionsUI.sections.addAlert.error.greaterThenThreshold0Text": "阈值 1 应 > 阈值 0。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredAggFieldText": "聚合字段必填。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredIndexText": "“索引”必填。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredTermSizedText": "“词大小”必填。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredThreshold0Text": "阈值 0 必填。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredThreshold1Text": "阈值 1 必填。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredTimeFieldText": "时间字段必填。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredTimeWindowSizeText": "“时间窗大小”必填。", - "xpack.triggersActionsUI.sections.addAlert.error.requiredtTermFieldText": "词字段必填。", + "xpack.stackAlerts.threshold.ui.validation.error.greaterThenThreshold0Text": "阈值 1 应 > 阈值 0。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredAggFieldText": "聚合字段必填。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredIndexText": "“索引”必填。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredTermSizedText": "“词大小”必填。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredThreshold0Text": "阈值 0 必填。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredThreshold1Text": "阈值 1 必填。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredTimeFieldText": "时间字段必填。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredTimeWindowSizeText": "“时间窗大小”必填。", + "xpack.stackAlerts.threshold.ui.validation.error.requiredTermFieldText": "词字段必填。", "xpack.triggersActionsUI.sections.addConnectorForm.flyoutTitle": "{actionTypeName} 连接器", "xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "选择连接器", "xpack.triggersActionsUI.sections.addConnectorForm.updateErrorNotificationText": "无法创建连接器。", @@ -20220,27 +20220,27 @@ "xpack.triggersActionsUI.sections.addModalConnectorForm.flyoutTitle": "{actionTypeName} 连接器", "xpack.triggersActionsUI.sections.addModalConnectorForm.saveButtonLabel": "保存", "xpack.triggersActionsUI.sections.addModalConnectorForm.updateSuccessNotificationText": "已创建“{connectorName}”", - "xpack.triggersActionsUI.sections.alertAdd.conditionPrompt": "定义条件", - "xpack.triggersActionsUI.sections.alertAdd.errorLoadingAlertVisualizationTitle": "无法加载告警可视化", + "xpack.stackAlerts.threshold.ui.conditionPrompt": "定义条件", + "xpack.stackAlerts.threshold.ui.visualization.errorLoadingAlertVisualizationTitle": "无法加载告警可视化", "xpack.triggersActionsUI.sections.alertAdd.flyoutTitle": "创建告警", - "xpack.triggersActionsUI.sections.alertAdd.geoThreshold.closePopoverLabel": "关闭", - "xpack.triggersActionsUI.sections.alertAdd.loadingAlertVisualizationDescription": "正在加载告警可视化……", + "xpack.stackAlerts.geoThreshold.ui.expressionPopover.closePopoverLabel": "关闭", + "xpack.stackAlerts.threshold.ui.visualization.loadingAlertVisualizationDescription": "正在加载告警可视化……", "xpack.triggersActionsUI.sections.alertAdd.operationName": "创建", - "xpack.triggersActionsUI.sections.alertAdd.previewAlertVisualizationDescription": "完成表达式以生成预览。", + "xpack.stackAlerts.threshold.ui.previewAlertVisualizationDescription": "完成表达式以生成预览。", "xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "无法创建告警。", "xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "已保存“{alertName}”", - "xpack.triggersActionsUI.sections.alertAdd.selectIndex": "选择索引", - "xpack.triggersActionsUI.sections.alertAdd.threshold.closeIndexPopoverLabel": "关闭", - "xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage": "表达式包含错误。", - "xpack.triggersActionsUI.sections.alertAdd.threshold.howToBroadenSearchQueryDescription": "使用 * 可扩大您的查询范围。", - "xpack.triggersActionsUI.sections.alertAdd.threshold.indexButtonLabel": "索引", - "xpack.triggersActionsUI.sections.alertAdd.threshold.indexLabel": "索引", - "xpack.triggersActionsUI.sections.alertAdd.threshold.indicesToQueryLabel": "要查询的索引", - "xpack.triggersActionsUI.sections.alertAdd.threshold.timeFieldLabel": "时间字段", - "xpack.triggersActionsUI.sections.alertAdd.threshold.timeFieldOptionLabel": "选择字段", - "xpack.triggersActionsUI.sections.alertAdd.thresholdPreviewChart.dataDoesNotExistTextMessage": "确认您的时间范围和筛选正确。", - "xpack.triggersActionsUI.sections.alertAdd.thresholdPreviewChart.noDataTitle": "没有数据匹配此查询", - "xpack.triggersActionsUI.sections.alertAdd.unableToLoadVisualizationMessage": "无法加载可视化", + "xpack.stackAlerts.threshold.ui.selectIndex": "选择索引", + "xpack.stackAlerts.threshold.ui.alertParams.closeIndexPopoverLabel": "关闭", + "xpack.stackAlerts.threshold.ui.alertParams.fixErrorInExpressionBelowValidationMessage": "表达式包含错误。", + "xpack.stackAlerts.threshold.ui.alertParams.howToBroadenSearchQueryDescription": "使用 * 可扩大您的查询范围。", + "xpack.stackAlerts.threshold.ui.alertParams.indexButtonLabel": "索引", + "xpack.stackAlerts.threshold.ui.alertParams.indexLabel": "索引", + "xpack.stackAlerts.threshold.ui.alertParams.indicesToQueryLabel": "要查询的索引", + "xpack.stackAlerts.threshold.ui.alertParams.timeFieldLabel": "时间字段", + "xpack.triggersActionsUI.sections.alertAdd.indexControls.timeFieldOptionLabel": "选择字段", + "xpack.stackAlerts.threshold.ui.visualization.thresholdPreviewChart.dataDoesNotExistTextMessage": "确认您的时间范围和筛选正确。", + "xpack.stackAlerts.threshold.ui.visualization.thresholdPreviewChart.noDataTitle": "没有数据匹配此查询", + "xpack.stackAlerts.threshold.ui.visualization.unableToLoadVisualizationMessage": "无法加载可视化", "xpack.triggersActionsUI.sections.alertDetails.alertInstances.disabledAlert": "此告警已禁用,无法显示。切换禁用 ↑ 以激活。", "xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.columns.duration": "持续时间", "xpack.triggersActionsUI.sections.alertDetails.alertInstancesList.columns.instance": "实例", diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index 32e157255c0cc..ef81065608ad4 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -220,7 +220,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent .... diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts index 54b69705637d4..8d10e531930cc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts @@ -89,8 +89,11 @@ export const getFields = async (http: HttpSetup, indexes: string[]) => { }; export const firstFieldOption = { - text: i18n.translate('xpack.triggersActionsUI.sections.alertAdd.threshold.timeFieldOptionLabel', { - defaultMessage: 'Select a field', - }), + text: i18n.translate( + 'xpack.triggersActionsUI.sections.alertAdd.indexControls.timeFieldOptionLabel', + { + defaultMessage: 'Select a field', + } + ), value: '', }; From b94d2c332ca2d2559a24c916804e1bd82628d64e Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 11:26:46 +0000 Subject: [PATCH 09/30] fixed import in test --- .../tests/alerting/builtin_alert_types/index_threshold/alert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts index c05fa6cf051ff..ed166227e15da 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts @@ -15,7 +15,7 @@ import { ObjectRemover, } from '../../../../../common/lib'; import { createEsDocuments } from './create_test_data'; -import { getAlertType } from '../../../../../../../plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/'; +import { getAlertType } from '../../../../../../../plugins/stack_alerts/public/alert_types/threshold/'; const ALERT_TYPE_ID = '.index-threshold'; const ACTION_TYPE_ID = '.index'; From 76be068f3e247dbf26501ef16d61327724dbb556 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 14:39:14 +0000 Subject: [PATCH 10/30] added basic conditions component --- .../alert_form/alert_conditions.test.tsx | 202 ++++++++++++++++++ .../sections/alert_form/alert_conditions.tsx | 55 +++++ 2 files changed, 257 insertions(+) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx new file mode 100644 index 0000000000000..aa2738ddbbf10 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx @@ -0,0 +1,202 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import * as React from 'react'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; +import { act } from 'react-dom/test-utils'; +import { ReactWrapper } from 'enzyme'; +import { ActionGroupWithCondition, AlertConditions } from './alert_conditions'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiTitle, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; + +describe('alert_conditions', () => { + async function setup(element: React.ReactElement): Promise> { + const wrapper = mountWithIntl(element); + + // Wait for active space to resolve before requesting the component to update + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + return wrapper; + } + + it('renders with custom headline', async () => { + const wrapper = await setup( + + ); + + expect(wrapper.find(EuiTitle).find(FormattedMessage).prop('id')).toMatchInlineSnapshot( + `"xpack.triggersActionsUI.sections.alertAdd.conditions.title"` + ); + expect( + wrapper.find(EuiTitle).find(FormattedMessage).prop('defaultMessage') + ).toMatchInlineSnapshot(`"Conditions:"`); + + expect(wrapper.find('EuiFlexItem').get(1)).toMatchInlineSnapshot(` + + Set different threshold with their own status + + `); + }); + + it('renders any action group with conditions on it', async () => { + const ConditionForm = ({ + actionGroup, + }: { + actionGroup?: ActionGroupWithCondition<{ someProp: string }>; + }) => { + return ( + + ID + {actionGroup?.id} + Name + {actionGroup?.name} + SomeProp + + {actionGroup?.conditions?.someProp} + + + ); + }; + + const wrapper = await setup( + + + + ); + + expect(wrapper.find(EuiDescriptionList).find(EuiDescriptionListDescription).get(0)) + .toMatchInlineSnapshot(` + + default + + `); + + expect(wrapper.find(EuiDescriptionList).find(EuiDescriptionListDescription).get(1)) + .toMatchInlineSnapshot(` + + Default + + `); + + expect(wrapper.find(EuiDescriptionList).find(EuiDescriptionListDescription).get(2)) + .toMatchInlineSnapshot(` + + my prop value + + `); + }); + + it('doesnt render action group without conditions', async () => { + const ConditionForm = ({ + actionGroup, + }: { + actionGroup?: ActionGroupWithCondition<{ someProp: string }>; + }) => { + return ( + + ID + {actionGroup?.id} + + ); + }; + + const wrapper = await setup( + + + + ); + + expect(wrapper.find(EuiDescriptionList).find(EuiDescriptionListDescription).get(0)) + .toMatchInlineSnapshot(` + + default + + `); + + expect(wrapper.find(EuiDescriptionList).find(EuiDescriptionListDescription).get(1)) + .toMatchInlineSnapshot(` + + shouldRender + + `); + + expect(wrapper.find(EuiDescriptionList).find(EuiDescriptionListDescription).length).toEqual(2); + }); + + it('passes in any additional props the container passes in', async () => { + const callbackProp = jest.fn(); + + const ConditionForm = ({ + actionGroup, + someCallbackProp, + }: { + actionGroup?: ActionGroupWithCondition<{ someProp: string }>; + someCallbackProp: (actionGroup: ActionGroupWithCondition<{ someProp: string }>) => void; + }) => { + if (!actionGroup) { + return
; + } + + // call callback when the actionGroup is available + someCallbackProp(actionGroup); + return ( + + ID + {actionGroup?.id} + Name + {actionGroup?.name} + SomeProp + + {actionGroup?.conditions?.someProp} + + + ); + }; + + const wrapper = await setup( + + + + ); + + expect(callbackProp).toHaveBeenCalledWith({ + id: 'default', + name: 'Default', + conditions: { someProp: 'my prop value' }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx new file mode 100644 index 0000000000000..4e2f8d6faaebe --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { PropsWithChildren } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlexItem, EuiFlexGroup, EuiTitle } from '@elastic/eui'; +import { ActionGroup } from '../../../../../alerts/common'; + +export interface ActionGroupWithCondition extends ActionGroup { + conditions?: T; +} + +interface AlertConditionsProps { + headline?: string; + actionGroups: Array>; + // getActionGroupComponent: (actionGroup: ActionGroupWithCondition) => ReactElement; +} + +export const AlertConditions = ({ + headline, + actionGroups, + children, +}: PropsWithChildren>) => { + return ( + + + + +
+ +
+
+
+ {headline && {headline}} +
+ + {actionGroups + .filter((actionGroup) => !!actionGroup.conditions) + .map((actionGroup) => ( + + {React.isValidElement(children) && + React.cloneElement(React.Children.only(children), { + actionGroup, + })} + + ))} + +
+ ); +}; From a8d1a8f3580830a11ca090b65ed912b7cd6c0590 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 14:42:30 +0000 Subject: [PATCH 11/30] updated i18n label --- x-pack/plugins/translations/translations/ja-JP.json | 2 +- x-pack/plugins/translations/translations/zh-CN.json | 2 +- .../triggers_actions_ui/server/data/lib/time_series_types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 06459bff92bc0..39a9d6562c94e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -19560,7 +19560,7 @@ "xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval]: [dateStart]が[dateEnd]と等しくない場合に指定する必要があります", "xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage": "無効な aggType:「{aggType}」", "xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage": "無効なthresholdComparatorが指定されました: {comparator}", - "xpack.stackAlerts.indexThreshold.invalidDateErrorMessage": "無効な日付{date}", + "xpack.triggersActionsUI.data.coreQueryParams.invalidDateErrorMessage": "無効な日付{date}", "xpack.triggersActionsUI.data.coreQueryParams.invalidDurationErrorMessage": "無効な期間:「{duration}」", "xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage": "無効なgroupBy:「{groupBy}」", "xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize]: {maxGroups}以下でなければなりません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 33a17f291d290..30125410f87f5 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -19579,7 +19579,7 @@ "xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval]:如果 [dateStart] 不等于 [dateEnd],则必须指定", "xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage": "aggType 无效:“{aggType}”", "xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage": "指定的 thresholdComparator 无效:{comparator}", - "xpack.stackAlerts.indexThreshold.invalidDateErrorMessage": "日期 {date} 无效", + "xpack.triggersActionsUI.data.coreQueryParams.invalidDateErrorMessage": "日期 {date} 无效", "xpack.triggersActionsUI.data.coreQueryParams.invalidDurationErrorMessage": "持续时间无效:“{duration}”", "xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage": "groupBy 无效:“{groupBy}”", "xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize]:必须小于或等于 {maxGroups}", diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts index 10a003603eb78..ef0fa15cf31e9 100644 --- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts +++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_types.ts @@ -83,7 +83,7 @@ function validateBody(anyParams: unknown): string | undefined { function validateDate(dateString: string): string | undefined { const parsed = Date.parse(dateString); if (isNaN(parsed)) { - return i18n.translate('xpack.stackAlerts.indexThreshold.invalidDateErrorMessage', { + return i18n.translate('xpack.triggersActionsUI.data.coreQueryParams.invalidDateErrorMessage', { defaultMessage: 'invalid date {date}', values: { date: dateString, From 0e3a745fd07e4a0d9712573080d445cbddbd9bbe Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 15:49:39 +0000 Subject: [PATCH 12/30] fixed i18n translations --- .../translations/translations/ja-JP.json | 72 +++++++++---------- .../translations/translations/zh-CN.json | 72 +++++++++---------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 39a9d6562c94e..fa54c7daab8b6 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20105,42 +20105,42 @@ "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "キャンセル", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "{numIdsToDelete, plural, one {{singleTitle}} other {# {multipleTitle}}}を削除 ", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.descriptionText": "{numIdsToDelete, plural, one {a deleted {singleTitle}} other {deleted {multipleTitle}}}を回復できません。", - "xpack.triggersActionsUI.geoThreshold.boundaryNameSelect": "境界名を選択", - "xpack.triggersActionsUI.geoThreshold.boundaryNameSelectLabel": "人間が読み取れる境界名(任意)", - "xpack.triggersActionsUI.geoThreshold.delayOffset": "遅延評価オフセット", - "xpack.triggersActionsUI.geoThreshold.delayOffsetTooltip": "遅延サイクルでアラートを評価し、データレイテンシに合わせて調整します", - "xpack.triggersActionsUI.geoThreshold.entityByLabel": "グループ基準", - "xpack.triggersActionsUI.geoThreshold.entityIndexLabel": "インデックス", - "xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryGeoFieldText": "境界地理フィールドは必須です。", - "xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryIndexTitleText": "境界インデックスパターンタイトルは必須です。", - "xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryTypeText": "境界タイプは必須です。", - "xpack.triggersActionsUI.geoThreshold.error.requiredDateFieldText": "日付フィールドが必要です。", - "xpack.triggersActionsUI.geoThreshold.error.requiredEntityText": "エンティティは必須です。", - "xpack.triggersActionsUI.geoThreshold.error.requiredGeoFieldText": "地理フィールドは必須です。", - "xpack.triggersActionsUI.geoThreshold.error.requiredIndexTitleText": "インデックスパターンが必要です。", - "xpack.triggersActionsUI.geoThreshold.error.requiredTrackingEventText": "追跡イベントは必須です。", - "xpack.triggersActionsUI.geoThreshold.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。", - "xpack.triggersActionsUI.geoThreshold.geofieldLabel": "地理空間フィールド", - "xpack.triggersActionsUI.geoThreshold.indexLabel": "インデックス", - "xpack.triggersActionsUI.geoThreshold.indexPatternSelectLabel": "インデックスパターン", - "xpack.triggersActionsUI.geoThreshold.indexPatternSelectPlaceholder": "インデックスパターンを選択", - "xpack.triggersActionsUI.geoThreshold.name.trackingThreshold": "追跡しきい値", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.doThisLinkTextDescription": "インデックスパターンを作成します", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.doThisPrefixDescription": "次のことが必要です ", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.doThisSuffixDescription": " 地理空間フィールドを含む", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.getStartedLinkText": "サンプルデータセットで始めましょう。", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.hintDescription": "地理空間データセットがありませんか? ", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.messageTitle": "地理空間フィールドを含むインデックスパターンが見つかりませんでした", - "xpack.triggersActionsUI.geoThreshold.selectBoundaryIndex": "境界を選択:", - "xpack.triggersActionsUI.geoThreshold.selectEntity": "エンティティを選択", - "xpack.triggersActionsUI.geoThreshold.selectGeoLabel": "ジオフィールドを選択", - "xpack.triggersActionsUI.geoThreshold.selectIndex": "条件を定義してください", - "xpack.triggersActionsUI.geoThreshold.selectLabel": "ジオフィールドを選択", - "xpack.triggersActionsUI.geoThreshold.selectOffset": "オフセットを選択(任意)", - "xpack.triggersActionsUI.geoThreshold.selectTimeLabel": "時刻フィールドを選択", - "xpack.triggersActionsUI.geoThreshold.timeFieldLabel": "時間フィールド", - "xpack.triggersActionsUI.geoThreshold.topHitsSplitFieldSelectPlaceholder": "エンティティフィールドを選択", - "xpack.triggersActionsUI.geoThreshold.whenEntityLabel": "エンティティ", + "xpack.stackAlerts.geoThreshold.boundaryNameSelect": "境界名を選択", + "xpack.stackAlerts.geoThreshold.boundaryNameSelectLabel": "人間が読み取れる境界名(任意)", + "xpack.stackAlerts.geoThreshold.delayOffset": "遅延評価オフセット", + "xpack.stackAlerts.geoThreshold.delayOffsetTooltip": "遅延サイクルでアラートを評価し、データレイテンシに合わせて調整します", + "xpack.stackAlerts.geoThreshold.entityByLabel": "グループ基準", + "xpack.stackAlerts.geoThreshold.entityIndexLabel": "インデックス", + "xpack.stackAlerts.geoThreshold.error.requiredBoundaryGeoFieldText": "境界地理フィールドは必須です。", + "xpack.stackAlerts.geoThreshold.error.requiredBoundaryIndexTitleText": "境界インデックスパターンタイトルは必須です。", + "xpack.stackAlerts.geoThreshold.error.requiredBoundaryTypeText": "境界タイプは必須です。", + "xpack.stackAlerts.geoThreshold.error.requiredDateFieldText": "日付フィールドが必要です。", + "xpack.stackAlerts.geoThreshold.error.requiredEntityText": "エンティティは必須です。", + "xpack.stackAlerts.geoThreshold.error.requiredGeoFieldText": "地理フィールドは必須です。", + "xpack.stackAlerts.geoThreshold.error.requiredIndexTitleText": "インデックスパターンが必要です。", + "xpack.stackAlerts.geoThreshold.error.requiredTrackingEventText": "追跡イベントは必須です。", + "xpack.stackAlerts.geoThreshold.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。", + "xpack.stackAlerts.geoThreshold.geofieldLabel": "地理空間フィールド", + "xpack.stackAlerts.geoThreshold.indexLabel": "インデックス", + "xpack.stackAlerts.geoThreshold.indexPatternSelectLabel": "インデックスパターン", + "xpack.stackAlerts.geoThreshold.indexPatternSelectPlaceholder": "インデックスパターンを選択", + "xpack.stackAlerts.geoThreshold.name.trackingThreshold": "追跡しきい値", + "xpack.stackAlerts.geoThreshold.noIndexPattern.doThisLinkTextDescription": "インデックスパターンを作成します", + "xpack.stackAlerts.geoThreshold.noIndexPattern.doThisPrefixDescription": "次のことが必要です ", + "xpack.stackAlerts.geoThreshold.noIndexPattern.doThisSuffixDescription": " 地理空間フィールドを含む", + "xpack.stackAlerts.geoThreshold.noIndexPattern.getStartedLinkText": "サンプルデータセットで始めましょう。", + "xpack.stackAlerts.geoThreshold.noIndexPattern.hintDescription": "地理空間データセットがありませんか? ", + "xpack.stackAlerts.geoThreshold.noIndexPattern.messageTitle": "地理空間フィールドを含むインデックスパターンが見つかりませんでした", + "xpack.stackAlerts.geoThreshold.selectBoundaryIndex": "境界を選択:", + "xpack.stackAlerts.geoThreshold.selectEntity": "エンティティを選択", + "xpack.stackAlerts.geoThreshold.selectGeoLabel": "ジオフィールドを選択", + "xpack.stackAlerts.geoThreshold.selectIndex": "条件を定義してください", + "xpack.stackAlerts.geoThreshold.selectLabel": "ジオフィールドを選択", + "xpack.stackAlerts.geoThreshold.selectOffset": "オフセットを選択(任意)", + "xpack.stackAlerts.geoThreshold.selectTimeLabel": "時刻フィールドを選択", + "xpack.stackAlerts.geoThreshold.timeFieldLabel": "時間フィールド", + "xpack.stackAlerts.geoThreshold.topHitsSplitFieldSelectPlaceholder": "エンティティフィールドを選択", + "xpack.stackAlerts.geoThreshold.whenEntityLabel": "エンティティ", "xpack.triggersActionsUI.home.alertsTabTitle": "アラート", "xpack.triggersActionsUI.home.appTitle": "アラートとアクション", "xpack.triggersActionsUI.home.breadcrumbTitle": "アラートとアクション", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 30125410f87f5..9cec5ecd5370b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20124,42 +20124,42 @@ "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "取消", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "删除{numIdsToDelete, plural, one {{singleTitle}} other { # 个{multipleTitle}}} ", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.descriptionText": "无法恢复{numIdsToDelete, plural, one {删除的{singleTitle}} other {删除的{multipleTitle}}}。", - "xpack.triggersActionsUI.geoThreshold.boundaryNameSelect": "选择边界名称", - "xpack.triggersActionsUI.geoThreshold.boundaryNameSelectLabel": "可人工读取的边界名称(可选)", - "xpack.triggersActionsUI.geoThreshold.delayOffset": "已延迟的评估偏移", - "xpack.triggersActionsUI.geoThreshold.delayOffsetTooltip": "评估延迟周期内的告警,以针对数据延迟进行调整", - "xpack.triggersActionsUI.geoThreshold.entityByLabel": "方式", - "xpack.triggersActionsUI.geoThreshold.entityIndexLabel": "索引", - "xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryGeoFieldText": "“边界地理”字段必填。", - "xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryIndexTitleText": "“边界索引模式标题”必填。", - "xpack.triggersActionsUI.geoThreshold.error.requiredBoundaryTypeText": "“边界类型”必填。", - "xpack.triggersActionsUI.geoThreshold.error.requiredDateFieldText": "“日期”字段必填。", - "xpack.triggersActionsUI.geoThreshold.error.requiredEntityText": "“实体”必填。", - "xpack.triggersActionsUI.geoThreshold.error.requiredGeoFieldText": "“地理”字段必填。", - "xpack.triggersActionsUI.geoThreshold.error.requiredIndexTitleText": "“索引模式”必填。", - "xpack.triggersActionsUI.geoThreshold.error.requiredTrackingEventText": "“跟踪事件”必填。", - "xpack.triggersActionsUI.geoThreshold.fixErrorInExpressionBelowValidationMessage": "表达式包含错误。", - "xpack.triggersActionsUI.geoThreshold.geofieldLabel": "地理空间字段", - "xpack.triggersActionsUI.geoThreshold.indexLabel": "索引", - "xpack.triggersActionsUI.geoThreshold.indexPatternSelectLabel": "索引模式", - "xpack.triggersActionsUI.geoThreshold.indexPatternSelectPlaceholder": "选择索引模式", - "xpack.triggersActionsUI.geoThreshold.name.trackingThreshold": "跟踪阈值", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.doThisLinkTextDescription": "创建索引模式", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.doThisPrefixDescription": "您将需要 ", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.doThisSuffixDescription": " (包含地理空间字段)。", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.getStartedLinkText": "开始使用一些样例数据集。", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.hintDescription": "没有任何地理空间数据集? ", - "xpack.triggersActionsUI.geoThreshold.noIndexPattern.messageTitle": "找不到任何具有地理空间字段的索引模式", - "xpack.triggersActionsUI.geoThreshold.selectBoundaryIndex": "选择边界:", - "xpack.triggersActionsUI.geoThreshold.selectEntity": "选择实体", - "xpack.triggersActionsUI.geoThreshold.selectGeoLabel": "选择地理字段", - "xpack.triggersActionsUI.geoThreshold.selectIndex": "定义条件", - "xpack.triggersActionsUI.geoThreshold.selectLabel": "选择地理字段", - "xpack.triggersActionsUI.geoThreshold.selectOffset": "选择偏移(可选)", - "xpack.triggersActionsUI.geoThreshold.selectTimeLabel": "选择时间字段", - "xpack.triggersActionsUI.geoThreshold.timeFieldLabel": "时间字段", - "xpack.triggersActionsUI.geoThreshold.topHitsSplitFieldSelectPlaceholder": "选择实体字段", - "xpack.triggersActionsUI.geoThreshold.whenEntityLabel": "当实体", + "xpack.stackAlerts.geoThreshold.boundaryNameSelect": "选择边界名称", + "xpack.stackAlerts.geoThreshold.boundaryNameSelectLabel": "可人工读取的边界名称(可选)", + "xpack.stackAlerts.geoThreshold.delayOffset": "已延迟的评估偏移", + "xpack.stackAlerts.geoThreshold.delayOffsetTooltip": "评估延迟周期内的告警,以针对数据延迟进行调整", + "xpack.stackAlerts.geoThreshold.entityByLabel": "方式", + "xpack.stackAlerts.geoThreshold.entityIndexLabel": "索引", + "xpack.stackAlerts.geoThreshold.error.requiredBoundaryGeoFieldText": "“边界地理”字段必填。", + "xpack.stackAlerts.geoThreshold.error.requiredBoundaryIndexTitleText": "“边界索引模式标题”必填。", + "xpack.stackAlerts.geoThreshold.error.requiredBoundaryTypeText": "“边界类型”必填。", + "xpack.stackAlerts.geoThreshold.error.requiredDateFieldText": "“日期”字段必填。", + "xpack.stackAlerts.geoThreshold.error.requiredEntityText": "“实体”必填。", + "xpack.stackAlerts.geoThreshold.error.requiredGeoFieldText": "“地理”字段必填。", + "xpack.stackAlerts.geoThreshold.error.requiredIndexTitleText": "“索引模式”必填。", + "xpack.stackAlerts.geoThreshold.error.requiredTrackingEventText": "“跟踪事件”必填。", + "xpack.stackAlerts.geoThreshold.fixErrorInExpressionBelowValidationMessage": "表达式包含错误。", + "xpack.stackAlerts.geoThreshold.geofieldLabel": "地理空间字段", + "xpack.stackAlerts.geoThreshold.indexLabel": "索引", + "xpack.stackAlerts.geoThreshold.indexPatternSelectLabel": "索引模式", + "xpack.stackAlerts.geoThreshold.indexPatternSelectPlaceholder": "选择索引模式", + "xpack.stackAlerts.geoThreshold.name.trackingThreshold": "跟踪阈值", + "xpack.stackAlerts.geoThreshold.noIndexPattern.doThisLinkTextDescription": "创建索引模式", + "xpack.stackAlerts.geoThreshold.noIndexPattern.doThisPrefixDescription": "您将需要 ", + "xpack.stackAlerts.geoThreshold.noIndexPattern.doThisSuffixDescription": " (包含地理空间字段)。", + "xpack.stackAlerts.geoThreshold.noIndexPattern.getStartedLinkText": "开始使用一些样例数据集。", + "xpack.stackAlerts.geoThreshold.noIndexPattern.hintDescription": "没有任何地理空间数据集? ", + "xpack.stackAlerts.geoThreshold.noIndexPattern.messageTitle": "找不到任何具有地理空间字段的索引模式", + "xpack.stackAlerts.geoThreshold.selectBoundaryIndex": "选择边界:", + "xpack.stackAlerts.geoThreshold.selectEntity": "选择实体", + "xpack.stackAlerts.geoThreshold.selectGeoLabel": "选择地理字段", + "xpack.stackAlerts.geoThreshold.selectIndex": "定义条件", + "xpack.stackAlerts.geoThreshold.selectLabel": "选择地理字段", + "xpack.stackAlerts.geoThreshold.selectOffset": "选择偏移(可选)", + "xpack.stackAlerts.geoThreshold.selectTimeLabel": "选择时间字段", + "xpack.stackAlerts.geoThreshold.timeFieldLabel": "时间字段", + "xpack.stackAlerts.geoThreshold.topHitsSplitFieldSelectPlaceholder": "选择实体字段", + "xpack.stackAlerts.geoThreshold.whenEntityLabel": "当实体", "xpack.triggersActionsUI.home.alertsTabTitle": "告警", "xpack.triggersActionsUI.home.appTitle": "告警和操作", "xpack.triggersActionsUI.home.breadcrumbTitle": "告警和操作", From 2f616f170d48399940a4cf6413f1b891d0af0369 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 17:56:10 +0000 Subject: [PATCH 13/30] added stackAlerts to limits --- packages/kbn-optimizer/limits.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 701b7cab21600..e326c8e2cac39 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -102,3 +102,4 @@ pageLoadAssetSize: visualizations: 295025 visualize: 57431 watcher: 43598 + stackAlerts: 29684 From 839463f631dc6be6a091c30dec41734d7a7e2d16 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 10 Nov 2020 18:47:52 +0000 Subject: [PATCH 14/30] fixed import --- .../tests/alerting/builtin_alert_types/index_threshold/alert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts index ed166227e15da..ec0b5ef20145e 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts @@ -15,7 +15,7 @@ import { ObjectRemover, } from '../../../../../common/lib'; import { createEsDocuments } from './create_test_data'; -import { getAlertType } from '../../../../../../../plugins/stack_alerts/public/alert_types/threshold/'; +import { getAlertType } from '../../../../../../../plugins/stack_alerts/public/alert_types/threshold'; const ALERT_TYPE_ID = '.index-threshold'; const ACTION_TYPE_ID = '.index'; From 845f35976fa60a321ca9302792a1f644e8dee55f Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 12 Nov 2020 09:45:31 +0000 Subject: [PATCH 15/30] make defaulted field non maybe --- x-pack/plugins/stack_alerts/common/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/stack_alerts/common/config.ts b/x-pack/plugins/stack_alerts/common/config.ts index 300b73a65bd47..2e997ce0ebad6 100644 --- a/x-pack/plugins/stack_alerts/common/config.ts +++ b/x-pack/plugins/stack_alerts/common/config.ts @@ -8,7 +8,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), - enableGeoTrackingThresholdAlert: schema.maybe(schema.boolean({ defaultValue: false })), + enableGeoTrackingThresholdAlert: schema.boolean({ defaultValue: false }), }); export type Config = TypeOf; From 74b779ad0f992a2af90e0be4aac1946608129d61 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 12 Nov 2020 13:29:43 +0000 Subject: [PATCH 16/30] added generic action group UI --- .../alerting_example/common/constants.ts | 9 + .../public/alert_types/always_firing.tsx | 145 ++++++++++++++-- .../server/alert_types/always_firing.ts | 47 ++++-- .../alert_form/alert_conditions.test.tsx | 53 +++++- .../sections/alert_form/alert_conditions.tsx | 155 +++++++++++++++--- .../sections/alert_form/alert_form.tsx | 6 +- .../application/sections/alert_form/index.tsx | 6 + .../public/application/sections/index.tsx | 6 + .../triggers_actions_ui/public/index.ts | 7 +- .../triggers_actions_ui/public/types.ts | 2 + 10 files changed, 380 insertions(+), 56 deletions(-) diff --git a/x-pack/examples/alerting_example/common/constants.ts b/x-pack/examples/alerting_example/common/constants.ts index dd9cc21954e61..40cc298db795a 100644 --- a/x-pack/examples/alerting_example/common/constants.ts +++ b/x-pack/examples/alerting_example/common/constants.ts @@ -8,6 +8,15 @@ export const ALERTING_EXAMPLE_APP_ID = 'AlertingExample'; // always firing export const DEFAULT_INSTANCES_TO_GENERATE = 5; +export interface AlwaysFiringParams { + instances?: number; + thresholds?: { + small?: number; + medium?: number; + large?: number; + }; +} +export type AlwaysFiringActionGroupIds = keyof AlwaysFiringParams['thresholds']; // Astros export enum Craft { diff --git a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx index a5d158fca836b..06fba57456263 100644 --- a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx +++ b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx @@ -4,17 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiFieldNumber, EuiFormRow } from '@elastic/eui'; +import React, { Fragment, useState } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFieldNumber, + EuiFormRow, + EuiPopover, + EuiExpression, + EuiSpacer, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AlertTypeModel } from '../../../../plugins/triggers_actions_ui/public'; -import { DEFAULT_INSTANCES_TO_GENERATE } from '../../common/constants'; - -interface AlwaysFiringParamsProps { - alertParams: { instances?: number }; - setAlertParams: (property: string, value: any) => void; - errors: { [key: string]: string[] }; -} +import { omit, pick } from 'lodash'; +import { + ActionGroupWithCondition, + AlertConditions, + AlertConditionsGroup, + AlertTypeModel, + AlertTypeParamsExpressionProps, + AlertsContextValue, +} from '../../../../plugins/triggers_actions_ui/public'; +import { + AlwaysFiringParams, + AlwaysFiringActionGroupIds, + DEFAULT_INSTANCES_TO_GENERATE, +} from '../../common/constants'; export function getAlertType(): AlertTypeModel { return { @@ -24,7 +38,7 @@ export function getAlertType(): AlertTypeModel { iconClass: 'bolt', documentationUrl: null, alertParamsExpression: AlwaysFiringExpression, - validate: (alertParams: AlwaysFiringParamsProps['alertParams']) => { + validate: (alertParams: AlwaysFiringParams) => { const { instances } = alertParams; const validationResult = { errors: { @@ -44,11 +58,21 @@ export function getAlertType(): AlertTypeModel { }; } -export const AlwaysFiringExpression: React.FunctionComponent = ({ - alertParams, - setAlertParams, -}) => { - const { instances = DEFAULT_INSTANCES_TO_GENERATE } = alertParams; +const DEFAULT_THRESHOLDS: AlwaysFiringParams['thresholds'] = { + small: 0, + medium: 5000, + large: 10000, +}; + +export const AlwaysFiringExpression: React.FunctionComponent> = ({ alertParams, setAlertParams, actionGroups, defaultActionGroupId }) => { + const { + instances = DEFAULT_INSTANCES_TO_GENERATE, + thresholds = pick(DEFAULT_THRESHOLDS, defaultActionGroupId), + } = alertParams; + return ( @@ -67,6 +91,95 @@ export const AlwaysFiringExpression: React.FunctionComponent + + + + + Number.isInteger(thresholds[actionGroup.id as AlwaysFiringActionGroupIds]) + ? { + ...actionGroup, + conditions: thresholds[actionGroup.id as AlwaysFiringActionGroupIds]!, + } + : actionGroup + )} + onInitializeConditionsFor={(actionGroup) => { + setAlertParams('thresholds', { + ...thresholds, + ...pick(DEFAULT_THRESHOLDS, actionGroup.id), + }); + }} + > + { + setAlertParams('thresholds', omit(thresholds, actionGroup.id)); + }} + > + { + setAlertParams('thresholds', { + ...thresholds, + [actionGroup.id]: actionGroup.conditions, + }); + }} + /> + + + + + ); }; + +interface TShirtSelectorProps { + actionGroup?: ActionGroupWithCondition; + setTShirtThreshold: (actionGroup: ActionGroupWithCondition) => void; +} +const TShirtSelector = ({ actionGroup, setTShirtThreshold }: TShirtSelectorProps) => { + const [isOpen, setIsOpen] = useState(false); + + if (!actionGroup) { + return null; + } + + return ( + setIsOpen(true)} + /> + } + isOpen={isOpen} + closePopover={() => setIsOpen(false)} + ownFocus + anchorPosition="downLeft" + > + + + {'Is Above'} + + + { + const conditions = parseInt(e.target.value, 10); + if (e.target.value && !isNaN(conditions)) { + setTShirtThreshold({ + ...actionGroup, + conditions, + }); + } + }} + /> + + + + ); +}; diff --git a/x-pack/examples/alerting_example/server/alert_types/always_firing.ts b/x-pack/examples/alerting_example/server/alert_types/always_firing.ts index d02406a23045e..3fe24932fccc9 100644 --- a/x-pack/examples/alerting_example/server/alert_types/always_firing.ts +++ b/x-pack/examples/alerting_example/server/alert_types/always_firing.ts @@ -7,29 +7,54 @@ import uuid from 'uuid'; import { range, random } from 'lodash'; import { AlertType } from '../../../../plugins/alerts/server'; -import { DEFAULT_INSTANCES_TO_GENERATE, ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; +import { + DEFAULT_INSTANCES_TO_GENERATE, + ALERTING_EXAMPLE_APP_ID, + AlwaysFiringParams, +} from '../../common/constants'; const ACTION_GROUPS = [ - { id: 'small', name: 'small' }, - { id: 'medium', name: 'medium' }, - { id: 'large', name: 'large' }, + { id: 'small', name: 'Small t-shirt' }, + { id: 'medium', name: 'Medium t-shirt' }, + { id: 'large', name: 'Large t-shirt' }, ]; +const DEFAULT_ACTION_GROUP = 'small'; -export const alertType: AlertType = { +function getTShirtSizeByIdAndThreshold(id: string, thresholds: AlwaysFiringParams['thresholds']) { + const idAsNumber = parseInt(id, 10); + if (!isNaN(idAsNumber)) { + if (thresholds?.large && thresholds.large < idAsNumber) { + return 'large'; + } + if (thresholds?.medium && thresholds.medium < idAsNumber) { + return 'medium'; + } + if (thresholds?.small && thresholds.small < idAsNumber) { + return 'small'; + } + } + return DEFAULT_ACTION_GROUP; +} + +export const alertType: AlertType = { id: 'example.always-firing', name: 'Always firing', actionGroups: ACTION_GROUPS, - defaultActionGroupId: 'small', - async executor({ services, params: { instances = DEFAULT_INSTANCES_TO_GENERATE }, state }) { + defaultActionGroupId: DEFAULT_ACTION_GROUP, + async executor({ + services, + params: { instances = DEFAULT_INSTANCES_TO_GENERATE, thresholds }, + state, + }) { const count = (state.count ?? 0) + 1; range(instances) - .map(() => ({ id: uuid.v4(), tshirtSize: ACTION_GROUPS[random(0, 2)].id! })) - .forEach((instance: { id: string; tshirtSize: string }) => { + .map(() => uuid.v4()) + .forEach((id: string) => { services - .alertInstanceFactory(instance.id) + .alertInstanceFactory(id) .replaceState({ triggerdOnCycle: count }) - .scheduleActions(instance.tshirtSize); + .scheduleActions(getTShirtSizeByIdAndThreshold(id, thresholds)); }); return { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx index aa2738ddbbf10..2b9d109d210a3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx @@ -7,13 +7,14 @@ import * as React from 'react'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; -import { ActionGroupWithCondition, AlertConditions } from './alert_conditions'; +import AlertConditions, { ActionGroupWithCondition } from './alert_conditions'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, EuiDescriptionList, EuiDescriptionListTitle, EuiDescriptionListDescription, + EuiButtonEmpty, } from '@elastic/eui'; describe('alert_conditions', () => { @@ -153,6 +154,56 @@ describe('alert_conditions', () => { expect(wrapper.find(EuiDescriptionList).find(EuiDescriptionListDescription).length).toEqual(2); }); + it('render add buttons for action group without conditions', async () => { + const onInitializeConditionsFor = jest.fn(); + + const ConditionForm = ({ + actionGroup, + }: { + actionGroup?: ActionGroupWithCondition<{ someProp: string }>; + }) => { + return ( + + ID + {actionGroup?.id} + + ); + }; + + const wrapper = await setup( + + + + ); + + expect(wrapper.find(EuiButtonEmpty).get(0)).toMatchInlineSnapshot(` + + Should Not Render + + `); + wrapper.find(EuiButtonEmpty).simulate('click'); + + expect(onInitializeConditionsFor).toHaveBeenCalledWith({ + id: 'shouldntRender', + name: 'Should Not Render', + }); + }); + it('passes in any additional props the container passes in', async () => { const callbackProp = jest.fn(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx index 4e2f8d6faaebe..9c3400a4a74c5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx @@ -3,53 +3,156 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { PropsWithChildren } from 'react'; +import React, { Fragment, PropsWithChildren } from 'react'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFlexItem, EuiFlexGroup, EuiTitle } from '@elastic/eui'; +import { + EuiFlexItem, + EuiText, + EuiFlexGroup, + EuiTitle, + EuiFormRow, + EuiButtonIcon, + EuiButtonEmpty, +} from '@elastic/eui'; +import { partition } from 'lodash'; import { ActionGroup } from '../../../../../alerts/common'; export interface ActionGroupWithCondition extends ActionGroup { conditions?: T; } -interface AlertConditionsProps { +export interface AlertConditionsProps { headline?: string; actionGroups: Array>; - // getActionGroupComponent: (actionGroup: ActionGroupWithCondition) => ReactElement; + onInitializeConditionsFor?: (actionGroup: ActionGroupWithCondition) => void; + onResetConditionsFor?: (actionGroup: ActionGroupWithCondition) => void; } export const AlertConditions = ({ headline, actionGroups, + onInitializeConditionsFor, + onResetConditionsFor, children, }: PropsWithChildren>) => { + const [withConditions, withoutConditions] = partition(actionGroups, (actionGroup) => + actionGroup.hasOwnProperty('conditions') + ); + return ( - - - - -
- -
-
-
- {headline && {headline}} -
- - {actionGroups - .filter((actionGroup) => !!actionGroup.conditions) - .map((actionGroup) => ( + + + + + +
+ +
+
+ {headline && ( + + + {headline} + + + )} +
+
+
+ + + {withConditions.map((actionGroup) => ( {React.isValidElement(children) && - React.cloneElement(React.Children.only(children), { - actionGroup, - })} + React.cloneElement( + React.Children.only(children), + onResetConditionsFor + ? { + actionGroup, + onResetConditionsFor, + } + : { actionGroup } + )} ))} - + {onInitializeConditionsFor && withoutConditions.length > 0 && ( + + + + + + {withoutConditions.map((actionGroup) => ( + + onInitializeConditionsFor(actionGroup)} + > + {actionGroup.name} + + + ))} + + + )} +
+
); }; + +export type AlertConditionsGroup = { + actionGroup?: ActionGroupWithCondition; +} & Pick, 'onResetConditionsFor'>; + +export const AlertConditionsGroup = ({ + actionGroup, + onResetConditionsFor, + children, + ...otherProps +}: PropsWithChildren>) => { + if (!actionGroup) { + return null; + } + + return ( + onResetConditionsFor(actionGroup)} + /> + ) + } + > + {React.isValidElement(children) ? ( + React.cloneElement(React.Children.only(children), { + actionGroup, + ...otherProps, + }) + ) : ( + + )} + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { AlertConditions as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index 213d1d7ad36df..ee308a3cb06ae 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -437,7 +437,9 @@ export const AlertForm = ({
)} - {AlertParamsExpressionComponent ? ( + {AlertParamsExpressionComponent && + defaultActionGroupId && + alertTypesIndex?.has(alert.alertTypeId) ? ( }> ) : null} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx index 79720edc4672e..1c499511ec921 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx @@ -5,6 +5,12 @@ */ import { lazy } from 'react'; import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; +export { + AlertConditions, + AlertConditionsGroup, + ActionGroupWithCondition, + AlertConditionsProps, +} from './alert_conditions'; export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_add'))); export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_edit'))); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx index 677ee139271c0..490aeb5be8bd3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/index.tsx @@ -6,6 +6,12 @@ import { lazy } from 'react'; import { suspendedComponentWithProps } from '../lib/suspended_component_with_props'; +export { + ActionGroupWithCondition, + AlertConditionsProps, + AlertConditions, + AlertConditionsGroup, +} from './alert_form'; export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_add'))); export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_form/alert_edit'))); diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index 3794112e1d502..bd8dde3fb7830 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -9,7 +9,12 @@ import { Plugin } from './plugin'; export { AlertsContextProvider, AlertsContextValue } from './application/context/alerts_context'; export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context'; export { AlertAdd } from './application/sections/alert_form'; -export { AlertEdit } from './application/sections'; +export { + AlertEdit, + AlertConditions, + AlertConditionsGroup, + ActionGroupWithCondition, +} from './application/sections'; export { ActionForm } from './application/sections/action_connector_form'; export { AlertAction, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 1a6b68080c9a4..678d4b5debba9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -169,6 +169,8 @@ export interface AlertTypeParamsExpressionProps< setAlertProperty: (key: string, value: any) => void; errors: IErrorObject; alertsContext: AlertsContextValue; + defaultActionGroupId: string; + actionGroups: ActionGroup[]; } export interface AlertTypeModel { From 9e2beace16db05bf7dbec8104d2e7ae3371eafb3 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 12 Nov 2020 14:47:21 +0000 Subject: [PATCH 17/30] removed import from plugin code as it causes FTR to fail --- .../alerting/builtin_alert_types/index_threshold/alert.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts index ec0b5ef20145e..e918ce174a031 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts @@ -15,7 +15,6 @@ import { ObjectRemover, } from '../../../../../common/lib'; import { createEsDocuments } from './create_test_data'; -import { getAlertType } from '../../../../../../../plugins/stack_alerts/public/alert_types/threshold'; const ALERT_TYPE_ID = '.index-threshold'; const ACTION_TYPE_ID = '.index'; @@ -27,7 +26,7 @@ const ALERT_INTERVALS_TO_WRITE = 5; const ALERT_INTERVAL_SECONDS = 3; const ALERT_INTERVAL_MILLIS = ALERT_INTERVAL_SECONDS * 1000; -const DefaultActionMessage = getAlertType().defaultActionMessage; +const DefaultActionMessage = `alert {{alertName}} group {{context.group}} value {{context.value}} exceeded threshold {{context.function}} over {{params.timeWindowSize}}{{params.timeWindowUnit}} on {{context.date}}`; // eslint-disable-next-line import/no-default-export export default function alertTests({ getService }: FtrProviderContext) { @@ -65,10 +64,6 @@ export default function alertTests({ getService }: FtrProviderContext) { await esTestIndexToolOutput.destroy(); }); - it('has a default action message', () => { - expect(DefaultActionMessage).to.be.ok(); - }); - // The tests below create two alerts, one that will fire, one that will // never fire; the tests ensure the ones that should fire, do fire, and // those that shouldn't fire, do not fire. From 036889f0acf4948510a1efced6762150ece29f64 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 12 Nov 2020 15:05:25 +0000 Subject: [PATCH 18/30] fixed typing --- .../server/alert_types/always_firing.ts | 2 +- .../alert_form/alert_conditions.test.tsx | 29 ++++++++++++------- .../sections/alert_form/alert_conditions.tsx | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/x-pack/examples/alerting_example/server/alert_types/always_firing.ts b/x-pack/examples/alerting_example/server/alert_types/always_firing.ts index 3fe24932fccc9..1900f55a51a55 100644 --- a/x-pack/examples/alerting_example/server/alert_types/always_firing.ts +++ b/x-pack/examples/alerting_example/server/alert_types/always_firing.ts @@ -5,7 +5,7 @@ */ import uuid from 'uuid'; -import { range, random } from 'lodash'; +import { range } from 'lodash'; import { AlertType } from '../../../../plugins/alerts/server'; import { DEFAULT_INSTANCES_TO_GENERATE, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx index 2b9d109d210a3..9850673106813 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx @@ -45,10 +45,15 @@ describe('alert_conditions', () => { wrapper.find(EuiTitle).find(FormattedMessage).prop('defaultMessage') ).toMatchInlineSnapshot(`"Conditions:"`); - expect(wrapper.find('EuiFlexItem').get(1)).toMatchInlineSnapshot(` - + expect(wrapper.find('[data-test-subj="alertConditionsHeadline"]').get(0)) + .toMatchInlineSnapshot(` + Set different threshold with their own status - + `); }); @@ -174,13 +179,13 @@ describe('alert_conditions', () => { { expect(wrapper.find(EuiButtonEmpty).get(0)).toMatchInlineSnapshot(` - Should Not Render + Should Render A Link `); wrapper.find(EuiButtonEmpty).simulate('click'); expect(onInitializeConditionsFor).toHaveBeenCalledWith({ - id: 'shouldntRender', - name: 'Should Not Render', + id: 'shouldRenderLink', + name: 'Should Render A Link', }); }); @@ -234,7 +241,7 @@ describe('alert_conditions', () => { ); }; - const wrapper = await setup( + await setup( ({ {headline && ( - + {headline} From 2f576aba82da1d06d6789e1cb910022af8533e04 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 12 Nov 2020 15:07:32 +0000 Subject: [PATCH 19/30] refactor always-firing --- .../public/alert_types/always_firing.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx index 06fba57456263..abbe1d2a48d11 100644 --- a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx +++ b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx @@ -73,6 +73,15 @@ export const AlwaysFiringExpression: React.FunctionComponent + Number.isInteger(thresholds[actionGroup.id as AlwaysFiringActionGroupIds]) + ? { + ...actionGroup, + conditions: thresholds[actionGroup.id as AlwaysFiringActionGroupIds]!, + } + : actionGroup + ); + return ( @@ -96,14 +105,7 @@ export const AlwaysFiringExpression: React.FunctionComponent - Number.isInteger(thresholds[actionGroup.id as AlwaysFiringActionGroupIds]) - ? { - ...actionGroup, - conditions: thresholds[actionGroup.id as AlwaysFiringActionGroupIds]!, - } - : actionGroup - )} + actionGroups={actionGroupsWithConditions} onInitializeConditionsFor={(actionGroup) => { setAlertParams('thresholds', { ...thresholds, From 9730de899e838a8ca8a06097b97aaf503d124e03 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 12 Nov 2020 16:37:17 +0000 Subject: [PATCH 20/30] bumped label of action group --- .../alert_form/alert_conditions.test.tsx | 2 +- .../sections/alert_form/alert_conditions.tsx | 65 +++--------- .../alert_conditions_group.test.tsx | 98 +++++++++++++++++++ .../alert_form/alert_conditions_group.tsx | 60 ++++++++++++ .../application/sections/alert_form/index.tsx | 2 +- 5 files changed, 173 insertions(+), 54 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx index 9850673106813..7cc5cfcaf669f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx @@ -7,7 +7,7 @@ import * as React from 'react'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; -import AlertConditions, { ActionGroupWithCondition } from './alert_conditions'; +import { AlertConditions, ActionGroupWithCondition } from './alert_conditions'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx index 09968e35f48a8..5dae433d4e8aa 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx @@ -18,9 +18,19 @@ import { import { partition } from 'lodash'; import { ActionGroup } from '../../../../../alerts/common'; -export interface ActionGroupWithCondition extends ActionGroup { - conditions?: T; -} +export type ActionGroupWithCondition = ActionGroup & + ( + | // allow isRequired=false with or without conditions + { + conditions?: T; + isRequired?: false; + } + // but if isRequired=true then conditions must be specified + | { + conditions: T; + isRequired: true; + } + ); export interface AlertConditionsProps { headline?: string; @@ -107,52 +117,3 @@ export const AlertConditions = ({ ); }; - -export type AlertConditionsGroup = { - actionGroup?: ActionGroupWithCondition; -} & Pick, 'onResetConditionsFor'>; - -export const AlertConditionsGroup = ({ - actionGroup, - onResetConditionsFor, - children, - ...otherProps -}: PropsWithChildren>) => { - if (!actionGroup) { - return null; - } - - return ( - onResetConditionsFor(actionGroup)} - /> - ) - } - > - {React.isValidElement(children) ? ( - React.cloneElement(React.Children.only(children), { - actionGroup, - ...otherProps, - }) - ) : ( - - )} - - ); -}; - -// eslint-disable-next-line import/no-default-export -export { AlertConditions as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.test.tsx new file mode 100644 index 0000000000000..b2d280e0998cd --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.test.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import * as React from 'react'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; +import { act } from 'react-dom/test-utils'; +import { ReactWrapper } from 'enzyme'; +import { AlertConditionsGroup } from './alert_conditions_group'; +import { EuiFormRow, EuiButtonIcon } from '@elastic/eui'; + +describe('alert_conditions_group', () => { + async function setup(element: React.ReactElement): Promise> { + const wrapper = mountWithIntl(element); + + // Wait for active space to resolve before requesting the component to update + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + return wrapper; + } + + it('renders with actionGroup name as label', async () => { + const InnerComponent = () =>
{'inner component'}
; + const wrapper = await setup( + + + + ); + + expect(wrapper.find(EuiFormRow).prop('label')).toMatchInlineSnapshot(` + + + My Group + + + `); + expect(wrapper.find(InnerComponent).prop('actionGroup')).toMatchInlineSnapshot(` + Object { + "id": "myGroup", + "name": "My Group", + } + `); + }); + + it('renders a reset button when onResetConditionsFor is specified', async () => { + const onResetConditionsFor = jest.fn(); + const wrapper = await setup( + +
{'inner component'}
+
+ ); + + expect(wrapper.find(EuiButtonIcon).prop('aria-label')).toMatchInlineSnapshot(`"Remove"`); + + wrapper.find(EuiButtonIcon).simulate('click'); + + expect(onResetConditionsFor).toHaveBeenCalledWith({ + id: 'myGroup', + name: 'My Group', + }); + }); + + it('shouldnt render a reset button when isRequired is true', async () => { + const onResetConditionsFor = jest.fn(); + const wrapper = await setup( + +
{'inner component'}
+
+ ); + + expect(wrapper.find(EuiButtonIcon).length).toEqual(0); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx new file mode 100644 index 0000000000000..b1c5796fd09d5 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { Fragment, PropsWithChildren } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFormRow, EuiButtonIcon, EuiTitle } from '@elastic/eui'; +import { AlertConditionsProps, ActionGroupWithCondition } from './alert_conditions'; + +export type AlertConditionsGroupProps = { + actionGroup?: ActionGroupWithCondition; +} & Pick, 'onResetConditionsFor'>; + +export const AlertConditionsGroup = ({ + actionGroup, + onResetConditionsFor, + children, + ...otherProps +}: PropsWithChildren>) => { + if (!actionGroup) { + return null; + } + + return ( + + {actionGroup.name} + + } + fullWidth + labelAppend={ + onResetConditionsFor && + !actionGroup.isRequired && ( + onResetConditionsFor(actionGroup)} + /> + ) + } + > + {React.isValidElement(children) ? ( + React.cloneElement(React.Children.only(children), { + actionGroup, + ...otherProps, + }) + ) : ( + + )} + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx index 1c499511ec921..421f0fc26dd68 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/index.tsx @@ -7,10 +7,10 @@ import { lazy } from 'react'; import { suspendedComponentWithProps } from '../../lib/suspended_component_with_props'; export { AlertConditions, - AlertConditionsGroup, ActionGroupWithCondition, AlertConditionsProps, } from './alert_conditions'; +export { AlertConditionsGroup } from './alert_conditions_group'; export const AlertAdd = suspendedComponentWithProps(lazy(() => import('./alert_add'))); export const AlertEdit = suspendedComponentWithProps(lazy(() => import('./alert_edit'))); From e3ac6a74586a5fa55779f2e08b88081fa21844da Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 12 Nov 2020 17:26:41 +0000 Subject: [PATCH 21/30] removed unused imports --- .../sections/alert_form/alert_conditions.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx index 5dae433d4e8aa..1952ff4cbd76c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx @@ -3,18 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, PropsWithChildren } from 'react'; -import { i18n } from '@kbn/i18n'; +import React, { PropsWithChildren } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - EuiFlexItem, - EuiText, - EuiFlexGroup, - EuiTitle, - EuiFormRow, - EuiButtonIcon, - EuiButtonEmpty, -} from '@elastic/eui'; +import { EuiFlexItem, EuiText, EuiFlexGroup, EuiTitle, EuiButtonEmpty } from '@elastic/eui'; import { partition } from 'lodash'; import { ActionGroup } from '../../../../../alerts/common'; From f2af61519dc46bb56f91c57349d056979e757043 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 16 Nov 2020 12:19:39 +0000 Subject: [PATCH 22/30] hide uptime flyout if no alertTypeId is selected --- .../alerts/uptime_alerts_flyout_wrapper.tsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx index de4e67cfe8edc..9caf0a62a4049 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx @@ -17,14 +17,15 @@ export const UptimeAlertsFlyoutWrapperComponent = ({ alertFlyoutVisible, alertTypeId, setAlertFlyoutVisibility, -}: Props) => ( - -); +}: Props) => + alertFlyoutVisible && alertTypeId ? ( + + ) : null; From f658c78bdecd8c5d03c097c13749f9c315dfb2e9 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 16 Nov 2020 12:50:38 +0000 Subject: [PATCH 23/30] hide built in action groups in conditions UI --- .../sections/alert_form/alert_conditions.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx index 1952ff4cbd76c..2bab44216a050 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx @@ -7,7 +7,9 @@ import React, { PropsWithChildren } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexItem, EuiText, EuiFlexGroup, EuiTitle, EuiButtonEmpty } from '@elastic/eui'; import { partition } from 'lodash'; -import { ActionGroup } from '../../../../../alerts/common'; +import { ActionGroup, getBuiltinActionGroups } from '../../../../../alerts/common'; + +const BUILT_IN_ACTION_GROUPS: Set = new Set(getBuiltinActionGroups().map(({ id }) => id)); export type ActionGroupWithCondition = ActionGroup & ( @@ -28,6 +30,7 @@ export interface AlertConditionsProps { actionGroups: Array>; onInitializeConditionsFor?: (actionGroup: ActionGroupWithCondition) => void; onResetConditionsFor?: (actionGroup: ActionGroupWithCondition) => void; + includeBuiltInActionGroups?: boolean; } export const AlertConditions = ({ @@ -35,10 +38,14 @@ export const AlertConditions = ({ actionGroups, onInitializeConditionsFor, onResetConditionsFor, + includeBuiltInActionGroups = false, children, }: PropsWithChildren>) => { - const [withConditions, withoutConditions] = partition(actionGroups, (actionGroup) => - actionGroup.hasOwnProperty('conditions') + const [withConditions, withoutConditions] = partition( + includeBuiltInActionGroups + ? actionGroups + : actionGroups.filter(({ id }) => !BUILT_IN_ACTION_GROUPS.has(id)), + (actionGroup) => actionGroup.hasOwnProperty('conditions') ); return ( From 1b1c5323a1e0052fe0d1a86dd3401072992e821e Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 16 Nov 2020 12:54:22 +0000 Subject: [PATCH 24/30] fixed typing --- .../application/sections/alert_form/alert_conditions.test.tsx | 2 +- .../sections/alert_form/alert_conditions_group.test.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx index 7cc5cfcaf669f..1732b98e462ce 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import * as React from 'react'; -import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; +import { mountWithIntl, nextTick } from '@kbn/test/jest'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; import { AlertConditions, ActionGroupWithCondition } from './alert_conditions'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.test.tsx index b2d280e0998cd..dd12af4ae9e62 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import * as React from 'react'; -import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; +import { mountWithIntl, nextTick } from '@kbn/test/jest'; import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; import { AlertConditionsGroup } from './alert_conditions_group'; From 158e1e3c28d7c123b467660a06dc26bc828f0913 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 17 Nov 2020 19:35:40 +0000 Subject: [PATCH 25/30] fixed typing in alert flyouts and race condition on alertType id update --- x-pack/plugins/alerts/common/alert.ts | 3 +- .../expressions/entity_index_expression.tsx | 8 +- .../public/application/lib/capabilities.ts | 7 +- .../action_connector_form/action_form.tsx | 4 +- .../action_type_form.tsx | 4 +- .../sections/alert_form/alert_add.tsx | 50 +++++---- .../sections/alert_form/alert_edit.tsx | 12 ++- .../sections/alert_form/alert_form.tsx | 41 +++++-- .../sections/alert_form/alert_reducer.ts | 100 ++++++++++++++---- .../triggers_actions_ui/public/types.ts | 6 +- .../alerts/uptime_alerts_flyout_wrapper.tsx | 23 ++-- 11 files changed, 172 insertions(+), 86 deletions(-) diff --git a/x-pack/plugins/alerts/common/alert.ts b/x-pack/plugins/alerts/common/alert.ts index 97a9a58400e38..88f6090d20737 100644 --- a/x-pack/plugins/alerts/common/alert.ts +++ b/x-pack/plugins/alerts/common/alert.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectAttributes } from 'kibana/server'; +import { SavedObjectAttribute, SavedObjectAttributes } from 'kibana/server'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type AlertTypeState = Record; @@ -37,6 +37,7 @@ export interface AlertExecutionStatus { } export type AlertActionParams = SavedObjectAttributes; +export type AlertActionParam = SavedObjectAttribute; export interface AlertAction { group: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx index e5e43210d1e6b..0a722734ffc5a 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/query_builder/expressions/entity_index_expression.tsx @@ -8,7 +8,11 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { IErrorObject, AlertsContextValue } from '../../../../../../triggers_actions_ui/public'; +import { + IErrorObject, + AlertsContextValue, + AlertTypeParamsExpressionProps, +} from '../../../../../../triggers_actions_ui/public'; import { ES_GEO_FIELD_TYPES } from '../../types'; import { GeoIndexPatternSelect } from '../util_components/geo_index_pattern_select'; import { SingleFieldSelect } from '../util_components/single_field_select'; @@ -23,7 +27,7 @@ interface Props { errors: IErrorObject; setAlertParamsDate: (date: string) => void; setAlertParamsGeoField: (geoField: string) => void; - setAlertProperty: (alertProp: string, alertParams: unknown) => void; + setAlertProperty: AlertTypeParamsExpressionProps['setAlertProperty']; setIndexPattern: (indexPattern: IIndexPattern) => void; indexPattern: IIndexPattern; isInvalid: boolean; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts index 9e89a38377a4d..7fb50eaab7d7d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert, AlertType } from '../../types'; +import { AlertType } from '../../types'; +import { InitialAlert } from '../sections/alert_form/alert_reducer'; /** * NOTE: Applications that want to show the alerting UIs will need to add @@ -21,9 +22,9 @@ export const hasExecuteActionsCapability = (capabilities: Capabilities) => export const hasDeleteActionsCapability = (capabilities: Capabilities) => capabilities?.actions?.delete; -export function hasAllPrivilege(alert: Alert, alertType?: AlertType): boolean { +export function hasAllPrivilege(alert: InitialAlert, alertType?: AlertType): boolean { return alertType?.authorizedConsumers[alert.consumer]?.all ?? false; } -export function hasReadPrivilege(alert: Alert, alertType?: AlertType): boolean { +export function hasReadPrivilege(alert: InitialAlert, alertType?: AlertType): boolean { return alertType?.authorizedConsumers[alert.consumer]?.read ?? false; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 50f5167b9e5c2..83e6386122eb2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -36,7 +36,7 @@ import { AddConnectorInline } from './connector_add_inline'; import { actionTypeCompare } from '../../lib/action_type_compare'; import { checkActionFormActionTypeEnabled } from '../../lib/check_action_type_enabled'; import { VIEW_LICENSE_OPTIONS_LINK, DEFAULT_HIDDEN_ACTION_TYPES } from '../../../common/constants'; -import { ActionGroup } from '../../../../../alerts/common'; +import { ActionGroup, AlertActionParam } from '../../../../../alerts/common'; export interface ActionAccordionFormProps { actions: AlertAction[]; @@ -45,7 +45,7 @@ export interface ActionAccordionFormProps { setActionIdByIndex: (id: string, index: number) => void; setActionGroupIdByIndex?: (group: string, index: number) => void; setAlertProperty: (actions: AlertAction[]) => void; - setActionParamsProperty: (key: string, value: any, index: number) => void; + setActionParamsProperty: (key: string, value: AlertActionParam, index: number) => void; http: HttpSetup; actionTypeRegistry: ActionTypeRegistryContract; toastNotifications: ToastsSetup; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index bd40d35b15b2d..5f1798d101d94 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -25,7 +25,7 @@ import { EuiLoadingSpinner, EuiBadge, } from '@elastic/eui'; -import { ResolvedActionGroup } from '../../../../../alerts/common'; +import { AlertActionParam, ResolvedActionGroup } from '../../../../../alerts/common'; import { IErrorObject, AlertAction, @@ -50,7 +50,7 @@ export type ActionTypeFormProps = { onAddConnector: () => void; onConnectorSelected: (id: string) => void; onDeleteAction: () => void; - setActionParamsProperty: (key: string, value: any, index: number) => void; + setActionParamsProperty: (key: string, value: AlertActionParam, index: number) => void; actionTypesIndex: ActionTypeIndex; connectors: ActionConnector[]; } & Pick< diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 89deb4b26f012..8af8ddb3fcfe1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { useCallback, useReducer, useState, useEffect } from 'react'; -import { isObject } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, EuiFlyoutHeader, EuiFlyout, EuiFlyoutBody, EuiPortal } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useAlertsContext } from '../../context/alerts_context'; import { Alert, AlertAction, IErrorObject } from '../../../types'; -import { AlertForm, validateBaseProperties } from './alert_form'; -import { alertReducer } from './alert_reducer'; +import { AlertForm, isValidAlert, validateBaseProperties } from './alert_form'; +import { alertReducer, InitialAlert, InitialAlertReducer } from './alert_reducer'; import { createAlert } from '../../lib/alert_api'; import { HealthCheck } from '../../components/health_check'; import { ConfirmAlertSave } from './confirm_alert_save'; @@ -36,7 +35,7 @@ export const AlertAdd = ({ alertTypeId, initialValues, }: AlertAddProps) => { - const initialAlert = ({ + const initialAlert: InitialAlert = { params: {}, consumer, alertTypeId, @@ -46,16 +45,19 @@ export const AlertAdd = ({ actions: [], tags: [], ...(initialValues ? initialValues : {}), - } as unknown) as Alert; + }; - const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert }); + const [{ alert }, dispatch] = useReducer(alertReducer as InitialAlertReducer, { + alert: initialAlert, + }); const [isSaving, setIsSaving] = useState(false); const [isConfirmAlertSaveModalOpen, setIsConfirmAlertSaveModalOpen] = useState(false); - const setAlert = (value: any) => { + const setAlert = (value: InitialAlert) => { dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; - const setAlertProperty = (key: string, value: any) => { + + const setAlertProperty = (key: Key, value: Alert[Key] | null) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }; @@ -72,7 +74,7 @@ export const AlertAdd = ({ const canShowActions = hasShowActionsCapability(capabilities); useEffect(() => { - setAlertProperty('alertTypeId', alertTypeId); + setAlertProperty('alertTypeId', alertTypeId ?? null); }, [alertTypeId]); const closeFlyout = useCallback(() => { @@ -100,7 +102,7 @@ export const AlertAdd = ({ ...(alertType ? alertType.validate(alert.params).errors : []), ...validateBaseProperties(alert).errors, } as IErrorObject; - const hasErrors = parseErrors(errors); + const hasErrors = !isValidAlert(alert, errors); const actionsErrors: Array<{ errors: IErrorObject; @@ -120,16 +122,18 @@ export const AlertAdd = ({ async function onSaveAlert(): Promise { try { - const newAlert = await createAlert({ http, alert }); - toastNotifications.addSuccess( - i18n.translate('xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText', { - defaultMessage: 'Created alert "{alertName}"', - values: { - alertName: newAlert.name, - }, - }) - ); - return newAlert; + if (isValidAlert(alert, errors)) { + const newAlert = await createAlert({ http, alert }); + toastNotifications.addSuccess( + i18n.translate('xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText', { + defaultMessage: 'Created alert "{alertName}"', + values: { + alertName: newAlert.name, + }, + }) + ); + return newAlert; + } } catch (errorRes) { toastNotifications.addDanger( errorRes.body?.message ?? @@ -206,11 +210,5 @@ export const AlertAdd = ({ ); }; -const parseErrors: (errors: IErrorObject) => boolean = (errors) => - !!Object.values(errors).find((errorList) => { - if (isObject(errorList)) return parseErrors(errorList as IErrorObject); - return errorList.length >= 1; - }); - // eslint-disable-next-line import/no-default-export export { AlertAdd as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 5eadc742a9dc8..a1ad04ea9104a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { useAlertsContext } from '../../context/alerts_context'; import { Alert, AlertAction, IErrorObject } from '../../../types'; import { AlertForm, validateBaseProperties } from './alert_form'; -import { alertReducer } from './alert_reducer'; +import { alertReducer, ConcreteAlertReducer } from './alert_reducer'; import { updateAlert } from '../../lib/alert_api'; import { HealthCheck } from '../../components/health_check'; import { HealthContextProvider } from '../../context/health_context'; @@ -34,14 +34,16 @@ interface AlertEditProps { } export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { - const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert }); + const [{ alert }, dispatch] = useReducer(alertReducer as ConcreteAlertReducer, { + alert: initialAlert, + }); const [isSaving, setIsSaving] = useState(false); const [hasActionsDisabled, setHasActionsDisabled] = useState(false); const [hasActionsWithBrokenConnector, setHasActionsWithBrokenConnector] = useState( false ); - const setAlert = (key: string, value: any) => { - dispatch({ command: { type: 'setAlert' }, payload: { key, value } }); + const setAlert = (value: Alert) => { + dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; const { @@ -55,7 +57,7 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { const closeFlyout = useCallback(() => { onClose(); - setAlert('alert', initialAlert); + setAlert(initialAlert); // eslint-disable-next-line react-hooks/exhaustive-deps }, [onClose]); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index 98db6f46d1497..b06fb3c39ea45 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -33,14 +33,14 @@ import { } from '@elastic/eui'; import { some, filter, map, fold } from 'fp-ts/lib/Option'; import { pipe } from 'fp-ts/lib/pipeable'; -import { capitalize } from 'lodash'; +import { capitalize, isObject } from 'lodash'; import { KibanaFeature } from '../../../../../features/public'; import { getDurationNumberInItsUnit, getDurationUnitValue, } from '../../../../../alerts/common/parse_duration'; import { loadAlertTypes } from '../../lib/alert_api'; -import { AlertReducerAction } from './alert_reducer'; +import { AlertReducerAction, InitialAlert } from './alert_reducer'; import { AlertTypeModel, Alert, @@ -48,18 +48,19 @@ import { AlertAction, AlertTypeIndex, AlertType, + ValidationResult, } from '../../../types'; import { getTimeOptions } from '../../../common/lib/get_time_options'; import { useAlertsContext } from '../../context/alerts_context'; import { ActionForm } from '../action_connector_form'; -import { ALERTS_FEATURE_ID } from '../../../../../alerts/common'; +import { AlertActionParam, ALERTS_FEATURE_ID } from '../../../../../alerts/common'; import { hasAllPrivilege, hasShowActionsCapability } from '../../lib/capabilities'; import { SolutionFilter } from './solution_filter'; import './alert_form.scss'; const ENTER_KEY = 13; -export function validateBaseProperties(alertObject: Alert) { +export function validateBaseProperties(alertObject: InitialAlert): ValidationResult { const validationResult = { errors: {} }; const errors = { name: new Array(), @@ -92,12 +93,25 @@ export function validateBaseProperties(alertObject: Alert) { return validationResult; } +const hasErrors: (errors: IErrorObject) => boolean = (errors) => + !!Object.values(errors).find((errorList) => { + if (isObject(errorList)) return hasErrors(errorList as IErrorObject); + return errorList.length >= 1; + }); + +export function isValidAlert( + alertObject: InitialAlert | Alert, + validationResult: IErrorObject +): alertObject is Alert { + return !hasErrors(validationResult); +} + function getProducerFeatureName(producer: string, kibanaFeatures: KibanaFeature[]) { return kibanaFeatures.find((featureItem) => featureItem.id === producer)?.name; } interface AlertFormProps { - alert: Alert; + alert: InitialAlert; dispatch: React.Dispatch; errors: IErrorObject; canChangeTrigger?: boolean; // to hide Change trigger button @@ -203,10 +217,13 @@ export const AlertForm = ({ useEffect(() => { setAlertTypeModel(alert.alertTypeId ? alertTypeRegistry.get(alert.alertTypeId) : null); - }, [alert, alertTypeRegistry]); + if (alert.alertTypeId && alertTypesIndex && alertTypesIndex.has(alert.alertTypeId)) { + setDefaultActionGroupId(alertTypesIndex.get(alert.alertTypeId)!.defaultActionGroupId); + } + }, [alert, alert.alertTypeId, alertTypesIndex, alertTypeRegistry]); const setAlertProperty = useCallback( - (key: string, value: any) => { + (key: Key, value: Alert[Key] | null) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); }, [dispatch] @@ -225,12 +242,16 @@ export const AlertForm = ({ dispatch({ command: { type: 'setScheduleProperty' }, payload: { key, value } }); }; - const setActionProperty = (key: string, value: any, index: number) => { + const setActionProperty = ( + key: Key, + value: AlertAction[Key] | null, + index: number + ) => { dispatch({ command: { type: 'setAlertActionProperty' }, payload: { key, value, index } }); }; const setActionParamsProperty = useCallback( - (key: string, value: any, index: number) => { + (key: string, value: AlertActionParam, index: number) => { dispatch({ command: { type: 'setAlertActionParams' }, payload: { key, value, index } }); }, [dispatch] @@ -438,6 +459,7 @@ export const AlertForm = ({ {AlertParamsExpressionComponent && defaultActionGroupId && + alert.alertTypeId && alertTypesIndex?.has(alert.alertTypeId) ? ( }> & + Pick; + +interface CommandType< + T extends | 'setAlert' | 'setProperty' | 'setScheduleProperty' | 'setAlertParams' | 'setAlertActionParams' - | 'setAlertActionProperty'; + | 'setAlertActionProperty' +> { + type: T; } export interface AlertState { - alert: any; + alert: InitialAlert; +} + +interface Payload { + key: Keys; + value: Value; + index?: number; +} + +interface AlertPayload { + key: Key; + value: Alert[Key] | null; + index?: number; +} + +interface AlertActionPayload { + key: Key; + value: AlertAction[Key] | null; + index?: number; } -export interface AlertReducerAction { - command: CommandType; - payload: { - key: string; - value: {}; - index?: number; - }; +interface AlertSchedulePayload { + key: Key; + value: IntervalSchedule[Key]; + index?: number; } -export const alertReducer = (state: any, action: AlertReducerAction) => { - const { command, payload } = action; +export type AlertReducerAction = + | { + command: CommandType<'setAlert'>; + payload: Payload<'alert', InitialAlert>; + } + | { + command: CommandType<'setProperty'>; + payload: AlertPayload; + } + | { + command: CommandType<'setScheduleProperty'>; + payload: AlertSchedulePayload; + } + | { + command: CommandType<'setAlertParams'>; + payload: Payload; + } + | { + command: CommandType<'setAlertActionParams'>; + payload: Payload; + } + | { + command: CommandType<'setAlertActionProperty'>; + payload: AlertActionPayload; + }; + +export type InitialAlertReducer = Reducer<{ alert: InitialAlert }, AlertReducerAction>; +export type ConcreteAlertReducer = Reducer<{ alert: Alert }, AlertReducerAction>; + +export const alertReducer = ( + state: { alert: AlertPhase }, + action: AlertReducerAction +) => { const { alert } = state; - switch (command.type) { + switch (action.command.type) { case 'setAlert': { - const { key, value } = payload; + const { key, value } = action.payload as Payload<'alert', AlertPhase>; if (key === 'alert') { return { ...state, @@ -45,7 +100,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setProperty': { - const { key, value } = payload; + const { key, value } = action.payload as AlertPayload; if (isEqual(alert[key], value)) { return state; } else { @@ -59,8 +114,8 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setScheduleProperty': { - const { key, value } = payload; - if (isEqual(alert.schedule[key], value)) { + const { key, value } = action.payload as AlertSchedulePayload; + if (alert.schedule && isEqual(alert.schedule[key], value)) { return state; } else { return { @@ -76,7 +131,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setAlertParams': { - const { key, value } = payload; + const { key, value } = action.payload as Payload>; if (isEqual(alert.params[key], value)) { return state; } else { @@ -93,7 +148,10 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setAlertActionParams': { - const { key, value, index } = payload; + const { key, value, index } = action.payload as Payload< + keyof AlertAction, + SavedObjectAttribute + >; if (index === undefined || isEqual(alert.actions[index][key], value)) { return state; } else { @@ -116,7 +174,7 @@ export const alertReducer = (state: any, action: AlertReducerAction) => { } } case 'setAlertActionProperty': { - const { key, value, index } = payload; + const { key, value, index } = action.payload as AlertActionPayload; if (index === undefined || isEqual(alert.actions[index][key], value)) { return state; } else { diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index ebd5dd4a28968..cc0522eeb52a1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -6,7 +6,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import type { HttpSetup, DocLinksStart, ToastsSetup } from 'kibana/public'; import { ComponentType } from 'react'; -import { ActionGroup } from '../../alerts/common'; +import { ActionGroup, AlertActionParam } from '../../alerts/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; import { @@ -52,7 +52,7 @@ export interface ActionConnectorFieldsProps { export interface ActionParamsProps { actionParams: TParams; index: number; - editAction: (property: string, value: any, index: number) => void; + editAction: (key: string, value: AlertActionParam, index: number) => void; errors: IErrorObject; messageVariables?: ActionVariable[]; defaultMessage?: string; @@ -166,7 +166,7 @@ export interface AlertTypeParamsExpressionProps< alertInterval: string; alertThrottle: string; setAlertParams: (property: string, value: any) => void; - setAlertProperty: (key: string, value: any) => void; + setAlertProperty: (key: Key, value: Alert[Key] | null) => void; errors: IErrorObject; alertsContext: AlertsContextValue; defaultActionGroupId: string; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx index 9caf0a62a4049..de4e67cfe8edc 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx @@ -17,15 +17,14 @@ export const UptimeAlertsFlyoutWrapperComponent = ({ alertFlyoutVisible, alertTypeId, setAlertFlyoutVisibility, -}: Props) => - alertFlyoutVisible && alertTypeId ? ( - - ) : null; +}: Props) => ( + +); From 988dc5f046ca016b9fbfab01426db8ab0494fc79 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 18 Nov 2020 16:33:41 +0000 Subject: [PATCH 26/30] added docs --- x-pack/plugins/triggers_actions_ui/README.md | 150 +++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index ef81065608ad4..3e5e95996c80f 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -25,6 +25,7 @@ Table of Contents - [GROUPED BY expression component](#grouped-by-expression-component) - [FOR THE LAST expression component](#for-the-last-expression-component) - [THRESHOLD expression component](#threshold-expression-component) + - [Alert Conditions Components](#alert-conditions-components) - [Embed the Create Alert flyout within any Kibana plugin](#embed-the-create-alert-flyout-within-any-kibana-plugin) - [Build and register Action Types](#build-and-register-action-types) - [Built-in Action Types](#built-in-action-types) @@ -634,6 +635,155 @@ interface ThresholdExpressionProps { |customComparators|(Optional) List of comparators that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts`.| |popupPosition|(Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space.| +## Alert Conditions Components +To aid in creating a uniform UX across Alert Types, we provide two components for specifying the conditions for detection of a certain alert under within any specific Action Groups: +1. `AlertConditions`: A component that generates a container which renders custom component for each Action Group which has had its _conditions_ specified. +2. `AlertConditionsGroup`: A component that provides a unified container for the Action Group with its name and a button for resetting its condition. + +These can be used by any Alert Type to easily create the UI for adding action groups along with an Alert Type specific component. + +For Example: +Given an Alert Type which requires different thresholds for each detected Action Group (for example), you might have a `ThresholdSpecifier` component for specifying the threshold for a specific Action Group. + +``` +const ThresholdSpecifier = ( + { + actionGroup, + setThreshold + } : { + actionGroup?: ActionGroupWithCondition; + setThreshold: (actionGroup: ActionGroupWithCondition) => void; +}) => { + if (!actionGroup) { + // render empty if no condition action group is specified + return ; + } + + return ( + { + const conditions = parseInt(e.target.value, 10); + if (e.target.value && !isNaN(conditions)) { + setThreshold({ + ...actionGroup, + conditions, + }); + } + }} + /> + ); +}; + +``` + +This component takes two props, one which is required (`actionGroup`) and one which is alert type specific (`setThreshold`). +The `actionGroup` will be populated by the `AlertConditions` component, but `setThreshold` will have to be provided by the AlertType itself. + +To understand how this is used, lets take a closer look at `actionGroup`: + +``` +type ActionGroupWithCondition = ActionGroup & + ( + | // allow isRequired=false with or without conditions + { + conditions?: T; + isRequired?: false; + } + // but if isRequired=true then conditions must be specified + | { + conditions: T; + isRequired: true; + } + ) +``` + +The `condition` field is Alert Type specific, and holds whichever type an Alert Type needs for specifying the condition under which a certain detection falls under that specific Action Group. +In our example, this is a `number` as that's all we need to speciufy the threshold which dictates whether an alert falls into one actio ngroup rather than another. + +The `isRequired` field specifies whether this specific action group is _required_, that is, you can't reset its condition and _have_ to specify a some condition for it. + +Using this `ThresholdSpecifier` component, we can now use `AlertConditionsGroup` & `AlertConditions` to enable the user to specify these thresholds for each action group in the alert type. + +Like so: +``` +interface ThresholdAlertTypeParams { + thresholds?: { + alert?: number; + warning?: number; + error?: number; + }; +} + +const DEFAULT_THRESHOLDS: ThresholdAlertTypeParams['threshold] = { + alert: 50, + warning: 80, + error: 90, +}; +``` + +``` + { + setAlertParams('thresholds', { + ...thresholds, + ...pick(DEFAULT_THRESHOLDS, actionGroup.id), + }); + }} +> + { + setAlertParams('thresholds', omit(thresholds, actionGroup.id)); + }} + > + { + setAlertParams('thresholds', { + ...thresholds, + [actionGroup.id]: actionGroup.conditions, + }); + }} + /> + + +``` + +### The AlertConditions component + +This component will render the `Conditions` header & headline, along with the selectors for adding every Action Group you specity. +Additionally it will clone its `children` for _each_ action group which has a `condition` specified for it, passing in the appropriate `actionGroup` prop for each one. + +|Property|Description| +|---|---| +|headline|The headline title displayed above the fields | +|actionGroups|A list of `ActionGroupWithCondition` which includes all the action group you wish to offer the user and what conditions they are already configured to follow| +|onInitializeConditionsFor|A callback which is called when the user ask for a certain actionGroup to be initialized with an initial default condition. If you have no specific default, that's fine, as the component will render the action group's field even if the condition is empty (using a `null` or an `undefined`) and determines whether to render these fields by _the very presence_ of a `condition` field| + +### The AlertConditionsGroup component + +This component renders a standard EuiTitle foe each action group, wrapping the Alert Type specific component, in addition to a "reset" button which allows the user to reset the condition for that action group. The definition of what a _reset_ actually means is Alert Type specific, and up to the implementor to decide. In some case it might mean removing the condition, in others it might mean to reset it to some default value on the server side. In either case, it should _delete_ the `condition` field from the appropriate `actionGroup` as per the above example. + +|Property|Description| +|---|---| +|onResetConditionsFor|A callback which is called when the user clicks the _reset_ button besides the action group's title. The implementor should use this to remove the `condition` from the specified actionGroup| + + ## Embed the Create Alert flyout within any Kibana plugin Follow the instructions bellow to embed the Create Alert flyout within any Kibana plugin: From 1ec6e70959bdf5eeb7e556379e3faaabba573fb3 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 19 Nov 2020 13:09:26 +0000 Subject: [PATCH 27/30] fixed typing --- .../sections/alert_details/components/alert_details.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index b38f0e749a28d..d7de7e0a82c1e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -75,8 +75,8 @@ export const AlertDetails: React.FunctionComponent = ({ chrome, } = useAppDependencies(); const [{}, dispatch] = useReducer(alertReducer, { alert }); - const setInitialAlert = (key: string, value: any) => { - dispatch({ command: { type: 'setAlert' }, payload: { key, value } }); + const setInitialAlert = (value: Alert) => { + dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; // Set breadcrumb and page title @@ -172,7 +172,7 @@ export const AlertDetails: React.FunctionComponent = ({ { - setInitialAlert('alert', alert); + setInitialAlert(alert); setEditFlyoutVisibility(false); }} /> From 781975eb344fa24c62b21ce4f1a9ca92d68df3fc Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 19 Nov 2020 14:14:25 +0000 Subject: [PATCH 28/30] use memo to avoid needless rerendering --- .../sections/alert_form/alert_add.tsx | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 8af8ddb3fcfe1..34a4c909c65a9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useReducer, useState, useEffect } from 'react'; +import React, { useCallback, useReducer, useMemo, useState, useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, EuiFlyoutHeader, EuiFlyout, EuiFlyoutBody, EuiPortal } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -35,17 +35,20 @@ export const AlertAdd = ({ alertTypeId, initialValues, }: AlertAddProps) => { - const initialAlert: InitialAlert = { - params: {}, - consumer, - alertTypeId, - schedule: { - interval: '1m', - }, - actions: [], - tags: [], - ...(initialValues ? initialValues : {}), - }; + const initialAlert: InitialAlert = useMemo( + () => ({ + params: {}, + consumer, + alertTypeId, + schedule: { + interval: '1m', + }, + actions: [], + tags: [], + ...(initialValues ? initialValues : {}), + }), + [alertTypeId, consumer, initialValues] + ); const [{ alert }, dispatch] = useReducer(alertReducer as InitialAlertReducer, { alert: initialAlert, From d799b959185dce1f0378ae53d162e7a474885a3f Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 19 Nov 2020 17:12:15 +0000 Subject: [PATCH 29/30] fixed i18n --- .../application/sections/alert_form/alert_conditions.tsx | 4 ++-- .../sections/alert_form/alert_conditions_group.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx index 2bab44216a050..1eb086dd6a2c5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx @@ -56,7 +56,7 @@ export const AlertConditions = ({
@@ -92,7 +92,7 @@ export const AlertConditions = ({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx index b1c5796fd09d5..879f276317503 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx @@ -37,7 +37,7 @@ export const AlertConditionsGroup = ({ iconType="minusInCircle" color="danger" aria-label={i18n.translate( - 'xpack.triggersActionsUI.sections.alertAdd.conditions.removeConditionLabel', + 'xpack.triggersActionsUI.sections.alertForm.conditions.removeConditionLabel', { defaultMessage: 'Remove', } From 3aceac362d7222c5770a685cdadae87eeb18926e Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 19 Nov 2020 17:48:30 +0000 Subject: [PATCH 30/30] fixed test --- .../application/sections/alert_form/alert_conditions.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx index 1732b98e462ce..8029b43a2cf53 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx @@ -39,7 +39,7 @@ describe('alert_conditions', () => { ); expect(wrapper.find(EuiTitle).find(FormattedMessage).prop('id')).toMatchInlineSnapshot( - `"xpack.triggersActionsUI.sections.alertAdd.conditions.title"` + `"xpack.triggersActionsUI.sections.alertForm.conditions.title"` ); expect( wrapper.find(EuiTitle).find(FormattedMessage).prop('defaultMessage')