From 542631fa6e950151f06bd24a09abb3cbb685b5de Mon Sep 17 00:00:00 2001 From: Ferran Diaz Date: Fri, 12 Jan 2024 12:17:27 +0100 Subject: [PATCH] feat: add alert escalation policy as part of the check/config --- examples/advanced-project/checkly.config.ts | 4 +- .../src/__checks__/website-group.check.ts | 4 +- .../src/constructs/alert-escalation-policy.ts | 59 +++++++++++++++++++ packages/cli/src/constructs/check-group.ts | 9 ++- packages/cli/src/constructs/check.ts | 13 +++- packages/cli/src/constructs/index.ts | 1 + .../cli/src/services/checkly-config-loader.ts | 2 +- 7 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 packages/cli/src/constructs/alert-escalation-policy.ts diff --git a/examples/advanced-project/checkly.config.ts b/examples/advanced-project/checkly.config.ts index a3b336e6..c7ffc55c 100644 --- a/examples/advanced-project/checkly.config.ts +++ b/examples/advanced-project/checkly.config.ts @@ -1,5 +1,5 @@ import { defineConfig } from 'checkly' -import { RetryStrategyBuilder } from 'checkly/constructs' +import { AlertEscalationBuilder, RetryStrategyBuilder } from 'checkly/constructs' /** * See https://www.checklyhq.com/docs/cli/project-structure/ @@ -27,6 +27,8 @@ const config = defineConfig({ runtimeId: '2023.09', /* Failed check runs will be retried before triggering alerts */ retryStrategy: RetryStrategyBuilder.fixedStrategy({ baseBackoffSeconds: 60, maxRetries: 4, sameRegion: true }), + /* All checks will have this alert escalation policy defined */ + alertEscalationPolicy: AlertEscalationBuilder.runBasedEscalation(1), /* A glob pattern that matches the Checks inside your repo, see https://www.checklyhq.com/docs/cli/using-check-test-match/ */ checkMatch: '**/__checks__/**/*.check.ts', browserChecks: { diff --git a/examples/advanced-project/src/__checks__/website-group.check.ts b/examples/advanced-project/src/__checks__/website-group.check.ts index 22b193c5..3a300afd 100644 --- a/examples/advanced-project/src/__checks__/website-group.check.ts +++ b/examples/advanced-project/src/__checks__/website-group.check.ts @@ -1,4 +1,4 @@ -import { CheckGroup, RetryStrategyBuilder } from 'checkly/constructs' +import { AlertEscalationBuilder, CheckGroup, RetryStrategyBuilder } from 'checkly/constructs' import { smsChannel, emailChannel } from '../alert-channels' const alertChannels = [smsChannel, emailChannel] /* @@ -24,6 +24,8 @@ export const websiteGroup = new CheckGroup('website-check-group-1', { apiCheckDefaults: {}, concurrency: 100, alertChannels, + /* All checks on this check group will have this alert escalation policy */ + alertEscalationPolicy: AlertEscalationBuilder.runBasedEscalation(1), /* * Failed check runs in this group will be retried before triggering alerts. * The wait time between retries will increase linearly: 30 seconds, 60 seconds, and then 90 seconds between the retries. diff --git a/packages/cli/src/constructs/alert-escalation-policy.ts b/packages/cli/src/constructs/alert-escalation-policy.ts new file mode 100644 index 00000000..b0fd2ae5 --- /dev/null +++ b/packages/cli/src/constructs/alert-escalation-policy.ts @@ -0,0 +1,59 @@ +// eslint-disable-next-line no-restricted-syntax +enum AlertEscalationType { + RUN = 'RUN_BASED', + TIME = 'TIME_BASED' +} + +export type Reminders = { + amount?: number, + interval?: number +} + +export interface AlertEscalation { + escalationType?: AlertEscalationType, + runBasedEscalation?: { + failedRunThreshold?: number + }, + timeBasedEscalation?: { + minutesFailingThreshold?: number + }, + reminders?: Reminders +} + +export type AlertEscalationOptions = Pick + +export class AlertEscalationBuilder { + private static DEFAULT_RUN_BASED_ESCALATION = { failedRunThreshold: 1 } + private static DEFAULT_TIME_BASED_ESCALATION = { minutesFailingThreshold: 5 } + private static DEFAULT_REMINDERS = { amount: 0, interval: 5 } + + static runBasedEscalation (failedRunThreshold: number, reminders?: Reminders) { + const options: AlertEscalationOptions = { + runBasedEscalation: { + failedRunThreshold, + }, + reminders, + } + return this.alertEscalation(AlertEscalationType.RUN, options) + } + + static timeBasedEscalation (minutesFailingThreshold: number, reminders?: Reminders) { + const options: AlertEscalationOptions = { + timeBasedEscalation: { + minutesFailingThreshold, + }, + reminders, + } + return this.alertEscalation(AlertEscalationType.TIME, options) + } + + private static alertEscalation (escalationType: AlertEscalationType, + options: AlertEscalationOptions): AlertEscalation { + return { + escalationType, + runBasedEscalation: options.runBasedEscalation ?? this.DEFAULT_RUN_BASED_ESCALATION, + timeBasedEscalation: options.timeBasedEscalation ?? this.DEFAULT_TIME_BASED_ESCALATION, + reminders: options.reminders ?? this.DEFAULT_REMINDERS, + } + } +} diff --git a/packages/cli/src/constructs/check-group.ts b/packages/cli/src/constructs/check-group.ts index 653ae46d..2f79000c 100644 --- a/packages/cli/src/constructs/check-group.ts +++ b/packages/cli/src/constructs/check-group.ts @@ -15,6 +15,7 @@ import { pathToPosix } from '../services/util' import type { Region } from '..' import type { Frequency } from './frequency' import type { RetryStrategy } from './retry-strategy' +import { AlertEscalation } from './alert-escalation-policy' const defaultApiCheckDefaults: ApiCheckDefaultConfig = { headers: [], @@ -90,6 +91,7 @@ export interface CheckGroupProps { alertChannels?: Array browserChecks?: BrowserCheckConfig, multiStepChecks?: MultiStepCheckConfig, + alertEscalationPolicy?: AlertEscalation, /** * A valid piece of Node.js code to run in the setup phase of an API check in this group. * @deprecated use the "ApiCheck.setupScript" property instead and use common JS/TS code @@ -141,6 +143,8 @@ export class CheckGroup extends Construct { multiStepChecks?: MultiStepCheckConfig retryStrategy?: RetryStrategy runParallel?: boolean + alertSettings?: AlertEscalation + useGlobalAlertSettings?: boolean static readonly __checklyType = 'check-group' @@ -166,7 +170,8 @@ export class CheckGroup extends Construct { // `frequency` is not a CheckGroup resource property. Not present in synthesize() this.frequency = props.frequency this.apiCheckDefaults = { ...defaultApiCheckDefaults, ...props.apiCheckDefaults } - + this.alertSettings = props.alertEscalationPolicy + this.useGlobalAlertSettings = !this.alertSettings this.environmentVariables = props.environmentVariables ?? [] this.environmentVariables.forEach(ev => { // only empty string is checked because the KeyValuePair.value doesn't allow undefined or null. @@ -280,6 +285,8 @@ export class CheckGroup extends Construct { environmentVariables: this.environmentVariables, retryStrategy: this.retryStrategy, runParallel: this.runParallel, + alertSettings: this.alertSettings, + useGlobalAlertSettings: this.useGlobalAlertSettings, } } } diff --git a/packages/cli/src/constructs/check.ts b/packages/cli/src/constructs/check.ts index 5d64a923..54b31c04 100644 --- a/packages/cli/src/constructs/check.ts +++ b/packages/cli/src/constructs/check.ts @@ -11,6 +11,7 @@ import type { CheckGroup } from './check-group' import { PrivateLocation } from './private-location' import { PrivateLocationCheckAssignment } from './private-location-check-assignment' import { RetryStrategy } from './retry-strategy' +import { AlertEscalation } from './alert-escalation-policy' export interface CheckProps { /** @@ -77,7 +78,11 @@ export interface CheckProps { /** * List of alert channels to notify when the check fails or recovers. */ - alertChannels?: Array + alertChannels?: Array, + /** + * Determines the alert escalation policy for that particular check + */ + alertEscalationPolicy?: AlertEscalation /** * Determines if the check is available only when 'test' runs (not included when 'deploy' is executed). */ @@ -111,6 +116,8 @@ export abstract class Check extends Construct { alertChannels?: Array testOnly?: boolean retryStrategy?: RetryStrategy + alertSettings?: AlertEscalation + useGlobalAlertSettings?: boolean runParallel?: boolean __checkFilePath?: string // internal variable to filter by check file name from the CLI @@ -148,6 +155,8 @@ export abstract class Check extends Construct { this.testOnly = props.testOnly ?? false this.retryStrategy = props.retryStrategy + this.alertSettings = props.alertEscalationPolicy + this.useGlobalAlertSettings = !this.alertSettings this.runParallel = props.runParallel ?? false this.__checkFilePath = Session.checkFilePath } @@ -225,6 +234,8 @@ export abstract class Check extends Construct { groupId: this.groupId, environmentVariables: this.environmentVariables, retryStrategy: this.retryStrategy, + alertSettings: this.alertSettings, + useGlobalAlertSettings: this.useGlobalAlertSettings, runParallel: this.runParallel, } } diff --git a/packages/cli/src/constructs/index.ts b/packages/cli/src/constructs/index.ts index 9e667d22..f1c1a3b5 100644 --- a/packages/cli/src/constructs/index.ts +++ b/packages/cli/src/constructs/index.ts @@ -24,3 +24,4 @@ export * from './dashboard' export * from './phone-call-alert-channel' export * from './retry-strategy' export * from './multi-step-check' +export * from './alert-escalation-policy' diff --git a/packages/cli/src/services/checkly-config-loader.ts b/packages/cli/src/services/checkly-config-loader.ts index 20808ea6..94ed4315 100644 --- a/packages/cli/src/services/checkly-config-loader.ts +++ b/packages/cli/src/services/checkly-config-loader.ts @@ -11,7 +11,7 @@ import * as fs from 'fs' export type CheckConfigDefaults = Pick + | 'alertChannels' | 'privateLocations' | 'retryStrategy' | 'alertEscalationPolicy'> export type BrowserCheckDefaults = Pick