Skip to content

Commit

Permalink
Merge branch 'master' into omit_deprecation_details
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Oct 11, 2021
2 parents 1218ab3 + e8d16cd commit 0d09309
Show file tree
Hide file tree
Showing 23 changed files with 828 additions and 139 deletions.
2 changes: 2 additions & 0 deletions x-pack/plugins/alerting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ The following table describes the properties of the `options` object.
|useSavedObjectReferences.extractReferences|(Optional) When developing a rule type, you can choose to implement hooks for extracting saved object references from rule parameters. This hook will be invoked when a rule is created or updated. Implementing this hook is optional, but if an extract hook is implemented, an inject hook must also be implemented.|Function
|useSavedObjectReferences.injectReferences|(Optional) When developing a rule type, you can choose to implement hooks for injecting saved object references into rule parameters. This hook will be invoked when a rule is retrieved (get or find). Implementing this hook is optional, but if an inject hook is implemented, an extract hook must also be implemented.|Function
|isExportable|Whether the rule type is exportable from the Saved Objects Management UI.|boolean|
|defaultScheduleInterval|The default interval that will show up in the UI when creating a rule of this rule type.|boolean|
|minimumScheduleInterval|The minimum interval that will be allowed for all rules of this rule type.|boolean|

### Executor

Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/alerting/common/alert_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface AlertType<
producer: string;
minimumLicenseRequired: LicenseType;
isExportable: boolean;
defaultScheduleInterval?: string;
minimumScheduleInterval?: string;
}

export interface ActionGroup<ActionGroupIds extends string> {
Expand Down
6 changes: 6 additions & 0 deletions x-pack/plugins/alerting/server/routes/rule_types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ describe('ruleTypesRoute', () => {
},
producer: 'test',
enabledInLicense: true,
minimumScheduleInterval: '1m',
defaultScheduleInterval: '10m',
} as RegistryAlertTypeWithAuth,
];
const expectedResult: Array<AsApiContract<RegistryAlertTypeWithAuth>> = [
Expand All @@ -70,7 +72,9 @@ describe('ruleTypesRoute', () => {
},
],
default_action_group_id: 'default',
default_schedule_interval: '10m',
minimum_license_required: 'basic',
minimum_schedule_interval: '1m',
is_exportable: true,
recovery_action_group: RecoveredActionGroup,
authorized_consumers: {},
Expand Down Expand Up @@ -102,10 +106,12 @@ describe('ruleTypesRoute', () => {
},
"authorized_consumers": Object {},
"default_action_group_id": "default",
"default_schedule_interval": "10m",
"enabled_in_license": true,
"id": "1",
"is_exportable": true,
"minimum_license_required": "basic",
"minimum_schedule_interval": "1m",
"name": "name",
"producer": "test",
"recovery_action_group": Object {
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/alerting/server/routes/rule_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const rewriteBodyRes: RewriteResponseCase<RegistryAlertTypeWithAuth[]> = (result
isExportable,
actionVariables,
authorizedConsumers,
minimumScheduleInterval,
defaultScheduleInterval,
...rest
}) => ({
...rest,
Expand All @@ -33,6 +35,8 @@ const rewriteBodyRes: RewriteResponseCase<RegistryAlertTypeWithAuth[]> = (result
is_exportable: isExportable,
action_variables: actionVariables,
authorized_consumers: authorizedConsumers,
minimum_schedule_interval: minimumScheduleInterval,
default_schedule_interval: defaultScheduleInterval,
})
);
};
Expand Down
57 changes: 56 additions & 1 deletion x-pack/plugins/alerting/server/rule_type_registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ describe('register()', () => {

test('throws if AlertType ruleTaskTimeout is not a valid duration', () => {
const alertType: AlertType<never, never, never, never, never, 'default'> = {
id: 123 as unknown as string,
id: '123',
name: 'Test',
actionGroups: [
{
Expand All @@ -138,6 +138,59 @@ describe('register()', () => {
);
});

test('throws if defaultScheduleInterval isnt valid', () => {
const alertType: AlertType<never, never, never, never, never, 'default'> = {
id: '123',
name: 'Test',
actionGroups: [
{
id: 'default',
name: 'Default',
},
],

defaultActionGroupId: 'default',
minimumLicenseRequired: 'basic',
isExportable: true,
executor: jest.fn(),
producer: 'alerts',
defaultScheduleInterval: 'foobar',
};
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);

expect(() => registry.register(alertType)).toThrowError(
new Error(
`Rule type \"123\" has invalid default interval: string is not a valid duration: foobar.`
)
);
});

test('throws if minimumScheduleInterval isnt valid', () => {
const alertType: AlertType<never, never, never, never, never, 'default'> = {
id: '123',
name: 'Test',
actionGroups: [
{
id: 'default',
name: 'Default',
},
],
defaultActionGroupId: 'default',
minimumLicenseRequired: 'basic',
isExportable: true,
executor: jest.fn(),
producer: 'alerts',
minimumScheduleInterval: 'foobar',
};
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);

expect(() => registry.register(alertType)).toThrowError(
new Error(
`Rule type \"123\" has invalid minimum interval: string is not a valid duration: foobar.`
)
);
});

test('throws if RuleType action groups contains reserved group id', () => {
const alertType: AlertType<never, never, never, never, never, 'default' | 'NotReserved'> = {
id: 'test',
Expand Down Expand Up @@ -465,10 +518,12 @@ describe('list()', () => {
"state": Array [],
},
"defaultActionGroupId": "testActionGroup",
"defaultScheduleInterval": undefined,
"enabledInLicense": false,
"id": "test",
"isExportable": true,
"minimumLicenseRequired": "basic",
"minimumScheduleInterval": undefined,
"name": "Test",
"producer": "alerts",
"recoveryActionGroup": Object {
Expand Down
44 changes: 44 additions & 0 deletions x-pack/plugins/alerting/server/rule_type_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export interface RegistryRuleType
| 'producer'
| 'minimumLicenseRequired'
| 'isExportable'
| 'minimumScheduleInterval'
| 'defaultScheduleInterval'
> {
id: string;
enabledInLicense: boolean;
Expand Down Expand Up @@ -188,6 +190,44 @@ export class RuleTypeRegistry {
}
alertType.actionVariables = normalizedActionVariables(alertType.actionVariables);

// validate defaultScheduleInterval here
if (alertType.defaultScheduleInterval) {
const invalidDefaultTimeout = validateDurationSchema(alertType.defaultScheduleInterval);
if (invalidDefaultTimeout) {
throw new Error(
i18n.translate(
'xpack.alerting.ruleTypeRegistry.register.invalidDefaultTimeoutAlertTypeError',
{
defaultMessage: 'Rule type "{id}" has invalid default interval: {errorMessage}.',
values: {
id: alertType.id,
errorMessage: invalidDefaultTimeout,
},
}
)
);
}
}

// validate minimumScheduleInterval here
if (alertType.minimumScheduleInterval) {
const invalidMinimumTimeout = validateDurationSchema(alertType.minimumScheduleInterval);
if (invalidMinimumTimeout) {
throw new Error(
i18n.translate(
'xpack.alerting.ruleTypeRegistry.register.invalidMinimumTimeoutAlertTypeError',
{
defaultMessage: 'Rule type "{id}" has invalid minimum interval: {errorMessage}.',
values: {
id: alertType.id,
errorMessage: invalidMinimumTimeout,
},
}
)
);
}
}

const normalizedAlertType = augmentActionGroupsWithReserved<
Params,
ExtractedParams,
Expand Down Expand Up @@ -287,6 +327,8 @@ export class RuleTypeRegistry {
producer,
minimumLicenseRequired,
isExportable,
minimumScheduleInterval,
defaultScheduleInterval,
},
]: [string, UntypedNormalizedAlertType]) => ({
id,
Expand All @@ -298,6 +340,8 @@ export class RuleTypeRegistry {
producer,
minimumLicenseRequired,
isExportable,
minimumScheduleInterval,
defaultScheduleInterval,
enabledInLicense: !!this.licenseState.getLicenseCheckForAlertType(
id,
name,
Expand Down
22 changes: 22 additions & 0 deletions x-pack/plugins/alerting/server/rules_client/rules_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,17 @@ export class RulesClient {

await this.validateActions(ruleType, data.actions);

// Validate intervals, if configured
if (ruleType.minimumScheduleInterval) {
const intervalInMs = parseDuration(data.schedule.interval);
const minimumScheduleIntervalInMs = parseDuration(ruleType.minimumScheduleInterval);
if (intervalInMs < minimumScheduleIntervalInMs) {
throw Boom.badRequest(
`Error updating rule: the interval is less than the minimum interval of ${ruleType.minimumScheduleInterval}`
);
}
}

// Extract saved object references for this rule
const {
references,
Expand Down Expand Up @@ -847,6 +858,17 @@ export class RulesClient {
);
await this.validateActions(ruleType, data.actions);

// Validate intervals, if configured
if (ruleType.minimumScheduleInterval) {
const intervalInMs = parseDuration(data.schedule.interval);
const minimumScheduleIntervalInMs = parseDuration(ruleType.minimumScheduleInterval);
if (intervalInMs < minimumScheduleIntervalInMs) {
throw Boom.badRequest(
`Error updating rule: the interval is less than the minimum interval of ${ruleType.minimumScheduleInterval}`
);
}
}

// Extract saved object references for this rule
const {
references,
Expand Down
26 changes: 26 additions & 0 deletions x-pack/plugins/alerting/server/rules_client/tests/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2268,4 +2268,30 @@ describe('create()', () => {
expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled();
expect(taskManager.schedule).not.toHaveBeenCalled();
});

test('throws error when updating with an interval less than the minimum configured one', async () => {
ruleTypeRegistry.get.mockImplementation(() => ({
id: '123',
name: 'Test',
actionGroups: [{ id: 'default', name: 'Default' }],
recoveryActionGroup: RecoveredActionGroup,
defaultActionGroupId: 'default',
minimumLicenseRequired: 'basic',
isExportable: true,
async executor() {},
producer: 'alerts',
minimumScheduleInterval: '5m',
useSavedObjectReferences: {
extractReferences: jest.fn(),
injectReferences: jest.fn(),
},
}));

const data = getMockData({ schedule: { interval: '1m' } });
await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot(
`"Error updating rule: the interval is less than the minimum interval of 5m"`
);
expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled();
expect(taskManager.schedule).not.toHaveBeenCalled();
});
});
46 changes: 46 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 @@ -140,6 +140,7 @@ describe('update()', () => {
recoveryActionGroup: RecoveredActionGroup,
async executor() {},
producer: 'alerts',
minimumScheduleInterval: '5s',
});
});

Expand Down Expand Up @@ -1966,4 +1967,49 @@ describe('update()', () => {
);
});
});

test('throws error when updating with an interval less than the minimum configured one', async () => {
await expect(
rulesClient.update({
id: '1',
data: {
schedule: { interval: '1s' },
name: 'abc',
tags: ['foo'],
params: {
bar: true,
},
throttle: null,
notifyWhen: 'onActiveAlert',
actions: [
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
{
group: 'default',
id: '1',
params: {
foo: true,
},
},
{
group: 'default',
id: '2',
params: {
foo: true,
},
},
],
},
})
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Error updating rule: the interval is less than the minimum interval of 5s"`
);
expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled();
expect(taskManager.schedule).not.toHaveBeenCalled();
});
});
2 changes: 2 additions & 0 deletions x-pack/plugins/alerting/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ export interface AlertType<
injectReferences: (params: ExtractedParams, references: SavedObjectReference[]) => Params;
};
isExportable: boolean;
defaultScheduleInterval?: string;
minimumScheduleInterval?: string;
ruleTaskTimeout?: string;
}
export type UntypedAlertType = AlertType<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ export enum SORT_ORDERS {
}

export const DEFAULT_SEARCH_PAGE_SIZE: number = 10;

export const DEFAULT_ALERT_INTERVAL = '1m';
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({
}}
actionTypeRegistry={actionTypeRegistry}
ruleTypeRegistry={ruleTypeRegistry}
ruleType={alertType}
onSave={setAlert}
/>
)}
Expand Down
Loading

0 comments on commit 0d09309

Please sign in to comment.