Skip to content

Commit

Permalink
[ResponseOps][FE] Alert creation delay based on user definition (elas…
Browse files Browse the repository at this point in the history
…tic#176346)

Resolves elastic#173009

## Summary

Adds a new input for the user to define the `alertDelay`. This input is
available for life-cycled alerts (stack and o11y) rule types.

### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios


### To verify

- Using the UI create a rule with the `alertDelay` field set.
- Verify that the field is saved properly and that you can edit the
`alertDelay`
- Verify that you can add the alert delay to existing rules. Create a
rule in a different branch and switch to this one. Edit the rule and set
the `alertDelay`. Verify that the rule saves and works as expected.

---------

Co-authored-by: Lisa Cawley <lcawley@elastic.co>
  • Loading branch information
2 people authored and fkanout committed Mar 4, 2024
1 parent 9754bf0 commit 1d073fb
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ const sampleRule: SanitizedRule<RuleTypeParams> & { activeSnoozes?: string[] } =
},
nextRun: DATE_2020,
revision: 0,
alertDelay: {
active: 10,
},
};

describe('rewriteRule', () => {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const rewriteRule = ({
activeSnoozes,
lastRun,
nextRun,
alertDelay,
...rest
}: SanitizedRule<RuleTypeParams> & { activeSnoozes?: string[] }) => ({
...rest,
Expand Down Expand Up @@ -78,4 +79,5 @@ export const rewriteRule = ({
...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}),
...(nextRun ? { next_run: nextRun } : {}),
...(apiKeyCreatedByUser !== undefined ? { api_key_created_by_user: apiKeyCreatedByUser } : {}),
...(alertDelay !== undefined ? { alert_delay: alertDelay } : {}),
});
10 changes: 10 additions & 0 deletions x-pack/plugins/alerting/server/routes/update_rule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ describe('updateRuleRoute', () => {
},
],
notifyWhen: RuleNotifyWhen.CHANGE,
alertDelay: {
active: 10,
},
};

const updateRequest: AsApiContract<UpdateOptions<{ otherField: boolean }>['data']> = {
Expand All @@ -73,6 +76,9 @@ describe('updateRuleRoute', () => {
alerts_filter: mockedAlert.actions[0].alertsFilter,
},
],
alert_delay: {
active: 10,
},
};

const updateResult: AsApiContract<PartialRule<{ otherField: boolean }>> = {
Expand All @@ -86,6 +92,7 @@ describe('updateRuleRoute', () => {
connector_type_id: actionTypeId,
alerts_filter: alertsFilter,
})),
alert_delay: mockedAlert.alertDelay,
};

it('updates a rule with proper parameters', async () => {
Expand Down Expand Up @@ -135,6 +142,9 @@ describe('updateRuleRoute', () => {
"uuid": "1234-5678",
},
],
"alertDelay": Object {
"active": 10,
},
"name": "abc",
"notifyWhen": "onActionGroupChange",
"params": Object {
Expand Down
10 changes: 9 additions & 1 deletion x-pack/plugins/alerting/server/routes/update_rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,22 @@ const bodySchema = schema.object({
)
)
),
alert_delay: schema.maybe(
schema.object({
active: schema.number(),
})
),
});

const rewriteBodyReq: RewriteRequestCase<UpdateOptions<RuleTypeParams>> = (result) => {
const { notify_when: notifyWhen, actions, ...rest } = result.data;
const { notify_when: notifyWhen, alert_delay: alertDelay, actions, ...rest } = result.data;
return {
...result,
data: {
...rest,
notifyWhen,
actions: rewriteActionsReq(actions),
alertDelay,
},
};
};
Expand All @@ -83,6 +89,7 @@ const rewriteBodyRes: RewriteResponseCase<PartialRule<RuleTypeParams>> = ({
isSnoozedUntil,
lastRun,
nextRun,
alertDelay,
...rest
}) => ({
...rest,
Expand Down Expand Up @@ -115,6 +122,7 @@ const rewriteBodyRes: RewriteResponseCase<PartialRule<RuleTypeParams>> = ({
...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}),
...(nextRun ? { next_run: nextRun } : {}),
...(apiKeyCreatedByUser !== undefined ? { api_key_created_by_user: apiKeyCreatedByUser } : {}),
...(alertDelay ? { alert_delay: alertDelay } : {}),
});

export const updateRuleRoute = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '../../types';
import { validateRuleTypeParams, getRuleNotifyWhenType } from '../../lib';
import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization';
import { parseDuration, getRuleCircuitBreakerErrorMessage } from '../../../common';
import { parseDuration, getRuleCircuitBreakerErrorMessage, AlertDelay } from '../../../common';
import { retryIfConflicts } from '../../lib/retry_if_conflicts';
import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation';
import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events';
Expand Down Expand Up @@ -51,6 +51,7 @@ export interface UpdateOptions<Params extends RuleTypeParams> {
params: Params;
throttle?: string | null;
notifyWhen?: RuleNotifyWhenType | null;
alertDelay?: AlertDelay;
};
allowMissingConnectorSecrets?: boolean;
shouldIncrementRevision?: ShouldIncrementRevision;
Expand Down
12 changes: 12 additions & 0 deletions x-pack/plugins/alerting/server/rules_client/tests/update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ describe('update()', () => {
scheduledTaskId: 'task-123',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
alertDelay: {
active: 5,
},
},
references: [
{
Expand Down Expand Up @@ -334,6 +337,9 @@ describe('update()', () => {
},
},
],
alertDelay: {
active: 10,
},
},
});
expect(result).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -364,6 +370,9 @@ describe('update()', () => {
},
},
],
"alertDelay": Object {
"active": 5,
},
"createdAt": 2019-02-12T21:01:22.479Z,
"enabled": true,
"id": "1",
Expand Down Expand Up @@ -422,6 +431,9 @@ describe('update()', () => {
"uuid": "102",
},
],
"alertDelay": Object {
"active": 10,
},
"alertTypeId": "myType",
"apiKey": null,
"apiKeyCreatedByUser": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export const transformRule: RewriteRequestCase<Rule> = ({
active_snoozes: activeSnoozes,
last_run: lastRun,
next_run: nextRun,
alert_delay: alertDelay,
...rest
}: any) => ({
ruleTypeId,
Expand All @@ -99,6 +100,7 @@ export const transformRule: RewriteRequestCase<Rule> = ({
...(lastRun ? { lastRun: transformLastRun(lastRun) } : {}),
...(nextRun ? { nextRun } : {}),
...(apiKeyCreatedByUser !== undefined ? { apiKeyCreatedByUser } : {}),
...(alertDelay ? { alertDelay } : {}),
...rest,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ describe('createRule', () => {
execution_status: { status: 'pending', last_execution_date: '2021-04-01T21:33:13.250Z' },
create_at: '2021-04-01T21:33:13.247Z',
updated_at: '2021-04-01T21:33:13.247Z',
alert_delay: {
active: 10,
},
};
const ruleToCreate: Omit<
RuleUpdates,
Expand Down Expand Up @@ -96,6 +99,9 @@ describe('createRule', () => {
updatedAt: new Date('2021-04-01T21:33:13.247Z'),
apiKeyOwner: '',
revision: 0,
alertDelay: {
active: 10,
},
};
http.post.mockResolvedValueOnce(resolvedValue);

Expand Down Expand Up @@ -148,6 +154,9 @@ describe('createRule', () => {
tags: [],
updatedAt: '2021-04-01T21:33:13.247Z',
updatedBy: undefined,
alertDelay: {
active: 10,
},
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type RuleCreateBody = Omit<
const rewriteBodyRequest: RewriteResponseCase<RuleCreateBody> = ({
ruleTypeId,
actions,
alertDelay,
...res
}): any => ({
...res,
Expand All @@ -43,6 +44,7 @@ const rewriteBodyRequest: RewriteResponseCase<RuleCreateBody> = ({
: {}),
})
),
...(alertDelay ? { alert_delay: alertDelay } : {}),
});

export async function createRule({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ describe('updateRule', () => {
apiKey: null,
apiKeyOwner: null,
revision: 0,
alertDelay: {
active: 10,
},
};
const resolvedValue: Rule = {
...ruleToUpdate,
Expand All @@ -51,7 +54,7 @@ describe('updateRule', () => {
Array [
"/api/alerting/rule/12%2F3",
Object {
"body": "{\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"actions\\":[]}",
"body": "{\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"actions\\":[],\\"alert_delay\\":{\\"active\\":10}}",
},
]
`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ import { transformRule } from './common_transformations';

type RuleUpdatesBody = Pick<
RuleUpdates,
'name' | 'tags' | 'schedule' | 'actions' | 'params' | 'throttle' | 'notifyWhen'
'name' | 'tags' | 'schedule' | 'actions' | 'params' | 'throttle' | 'notifyWhen' | 'alertDelay'
>;
const rewriteBodyRequest: RewriteResponseCase<RuleUpdatesBody> = ({ actions, ...res }): any => ({
const rewriteBodyRequest: RewriteResponseCase<RuleUpdatesBody> = ({
actions,
alertDelay,
...res
}): any => ({
...res,
actions: actions.map(
({ group, id, params, frequency, uuid, alertsFilter, useAlertDataForTemplate }) => ({
Expand All @@ -34,6 +38,7 @@ const rewriteBodyRequest: RewriteResponseCase<RuleUpdatesBody> = ({ actions, ...
...(uuid && { uuid }),
})
),
...(alertDelay ? { alert_delay: alertDelay } : {}),
});

export async function updateRule({
Expand All @@ -42,14 +47,16 @@ export async function updateRule({
id,
}: {
http: HttpSetup;
rule: Pick<RuleUpdates, 'name' | 'tags' | 'schedule' | 'params' | 'actions'>;
rule: Pick<RuleUpdates, 'name' | 'tags' | 'schedule' | 'params' | 'actions' | 'alertDelay'>;
id: string;
}): Promise<Rule> {
const res = await http.put<AsApiContract<Rule>>(
`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}`,
{
body: JSON.stringify(
rewriteBodyRequest(pick(rule, ['name', 'tags', 'schedule', 'params', 'actions']))
rewriteBodyRequest(
pick(rule, ['name', 'tags', 'schedule', 'params', 'actions', 'alertDelay'])
)
),
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,9 @@ describe('rule_form', () => {
enabled: false,
mutedInstanceIds: [],
...(!showRulesList ? { ruleTypeId: ruleType.id } : {}),
alertDelay: {
active: 1,
},
} as unknown as Rule;

wrapper = mountWithIntl(
Expand Down Expand Up @@ -1034,6 +1037,24 @@ describe('rule_form', () => {

expect(wrapper.find(ActionForm).props().hasFieldsForAAD).toEqual(true);
});

it('renders rule alert delay', async () => {
const getAlertDelayInput = () => {
return wrapper.find('[data-test-subj="alertDelayInput"] input').first();
};

await setup();
expect(getAlertDelayInput().props().value).toEqual(1);

getAlertDelayInput().simulate('change', { target: { value: '2' } });
expect(getAlertDelayInput().props().value).toEqual(2);

getAlertDelayInput().simulate('change', { target: { value: '20' } });
expect(getAlertDelayInput().props().value).toEqual(20);

getAlertDelayInput().simulate('change', { target: { value: '999' } });
expect(getAlertDelayInput().props().value).toEqual(999);
});
});

describe('rule_form create rule non ruleing consumer and producer', () => {
Expand Down

0 comments on commit 1d073fb

Please sign in to comment.