mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[ResponseOps][Rules] Move the params of es query and index threshold rule types (#210197)
Connected with https://github.com/elastic/kibana/issues/195188 ## Summary - Moved params of es query rule type to `@kbn/response-ops-rule-params/es_query` package - Moved params of index threshold rule type to `@kbn/response-ops-rule-params/index_threshold` package The following constants for the es query rule type have been duplicated: - MAX_SELECTABLE_SOURCE_FIELDS - MAX_SELECTABLE_GROUP_BY_TERMS - ES_QUERY_MAX_HITS_PER_EXECUTION
This commit is contained in:
parent
3ce4cf575b
commit
71254c8ee5
40 changed files with 576 additions and 556 deletions
|
@ -12,6 +12,20 @@
|
|||
*/
|
||||
export const MAX_DATA_VIEW_FIELD_DESCRIPTION_LENGTH = 300;
|
||||
|
||||
export const MAX_SELECTABLE_SOURCE_FIELDS = 5;
|
||||
export const MAX_SELECTABLE_GROUP_BY_TERMS = 4;
|
||||
export const ES_QUERY_MAX_HITS_PER_EXECUTION = 10000;
|
||||
export const MAX_GROUPS = 1000;
|
||||
|
||||
export enum Comparator {
|
||||
GT = '>',
|
||||
LT = '<',
|
||||
GT_OR_EQ = '>=',
|
||||
LT_OR_EQ = '<=',
|
||||
BETWEEN = 'between',
|
||||
NOT_BETWEEN = 'notBetween',
|
||||
}
|
||||
|
||||
/**
|
||||
* All runtime field types.
|
||||
* @public
|
||||
|
|
|
@ -9,3 +9,5 @@
|
|||
|
||||
export * from './search_configuration_schema';
|
||||
export { dataViewSpecSchema } from './data_view_spec_schema';
|
||||
export { MAX_GROUPS } from './constants';
|
||||
export { ComparatorFns } from './utils';
|
||||
|
|
|
@ -10,8 +10,19 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { buildEsQuery as kbnBuildEsQuery } from '@kbn/es-query';
|
||||
import {
|
||||
buildEsQuery as kbnBuildEsQuery,
|
||||
toElasticsearchQuery,
|
||||
fromKueryExpression,
|
||||
} from '@kbn/es-query';
|
||||
|
||||
import { Comparator } from './constants';
|
||||
|
||||
export type ComparatorFn = (value: number, threshold: number[]) => boolean;
|
||||
|
||||
const TimeWindowUnits = new Set(['s', 'm', 'h', 'd']);
|
||||
const AggTypes = new Set(['count', 'avg', 'min', 'max', 'sum']);
|
||||
export const betweenComparators = new Set(['between', 'notBetween']);
|
||||
export const jobsSelectionSchema = schema.object(
|
||||
{
|
||||
jobIds: schema.arrayOf(schema.string(), { defaultValue: [] }),
|
||||
|
@ -80,3 +91,102 @@ export type TimeUnitChar = 's' | 'm' | 'h' | 'd';
|
|||
export enum LEGACY_COMPARATORS {
|
||||
OUTSIDE_RANGE = 'outside',
|
||||
}
|
||||
|
||||
export function validateTimeWindowUnits(timeWindowUnit: string): string | undefined {
|
||||
if (TimeWindowUnits.has(timeWindowUnit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.coreQueryParams.invalidTimeWindowUnitsErrorMessage',
|
||||
{
|
||||
defaultMessage: 'invalid timeWindowUnit: "{timeWindowUnit}"',
|
||||
values: {
|
||||
timeWindowUnit,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function validateAggType(aggType: string): string | undefined {
|
||||
if (AggTypes.has(aggType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.data.coreQueryParams.invalidAggTypeErrorMessage',
|
||||
{
|
||||
defaultMessage: 'invalid aggType: "{aggType}"',
|
||||
values: {
|
||||
aggType,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function validateGroupBy(groupBy: string): string | undefined {
|
||||
if (groupBy === 'all' || groupBy === 'top') {
|
||||
return;
|
||||
}
|
||||
|
||||
return i18n.translate('xpack.responseOps.ruleParams.coreQueryParams.invalidGroupByErrorMessage', {
|
||||
defaultMessage: 'invalid groupBy: "{groupBy}"',
|
||||
values: {
|
||||
groupBy,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const ComparatorFns = new Map<Comparator, ComparatorFn>([
|
||||
[Comparator.LT, (value: number, threshold: number[]) => value < threshold[0]],
|
||||
[Comparator.LT_OR_EQ, (value: number, threshold: number[]) => value <= threshold[0]],
|
||||
[Comparator.GT_OR_EQ, (value: number, threshold: number[]) => value >= threshold[0]],
|
||||
[Comparator.GT, (value: number, threshold: number[]) => value > threshold[0]],
|
||||
[
|
||||
Comparator.BETWEEN,
|
||||
(value: number, threshold: number[]) => value >= threshold[0] && value <= threshold[1],
|
||||
],
|
||||
[
|
||||
Comparator.NOT_BETWEEN,
|
||||
(value: number, threshold: number[]) => value < threshold[0] || value > threshold[1],
|
||||
],
|
||||
]);
|
||||
|
||||
export const getComparatorSchemaType = (validate: (comparator: Comparator) => string | void) =>
|
||||
schema.oneOf(
|
||||
[
|
||||
schema.literal(Comparator.GT),
|
||||
schema.literal(Comparator.LT),
|
||||
schema.literal(Comparator.GT_OR_EQ),
|
||||
schema.literal(Comparator.LT_OR_EQ),
|
||||
schema.literal(Comparator.BETWEEN),
|
||||
schema.literal(Comparator.NOT_BETWEEN),
|
||||
],
|
||||
{ validate }
|
||||
);
|
||||
|
||||
export const ComparatorFnNames = new Set(ComparatorFns.keys());
|
||||
|
||||
export function validateKuery(query: string): string | undefined {
|
||||
try {
|
||||
toElasticsearchQuery(fromKueryExpression(query));
|
||||
} catch (e) {
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.coreQueryParams.invalidKQLQueryErrorMessage',
|
||||
{
|
||||
defaultMessage: 'Filter query is invalid.',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateComparator(comparator: Comparator): string | undefined {
|
||||
if (ComparatorFnNames.has(comparator)) return;
|
||||
|
||||
return i18n.translate('xpack.responseOps.ruleParams.invalidComparatorErrorMessage', {
|
||||
defaultMessage: 'invalid thresholdComparator specified: {comparator}',
|
||||
values: {
|
||||
comparator,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { EsQueryRuleParamsSchema } from './latest';
|
||||
export { EsQueryRuleParamsSchema as EsQueryRuleParamsSchemaV1 } from './v1';
|
||||
|
||||
export type { EsQueryRuleParams } from './latest';
|
||||
export type { EsQueryRuleParams as EsQueryRuleParamsV1 } from './latest';
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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,217 @@
|
|||
/*
|
||||
* 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, TypeOf } from '@kbn/config-schema';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import {
|
||||
validateTimeWindowUnits,
|
||||
validateAggType,
|
||||
validateGroupBy,
|
||||
getComparatorSchemaType,
|
||||
betweenComparators,
|
||||
validateComparator,
|
||||
} from '../common/utils';
|
||||
|
||||
import {
|
||||
MAX_SELECTABLE_SOURCE_FIELDS,
|
||||
MAX_SELECTABLE_GROUP_BY_TERMS,
|
||||
ES_QUERY_MAX_HITS_PER_EXECUTION,
|
||||
MAX_GROUPS,
|
||||
Comparator,
|
||||
} from '../common/constants';
|
||||
|
||||
const EsQueryRuleParamsSchemaProperties = {
|
||||
size: schema.number({ min: 0, max: ES_QUERY_MAX_HITS_PER_EXECUTION }),
|
||||
timeWindowSize: schema.number({ min: 1 }),
|
||||
excludeHitsFromPreviousRun: schema.boolean({ defaultValue: true }),
|
||||
timeWindowUnit: schema.string({ validate: validateTimeWindowUnits }),
|
||||
threshold: schema.arrayOf(schema.number(), { minSize: 1, maxSize: 2 }),
|
||||
thresholdComparator: getComparatorSchemaType(validateComparator),
|
||||
// aggregation type
|
||||
aggType: schema.string({ validate: validateAggType, defaultValue: 'count' }),
|
||||
// aggregation field
|
||||
aggField: schema.maybe(schema.string({ minLength: 1 })),
|
||||
// how to group
|
||||
groupBy: schema.string({ validate: validateGroupBy, defaultValue: 'all' }),
|
||||
// field to group on (for groupBy: top)
|
||||
termField: schema.maybe(
|
||||
schema.oneOf([
|
||||
schema.string({ minLength: 1 }),
|
||||
schema.arrayOf(schema.string(), { minSize: 2, maxSize: MAX_SELECTABLE_GROUP_BY_TERMS }),
|
||||
])
|
||||
),
|
||||
// limit on number of groups returned
|
||||
termSize: schema.maybe(schema.number({ min: 1 })),
|
||||
searchType: schema.oneOf(
|
||||
[schema.literal('searchSource'), schema.literal('esQuery'), schema.literal('esqlQuery')],
|
||||
{
|
||||
defaultValue: 'esQuery',
|
||||
}
|
||||
),
|
||||
timeField: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('esQuery'),
|
||||
schema.string({ minLength: 1 }),
|
||||
schema.maybe(schema.string({ minLength: 1 }))
|
||||
),
|
||||
// searchSource rule param only
|
||||
searchConfiguration: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('searchSource'),
|
||||
schema.object({}, { unknowns: 'allow' }),
|
||||
schema.never()
|
||||
),
|
||||
// esQuery rule params only
|
||||
esQuery: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('esQuery'),
|
||||
schema.string({ minLength: 1 }),
|
||||
schema.never()
|
||||
),
|
||||
index: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('esQuery'),
|
||||
schema.arrayOf(schema.string({ minLength: 1 }), { minSize: 1 }),
|
||||
schema.never()
|
||||
),
|
||||
// esqlQuery rule params only
|
||||
esqlQuery: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('esqlQuery'),
|
||||
schema.object({ esql: schema.string({ minLength: 1 }) }),
|
||||
schema.never()
|
||||
),
|
||||
sourceFields: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
label: schema.string(),
|
||||
searchPath: schema.string(),
|
||||
}),
|
||||
{
|
||||
maxSize: MAX_SELECTABLE_SOURCE_FIELDS,
|
||||
}
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
// rule type parameters
|
||||
export type EsQueryRuleParams = TypeOf<typeof EsQueryRuleParamsSchema>;
|
||||
|
||||
function isSearchSourceRule(searchType: EsQueryRuleParams['searchType']) {
|
||||
return searchType === 'searchSource';
|
||||
}
|
||||
|
||||
function isEsqlQueryRule(searchType: EsQueryRuleParams['searchType']) {
|
||||
return searchType === 'esqlQuery';
|
||||
}
|
||||
|
||||
// using direct type not allowed, circular reference, so body is typed to any
|
||||
function validateParams(anyParams: unknown): string | undefined {
|
||||
const {
|
||||
esQuery,
|
||||
thresholdComparator,
|
||||
threshold,
|
||||
searchType,
|
||||
aggType,
|
||||
aggField,
|
||||
groupBy,
|
||||
termField,
|
||||
termSize,
|
||||
} = anyParams as EsQueryRuleParams;
|
||||
|
||||
if (betweenComparators.has(thresholdComparator) && threshold.length === 1) {
|
||||
return i18n.translate('responseOps.ruleParams.esQuery.invalidThreshold2ErrorMessage', {
|
||||
defaultMessage:
|
||||
'[threshold]: must have two elements for the "{thresholdComparator}" comparator',
|
||||
values: {
|
||||
thresholdComparator,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (aggType !== 'count' && !aggField) {
|
||||
return i18n.translate('responseOps.ruleParams.esQuery.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.responseOps.ruleParams.esQuery.termFieldRequiredErrorMessage', {
|
||||
defaultMessage: '[termField]: termField required when [groupBy] is top',
|
||||
});
|
||||
}
|
||||
if (termSize == null) {
|
||||
return i18n.translate('xpack.responseOps.ruleParams.esQuery.termSizeRequiredErrorMessage', {
|
||||
defaultMessage: '[termSize]: termSize required when [groupBy] is top',
|
||||
});
|
||||
}
|
||||
if (termSize > MAX_GROUPS) {
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.esQuery.invalidTermSizeMaximumErrorMessage',
|
||||
{
|
||||
defaultMessage: '[termSize]: must be less than or equal to {maxGroups}',
|
||||
values: {
|
||||
maxGroups: MAX_GROUPS,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isSearchSourceRule(searchType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEsqlQueryRule(searchType)) {
|
||||
const { timeField } = anyParams as EsQueryRuleParams;
|
||||
|
||||
if (!timeField) {
|
||||
return i18n.translate('xpack.responseOps.ruleParams.esQuery.esqlTimeFieldErrorMessage', {
|
||||
defaultMessage: '[timeField]: is required',
|
||||
});
|
||||
}
|
||||
if (thresholdComparator !== Comparator.GT) {
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.esQuery.esqlThresholdComparatorErrorMessage',
|
||||
{
|
||||
defaultMessage: '[thresholdComparator]: is required to be greater than',
|
||||
}
|
||||
);
|
||||
}
|
||||
if (threshold && threshold[0] !== 0) {
|
||||
return i18n.translate('xpack.responseOps.ruleParams.esQuery.esqlThresholdErrorMessage', {
|
||||
defaultMessage: '[threshold]: is required to be 0',
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedQuery = JSON.parse(esQuery);
|
||||
|
||||
if (parsedQuery && !parsedQuery.query) {
|
||||
return i18n.translate('xpack.responseOps.ruleParams.esQuery.missingEsQueryErrorMessage', {
|
||||
defaultMessage: '[esQuery]: must contain "query"',
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
return i18n.translate('xpack.responseOps.ruleParams.esQuery.invalidEsQueryErrorMessage', {
|
||||
defaultMessage: '[esQuery]: must be valid JSON',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const EsQueryRuleParamsSchema = schema.object(EsQueryRuleParamsSchemaProperties, {
|
||||
validate: validateParams,
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 { ParamsSchema } from './latest';
|
||||
export { ParamsSchema as ParamsSchemaV1 } from './latest';
|
||||
|
||||
export { type Params } from './latest';
|
||||
export { type Params as ParamsV1 } from './latest';
|
||||
|
||||
export type { CoreQueryParams } from './latest';
|
||||
export type { CoreQueryParams as CoreQueryParamsV1 } from './latest';
|
||||
|
||||
export { CoreQueryParamsSchemaProperties } from './latest';
|
||||
export { validateCoreQueryBody } from './latest';
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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,134 @@
|
|||
/*
|
||||
* 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, TypeOf } from '@kbn/config-schema';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { MAX_GROUPS } from '../common/constants';
|
||||
import {
|
||||
validateAggType,
|
||||
validateGroupBy,
|
||||
validateKuery,
|
||||
validateTimeWindowUnits,
|
||||
getComparatorSchemaType,
|
||||
betweenComparators,
|
||||
validateComparator,
|
||||
} from '../common/utils';
|
||||
|
||||
export type Params = TypeOf<typeof ParamsSchema>;
|
||||
|
||||
export const CoreQueryParamsSchemaProperties = {
|
||||
// name of the indices to search
|
||||
index: schema.oneOf([
|
||||
schema.string({ minLength: 1 }),
|
||||
schema.arrayOf(schema.string({ minLength: 1 }), { minSize: 1 }),
|
||||
]),
|
||||
// field in index used for date/time
|
||||
timeField: schema.string({ minLength: 1 }),
|
||||
// aggregation type
|
||||
aggType: schema.string({ validate: validateAggType, defaultValue: 'count' }),
|
||||
// aggregation field
|
||||
aggField: schema.maybe(schema.string({ minLength: 1 })),
|
||||
// how to group
|
||||
groupBy: schema.string({ validate: validateGroupBy, defaultValue: 'all' }),
|
||||
// field to group on (for groupBy: top)
|
||||
termField: schema.maybe(schema.string({ minLength: 1 })),
|
||||
// filter field
|
||||
filterKuery: schema.maybe(schema.string({ validate: validateKuery })),
|
||||
// limit on number of groups returned
|
||||
termSize: schema.maybe(schema.number({ min: 1 })),
|
||||
// size of time window for date range aggregations
|
||||
timeWindowSize: schema.number({ min: 1 }),
|
||||
// units of time window for date range aggregations
|
||||
timeWindowUnit: schema.string({ validate: validateTimeWindowUnits }),
|
||||
};
|
||||
|
||||
export const CoreQueryParamsSchema = schema.object(CoreQueryParamsSchemaProperties);
|
||||
export type CoreQueryParams = TypeOf<typeof CoreQueryParamsSchema>;
|
||||
|
||||
export const ParamsSchema = schema.object(
|
||||
{
|
||||
...CoreQueryParamsSchemaProperties,
|
||||
// the comparison function to use to determine if the threshold as been met
|
||||
thresholdComparator: getComparatorSchemaType(validateComparator),
|
||||
// the values to use as the threshold; `between` and `notBetween` require
|
||||
// two values, the others require one.
|
||||
threshold: schema.arrayOf(schema.number(), { minSize: 1, maxSize: 2 }),
|
||||
},
|
||||
{ validate: validateParams }
|
||||
);
|
||||
|
||||
// using direct type not allowed, circular reference, so body is typed to any
|
||||
function validateParams(anyParams: unknown): string | undefined {
|
||||
// validate core query parts, return if it fails validation (returning string)
|
||||
const coreQueryValidated = validateCoreQueryBody(anyParams);
|
||||
if (coreQueryValidated) return coreQueryValidated;
|
||||
|
||||
const { thresholdComparator, threshold }: Params = anyParams as Params;
|
||||
|
||||
if (betweenComparators.has(thresholdComparator) && threshold.length === 1) {
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.indexThreshold.invalidThreshold2ErrorMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'[threshold]: must have two elements for the "{thresholdComparator}" comparator',
|
||||
values: {
|
||||
thresholdComparator,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateCoreQueryBody(anyParams: unknown): string | undefined {
|
||||
const { aggType, aggField, groupBy, termField, termSize }: CoreQueryParams =
|
||||
anyParams as CoreQueryParams;
|
||||
if (aggType !== 'count' && !aggField) {
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.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.responseOps.ruleParams.coreQueryParams.termFieldRequiredErrorMessage',
|
||||
{
|
||||
defaultMessage: '[termField]: termField required when [groupBy] is top',
|
||||
}
|
||||
);
|
||||
}
|
||||
if (termSize == null) {
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.coreQueryParams.termSizeRequiredErrorMessage',
|
||||
{
|
||||
defaultMessage: '[termSize]: termSize required when [groupBy] is top',
|
||||
}
|
||||
);
|
||||
}
|
||||
if (termSize > MAX_GROUPS) {
|
||||
return i18n.translate(
|
||||
'xpack.responseOps.ruleParams.coreQueryParams.invalidTermSizeMaximumErrorMessage',
|
||||
{
|
||||
defaultMessage: '[termSize]: must be less than or equal to {maxGroups}',
|
||||
values: {
|
||||
maxGroups: MAX_GROUPS,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41804,24 +41804,14 @@
|
|||
"xpack.stackAlerts.esQuery.actionVariableContextThresholdLabel": "Un tableau des valeurs des règles de seuil. Il y a deux valeurs pour les seuils \"Between\" (Intermédiaires) et \"notBetween\" (Non-intermédiaires).",
|
||||
"xpack.stackAlerts.esQuery.actionVariableContextTitleLabel": "Titre pour l'alerte.",
|
||||
"xpack.stackAlerts.esQuery.actionVariableContextValueLabel": "Valeur ayant rempli la condition de seuil.",
|
||||
"xpack.stackAlerts.esQuery.aggTypeRequiredErrorMessage": "[aggField] : doit posséder une valeur lorsque [aggType] est \"{aggType}\"",
|
||||
"xpack.stackAlerts.esQuery.alertTypeContextConditionsDescription": "Le nombre de documents correspondants {groupCondition}{aggCondition} est {negation}{thresholdComparator} {threshold}",
|
||||
"xpack.stackAlerts.esQuery.alertTypeContextReasonDescription": "Le décompte de documents est {value} au cours des derniers/dernières {window}{verb}{index}. Signaler quand {comparator} {threshold}.",
|
||||
"xpack.stackAlerts.esQuery.alertTypeContextSubjectTitle": "règle \"{name}\" : {verb}",
|
||||
"xpack.stackAlerts.esQuery.alertTypeTitle": "Recherche Elasticsearch",
|
||||
"xpack.stackAlerts.esQuery.esqlAlertTypeContextConditionsDescription": "Recherche {negation} de documents{groupCondition}",
|
||||
"xpack.stackAlerts.esQuery.esqlThresholdComparatorErrorMessage": "[thresholdComparator] : doit être plus élevé que",
|
||||
"xpack.stackAlerts.esQuery.esqlThresholdErrorMessage": "[threshold] :doit correspondre à 0",
|
||||
"xpack.stackAlerts.esQuery.esqlTimeFieldErrorMessage": "[timeField] : est requis",
|
||||
"xpack.stackAlerts.esQuery.invalidComparatorErrorMessage": "thresholdComparator spécifié non valide : {comparator}",
|
||||
"xpack.stackAlerts.esQuery.invalidEsQueryErrorMessage": "[esQuery] : doit être au format JSON valide",
|
||||
"xpack.stackAlerts.esQuery.invalidQueryErrorMessage": "requête spécifiée non valide : \"{query}\" - la requête doit être au format JSON",
|
||||
"xpack.stackAlerts.esQuery.invalidTermSizeMaximumErrorMessage": "[termSize] : doit être inférieure ou égale à {maxGroups}",
|
||||
"xpack.stackAlerts.esQuery.invalidThreshold2ErrorMessage": "[threshold] : requiert deux éléments pour le comparateur \"{thresholdComparator}\"",
|
||||
"xpack.stackAlerts.esQuery.missingEsQueryErrorMessage": "[esQuery] : doit contenir \"query\"",
|
||||
"xpack.stackAlerts.esQuery.serverless.sizeErrorMessage": "[size] : doit être inférieur ou égal à {maxSize}",
|
||||
"xpack.stackAlerts.esQuery.termFieldRequiredErrorMessage": "[termField] : termField requis lorsque [groupBy] est Premiers",
|
||||
"xpack.stackAlerts.esQuery.termSizeRequiredErrorMessage": "[termSize] : termSize requis lorsque [groupBy] est Premiers",
|
||||
"xpack.stackAlerts.esQuery.ui.alertParams.fixErrorInExpressionBelowValidationMessage": "L'expression contient des erreurs.",
|
||||
"xpack.stackAlerts.esQuery.ui.alertType.defaultActionMessage": "La règle de requête Elasticsearch '{{rule.name}}' est active : - Valeur : '{{context.value}}' - Conditions remplies : '{{context.conditions}}' sur '{{rule.params.timeWindowSize}}''{{rule.params.timeWindowUnit}}' - Horodatage : '{{context.date}}' - Lien : '{{context.link}}'",
|
||||
"xpack.stackAlerts.esQuery.ui.alertType.descriptionText": "Alerte lorsque des correspondances sont trouvées au cours de la dernière exécution de la requête.",
|
||||
|
@ -41947,7 +41937,6 @@
|
|||
"xpack.stackAlerts.indexThreshold.alertTypeRecoveryContextSubjectTitle": "groupe {group} de l'alerte {name} récupéré",
|
||||
"xpack.stackAlerts.indexThreshold.alertTypeTitle": "Seuil de l'index",
|
||||
"xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage": "thresholdComparator spécifié non valide : {comparator}",
|
||||
"xpack.stackAlerts.indexThreshold.invalidThreshold2ErrorMessage": "[threshold] : requiert deux éléments pour le comparateur \"{thresholdComparator}\"",
|
||||
"xpack.stackAlerts.threshold.ui.alertParams.fixErrorInExpressionBelowValidationMessage": "L'expression contient des erreurs.",
|
||||
"xpack.stackAlerts.threshold.ui.alertType.defaultActionMessage": "La règle '{{rule.name}}' est active pour le groupe '{{context.group}}' : - Valeur : '{{context.value}}' - Conditions remplies : '{{context.conditions}}' sur '{{rule.params.timeWindowSize}}{{rule.params.timeWindowUnit}}' - Horodatage : '{{context.date}}'",
|
||||
"xpack.stackAlerts.threshold.ui.alertType.descriptionText": "Alerte lorsqu'une recherche agrégée atteint le seuil.",
|
||||
|
@ -44657,20 +44646,13 @@
|
|||
"xpack.triggersActionsUI.connectors.home.description": "Connectez des logiciels tiers avec vos données d'alerting.",
|
||||
"xpack.triggersActionsUI.connectors.home.documentationButtonLabel": "Documentation",
|
||||
"xpack.triggersActionsUI.connectors.home.logsTabTitle": "Logs",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.aggTypeRequiredErrorMessage": "[aggField] : doit posséder une valeur lorsque [aggType] est \"{aggType}\"",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage": "[dateStart] : est postérieure à [dateEnd]",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.formattedFieldErrorMessage": "format {formatName} non valide pour {fieldName} : \"{fieldValue}\"",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval] : doit être spécifié si [dateStart] n'est pas égale à [dateEnd]",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage": "aggType non valide : \"{aggType}\"",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidDateErrorMessage": "date {date} non valide",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidDurationErrorMessage": "durée non valide : \"{duration}\"",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage": "groupBy non valide : \"{groupBy}\"",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidKQLQueryErrorMessage": "La requête de filtre n'est pas valide.",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize] : doit être inférieure ou égale à {maxGroups}",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidTimeWindowUnitsErrorMessage": "timeWindowUnit non valide : \"{timeWindowUnit}\"",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.maxIntervalsErrorMessage": "le nombre calculé d'intervalles {intervals} est supérieur au maximum {maxIntervals}",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.termFieldRequiredErrorMessage": "[termField] : termField requis lorsque [groupBy] est Premiers",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage": "[termSize] : termSize requis lorsque [groupBy] est Premiers",
|
||||
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "Annuler",
|
||||
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "Supprimer {numIdsToDelete, plural, one {{singleTitle}} other {# {multipleTitle}}}",
|
||||
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.descriptionText": "Vous ne pourrez pas récupérer {numIdsToDelete, plural, one {un {singleTitle} supprimé} other {des {multipleTitle} supprimés}}.",
|
||||
|
|
|
@ -41657,24 +41657,14 @@
|
|||
"xpack.stackAlerts.esQuery.actionVariableContextThresholdLabel": "ルールのしきい値の配列。betweenとnotBetweenのしきい値には2つの値があります。",
|
||||
"xpack.stackAlerts.esQuery.actionVariableContextTitleLabel": "アラートのタイトル。",
|
||||
"xpack.stackAlerts.esQuery.actionVariableContextValueLabel": "しきい値条件を満たした値。",
|
||||
"xpack.stackAlerts.esQuery.aggTypeRequiredErrorMessage": "[aggType]が「{aggType}」のときには[aggField]に値が必要です",
|
||||
"xpack.stackAlerts.esQuery.alertTypeContextConditionsDescription": "一致するドキュメント数{groupCondition}{aggCondition}は{negation}{thresholdComparator} {threshold}です",
|
||||
"xpack.stackAlerts.esQuery.alertTypeContextReasonDescription": "前回の{window}{verb}{index}ではドキュメント数は{value}です。{comparator} {threshold}のときにアラートを発行します。",
|
||||
"xpack.stackAlerts.esQuery.alertTypeContextSubjectTitle": "ルール''{name}''{verb}",
|
||||
"xpack.stackAlerts.esQuery.alertTypeTitle": "Elasticsearch クエリー",
|
||||
"xpack.stackAlerts.esQuery.esqlAlertTypeContextConditionsDescription": "クエリー{negation}ドキュメント{groupCondition}",
|
||||
"xpack.stackAlerts.esQuery.esqlThresholdComparatorErrorMessage": "[thresholdComparator]:はそれよりも大きくなければなりません",
|
||||
"xpack.stackAlerts.esQuery.esqlThresholdErrorMessage": "[threshold]は0でなければなりません",
|
||||
"xpack.stackAlerts.esQuery.esqlTimeFieldErrorMessage": "[timeField]は必須です",
|
||||
"xpack.stackAlerts.esQuery.invalidComparatorErrorMessage": "無効な thresholdComparator が指定されました:{comparator}",
|
||||
"xpack.stackAlerts.esQuery.invalidEsQueryErrorMessage": "[esQuery]:有効なJSONでなければなりません",
|
||||
"xpack.stackAlerts.esQuery.invalidQueryErrorMessage": "無効なクエリーが指定されました: \"{query}\" - クエリーはJSONでなければなりません",
|
||||
"xpack.stackAlerts.esQuery.invalidTermSizeMaximumErrorMessage": "[termSize]:{maxGroups}以下でなければなりません。",
|
||||
"xpack.stackAlerts.esQuery.invalidThreshold2ErrorMessage": "[threshold]:「{thresholdComparator}」比較子の場合には2つの要素が必要です",
|
||||
"xpack.stackAlerts.esQuery.missingEsQueryErrorMessage": "[esQuery]:「query」を含む必要があります",
|
||||
"xpack.stackAlerts.esQuery.serverless.sizeErrorMessage": "[size]:{maxSize}以下でなければなりません",
|
||||
"xpack.stackAlerts.esQuery.termFieldRequiredErrorMessage": "[termField]:[groupBy]がトップのときにはtermFieldが必要です",
|
||||
"xpack.stackAlerts.esQuery.termSizeRequiredErrorMessage": "[termSize]:[groupBy]がトップのときにはtermSizeが必要です",
|
||||
"xpack.stackAlerts.esQuery.ui.alertParams.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。",
|
||||
"xpack.stackAlerts.esQuery.ui.alertType.defaultActionMessage": "Elasticsearchクエリルール'{{rule.name}}'がアクティブです:- 値:'{{context.value}}' - 満たされた条件:'{{rule.params.timeWindowSize}}''{{rule.params.timeWindowUnit}}'の'{{context.conditions}}' - タイムスタンプ:'{{context.date}}' - リンク:'{{context.link}}'",
|
||||
"xpack.stackAlerts.esQuery.ui.alertType.descriptionText": "前回のクエリ実行中に一致が見つかったときにアラートを発行します。",
|
||||
|
@ -41800,7 +41790,6 @@
|
|||
"xpack.stackAlerts.indexThreshold.alertTypeRecoveryContextSubjectTitle": "アラート{name}グループ{group}が回復されました",
|
||||
"xpack.stackAlerts.indexThreshold.alertTypeTitle": "インデックスしきい値",
|
||||
"xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage": "無効な thresholdComparator が指定されました:{comparator}",
|
||||
"xpack.stackAlerts.indexThreshold.invalidThreshold2ErrorMessage": "[threshold]:「{thresholdComparator}」比較子の場合には2つの要素が必要です",
|
||||
"xpack.stackAlerts.threshold.ui.alertParams.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。",
|
||||
"xpack.stackAlerts.threshold.ui.alertType.defaultActionMessage": "グループ'{{context.group}}'のルール'{{rule.name}}'がアクティブです:- 値:'{{context.value}}' - 満たされた条件:'{{rule.params.timeWindowSize}}{{rule.params.timeWindowUnit}}'の'{{context.conditions}}' - タイムスタンプ:'{{context.date}}'",
|
||||
"xpack.stackAlerts.threshold.ui.alertType.descriptionText": "アグリゲーションされたクエリがしきい値に達したときにアラートを発行します。",
|
||||
|
@ -44509,20 +44498,13 @@
|
|||
"xpack.triggersActionsUI.connectors.home.description": "サードパーティソフトウェアをアラートデータに連携します。",
|
||||
"xpack.triggersActionsUI.connectors.home.documentationButtonLabel": "ドキュメント",
|
||||
"xpack.triggersActionsUI.connectors.home.logsTabTitle": "ログ",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.aggTypeRequiredErrorMessage": "[aggType]が「{aggType}」のときには[aggField]に値が必要です",
|
||||
"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.triggersActionsUI.data.coreQueryParams.invalidDateErrorMessage": "無効な日付{date}",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidDurationErrorMessage": "無効な期間:「{duration}」",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage": "無効なgroupBy:「{groupBy}」",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidKQLQueryErrorMessage": "フィルタークエリは無効です。",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize]:{maxGroups}以下でなければなりません。",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidTimeWindowUnitsErrorMessage": "無効な timeWindowUnit:「{timeWindowUnit}」",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.maxIntervalsErrorMessage": "間隔{intervals}の計算値が{maxIntervals}よりも大です",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.termFieldRequiredErrorMessage": "[termField]:[groupBy]がトップのときにはtermFieldが必要です",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage": "[termSize]:[groupBy]がトップのときにはtermSizeが必要です",
|
||||
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "キャンセル",
|
||||
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "{numIdsToDelete, plural, one {{singleTitle}} other {# {multipleTitle}}}を削除",
|
||||
"xpack.triggersActionsUI.globalAlerts.alertStats.disabled": "無効",
|
||||
|
|
|
@ -41046,16 +41046,8 @@
|
|||
"xpack.stackAlerts.esQuery.alertTypeContextReasonDescription": "过去 {window}{verb}{index} 的文档计数为 {value}。{comparator} {threshold} 时告警。",
|
||||
"xpack.stackAlerts.esQuery.alertTypeTitle": "Elasticsearch 查询",
|
||||
"xpack.stackAlerts.esQuery.esqlAlertTypeContextConditionsDescription": "查询{negation} 文档{groupCondition}",
|
||||
"xpack.stackAlerts.esQuery.esqlThresholdComparatorErrorMessage": "[thresholdComparator]:必须大于",
|
||||
"xpack.stackAlerts.esQuery.esqlThresholdErrorMessage": "[threshold]:必须为 0",
|
||||
"xpack.stackAlerts.esQuery.esqlTimeFieldErrorMessage": "[timeField]:必填",
|
||||
"xpack.stackAlerts.esQuery.invalidComparatorErrorMessage": "指定的 thresholdComparator 无效:{comparator}",
|
||||
"xpack.stackAlerts.esQuery.invalidEsQueryErrorMessage": "[esQuery]:必须是有效的 JSON",
|
||||
"xpack.stackAlerts.esQuery.invalidTermSizeMaximumErrorMessage": "[termSize]:必须小于或等于 {maxGroups}",
|
||||
"xpack.stackAlerts.esQuery.missingEsQueryErrorMessage": "[esQuery]:必须包含'query'",
|
||||
"xpack.stackAlerts.esQuery.serverless.sizeErrorMessage": "[size]:必须小于或等于 {maxSize}",
|
||||
"xpack.stackAlerts.esQuery.termFieldRequiredErrorMessage": "[termField]:[groupBy] 为 top 时,termField 为必需",
|
||||
"xpack.stackAlerts.esQuery.termSizeRequiredErrorMessage": "[termSize]:[groupBy] 为 top 时,termSize 为必需",
|
||||
"xpack.stackAlerts.esQuery.ui.alertParams.fixErrorInExpressionBelowValidationMessage": "表达式包含错误。",
|
||||
"xpack.stackAlerts.esQuery.ui.alertType.defaultActionMessage": "Elasticsearch 查询规则 '{{rule.name}}' 处于活动状态:- 值:'{{context.value}}' - 满足的条件:'{{context.conditions}}' 超过 '{{rule.params.timeWindowSize}}''{{rule.params.timeWindowUnit}}' - 时间戳:'{{context.date}}' - 链接:'{{context.link}}'",
|
||||
"xpack.stackAlerts.esQuery.ui.alertType.descriptionText": "在运行最新查询期间找到匹配项时告警。",
|
||||
|
@ -43858,10 +43850,7 @@
|
|||
"xpack.triggersActionsUI.data.coreQueryParams.intervalRequiredErrorMessage": "[interval]:如果 [dateStart] 不等于 [dateEnd],则必须指定",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidDateErrorMessage": "日期 {date} 无效",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidKQLQueryErrorMessage": "筛选查询无效。",
|
||||
"xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage": "[termSize]:必须小于或等于 {maxGroups}",
|
||||
"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.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "取消",
|
||||
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "删除{numIdsToDelete, plural, one {{singleTitle}} other { # 个{multipleTitle}}}",
|
||||
"xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.descriptionText": "无法恢复{numIdsToDelete, plural, one {删除的{singleTitle}} other {删除的{multipleTitle}}}。",
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
import { Comparator } from './comparator_types';
|
||||
|
||||
export type ComparatorFn = (value: number, threshold: number[]) => boolean;
|
||||
|
||||
const humanReadableComparators = new Map<Comparator, string>([
|
||||
[Comparator.LT, 'less than'],
|
||||
[Comparator.LT_OR_EQ, 'less than or equal to'],
|
||||
|
@ -18,21 +16,6 @@ const humanReadableComparators = new Map<Comparator, string>([
|
|||
[Comparator.NOT_BETWEEN, 'not between'],
|
||||
]);
|
||||
|
||||
export const ComparatorFns = new Map<Comparator, ComparatorFn>([
|
||||
[Comparator.LT, (value: number, threshold: number[]) => value < threshold[0]],
|
||||
[Comparator.LT_OR_EQ, (value: number, threshold: number[]) => value <= threshold[0]],
|
||||
[Comparator.GT_OR_EQ, (value: number, threshold: number[]) => value >= threshold[0]],
|
||||
[Comparator.GT, (value: number, threshold: number[]) => value > threshold[0]],
|
||||
[
|
||||
Comparator.BETWEEN,
|
||||
(value: number, threshold: number[]) => value >= threshold[0] && value <= threshold[1],
|
||||
],
|
||||
[
|
||||
Comparator.NOT_BETWEEN,
|
||||
(value: number, threshold: number[]) => value < threshold[0] || value > threshold[1],
|
||||
],
|
||||
]);
|
||||
|
||||
export const getComparatorScript = (
|
||||
comparator: Comparator,
|
||||
threshold: number[],
|
||||
|
@ -72,8 +55,6 @@ export const getComparatorScript = (
|
|||
}
|
||||
};
|
||||
|
||||
export const ComparatorFnNames = new Set(ComparatorFns.keys());
|
||||
|
||||
export function getHumanReadableComparator(comparator: Comparator) {
|
||||
return humanReadableComparators.has(comparator)
|
||||
? humanReadableComparators.get(comparator)
|
||||
|
|
|
@ -5,12 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export {
|
||||
ComparatorFns,
|
||||
getComparatorScript,
|
||||
ComparatorFnNames,
|
||||
getHumanReadableComparator,
|
||||
} from './comparator';
|
||||
export { getComparatorScript, getHumanReadableComparator } from './comparator';
|
||||
|
||||
export type { EsqlTable } from './esql_query_utils';
|
||||
export { rowToDocument, transformDatatableToEsqlTable, toEsQueryHits } from './esql_query_utils';
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
addMessages,
|
||||
getContextConditionsDescription,
|
||||
} from './action_context';
|
||||
import { EsQueryRuleParams, EsQueryRuleParamsSchema } from './rule_type_params';
|
||||
import { EsQueryRuleParams, EsQueryRuleParamsSchema } from '@kbn/response-ops-rule-params/es_query';
|
||||
import { Comparator } from '../../../common/comparator_types';
|
||||
|
||||
describe('addMessages', () => {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { AlertInstanceContext } from '@kbn/alerting-plugin/server';
|
||||
import { EsQueryRuleParams } from './rule_type_params';
|
||||
import type { EsQueryRuleParams } from '@kbn/response-ops-rule-params/es_query';
|
||||
import { Comparator } from '../../../common/comparator_types';
|
||||
import { getHumanReadableComparator } from '../../../common';
|
||||
import { isEsqlQueryRule } from './util';
|
||||
|
|
|
@ -14,7 +14,7 @@ import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks';
|
|||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import { createSearchSourceMock } from '@kbn/data-plugin/common/search/search_source/mocks';
|
||||
import { ISearchStartSearchSource } from '@kbn/data-plugin/common';
|
||||
import { EsQueryRuleParams } from './rule_type_params';
|
||||
import { EsQueryRuleParams } from '@kbn/response-ops-rule-params/es_query';
|
||||
import { FetchEsQueryOpts } from './lib/fetch_es_query';
|
||||
import { FetchSearchSourceQueryOpts } from './lib/fetch_search_source_query';
|
||||
import { FetchEsqlQueryOpts } from './lib/fetch_esql_query';
|
||||
|
|
|
@ -18,8 +18,9 @@ import {
|
|||
} from '@kbn/rule-data-utils';
|
||||
|
||||
import { AlertsClientError } from '@kbn/alerting-plugin/server';
|
||||
import { EsQueryRuleParams } from '@kbn/response-ops-rule-params/es_query';
|
||||
|
||||
import { ComparatorFns } from '../../../common';
|
||||
import { ComparatorFns } from '@kbn/response-ops-rule-params/common';
|
||||
import {
|
||||
addMessages,
|
||||
EsQueryRuleActionContext,
|
||||
|
@ -33,7 +34,6 @@ import {
|
|||
} from './types';
|
||||
import { ActionGroupId, ConditionMetAlertInstanceId } from './constants';
|
||||
import { fetchEsQuery } from './lib/fetch_es_query';
|
||||
import { EsQueryRuleParams } from './rule_type_params';
|
||||
import { fetchSearchSourceQuery } from './lib/fetch_search_source_query';
|
||||
import { isEsqlQueryRule, isSearchSourceRule } from './util';
|
||||
import { fetchEsqlQuery } from './lib/fetch_esql_query';
|
||||
|
|
|
@ -12,7 +12,8 @@ import { RuleExecutorServicesMock, alertsMock } from '@kbn/alerting-plugin/serve
|
|||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
|
||||
import { getRuleType } from './rule_type';
|
||||
import { EsQueryRuleParams, EsQueryRuleState } from './rule_type_params';
|
||||
import { EsQueryRuleState } from './rule_type_params';
|
||||
import { EsQueryRuleParams } from '@kbn/response-ops-rule-params/es_query';
|
||||
import { ActionContext } from './action_context';
|
||||
import type { ESSearchResponse, ESSearchRequest } from '@kbn/es-types';
|
||||
import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks';
|
||||
|
|
|
@ -9,16 +9,19 @@ import { i18n } from '@kbn/i18n';
|
|||
import { CoreSetup, DEFAULT_APP_CATEGORIES } from '@kbn/core/server';
|
||||
import { extractReferences, injectReferences } from '@kbn/data-plugin/common';
|
||||
import { ES_QUERY_ID, STACK_ALERTS_FEATURE_ID } from '@kbn/rule-data-utils';
|
||||
import {
|
||||
EsQueryRuleParamsSchema,
|
||||
type EsQueryRuleParams,
|
||||
} from '@kbn/response-ops-rule-params/es_query';
|
||||
import { STACK_ALERTS_AAD_CONFIG } from '..';
|
||||
import { RuleType } from '../../types';
|
||||
import { ActionContext } from './action_context';
|
||||
import {
|
||||
EsQueryRuleParams,
|
||||
EsQueryRuleParamsExtractedParams,
|
||||
EsQueryRuleParamsSchema,
|
||||
EsQueryRuleState,
|
||||
validateServerless,
|
||||
} from './rule_type_params';
|
||||
|
||||
import { ExecutorOptions } from './types';
|
||||
import { ActionGroupId } from './constants';
|
||||
import { executor } from './executor';
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
*/
|
||||
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import { MAX_GROUPS } from '@kbn/triggers-actions-ui-plugin/server';
|
||||
import { MAX_GROUPS } from '@kbn/response-ops-rule-params/common';
|
||||
import type { Writable } from '@kbn/utility-types';
|
||||
import { Comparator } from '../../../common/comparator_types';
|
||||
import { ES_QUERY_MAX_HITS_PER_EXECUTION } from '../../../common';
|
||||
import { EsQueryRuleParamsSchema, EsQueryRuleParams, validateServerless } from './rule_type_params';
|
||||
import { validateServerless } from './rule_type_params';
|
||||
import { EsQueryRuleParamsSchema, EsQueryRuleParams } from '@kbn/response-ops-rule-params/es_query';
|
||||
|
||||
const DefaultParams: Writable<Partial<EsQueryRuleParams>> = {
|
||||
index: ['index-name'],
|
||||
|
|
|
@ -6,30 +6,11 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import {
|
||||
validateTimeWindowUnits,
|
||||
validateAggType,
|
||||
validateGroupBy,
|
||||
MAX_GROUPS,
|
||||
} from '@kbn/triggers-actions-ui-plugin/server';
|
||||
import { RuleTypeState } from '@kbn/alerting-plugin/server';
|
||||
import { type EsQueryRuleParams } from '@kbn/response-ops-rule-params/es_query';
|
||||
import { SerializedSearchSourceFields } from '@kbn/data-plugin/common';
|
||||
import {
|
||||
MAX_SELECTABLE_SOURCE_FIELDS,
|
||||
MAX_SELECTABLE_GROUP_BY_TERMS,
|
||||
} from '../../../common/constants';
|
||||
import {
|
||||
ComparatorFnNames,
|
||||
ES_QUERY_MAX_HITS_PER_EXECUTION,
|
||||
ES_QUERY_MAX_HITS_PER_EXECUTION_SERVERLESS,
|
||||
} from '../../../common';
|
||||
import { Comparator } from '../../../common/comparator_types';
|
||||
import { getComparatorSchemaType } from '../lib/comparator';
|
||||
import { isEsqlQueryRule, isSearchSourceRule } from './util';
|
||||
import { ES_QUERY_MAX_HITS_PER_EXECUTION_SERVERLESS } from '../../../common';
|
||||
|
||||
// rule type parameters
|
||||
export type EsQueryRuleParams = TypeOf<typeof EsQueryRuleParamsSchema>;
|
||||
export interface EsQueryRuleState extends RuleTypeState {
|
||||
latestTimestamp: string | undefined;
|
||||
}
|
||||
|
@ -40,181 +21,6 @@ export type EsQueryRuleParamsExtractedParams = Omit<EsQueryRuleParams, 'searchCo
|
|||
};
|
||||
};
|
||||
|
||||
const EsQueryRuleParamsSchemaProperties = {
|
||||
size: schema.number({ min: 0, max: ES_QUERY_MAX_HITS_PER_EXECUTION }),
|
||||
timeWindowSize: schema.number({ min: 1 }),
|
||||
excludeHitsFromPreviousRun: schema.boolean({ defaultValue: true }),
|
||||
timeWindowUnit: schema.string({ validate: validateTimeWindowUnits }),
|
||||
threshold: schema.arrayOf(schema.number(), { minSize: 1, maxSize: 2 }),
|
||||
thresholdComparator: getComparatorSchemaType(validateComparator),
|
||||
// aggregation type
|
||||
aggType: schema.string({ validate: validateAggType, defaultValue: 'count' }),
|
||||
// aggregation field
|
||||
aggField: schema.maybe(schema.string({ minLength: 1 })),
|
||||
// how to group
|
||||
groupBy: schema.string({ validate: validateGroupBy, defaultValue: 'all' }),
|
||||
// field to group on (for groupBy: top)
|
||||
termField: schema.maybe(
|
||||
schema.oneOf([
|
||||
schema.string({ minLength: 1 }),
|
||||
schema.arrayOf(schema.string(), { minSize: 2, maxSize: MAX_SELECTABLE_GROUP_BY_TERMS }),
|
||||
])
|
||||
),
|
||||
// limit on number of groups returned
|
||||
termSize: schema.maybe(schema.number({ min: 1 })),
|
||||
searchType: schema.oneOf(
|
||||
[schema.literal('searchSource'), schema.literal('esQuery'), schema.literal('esqlQuery')],
|
||||
{
|
||||
defaultValue: 'esQuery',
|
||||
}
|
||||
),
|
||||
timeField: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('esQuery'),
|
||||
schema.string({ minLength: 1 }),
|
||||
schema.maybe(schema.string({ minLength: 1 }))
|
||||
),
|
||||
// searchSource rule param only
|
||||
searchConfiguration: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('searchSource'),
|
||||
schema.object({}, { unknowns: 'allow' }),
|
||||
schema.never()
|
||||
),
|
||||
// esQuery rule params only
|
||||
esQuery: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('esQuery'),
|
||||
schema.string({ minLength: 1 }),
|
||||
schema.never()
|
||||
),
|
||||
index: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('esQuery'),
|
||||
schema.arrayOf(schema.string({ minLength: 1 }), { minSize: 1 }),
|
||||
schema.never()
|
||||
),
|
||||
// esqlQuery rule params only
|
||||
esqlQuery: schema.conditional(
|
||||
schema.siblingRef('searchType'),
|
||||
schema.literal('esqlQuery'),
|
||||
schema.object({ esql: schema.string({ minLength: 1 }) }),
|
||||
schema.never()
|
||||
),
|
||||
sourceFields: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
label: schema.string(),
|
||||
searchPath: schema.string(),
|
||||
}),
|
||||
{
|
||||
maxSize: MAX_SELECTABLE_SOURCE_FIELDS,
|
||||
}
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
export const EsQueryRuleParamsSchema = schema.object(EsQueryRuleParamsSchemaProperties, {
|
||||
validate: validateParams,
|
||||
});
|
||||
|
||||
const betweenComparators = new Set(['between', 'notBetween']);
|
||||
|
||||
// using direct type not allowed, circular reference, so body is typed to any
|
||||
function validateParams(anyParams: unknown): string | undefined {
|
||||
const {
|
||||
esQuery,
|
||||
thresholdComparator,
|
||||
threshold,
|
||||
searchType,
|
||||
aggType,
|
||||
aggField,
|
||||
groupBy,
|
||||
termField,
|
||||
termSize,
|
||||
} = anyParams as EsQueryRuleParams;
|
||||
|
||||
if (betweenComparators.has(thresholdComparator) && threshold.length === 1) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.invalidThreshold2ErrorMessage', {
|
||||
defaultMessage:
|
||||
'[threshold]: must have two elements for the "{thresholdComparator}" comparator',
|
||||
values: {
|
||||
thresholdComparator,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (aggType !== 'count' && !aggField) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.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.esQuery.termFieldRequiredErrorMessage', {
|
||||
defaultMessage: '[termField]: termField required when [groupBy] is top',
|
||||
});
|
||||
}
|
||||
if (termSize == null) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.termSizeRequiredErrorMessage', {
|
||||
defaultMessage: '[termSize]: termSize required when [groupBy] is top',
|
||||
});
|
||||
}
|
||||
if (termSize > MAX_GROUPS) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.invalidTermSizeMaximumErrorMessage', {
|
||||
defaultMessage: '[termSize]: must be less than or equal to {maxGroups}',
|
||||
values: {
|
||||
maxGroups: MAX_GROUPS,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isSearchSourceRule(searchType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEsqlQueryRule(searchType)) {
|
||||
const { timeField } = anyParams as EsQueryRuleParams;
|
||||
|
||||
if (!timeField) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.esqlTimeFieldErrorMessage', {
|
||||
defaultMessage: '[timeField]: is required',
|
||||
});
|
||||
}
|
||||
if (thresholdComparator !== Comparator.GT) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.esqlThresholdComparatorErrorMessage', {
|
||||
defaultMessage: '[thresholdComparator]: is required to be greater than',
|
||||
});
|
||||
}
|
||||
if (threshold && threshold[0] !== 0) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.esqlThresholdErrorMessage', {
|
||||
defaultMessage: '[threshold]: is required to be 0',
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedQuery = JSON.parse(esQuery);
|
||||
|
||||
if (parsedQuery && !parsedQuery.query) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.missingEsQueryErrorMessage', {
|
||||
defaultMessage: '[esQuery]: must contain "query"',
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.invalidEsQueryErrorMessage', {
|
||||
defaultMessage: '[esQuery]: must be valid JSON',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function validateServerless(params: EsQueryRuleParams) {
|
||||
const { size } = params;
|
||||
if (size > ES_QUERY_MAX_HITS_PER_EXECUTION_SERVERLESS) {
|
||||
|
@ -228,14 +34,3 @@ export function validateServerless(params: EsQueryRuleParams) {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateComparator(comparator: Comparator): string | undefined {
|
||||
if (ComparatorFnNames.has(comparator)) return;
|
||||
|
||||
return i18n.translate('xpack.stackAlerts.esQuery.invalidComparatorErrorMessage', {
|
||||
defaultMessage: 'invalid thresholdComparator specified: {comparator}',
|
||||
values: {
|
||||
comparator,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EsQueryRuleParams } from '@kbn/response-ops-rule-params/es_query';
|
||||
import { RuleExecutorOptions, RuleTypeParams } from '../../types';
|
||||
import { ActionContext } from './action_context';
|
||||
import { EsQueryRuleParams, EsQueryRuleState } from './rule_type_params';
|
||||
import { EsQueryRuleState } from './rule_type_params';
|
||||
import { ActionGroupId } from './constants';
|
||||
import { StackAlertType } from '../types';
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { EsQueryRuleParams } from '@kbn/response-ops-rule-params/es_query';
|
||||
import { OnlyEsQueryRuleParams } from './types';
|
||||
import { EsQueryRuleParams } from './rule_type_params';
|
||||
|
||||
export function isEsQueryRule(searchType: EsQueryRuleParams['searchType']) {
|
||||
return searchType === 'esQuery';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { BaseActionContext, addMessages } from './action_context';
|
||||
import { ParamsSchema } from './rule_type_params';
|
||||
import { ParamsSchema } from '@kbn/response-ops-rule-params/index_threshold';
|
||||
|
||||
describe('ActionContext', () => {
|
||||
it('generates expected properties if aggField is null', async () => {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { AlertInstanceContext } from '@kbn/alerting-plugin/server';
|
||||
import { Params } from './rule_type_params';
|
||||
import type { Params } from '@kbn/response-ops-rule-params/index_threshold';
|
||||
|
||||
// rule type context provided to actions
|
||||
export interface ActionContext extends BaseActionContext {
|
||||
|
|
|
@ -10,7 +10,6 @@ import { getRuleType } from './rule_type';
|
|||
|
||||
// future enhancement: make these configurable?
|
||||
export const MAX_INTERVALS = 1000;
|
||||
export const MAX_GROUPS = 1000;
|
||||
export const DEFAULT_GROUPS = 100;
|
||||
|
||||
export function register(params: RegisterRuleTypesParams) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { loggingSystemMock } from '@kbn/core/server/mocks';
|
|||
import { RuleExecutorServices } from '@kbn/alerting-plugin/server';
|
||||
import { getRuleType, ActionGroupId } from './rule_type';
|
||||
import { ActionContext } from './action_context';
|
||||
import { Params } from './rule_type_params';
|
||||
import type { Params } from '@kbn/response-ops-rule-params/index_threshold';
|
||||
import { TIME_SERIES_BUCKET_SELECTOR_FIELD } from '@kbn/triggers-actions-ui-plugin/server';
|
||||
import { RuleExecutorServicesMock, alertsMock } from '@kbn/alerting-plugin/server/mocks';
|
||||
import { Comparator } from '../../../common/comparator_types';
|
||||
|
|
|
@ -18,10 +18,11 @@ import {
|
|||
STACK_ALERTS_FEATURE_ID,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { AlertsClientError } from '@kbn/alerting-plugin/server';
|
||||
import { type Params, ParamsSchema } from '@kbn/response-ops-rule-params/index_threshold';
|
||||
import { ComparatorFns } from '@kbn/response-ops-rule-params/common';
|
||||
import { ALERT_EVALUATION_CONDITIONS, ALERT_TITLE, STACK_ALERTS_AAD_CONFIG } from '..';
|
||||
import { ComparatorFns, getComparatorScript, getHumanReadableComparator } from '../../../common';
|
||||
import { getComparatorScript, getHumanReadableComparator } from '../../../common';
|
||||
import { ActionContext, BaseActionContext, addMessages } from './action_context';
|
||||
import { Params, ParamsSchema } from './rule_type_params';
|
||||
import { RuleType, RuleExecutorOptions, StackAlertsStartDeps } from '../../types';
|
||||
import { StackAlertType } from '../types';
|
||||
|
||||
|
|
|
@ -5,10 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ParamsSchema, Params } from './rule_type_params';
|
||||
import {
|
||||
ParamsSchema,
|
||||
type Params,
|
||||
type CoreQueryParams,
|
||||
} from '@kbn/response-ops-rule-params/index_threshold';
|
||||
import { MAX_GROUPS } from '@kbn/response-ops-rule-params/common';
|
||||
import { ObjectType, TypeOf } from '@kbn/config-schema';
|
||||
import type { Writable } from '@kbn/utility-types';
|
||||
import { CoreQueryParams, MAX_GROUPS } from '@kbn/triggers-actions-ui-plugin/server';
|
||||
import { Comparator } from '../../../common/comparator_types';
|
||||
|
||||
const DefaultParams: Writable<Partial<Params>> = {
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import {
|
||||
CoreQueryParamsSchemaProperties,
|
||||
validateCoreQueryBody,
|
||||
} from '@kbn/triggers-actions-ui-plugin/server';
|
||||
import { ComparatorFnNames } from '../../../common';
|
||||
import { Comparator } from '../../../common/comparator_types';
|
||||
import { getComparatorSchemaType } from '../lib/comparator';
|
||||
|
||||
// rule type parameters
|
||||
|
||||
export type Params = TypeOf<typeof ParamsSchema>;
|
||||
|
||||
export const ParamsSchema = schema.object(
|
||||
{
|
||||
...CoreQueryParamsSchemaProperties,
|
||||
// the comparison function to use to determine if the threshold as been met
|
||||
thresholdComparator: getComparatorSchemaType(validateComparator),
|
||||
// the values to use as the threshold; `between` and `notBetween` require
|
||||
// two values, the others require one.
|
||||
threshold: schema.arrayOf(schema.number(), { minSize: 1, maxSize: 2 }),
|
||||
},
|
||||
{ validate: validateParams }
|
||||
);
|
||||
|
||||
const betweenComparators = new Set(['between', 'notBetween']);
|
||||
|
||||
// using direct type not allowed, circular reference, so body is typed to any
|
||||
function validateParams(anyParams: unknown): string | undefined {
|
||||
// validate core query parts, return if it fails validation (returning string)
|
||||
const coreQueryValidated = validateCoreQueryBody(anyParams);
|
||||
if (coreQueryValidated) return coreQueryValidated;
|
||||
|
||||
const { thresholdComparator, threshold }: Params = anyParams as Params;
|
||||
|
||||
if (betweenComparators.has(thresholdComparator) && threshold.length === 1) {
|
||||
return i18n.translate('xpack.stackAlerts.indexThreshold.invalidThreshold2ErrorMessage', {
|
||||
defaultMessage:
|
||||
'[threshold]: must have two elements for the "{thresholdComparator}" comparator',
|
||||
values: {
|
||||
thresholdComparator,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function validateComparator(comparator: Comparator): string | undefined {
|
||||
if (ComparatorFnNames.has(comparator)) return;
|
||||
|
||||
return i18n.translate('xpack.stackAlerts.indexThreshold.invalidComparatorErrorMessage', {
|
||||
defaultMessage: 'invalid thresholdComparator specified: {comparator}',
|
||||
values: {
|
||||
comparator,
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { Comparator } from '../../../common/comparator_types';
|
||||
|
||||
export const getComparatorSchemaType = (validate: (comparator: Comparator) => string | void) =>
|
||||
schema.oneOf(
|
||||
[
|
||||
schema.literal(Comparator.GT),
|
||||
schema.literal(Comparator.LT),
|
||||
schema.literal(Comparator.GT_OR_EQ),
|
||||
schema.literal(Comparator.LT_OR_EQ),
|
||||
schema.literal(Comparator.BETWEEN),
|
||||
schema.literal(Comparator.NOT_BETWEEN),
|
||||
],
|
||||
{ validate }
|
||||
);
|
|
@ -1,14 +1,9 @@
|
|||
{
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"outDir": "target/types"
|
||||
},
|
||||
"include": [
|
||||
"server/**/*",
|
||||
"server/**/*.json",
|
||||
"public/**/*",
|
||||
"common/*"
|
||||
],
|
||||
"include": ["server/**/*", "server/**/*.json", "public/**/*", "common/*"],
|
||||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
"@kbn/alerting-plugin",
|
||||
|
@ -58,7 +53,5 @@
|
|||
"@kbn/alerting-rule-utils",
|
||||
"@kbn/response-ops-rule-params"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
]
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -9,18 +9,10 @@ import { Logger, IRouter } from '@kbn/core/server';
|
|||
import { timeSeriesQuery } from './lib/time_series_query';
|
||||
import { registerRoutes } from './routes';
|
||||
|
||||
export type { TimeSeriesQuery, CoreQueryParams } from './lib';
|
||||
export {
|
||||
TIME_SERIES_BUCKET_SELECTOR_FIELD,
|
||||
CoreQueryParamsSchemaProperties,
|
||||
validateCoreQueryBody,
|
||||
validateTimeWindowUnits,
|
||||
validateAggType,
|
||||
validateGroupBy,
|
||||
} from './lib';
|
||||
export type { TimeSeriesQuery } from './lib';
|
||||
export { TIME_SERIES_BUCKET_SELECTOR_FIELD } from './lib';
|
||||
|
||||
// future enhancement: make these configurable?
|
||||
export const MAX_GROUPS = 1000;
|
||||
export const DEFAULT_GROUPS = 100;
|
||||
|
||||
export function getService() {
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
import { ObjectType } from '@kbn/config-schema';
|
||||
import type { Writable } from '@kbn/utility-types';
|
||||
import { CoreQueryParams } from './core_query_types';
|
||||
import { MAX_GROUPS } from '..';
|
||||
import type { CoreQueryParams } from '@kbn/response-ops-rule-params/index_threshold';
|
||||
import { MAX_GROUPS } from '@kbn/response-ops-rule-params/common';
|
||||
|
||||
const DefaultParams: Writable<Partial<CoreQueryParams>> = {
|
||||
index: 'index-name',
|
||||
|
|
|
@ -8,136 +8,8 @@
|
|||
// common properties on time_series_query and alert_type_params
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
|
||||
import { toElasticsearchQuery, fromKueryExpression } from '@kbn/es-query';
|
||||
import { MAX_GROUPS } from '..';
|
||||
|
||||
export const CoreQueryParamsSchemaProperties = {
|
||||
// name of the indices to search
|
||||
index: schema.oneOf([
|
||||
schema.string({ minLength: 1 }),
|
||||
schema.arrayOf(schema.string({ minLength: 1 }), { minSize: 1 }),
|
||||
]),
|
||||
// field in index used for date/time
|
||||
timeField: schema.string({ minLength: 1 }),
|
||||
// aggregation type
|
||||
aggType: schema.string({ validate: validateAggType, defaultValue: 'count' }),
|
||||
// aggregation field
|
||||
aggField: schema.maybe(schema.string({ minLength: 1 })),
|
||||
// how to group
|
||||
groupBy: schema.string({ validate: validateGroupBy, defaultValue: 'all' }),
|
||||
// field to group on (for groupBy: top)
|
||||
termField: schema.maybe(schema.string({ minLength: 1 })),
|
||||
// filter field
|
||||
filterKuery: schema.maybe(schema.string({ validate: validateKuery })),
|
||||
// limit on number of groups returned
|
||||
termSize: schema.maybe(schema.number({ min: 1 })),
|
||||
// size of time window for date range aggregations
|
||||
timeWindowSize: schema.number({ min: 1 }),
|
||||
// units of time window for date range aggregations
|
||||
timeWindowUnit: schema.string({ validate: validateTimeWindowUnits }),
|
||||
};
|
||||
|
||||
const CoreQueryParamsSchema = schema.object(CoreQueryParamsSchemaProperties);
|
||||
export type CoreQueryParams = TypeOf<typeof CoreQueryParamsSchema>;
|
||||
|
||||
// Meant to be used in a "subclass"'s schema body validator, so the
|
||||
// anyParams object is assumed to have been validated with the schema
|
||||
// above.
|
||||
// Using direct type not allowed, circular reference, so body is typed to unknown.
|
||||
export function validateCoreQueryBody(anyParams: unknown): string | undefined {
|
||||
const { aggType, aggField, groupBy, termField, termSize }: CoreQueryParams =
|
||||
anyParams as CoreQueryParams;
|
||||
if (aggType !== 'count' && !aggField) {
|
||||
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.triggersActionsUI.data.coreQueryParams.termFieldRequiredErrorMessage',
|
||||
{
|
||||
defaultMessage: '[termField]: termField required when [groupBy] is top',
|
||||
}
|
||||
);
|
||||
}
|
||||
if (termSize == null) {
|
||||
return i18n.translate(
|
||||
'xpack.triggersActionsUI.data.coreQueryParams.termSizeRequiredErrorMessage',
|
||||
{
|
||||
defaultMessage: '[termSize]: termSize required when [groupBy] is top',
|
||||
}
|
||||
);
|
||||
}
|
||||
if (termSize > MAX_GROUPS) {
|
||||
return i18n.translate(
|
||||
'xpack.triggersActionsUI.data.coreQueryParams.invalidTermSizeMaximumErrorMessage',
|
||||
{
|
||||
defaultMessage: '[termSize]: must be less than or equal to {maxGroups}',
|
||||
values: {
|
||||
maxGroups: MAX_GROUPS,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AggTypes = new Set(['count', 'avg', 'min', 'max', 'sum']);
|
||||
|
||||
export function validateAggType(aggType: string): string | undefined {
|
||||
if (AggTypes.has(aggType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return i18n.translate('xpack.triggersActionsUI.data.coreQueryParams.invalidAggTypeErrorMessage', {
|
||||
defaultMessage: 'invalid aggType: "{aggType}"',
|
||||
values: {
|
||||
aggType,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function validateGroupBy(groupBy: string): string | undefined {
|
||||
if (groupBy === 'all' || groupBy === 'top') {
|
||||
return;
|
||||
}
|
||||
|
||||
return i18n.translate('xpack.triggersActionsUI.data.coreQueryParams.invalidGroupByErrorMessage', {
|
||||
defaultMessage: 'invalid groupBy: "{groupBy}"',
|
||||
values: {
|
||||
groupBy,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const TimeWindowUnits = new Set(['s', 'm', 'h', 'd']);
|
||||
|
||||
export function validateTimeWindowUnits(timeWindowUnit: string): string | undefined {
|
||||
if (TimeWindowUnits.has(timeWindowUnit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return i18n.translate(
|
||||
'xpack.triggersActionsUI.data.coreQueryParams.invalidTimeWindowUnitsErrorMessage',
|
||||
{
|
||||
defaultMessage: 'invalid timeWindowUnit: "{timeWindowUnit}"',
|
||||
values: {
|
||||
timeWindowUnit,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function validateKuery(query: string): string | undefined {
|
||||
try {
|
||||
|
|
|
@ -7,11 +7,3 @@
|
|||
|
||||
export type { TimeSeriesQuery } from './time_series_query';
|
||||
export { TIME_SERIES_BUCKET_SELECTOR_FIELD } from './time_series_query';
|
||||
export type { CoreQueryParams } from './core_query_types';
|
||||
export {
|
||||
CoreQueryParamsSchemaProperties,
|
||||
validateCoreQueryBody,
|
||||
validateTimeWindowUnits,
|
||||
validateAggType,
|
||||
validateGroupBy,
|
||||
} from './core_query_types';
|
||||
|
|
|
@ -12,7 +12,10 @@ import { i18n } from '@kbn/i18n';
|
|||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
|
||||
import { parseDuration } from '@kbn/alerting-plugin/server';
|
||||
import { CoreQueryParamsSchemaProperties, validateCoreQueryBody } from './core_query_types';
|
||||
import {
|
||||
CoreQueryParamsSchemaProperties,
|
||||
validateCoreQueryBody,
|
||||
} from '@kbn/response-ops-rule-params/index_threshold';
|
||||
import {
|
||||
MAX_INTERVALS,
|
||||
getDateStartAfterDateEndErrorMessage,
|
||||
|
|
|
@ -8,17 +8,8 @@ import { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/serv
|
|||
import { configSchema, ConfigSchema } from './config';
|
||||
|
||||
export type { PluginStartContract } from './plugin';
|
||||
export type { TimeSeriesQuery, CoreQueryParams } from './data';
|
||||
export {
|
||||
CoreQueryParamsSchemaProperties,
|
||||
validateCoreQueryBody,
|
||||
validateTimeWindowUnits,
|
||||
validateAggType,
|
||||
validateGroupBy,
|
||||
MAX_GROUPS,
|
||||
DEFAULT_GROUPS,
|
||||
TIME_SERIES_BUCKET_SELECTOR_FIELD,
|
||||
} from './data';
|
||||
export type { TimeSeriesQuery } from './data';
|
||||
export { DEFAULT_GROUPS, TIME_SERIES_BUCKET_SELECTOR_FIELD } from './data';
|
||||
|
||||
export const config: PluginConfigDescriptor<ConfigSchema> = {
|
||||
exposeToBrowser: {
|
||||
|
|
|
@ -75,7 +75,8 @@
|
|||
"@kbn/response-ops-rule-form",
|
||||
"@kbn/charts-theme",
|
||||
"@kbn/rrule",
|
||||
"@kbn/core-notifications-browser-mocks"
|
||||
"@kbn/core-notifications-browser-mocks",
|
||||
"@kbn/response-ops-rule-params"
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue