Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cli/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export default class Deploy extends AuthCommand {
ignoreDirectoriesMatch: checklyConfig.checks?.ignoreDirectoriesMatch,
checkDefaults: checklyConfig.checks,
browserCheckDefaults: checklyConfig.checks?.browserChecks,
monitorDefaults: checklyConfig.checks?.monitors,
availableRuntimes: avilableRuntimes.reduce((acc, runtime) => {
acc[runtime.name] = runtime
return acc
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/import/plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,7 @@ ${chalk.cyan('For safety, resources are not deletable until the plan has been co
ignoreDirectoriesMatch: checklyConfig.checks?.ignoreDirectoriesMatch,
checkDefaults: checklyConfig.checks,
browserCheckDefaults: checklyConfig.checks?.browserChecks,
monitorDefaults: checklyConfig.checks?.monitors,
availableRuntimes: availableRuntimes.reduce((acc, runtime) => {
acc[runtime.name] = runtime
return acc
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/pw-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export default class PwTestCommand extends AuthCommand {
checkMatch: checklyConfig.checks?.checkMatch,
ignoreDirectoriesMatch: checklyConfig.checks?.ignoreDirectoriesMatch,
checkDefaults: checklyConfig.checks,
monitorDefaults: checklyConfig.checks?.monitors,
availableRuntimes: availableRuntimes.reduce((acc, runtime) => {
acc[runtime.name] = runtime
return acc
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ export default class Test extends AuthCommand {
ignoreDirectoriesMatch: checklyConfig.checks?.ignoreDirectoriesMatch,
checkDefaults: checklyConfig.checks,
browserCheckDefaults: checklyConfig.checks?.browserChecks,
monitorDefaults: checklyConfig.checks?.monitors,
availableRuntimes: availableRuntimes.reduce((acc, runtime) => {
acc[runtime.name] = runtime
return acc
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default class Validate extends AuthCommand {
ignoreDirectoriesMatch: checklyConfig.checks?.ignoreDirectoriesMatch,
checkDefaults: checklyConfig.checks,
browserCheckDefaults: checklyConfig.checks?.browserChecks,
monitorDefaults: checklyConfig.checks?.monitors,
availableRuntimes: avilableRuntimes.reduce((acc, runtime) => {
acc[runtime.name] = runtime
return acc
Expand Down
39 changes: 36 additions & 3 deletions packages/cli/src/constructs/__tests__/tcp-monitor.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect } from 'vitest'
import { describe, it, expect, afterAll, beforeEach } from 'vitest'

import { TcpMonitor, CheckGroup, TcpRequest } from '../index'
import { Project, Session } from '../project'
Expand All @@ -9,6 +9,14 @@ const request: TcpRequest = {
}

describe('TcpMonitor', () => {
function clearDefaults () {
Session.checkDefaults = undefined
Session.monitorDefaults = undefined
}

beforeEach(clearDefaults)
afterAll(clearDefaults)

it('should apply default check settings', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
Expand All @@ -19,10 +27,36 @@ describe('TcpMonitor', () => {
name: 'Test Check',
request,
})
Session.checkDefaults = undefined
expect(check).toMatchObject({ tags: ['default tags'] })
})

it('should apply default monitor settings', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
Session.monitorDefaults = { tags: ['default tags'] }
const check = new TcpMonitor('test-check', {
name: 'Test Check',
request,
})
expect(check).toMatchObject({ tags: ['default tags'] })
})

it('should prefer monitor settings over check settings', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
Session.checkDefaults = { tags: ['check default tags'] }
Session.monitorDefaults = { tags: ['monitor default tags'] }
const check = new TcpMonitor('test-check', {
name: 'Test Check',
request,
})
expect(check).toMatchObject({ tags: ['monitor default tags'] })
})

it('should overwrite default check settings with check-specific config', () => {
Session.project = new Project('project-id', {
name: 'Test Project',
Expand All @@ -34,7 +68,6 @@ describe('TcpMonitor', () => {
tags: ['test check'],
request,
})
Session.checkDefaults = undefined
expect(check).toMatchObject({ tags: ['test check'] })
})

Expand Down
7 changes: 6 additions & 1 deletion packages/cli/src/constructs/check-group-ref.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CheckConfigDefaults } from '../services/checkly-config-loader'
import { CheckConfigDefaults, MonitorConfigDefaults } from '../services/checkly-config-loader'
import { CheckGroupV1 } from './check-group-v1'
import { Construct } from './construct'
import { Session } from './project'
Expand Down Expand Up @@ -32,6 +32,11 @@ export class CheckGroupRef extends Construct {
return {}
}

public getMonitorDefaults (): MonitorConfigDefaults {
// See the comment for getCheckDefaults(), the same applies here.
return {}
}

synthesize () {
return null
}
Expand Down
22 changes: 18 additions & 4 deletions packages/cli/src/constructs/check-group-v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import { ApiCheckDefaultConfig } from './api-check'
import type { Region } from '..'
import { type Frequency } from './frequency'
import {
type RetryStrategy,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type RetryStrategyBuilder, // Used for @links in comments.
} from './retry-strategy'
import { AlertEscalation } from './alert-escalation-policy'
import { Diagnostics } from './diagnostics'
import { DeprecatedConstructDiagnostic, DeprecatedPropertyDiagnostic, InvalidPropertyValueDiagnostic } from './construct-diagnostics'
import CheckTypes from '../constants'
import { CheckConfigDefaults } from '../services/checkly-config-loader'
import { CheckConfigDefaults, MonitorConfigDefaults } from '../services/checkly-config-loader'
import { pathToPosix } from '../services/util'
import { AlertChannelSubscription } from './alert-channel-subscription'
import { BrowserCheck } from './browser-check'
Expand All @@ -28,6 +27,8 @@ import { PrivateLocationGroupAssignment } from './private-location-group-assignm
import { Ref } from './ref'
import { Session } from './project'
import { validateDeprecatedDoubleCheck } from './internal/common-diagnostics'
import { CheckRetryStrategy } from './check'
import { MonitorRetryStrategy } from './monitor'

const defaultApiCheckDefaults: ApiCheckDefaultConfig = {
headers: [],
Expand All @@ -53,6 +54,13 @@ type MultiStepCheckConfig = CheckConfigDefaults & {
testMatch: string | string[],
}

/**
* Retry strategies supported by groups.
*/
export type GroupRetryStrategy =
| CheckRetryStrategy
| MonitorRetryStrategy

export interface CheckGroupV1Props {
/**
* The name of the check group.
Expand Down Expand Up @@ -236,7 +244,7 @@ export interface CheckGroupV1Props {
*
* If not set, retries are disabled for all checks in the group.
*/
retryStrategy?: RetryStrategy
retryStrategy?: GroupRetryStrategy

/**
* Determines whether the checks in the group should run on all selected
Expand Down Expand Up @@ -298,7 +306,7 @@ export class CheckGroupV1 extends Construct {
apiCheckDefaults: ApiCheckDefaultConfig
browserChecks?: BrowserCheckConfig
multiStepChecks?: MultiStepCheckConfig
retryStrategy?: RetryStrategy
retryStrategy?: GroupRetryStrategy
runParallel?: boolean
alertSettings?: AlertEscalation
useGlobalAlertSettings?: boolean
Expand Down Expand Up @@ -485,6 +493,12 @@ export class CheckGroupV1 extends Construct {
}
}

public getMonitorDefaults (): MonitorConfigDefaults {
return {
frequency: this.frequency,
}
}

synthesize () {
return {
name: this.name,
Expand Down
5 changes: 2 additions & 3 deletions packages/cli/src/constructs/check-group-v2.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { PrivateLocation, PrivateLocationRef } from './private-location'
import type { Region } from '..'
import {
type RetryStrategy,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type RetryStrategyBuilder, // Used for @links in comments.
} from './retry-strategy'
Expand All @@ -11,7 +10,7 @@ import {
type AlertEscalationBuilder, // Used for @links in comments.
} from './alert-escalation-policy'
import { Diagnostics } from './diagnostics'
import { CheckGroupV1, CheckGroupV1Props } from './check-group-v1'
import { CheckGroupV1, CheckGroupV1Props, GroupRetryStrategy } from './check-group-v1'
import { validateRemovedDoubleCheck } from './internal/common-diagnostics'

export interface CheckGroupV2Props extends Omit<CheckGroupV1Props, 'alertEscalationPolicy'> {
Expand Down Expand Up @@ -73,7 +72,7 @@ export interface CheckGroupV2Props extends Omit<CheckGroupV1Props, 'alertEscalat
*
* If not set, individual check settings are used.
*/
retryStrategy?: RetryStrategy
retryStrategy?: GroupRetryStrategy

/**
* Determines whether the checks in the group should run on all selected
Expand Down
22 changes: 19 additions & 3 deletions packages/cli/src/constructs/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,29 @@ import type { Region } from '..'
import type { CheckGroupV1, CheckGroupV2, CheckGroupRef } from './check-group'
import { PrivateLocation, PrivateLocationRef } from './private-location'
import { PrivateLocationCheckAssignment } from './private-location-check-assignment'
import { RetryStrategy } from './retry-strategy'
import {
ExponentialRetryStrategy,
FixedRetryStrategy,
LinearRetryStrategy,
NoRetriesRetryStrategy,
SingleRetryStrategy,
} from './retry-strategy'
import { AlertEscalation } from './alert-escalation-policy'
import { IncidentTrigger } from './incident'
import { ConfigDefaultsGetter, makeConfigDefaultsGetter } from './check-config'
import { Diagnostics } from './diagnostics'
import { validateDeprecatedDoubleCheck } from './internal/common-diagnostics'

/**
* Retry strategies supported by checks.
*/
export type CheckRetryStrategy =
| LinearRetryStrategy
| ExponentialRetryStrategy
| FixedRetryStrategy
| SingleRetryStrategy
| NoRetriesRetryStrategy

/**
* Base configuration properties for all check types.
* These properties are inherited by ApiCheck, BrowserCheck, and other check types.
Expand Down Expand Up @@ -194,7 +210,7 @@ export interface CheckProps {
* })
* ```
*/
retryStrategy?: RetryStrategy
retryStrategy?: CheckRetryStrategy

/**
* Determines whether the check should run on all selected locations in parallel or round-robin.
Expand Down Expand Up @@ -227,7 +243,7 @@ export abstract class Check extends Construct {
groupId?: Ref
alertChannels?: Array<AlertChannel|AlertChannelRef>
testOnly?: boolean
retryStrategy?: RetryStrategy
retryStrategy?: CheckRetryStrategy
alertSettings?: AlertEscalation
useGlobalAlertSettings?: boolean
runParallel?: boolean
Expand Down
57 changes: 55 additions & 2 deletions packages/cli/src/constructs/monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,20 @@ import { CheckGroupV2 } from './check-group-v2'
import { Frequency } from './frequency'
import { IncidentTrigger } from './incident'
import { PrivateLocation, PrivateLocationRef } from './private-location'
import { RetryStrategy } from './retry-strategy'
import { NoRetriesRetryStrategy, RetryStrategyType, SingleRetryStrategy } from './retry-strategy'
import { Check, CheckProps } from './check'
import { Diagnostics } from './diagnostics'
import { validateRemovedDoubleCheck } from './internal/common-diagnostics'
import { InvalidPropertyValueDiagnostic } from './construct-diagnostics'
import { ConfigDefaultsGetter, makeConfigDefaultsGetter } from './check-config'
import { Session } from './project'

/**
* Retry strategies supported by monitors.
*/
export type MonitorRetryStrategy =
| SingleRetryStrategy
| NoRetriesRetryStrategy

export interface MonitorProps extends Omit<CheckProps, 'doubleCheck'> {
/**
Expand Down Expand Up @@ -74,8 +84,19 @@ export interface MonitorProps extends Omit<CheckProps, 'doubleCheck'> {
/**
* Sets a retry policy for the monitor. Use RetryStrategyBuilder to create a
* suitable retry strategy.
*
* Note that monitors only support a single retry.
*
* @example
* ```typescript
* // Single retry
* RetryStrategyBuilder.singleRetry()
*
* // No retries
* RetryStrategyBuilder.noRetries()
* ```
*/
retryStrategy?: RetryStrategy
retryStrategy?: MonitorRetryStrategy
/**
* Determines whether the monitor should create and resolve an incident
* based on its alert configuration.
Expand All @@ -95,6 +116,38 @@ export abstract class Monitor extends Check {
await validateRemovedDoubleCheck(diagnostics, this)
}

async validate (diagnostics: Diagnostics): Promise<void> {
await super.validate(diagnostics)

if (this.retryStrategy) {
const supported: RetryStrategyType[] = ['SINGLE', 'NO_RETRIES']
if (!supported.includes(this.retryStrategy.type)) {
diagnostics.add(new InvalidPropertyValueDiagnostic(
'retryStrategy',
new Error(
`Monitors only support a single retry. The following options ` +
`are available:` +
`\n\n` +
` // Single retry\n` +
` RetryStrategyBuilder.singleRetry()` +
`\n\n` +
` // No retries\n` +
` RetryStrategyBuilder.noRetries()`,
),
))
}
}
}

protected configDefaultsGetter (props: MonitorProps): ConfigDefaultsGetter {
return makeConfigDefaultsGetter(
props.group?.getMonitorDefaults(),
Session.monitorDefaults,
props.group?.getCheckDefaults(),
Session.checkDefaults,
)
}

synthesize() {
return {
...super.synthesize(),
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/constructs/project.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'node:path'

import * as api from '../rest/api'
import { CheckConfigDefaults } from '../services/checkly-config-loader'
import { CheckConfigDefaults, MonitorConfigDefaults } from '../services/checkly-config-loader'
import { Parser } from '../services/check-parser/parser'
import { Construct } from './construct'
import { ValidationError } from './validator-error'
Expand Down Expand Up @@ -226,6 +226,7 @@ export class Session {
static checkFilter?: CheckFilter
static browserCheckDefaults?: CheckConfigDefaults
static multiStepCheckDefaults?: CheckConfigDefaults
static monitorDefaults?: MonitorConfigDefaults
static checkFilePath?: string
static checkFileAbsolutePath?: string
static availableRuntimes: Record<string, Runtime>
Expand Down
Loading