From 07557b686c79c2af9dd3b2789c66efc1f6ca840d Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 4 Feb 2025 21:01:05 +0100 Subject: [PATCH] [ResponseOps] Move custom threshold rule params to the package (#208686) Fixes: https://github.com/elastic/kibana/issues/195191 Move log threshold rule type params to the new package. P.S.: I've moved function `validateKQLStringFilter` and test for it in my previous PR: https://github.com/elastic/kibana/pull/205507 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- .../rule_params/custom_threshold/index.ts | 11 +++ .../rule_params/custom_threshold/latest.ts | 10 +++ .../rule_params/custom_threshold/v1.ts | 87 +++++++++++++++++++ .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../register_custom_threshold_rule_type.ts | 79 +---------------- .../lib/rules/custom_threshold/utils.test.ts | 25 +----- .../lib/rules/custom_threshold/utils.ts | 22 ----- .../plugins/observability/tsconfig.json | 3 +- 10 files changed, 114 insertions(+), 126 deletions(-) create mode 100644 src/platform/packages/shared/response-ops/rule_params/custom_threshold/index.ts create mode 100644 src/platform/packages/shared/response-ops/rule_params/custom_threshold/latest.ts create mode 100644 src/platform/packages/shared/response-ops/rule_params/custom_threshold/v1.ts diff --git a/src/platform/packages/shared/response-ops/rule_params/custom_threshold/index.ts b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/index.ts new file mode 100644 index 0000000000000..b0bbf12bb85fe --- /dev/null +++ b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { customThresholdParamsSchema } from './latest'; +export { customThresholdParamsSchema as customThresholdParamsSchemaV1 } from './v1'; diff --git a/src/platform/packages/shared/response-ops/rule_params/custom_threshold/latest.ts b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/latest.ts new file mode 100644 index 0000000000000..f278309c22b03 --- /dev/null +++ b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/latest.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 + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export * from './v1'; diff --git a/src/platform/packages/shared/response-ops/rule_params/custom_threshold/v1.ts b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/v1.ts new file mode 100644 index 0000000000000..dc7034077b1d0 --- /dev/null +++ b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/v1.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { schema } from '@kbn/config-schema'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { dataViewSpecSchema } from '../common'; +import { oneOfLiterals, validateKQLStringFilter, LEGACY_COMPARATORS } from '../common/utils'; + +const allowedAggregators = [ + 'avg', + 'sum', + 'min', + 'max', + 'cardinality', + 'rate', + 'p95', + 'p99', + 'last_value', +]; + +const comparators = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS }); + +const searchConfigSchema = schema.object({ + index: schema.oneOf([schema.string(), dataViewSpecSchema]), + query: schema.object({ + language: schema.string(), + query: schema.string({ + validate: validateKQLStringFilter, + }), + }), + filter: schema.maybe( + schema.arrayOf( + schema.object({ + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + meta: schema.recordOf(schema.string(), schema.any()), + }) + ) + ), +}); + +const customCriterion = schema.object({ + threshold: schema.arrayOf(schema.number()), + comparator: oneOfLiterals(comparators), + timeUnit: schema.string(), + timeSize: schema.number(), + aggType: schema.maybe(schema.literal('custom')), + metric: schema.never(), + metrics: schema.arrayOf( + schema.oneOf([ + schema.object({ + name: schema.string(), + aggType: oneOfLiterals(allowedAggregators), + field: schema.string(), + filter: schema.never(), + }), + schema.object({ + name: schema.string(), + aggType: schema.literal('count'), + filter: schema.maybe( + schema.string({ + validate: validateKQLStringFilter, + }) + ), + field: schema.never(), + }), + ]) + ), + equation: schema.maybe(schema.string()), + label: schema.maybe(schema.string()), +}); + +export const customThresholdParamsSchema = schema.object( + { + criteria: schema.arrayOf(customCriterion), + groupBy: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), + alertOnNoData: schema.maybe(schema.boolean()), + alertOnGroupDisappear: schema.maybe(schema.boolean()), + searchConfiguration: searchConfigSchema, + }, + { unknowns: 'allow' } +); diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index fd248475915aa..4929b77f19db7 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -31100,7 +31100,6 @@ "xpack.observability.customThreshold.rule.reason.forTheLast": "durée : {duration}", "xpack.observability.customThreshold.rule.reason.group": "groupe : {group}", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "Une description concise de la raison du signalement", - "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQuery doit être un filtre KQL valide (erreur : {errorMessage})", "xpack.observability.customThreshold.rule.sourceConfiguration.missingHttp": "Échec de chargement de la source : Aucun client HTTP disponible.", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureBody": "Nous n'avons pas pu appliquer les modifications à la configuration des indicateurs. Réessayez plus tard.", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureTitle": "La mise à jour de la configuration a échoué", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index 3f206cb9abb2b..2ac6a1f8e2fad 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -30964,7 +30964,6 @@ "xpack.observability.customThreshold.rule.reason.forTheLast": "duration: {duration}", "xpack.observability.customThreshold.rule.reason.group": "グループ:{group}", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "アラートの理由の簡潔な説明", - "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQueryは有効なKQLフィルターでなければなりません(エラー:{errorMessage})", "xpack.observability.customThreshold.rule.sourceConfiguration.missingHttp": "ソースの読み込みに失敗しました:HTTPクライアントがありません。", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureBody": "変更をメトリック構成に適用できませんでした。しばらくたってから再試行してください。", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureTitle": "構成の更新が失敗しました", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index 0e58d1cdca1d0..318453f80a687 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -30511,7 +30511,6 @@ "xpack.observability.customThreshold.rule.reason.forTheLast": "持续时间:{duration}", "xpack.observability.customThreshold.rule.reason.group": "组:{group}", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "告警原因的简洁描述", - "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQuery 必须是有效的 KQL 筛选(错误:{errorMessage})", "xpack.observability.customThreshold.rule.sourceConfiguration.missingHttp": "无法加载源:无 HTTP 客户端可用。", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureBody": "无法对指标配置应用更改。请稍后重试。", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureTitle": "配置更新失败", diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts index 2fe8cbe76d294..40b32835a0656 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts @@ -6,22 +6,17 @@ */ import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; import { extractReferences, injectReferences } from '@kbn/data-plugin/common'; -import { dataViewSpecSchema } from '@kbn/data-views-plugin/server/rest_api_routes/schema'; import { i18n } from '@kbn/i18n'; import { IRuleTypeAlerts, GetViewInAppRelativeUrlFnOpts } from '@kbn/alerting-plugin/server'; import { IBasePath, Logger } from '@kbn/core/server'; import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { LicenseType } from '@kbn/licensing-plugin/server'; -import { COMPARATORS } from '@kbn/alerting-comparators'; import { EsQueryRuleParamsExtractedParams } from '@kbn/stack-alerts-plugin/server/rule_types/es_query/rule_type_params'; -import { LEGACY_COMPARATORS } from '../../../../common/utils/convert_legacy_outside_comparator'; +import { customThresholdParamsSchema } from '@kbn/response-ops-rule-params/custom_threshold'; import { observabilityFeatureId, observabilityPaths } from '../../../../common'; -import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { THRESHOLD_RULE_REGISTRATION_CONTEXT } from '../../../common/constants'; - import { alertDetailUrlActionVariableDescription, cloudActionVariableDescription, @@ -36,7 +31,6 @@ import { valueActionVariableDescription, viewInAppUrlActionVariableDescription, } from './translations'; -import { oneOfLiterals, validateKQLStringFilter } from './utils'; import { createCustomThresholdExecutor, CustomThresholdLocators, @@ -53,79 +47,12 @@ export const MetricsRulesTypeAlertDefinition: IRuleTypeAlerts { it('flattens multi level item', () => { @@ -57,29 +57,6 @@ describe('FlattenObject', () => { }); }); -describe('validateKQLStringFilter', () => { - const data = [ - // input, output - ['', undefined], - ['host.name:host-0', undefined], - ]; - const dataWithError = [ - // input, output - [ - ':*', - 'filterQuery must be a valid KQL filter (error: Expected "(", NOT, end of input, field name, value, whitespace but ":" found.', - ], - ]; - - test.each(data)('validateKQLStringFilter(%s): %o', (input: any, output: any) => { - expect(validateKQLStringFilter(input)).toEqual(output); - }); - - test.each(dataWithError)('validateKQLStringFilter(%s): %o', (input: any, output: any) => { - expect(validateKQLStringFilter(input)).toContain(output); - }); -}); - describe('getFormattedGroupBy', () => { it('should format groupBy correctly for empty input', () => { expect(getFormattedGroupBy(undefined, new Set())).toEqual({}); diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/utils.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/utils.ts index ca9b378d74eaa..fd8420858a430 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/utils.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/utils.ts @@ -6,8 +6,6 @@ */ import { isError } from 'lodash'; -import { buildEsQuery as kbnBuildEsQuery } from '@kbn/es-query'; -import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { Logger, LogMeta } from '@kbn/logging'; import type { ElasticsearchClient, IBasePath } from '@kbn/core/server'; @@ -45,26 +43,6 @@ export const oneOfLiterals = (arrayOfLiterals: Readonly) => arrayOfLiterals.includes(value) ? undefined : `must be one of ${arrayOfLiterals.join(' | ')}`, }); -export const validateKQLStringFilter = (value: string) => { - if (value === '') { - // Allow clearing the filter. - return; - } - - try { - kbnBuildEsQuery(undefined, [{ query: value, language: 'kuery' }], [], { - allowLeadingWildcards: true, - queryStringOptions: {}, - ignoreFilterIfFieldNotInIndex: false, - }); - } catch (e) { - return i18n.translate('xpack.observability.customThreshold.rule.schema.invalidFilterQuery', { - defaultMessage: 'filterQuery must be a valid KQL filter (error: {errorMessage})', - values: { errorMessage: e?.message }, - }); - } -}; - export const createScopedLogger = ( logger: Logger, scope: string, diff --git a/x-pack/solutions/observability/plugins/observability/tsconfig.json b/x-pack/solutions/observability/plugins/observability/tsconfig.json index d78e468013be7..b096a09f5eeb2 100644 --- a/x-pack/solutions/observability/plugins/observability/tsconfig.json +++ b/x-pack/solutions/observability/plugins/observability/tsconfig.json @@ -114,7 +114,8 @@ "@kbn/response-ops-rule-form", "@kbn/streams-plugin", "@kbn/data-service", - "@kbn/ebt-tools" + "@kbn/ebt-tools", + "@kbn/response-ops-rule-params" ], "exclude": ["target/**/*"] }