Allowing validation of multiple actions with same connector id (#98065)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
ymao1 2021-05-03 10:11:49 -04:00 committed by GitHub
parent 2fbfca63d7
commit 951da754e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 140 additions and 41 deletions

View file

@ -13,7 +13,7 @@ import {
getAlertWithInvalidatedFields,
} from './value_validators';
import uuid from 'uuid';
import { Alert, UserConfiguredActionConnector } from '../../types';
import { Alert, IErrorObject, UserConfiguredActionConnector } from '../../types';
describe('throwIfAbsent', () => {
test('throws if value is absent', () => {
@ -156,7 +156,7 @@ describe('getConnectorWithInvalidatedFields', () => {
});
describe('getAlertWithInvalidatedFields', () => {
test('set nulls to all required undefined fields in alert', () => {
test('sets to null all fields that are required but undefined in alert', () => {
const alert: Alert = {
params: {},
consumer: 'test',
@ -169,14 +169,40 @@ describe('getAlertWithInvalidatedFields', () => {
enabled: false,
mutedInstanceIds: [],
} as any;
const baseAlertErrors = { name: ['Name is required.'] };
const actionsErrors = {};
const baseAlertErrors = {
name: ['Name is required.'],
alertTypeId: ['Rule type is required.'],
};
const actionsErrors: IErrorObject[] = [];
const paramsErrors = {};
getAlertWithInvalidatedFields(alert, paramsErrors, baseAlertErrors, actionsErrors);
expect(alert.name).toBeNull();
expect(alert.alertTypeId).toBeNull();
});
test('set nulls to all required undefined fields in alert params', () => {
test('does not set to null any fields that are required and defined but invalid in alert', () => {
const alert: Alert = {
name: 'test',
id: '123',
params: {},
consumer: '@@@@',
schedule: {
interval: '1m',
},
actions: [],
tags: [],
muteAll: false,
enabled: false,
mutedInstanceIds: [],
} as any;
const baseAlertErrors = { consumer: ['Consumer is invalid.'] };
const actionsErrors: IErrorObject[] = [];
const paramsErrors = {};
getAlertWithInvalidatedFields(alert, paramsErrors, baseAlertErrors, actionsErrors);
expect(alert.consumer).toEqual('@@@@');
});
test('set to null all fields that are required but undefined in alert params', () => {
const alert: Alert = {
name: 'test',
alertTypeId: '.threshold',
@ -204,18 +230,23 @@ describe('getAlertWithInvalidatedFields', () => {
updatedBy: '',
};
const baseAlertErrors = {};
const actionsErrors = {};
const paramsErrors = { index: ['Index is required.'] };
const actionsErrors: IErrorObject[] = [];
const paramsErrors = { index: ['Index is required.'], timeField: ['Time field is required.'] };
getAlertWithInvalidatedFields(alert, paramsErrors, baseAlertErrors, actionsErrors);
expect(alert.params.index).toBeNull();
expect(alert.params.timeField).toBeNull();
});
test('do not set nulls to the invalid fields with values in the connector properties, config and secrets', () => {
test('does not set to null any fields that are required and defined but invalid in alert params', () => {
const alert: Alert = {
name: 'test',
alertTypeId: '.threshold',
id: '123',
params: {},
consumer: '@@@@',
params: {
aggField: 'foo',
termSize: 'big',
},
consumer: 'test',
schedule: {
interval: '1m',
},
@ -224,15 +255,30 @@ describe('getAlertWithInvalidatedFields', () => {
muteAll: false,
enabled: false,
mutedInstanceIds: [],
} as any;
const baseAlertErrors = { consumer: ['Consumer is invalid.'] };
const actionsErrors = {};
const paramsErrors = {};
createdBy: '',
apiKeyOwner: '',
createdAt: new Date(),
executionStatus: {
status: 'ok',
lastExecutionDate: new Date(),
},
notifyWhen: 'onActionGroupChange',
throttle: '',
updatedAt: new Date(),
updatedBy: '',
};
const baseAlertErrors = {};
const actionsErrors: IErrorObject[] = [];
const paramsErrors = {
aggField: ['Aggregation field is invalid.'],
termSize: ['Term size is invalid.'],
};
getAlertWithInvalidatedFields(alert, paramsErrors, baseAlertErrors, actionsErrors);
expect(alert.consumer).toEqual('@@@@');
expect(alert.params.aggField).toEqual('foo');
expect(alert.params.termSize).toEqual('big');
});
test('if complex alert action fields which is required is set to nulls if it is undefined', () => {
test('set to null all fields that are required but undefined in alert actions', () => {
const alert: Alert = {
name: 'test',
alertTypeId: '.threshold',
@ -271,9 +317,70 @@ describe('getAlertWithInvalidatedFields', () => {
updatedBy: '',
};
const baseAlertErrors = {};
const actionsErrors = { '123': { 'incident.field.name': ['Name is required.'] } };
const actionsErrors = [{ 'incident.field.name': ['Name is required.'] }];
const paramsErrors = {};
getAlertWithInvalidatedFields(alert, paramsErrors, baseAlertErrors, actionsErrors);
expect((alert.actions[0].params as any).incident.field.name).toBeNull();
});
test('validates multiple alert actions with the same connector id', () => {
const alert: Alert = {
name: 'test',
alertTypeId: '.threshold',
id: '123',
params: {},
consumer: 'test',
schedule: {
interval: '1m',
},
actions: [
{
actionTypeId: 'test',
group: 'qwer',
id: '123',
params: {
incident: {
field: {},
},
},
},
{
actionTypeId: 'test',
group: 'qwer',
id: '123',
params: {
incident: {
field: {
name: 'myIncident',
},
},
},
},
],
tags: [],
muteAll: false,
enabled: false,
mutedInstanceIds: [],
createdBy: '',
apiKeyOwner: '',
createdAt: new Date(),
executionStatus: {
status: 'ok',
lastExecutionDate: new Date(),
},
notifyWhen: 'onActionGroupChange',
throttle: '',
updatedAt: new Date(),
updatedBy: '',
};
const baseAlertErrors = {};
const actionsErrors = [
{ 'incident.field.name': ['Name is required.'] },
{ 'incident.field.name': ['Name is invalid.'] },
];
const paramsErrors = {};
getAlertWithInvalidatedFields(alert, paramsErrors, baseAlertErrors, actionsErrors);
expect((alert.actions[0].params as any).incident.field.name).toBeNull();
expect((alert.actions[1].params as any).incident.field.name).toEqual('myIncident');
});
});

View file

@ -74,7 +74,7 @@ export function getAlertWithInvalidatedFields(
alert: Alert,
paramsErrors: IErrorObject,
baseAlertErrors: IErrorObject,
actionsErrors: Record<string, IErrorObject>
actionsErrors: IErrorObject[]
) {
Object.keys(paramsErrors).forEach((errorKey) => {
if (paramsErrors[errorKey].length >= 1 && get(alert.params, errorKey) === undefined) {
@ -86,17 +86,15 @@ export function getAlertWithInvalidatedFields(
set(alert, errorKey, null);
}
});
Object.keys(actionsErrors).forEach((actionId) => {
const actionToValidate = alert.actions.find((action) => action.id === actionId);
Object.keys(actionsErrors[actionId]).forEach((errorKey) => {
if (
actionToValidate &&
actionsErrors[actionId][errorKey].length >= 1 &&
get(actionToValidate!.params, errorKey) === undefined
) {
set(actionToValidate!.params, errorKey, null);
}
});
actionsErrors.forEach((error: IErrorObject, index: number) => {
const actionToValidate = alert.actions.length > index ? alert.actions[index] : null;
if (actionToValidate) {
Object.keys(error).forEach((errorKey) => {
if (error[errorKey].length >= 1 && get(actionToValidate!.params, errorKey) === undefined) {
set(actionToValidate!.params, errorKey, null);
}
});
}
});
return alert;
}

View file

@ -135,14 +135,10 @@ export function getAlertErrors(
...alertBaseErrors,
} as IErrorObject;
const alertActionsErrors = alert.actions.reduce((prev, alertAction: AlertAction) => {
return {
...prev,
[alertAction.id]: actionTypeRegistry
.get(alertAction.actionTypeId)
?.validateParams(alertAction.params).errors,
};
}, {}) as Record<string, IErrorObject>;
const alertActionsErrors = alert.actions.map((alertAction: AlertAction) => {
return actionTypeRegistry.get(alertAction.actionTypeId)?.validateParams(alertAction.params)
.errors;
});
return {
alertParamsErrors,
alertBaseErrors,
@ -160,13 +156,11 @@ export const hasObjectErrors: (errors: IErrorObject) => boolean = (errors) =>
export function isValidAlert(
alertObject: InitialAlert | Alert,
validationResult: IErrorObject,
actionsErrors: Record<string, IErrorObject>
actionsErrors: IErrorObject[]
): alertObject is Alert {
return (
!hasObjectErrors(validationResult) &&
Object.keys(actionsErrors).find((actionErrorsKey) =>
hasObjectErrors(actionsErrors[actionErrorsKey])
) === undefined
actionsErrors.every((error: IErrorObject) => !hasObjectErrors(error))
);
}