mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[RAC][Observability] Use flattened type for rule params in Observability (#120758)
* add kibana.alert.rule.parameters as a flattened type * temp * rule_data_formatter * fix bug in search strategy with flattend field type where prefix was wrong (kibana.alert.rule.parameters was ignored) * fix inventory rule data formatters * remove console log * hack that prepends kibana.alerts.rule.parameters in the nested subfields * import ALERT_RULE_PARAMETERS from kbn rule data utils * remove console log * format custom metric link * remove ALERT_PARAMS from technical field names * fix bug in timelines plugin to use dotField instead of prependField & fix failing tests * remove console log and unused variable * delete kibana.alert.rule.params from the mapping * flatten kibana.alert.rule.parameters and add some unit tests * fix rule_data_formatter * handle scenario of having multiple items in an array (multiple conditions setup in the rule) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
ecf2265d56
commit
cdd66ea0eb
7 changed files with 149 additions and 35 deletions
|
@ -53,7 +53,6 @@ const ALERT_RULE_LICENSE = `${ALERT_RULE_NAMESPACE}.license` as const;
|
||||||
const ALERT_RULE_CATEGORY = `${ALERT_RULE_NAMESPACE}.category` as const;
|
const ALERT_RULE_CATEGORY = `${ALERT_RULE_NAMESPACE}.category` as const;
|
||||||
const ALERT_RULE_NAME = `${ALERT_RULE_NAMESPACE}.name` as const;
|
const ALERT_RULE_NAME = `${ALERT_RULE_NAMESPACE}.name` as const;
|
||||||
const ALERT_RULE_NOTE = `${ALERT_RULE_NAMESPACE}.note` as const;
|
const ALERT_RULE_NOTE = `${ALERT_RULE_NAMESPACE}.note` as const;
|
||||||
const ALERT_RULE_PARAMS = `${ALERT_RULE_NAMESPACE}.params` as const;
|
|
||||||
const ALERT_RULE_PARAMETERS = `${ALERT_RULE_NAMESPACE}.parameters` as const;
|
const ALERT_RULE_PARAMETERS = `${ALERT_RULE_NAMESPACE}.parameters` as const;
|
||||||
const ALERT_RULE_REFERENCES = `${ALERT_RULE_NAMESPACE}.references` as const;
|
const ALERT_RULE_REFERENCES = `${ALERT_RULE_NAMESPACE}.references` as const;
|
||||||
const ALERT_RULE_RISK_SCORE = `${ALERT_RULE_NAMESPACE}.risk_score` as const;
|
const ALERT_RULE_RISK_SCORE = `${ALERT_RULE_NAMESPACE}.risk_score` as const;
|
||||||
|
@ -113,7 +112,6 @@ const fields = {
|
||||||
ALERT_RULE_LICENSE,
|
ALERT_RULE_LICENSE,
|
||||||
ALERT_RULE_NAME,
|
ALERT_RULE_NAME,
|
||||||
ALERT_RULE_NOTE,
|
ALERT_RULE_NOTE,
|
||||||
ALERT_RULE_PARAMS,
|
|
||||||
ALERT_RULE_PARAMETERS,
|
ALERT_RULE_PARAMETERS,
|
||||||
ALERT_RULE_REFERENCES,
|
ALERT_RULE_REFERENCES,
|
||||||
ALERT_RULE_RISK_SCORE,
|
ALERT_RULE_RISK_SCORE,
|
||||||
|
@ -171,7 +169,6 @@ export {
|
||||||
ALERT_RULE_LICENSE,
|
ALERT_RULE_LICENSE,
|
||||||
ALERT_RULE_NAME,
|
ALERT_RULE_NAME,
|
||||||
ALERT_RULE_NOTE,
|
ALERT_RULE_NOTE,
|
||||||
ALERT_RULE_PARAMS,
|
|
||||||
ALERT_RULE_PARAMETERS,
|
ALERT_RULE_PARAMETERS,
|
||||||
ALERT_RULE_REFERENCES,
|
ALERT_RULE_REFERENCES,
|
||||||
ALERT_RULE_RISK_SCORE,
|
ALERT_RULE_RISK_SCORE,
|
||||||
|
|
|
@ -72,9 +72,7 @@ export const eventHit = {
|
||||||
],
|
],
|
||||||
astring: 'cool',
|
astring: 'cool',
|
||||||
aNumber: 1,
|
aNumber: 1,
|
||||||
anObject: {
|
neat: true,
|
||||||
neat: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,35 +7,45 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ALERT_REASON,
|
ALERT_REASON,
|
||||||
ALERT_RULE_PARAMS,
|
ALERT_RULE_PARAMETERS,
|
||||||
TIMESTAMP,
|
TIMESTAMP,
|
||||||
} from '@kbn/rule-data-utils/technical_field_names';
|
} from '@kbn/rule-data-utils/technical_field_names';
|
||||||
import { encode } from 'rison-node';
|
import { encode } from 'rison-node';
|
||||||
import { stringify } from 'query-string';
|
import { stringify } from 'query-string';
|
||||||
import { ObservabilityRuleTypeFormatter } from '../../../../observability/public';
|
import { ObservabilityRuleTypeFormatter } from '../../../../observability/public';
|
||||||
import { InventoryMetricThresholdParams } from '../../../common/alerting/metrics';
|
|
||||||
|
|
||||||
export const formatReason: ObservabilityRuleTypeFormatter = ({ fields }) => {
|
export const formatReason: ObservabilityRuleTypeFormatter = ({ fields }) => {
|
||||||
const reason = fields[ALERT_REASON] ?? '-';
|
const reason = fields[ALERT_REASON] ?? '-';
|
||||||
const ruleParams = parseRuleParams(fields[ALERT_RULE_PARAMS]);
|
const nodeTypeField = `${ALERT_RULE_PARAMETERS}.nodeType`;
|
||||||
|
const nodeType = fields[nodeTypeField];
|
||||||
let link = '/app/metrics/link-to/inventory?';
|
let link = '/app/metrics/link-to/inventory?';
|
||||||
|
|
||||||
if (ruleParams) {
|
if (nodeType) {
|
||||||
const linkToParams: Record<string, any> = {
|
const linkToParams: Record<string, any> = {
|
||||||
nodeType: ruleParams.nodeType,
|
nodeType: fields[nodeTypeField][0],
|
||||||
timestamp: Date.parse(fields[TIMESTAMP]),
|
timestamp: Date.parse(fields[TIMESTAMP]),
|
||||||
customMetric: '',
|
customMetric: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
// We always pick the first criteria metric for the URL
|
// We always pick the first criteria metric for the URL
|
||||||
const criteria = ruleParams.criteria[0];
|
const criteriaMetric = fields[`${ALERT_RULE_PARAMETERS}.criteria.metric`][0];
|
||||||
if (criteria.customMetric && criteria.customMetric.id !== 'alert-custom-metric') {
|
const criteriaCustomMetricId = fields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.id`][0];
|
||||||
const customMetric = encode(criteria.customMetric);
|
if (criteriaCustomMetricId !== 'alert-custom-metric') {
|
||||||
|
const criteriaCustomMetricAggregation =
|
||||||
|
fields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.aggregation`][0];
|
||||||
|
const criteriaCustomMetricField =
|
||||||
|
fields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.field`][0];
|
||||||
|
|
||||||
|
const customMetric = encode({
|
||||||
|
id: criteriaCustomMetricId,
|
||||||
|
type: 'custom',
|
||||||
|
field: criteriaCustomMetricField,
|
||||||
|
aggregation: criteriaCustomMetricAggregation,
|
||||||
|
});
|
||||||
linkToParams.customMetric = customMetric;
|
linkToParams.customMetric = customMetric;
|
||||||
linkToParams.metric = customMetric;
|
linkToParams.metric = customMetric;
|
||||||
} else {
|
} else {
|
||||||
linkToParams.metric = encode({ type: criteria.metric });
|
linkToParams.metric = encode({ type: criteriaMetric });
|
||||||
}
|
}
|
||||||
|
|
||||||
link += stringify(linkToParams);
|
link += stringify(linkToParams);
|
||||||
|
@ -46,11 +56,3 @@ export const formatReason: ObservabilityRuleTypeFormatter = ({ fields }) => {
|
||||||
link,
|
link,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
function parseRuleParams(params?: string): InventoryMetricThresholdParams | undefined {
|
|
||||||
try {
|
|
||||||
return typeof params === 'string' ? JSON.parse(params) : undefined;
|
|
||||||
} catch (_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { ALERT_REASON, ALERT_RULE_PARAMS } from '@kbn/rule-data-utils';
|
import { ALERT_REASON, ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { first, get, last } from 'lodash';
|
import { first, get, last } from 'lodash';
|
||||||
import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label';
|
import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label';
|
||||||
|
@ -74,7 +74,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
||||||
id,
|
id,
|
||||||
fields: {
|
fields: {
|
||||||
[ALERT_REASON]: reason,
|
[ALERT_REASON]: reason,
|
||||||
[ALERT_RULE_PARAMS]: JSON.stringify(params),
|
[ALERT_RULE_PARAMETERS]: params as any, // the type assumes the object is already flattened when writing the same way as when reading https://github.com/elastic/kibana/blob/main/x-pack/plugins/rule_registry/common/field_map/runtime_type_from_fieldmap.ts#L60
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ export const technicalRuleFieldMap = {
|
||||||
Fields.EVENT_ACTION,
|
Fields.EVENT_ACTION,
|
||||||
Fields.TAGS
|
Fields.TAGS
|
||||||
),
|
),
|
||||||
[Fields.ALERT_RULE_PARAMS]: { type: 'keyword', index: false },
|
|
||||||
[Fields.ALERT_RULE_PARAMETERS]: { type: 'flattened', ignore_above: 4096 },
|
[Fields.ALERT_RULE_PARAMETERS]: { type: 'flattened', ignore_above: 4096 },
|
||||||
[Fields.ALERT_RULE_TYPE_ID]: { type: 'keyword', required: true },
|
[Fields.ALERT_RULE_TYPE_ID]: { type: 'keyword', required: true },
|
||||||
[Fields.ALERT_RULE_CONSUMER]: { type: 'keyword', required: true },
|
[Fields.ALERT_RULE_CONSUMER]: { type: 'keyword', required: true },
|
||||||
|
|
|
@ -131,7 +131,115 @@ describe('Events Details Helpers', () => {
|
||||||
const result = getDataFromFieldsHits(whackFields);
|
const result = getDataFromFieldsHits(whackFields);
|
||||||
expect(result).toEqual(whackResultFields);
|
expect(result).toEqual(whackResultFields);
|
||||||
});
|
});
|
||||||
|
it('flattens alert parameters', () => {
|
||||||
|
const ruleParameterFields = {
|
||||||
|
'kibana.alert.rule.parameters': [
|
||||||
|
{
|
||||||
|
nodeType: 'host',
|
||||||
|
criteria: [
|
||||||
|
{
|
||||||
|
metric: 'cpu',
|
||||||
|
comparator: '>',
|
||||||
|
threshold: [3],
|
||||||
|
timeSize: 1,
|
||||||
|
timeUnit: 'm',
|
||||||
|
customMetric: {
|
||||||
|
type: 'custom',
|
||||||
|
id: 'alert-custom-metric',
|
||||||
|
field: '',
|
||||||
|
aggregation: 'avg',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sourceId: 'default',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const ruleParametersResultFields = [
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.nodeType',
|
||||||
|
values: ['host'],
|
||||||
|
originalValue: ['host'],
|
||||||
|
isObjectArray: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.metric',
|
||||||
|
isObjectArray: false,
|
||||||
|
originalValue: ['cpu'],
|
||||||
|
values: ['cpu'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.comparator',
|
||||||
|
values: ['>'],
|
||||||
|
originalValue: ['>'],
|
||||||
|
isObjectArray: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.threshold',
|
||||||
|
isObjectArray: false,
|
||||||
|
originalValue: ['3'],
|
||||||
|
values: ['3'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.timeSize',
|
||||||
|
isObjectArray: false,
|
||||||
|
originalValue: ['1'],
|
||||||
|
values: ['1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.timeUnit',
|
||||||
|
values: ['m'],
|
||||||
|
originalValue: ['m'],
|
||||||
|
isObjectArray: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.customMetric.type',
|
||||||
|
isObjectArray: false,
|
||||||
|
originalValue: ['custom'],
|
||||||
|
values: ['custom'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.customMetric.id',
|
||||||
|
isObjectArray: false,
|
||||||
|
originalValue: ['alert-custom-metric'],
|
||||||
|
values: ['alert-custom-metric'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.customMetric.field',
|
||||||
|
isObjectArray: false,
|
||||||
|
originalValue: [''],
|
||||||
|
values: [''],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.criteria.customMetric.aggregation',
|
||||||
|
isObjectArray: false,
|
||||||
|
originalValue: ['avg'],
|
||||||
|
values: ['avg'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: 'kibana',
|
||||||
|
field: 'kibana.alert.rule.parameters.sourceId',
|
||||||
|
isObjectArray: false,
|
||||||
|
originalValue: ['default'],
|
||||||
|
values: ['default'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const result = getDataFromFieldsHits(ruleParameterFields);
|
||||||
|
expect(result).toEqual(ruleParametersResultFields);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#getDataFromSourceHits', () => {
|
it('#getDataFromSourceHits', () => {
|
||||||
const _source: EventSource = {
|
const _source: EventSource = {
|
||||||
'@timestamp': '2021-02-24T00:41:06.527Z',
|
'@timestamp': '2021-02-24T00:41:06.527Z',
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
import { get, isEmpty, isNumber, isObject, isString } from 'lodash/fp';
|
import { get, isEmpty, isNumber, isObject, isString } from 'lodash/fp';
|
||||||
|
|
||||||
|
import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils/technical_field_names';
|
||||||
import { EventHit, EventSource, TimelineEventsDetailsItem } from '../search_strategy';
|
import { EventHit, EventSource, TimelineEventsDetailsItem } from '../search_strategy';
|
||||||
import { toObjectArrayOfStrings, toStringArray } from './to_array';
|
import { toObjectArrayOfStrings, toStringArray } from './to_array';
|
||||||
|
|
||||||
export const baseCategoryFields = ['@timestamp', 'labels', 'message', 'tags'];
|
export const baseCategoryFields = ['@timestamp', 'labels', 'message', 'tags'];
|
||||||
|
|
||||||
export const getFieldCategory = (field: string): string => {
|
export const getFieldCategory = (field: string): string => {
|
||||||
|
@ -38,6 +38,9 @@ export const formatGeoLocation = (item: unknown[]) => {
|
||||||
export const isGeoField = (field: string) =>
|
export const isGeoField = (field: string) =>
|
||||||
field.includes('geo.location') || field.includes('geoip.location');
|
field.includes('geo.location') || field.includes('geoip.location');
|
||||||
|
|
||||||
|
export const isRuleParametersFieldOrSubfield = (field: string, prependField?: string) =>
|
||||||
|
prependField?.includes(ALERT_RULE_PARAMETERS) || field === ALERT_RULE_PARAMETERS;
|
||||||
|
|
||||||
export const getDataFromSourceHits = (
|
export const getDataFromSourceHits = (
|
||||||
sources: EventSource,
|
sources: EventSource,
|
||||||
category?: string,
|
category?: string,
|
||||||
|
@ -79,7 +82,6 @@ export const getDataFromFieldsHits = (
|
||||||
): TimelineEventsDetailsItem[] =>
|
): TimelineEventsDetailsItem[] =>
|
||||||
Object.keys(fields).reduce<TimelineEventsDetailsItem[]>((accumulator, field) => {
|
Object.keys(fields).reduce<TimelineEventsDetailsItem[]>((accumulator, field) => {
|
||||||
const item: unknown[] = fields[field];
|
const item: unknown[] = fields[field];
|
||||||
|
|
||||||
const fieldCategory =
|
const fieldCategory =
|
||||||
prependFieldCategory != null ? prependFieldCategory : getFieldCategory(field);
|
prependFieldCategory != null ? prependFieldCategory : getFieldCategory(field);
|
||||||
if (isGeoField(field)) {
|
if (isGeoField(field)) {
|
||||||
|
@ -112,13 +114,21 @@ export const getDataFromFieldsHits = (
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// format nested fields
|
// format nested fields
|
||||||
const nestedFields = Array.isArray(item)
|
let nestedFields;
|
||||||
? item
|
if (isRuleParametersFieldOrSubfield(field, prependField)) {
|
||||||
.reduce((acc, i) => [...acc, getDataFromFieldsHits(i, dotField, fieldCategory)], [])
|
nestedFields = Array.isArray(item)
|
||||||
.flat()
|
? item
|
||||||
: getDataFromFieldsHits(item, prependField, fieldCategory);
|
.reduce((acc, i) => [...acc, getDataFromFieldsHits(i, dotField, fieldCategory)], [])
|
||||||
|
.flat()
|
||||||
|
: getDataFromFieldsHits(item, dotField, fieldCategory);
|
||||||
|
} else {
|
||||||
|
nestedFields = Array.isArray(item)
|
||||||
|
? item
|
||||||
|
.reduce((acc, i) => [...acc, getDataFromFieldsHits(i, dotField, fieldCategory)], [])
|
||||||
|
.flat()
|
||||||
|
: getDataFromFieldsHits(item, prependField, fieldCategory);
|
||||||
|
}
|
||||||
|
|
||||||
// combine duplicate fields
|
// combine duplicate fields
|
||||||
const flat: Record<string, TimelineEventsDetailsItem> = [
|
const flat: Record<string, TimelineEventsDetailsItem> = [
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue