mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[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 <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
3d7ccc5544
commit
07557b686c
10 changed files with 114 additions and 126 deletions
|
@ -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';
|
|
@ -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';
|
|
@ -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' }
|
||||
);
|
|
@ -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é",
|
||||
|
|
|
@ -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": "構成の更新が失敗しました",
|
||||
|
|
|
@ -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": "配置更新失败",
|
||||
|
|
|
@ -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<CustomThresholdAle
|
|||
shouldWrite: true,
|
||||
};
|
||||
|
||||
export const searchConfigurationSchema = 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()),
|
||||
})
|
||||
)
|
||||
),
|
||||
});
|
||||
|
||||
export function thresholdRuleType(
|
||||
basePath: IBasePath,
|
||||
config: ObservabilityConfig,
|
||||
logger: Logger,
|
||||
locators: CustomThresholdLocators
|
||||
) {
|
||||
const comparators = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS });
|
||||
const baseCriterion = {
|
||||
threshold: schema.arrayOf(schema.number()),
|
||||
comparator: oneOfLiterals(comparators),
|
||||
timeUnit: schema.string(),
|
||||
timeSize: schema.number(),
|
||||
};
|
||||
const allowedAggregators = Object.values(Aggregators);
|
||||
allowedAggregators.splice(Object.values(Aggregators).indexOf(Aggregators.COUNT), 1);
|
||||
|
||||
const customCriterion = schema.object({
|
||||
...baseCriterion,
|
||||
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()),
|
||||
});
|
||||
|
||||
const paramsSchema = 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: searchConfigurationSchema,
|
||||
},
|
||||
{ unknowns: 'allow' }
|
||||
);
|
||||
|
||||
return {
|
||||
id: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID,
|
||||
name: i18n.translate('xpack.observability.threshold.ruleName', {
|
||||
|
@ -133,12 +60,12 @@ export function thresholdRuleType(
|
|||
}),
|
||||
fieldsForAAD: CUSTOM_THRESHOLD_AAD_FIELDS,
|
||||
validate: {
|
||||
params: paramsSchema,
|
||||
params: customThresholdParamsSchema,
|
||||
},
|
||||
schemas: {
|
||||
params: {
|
||||
type: 'config-schema' as const,
|
||||
schema: paramsSchema,
|
||||
schema: customThresholdParamsSchema,
|
||||
},
|
||||
},
|
||||
defaultActionGroupId: FIRED_ACTION.id,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { flattenObject, getFormattedGroupBy, validateKQLStringFilter } from './utils';
|
||||
import { flattenObject, getFormattedGroupBy } from './utils';
|
||||
|
||||
describe('FlattenObject', () => {
|
||||
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<string>())).toEqual({});
|
||||
|
|
|
@ -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<string[]>) =>
|
|||
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,
|
||||
|
|
|
@ -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/**/*"]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue