packages/aws-cdk-lib/aws-events/lib/schedule.ts (70 lines of code) (raw):

import { Construct } from 'constructs'; import { Annotations, Duration, UnscopedValidationError } from '../../core'; /** * Schedule for scheduled event rules * * Note that rates cannot be defined in fractions of minutes. * * @see https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduled-events.html */ export abstract class Schedule { /** * Construct a schedule from a literal schedule expression * * @param expression The expression to use. Must be in a format that EventBridge will recognize */ public static expression(expression: string): Schedule { return new LiteralSchedule(expression); } /** * Construct a schedule from an interval and a time unit * * Rates may be defined with any unit of time, but when converted into minutes, the duration must be a positive whole number of minutes. */ public static rate(duration: Duration): Schedule { if (duration.isUnresolved()) { const validDurationUnit = ['minute', 'minutes', 'hour', 'hours', 'day', 'days']; if (validDurationUnit.indexOf(duration.unitLabel()) === -1) { throw new UnscopedValidationError("Allowed units for scheduling are: 'minute', 'minutes', 'hour', 'hours', 'day', 'days'"); } return new LiteralSchedule(`rate(${duration.formatTokenToNumber()})`); } if (duration.toMinutes() === 0) { throw new UnscopedValidationError('Duration cannot be 0'); } let rate = maybeRate(duration.toDays({ integral: false }), 'day'); if (rate === undefined) { rate = maybeRate(duration.toHours({ integral: false }), 'hour'); } if (rate === undefined) { rate = makeRate(duration.toMinutes({ integral: true }), 'minute'); } return new LiteralSchedule(rate); } /** * Create a schedule from a set of cron fields */ public static cron(options: CronOptions): Schedule { if (options.weekDay !== undefined && options.day !== undefined) { throw new UnscopedValidationError('Cannot supply both \'day\' and \'weekDay\', use at most one'); } const minute = fallback(options.minute, '*'); const hour = fallback(options.hour, '*'); const month = fallback(options.month, '*'); const year = fallback(options.year, '*'); // Weekday defaults to '?' if not supplied. If it is supplied, day must become '?' const day = fallback(options.day, options.weekDay !== undefined ? '?' : '*'); const weekDay = fallback(options.weekDay, '?'); return new class extends Schedule { public readonly expressionString: string = `cron(${minute} ${hour} ${day} ${month} ${weekDay} ${year})`; public _bind(scope: Construct) { if (!options.minute) { Annotations.of(scope).addWarningV2('@aws-cdk/aws-events:scheduleWillRunEveryMinute', 'cron: If you don\'t pass \'minute\', by default the event runs every minute. Pass \'minute: \'*\'\' if that\'s what you intend, or \'minute: 0\' to run once per hour instead.'); } return new LiteralSchedule(this.expressionString); } }; } /** * Retrieve the expression for this schedule */ public abstract readonly expressionString: string; protected constructor() {} /** * * @internal */ public abstract _bind(scope: Construct): void; } /** * Options to configure a cron expression * * All fields are strings so you can use complex expressions. Absence of * a field implies '*' or '?', whichever one is appropriate. * * @see https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduled-events.html#cron-expressions */ export interface CronOptions { /** * The minute to run this rule at * * @default - Every minute */ readonly minute?: string; /** * The hour to run this rule at * * @default - Every hour */ readonly hour?: string; /** * The day of the month to run this rule at * * @default - Every day of the month */ readonly day?: string; /** * The month to run this rule at * * @default - Every month */ readonly month?: string; /** * The year to run this rule at * * @default - Every year */ readonly year?: string; /** * The day of the week to run this rule at * * @default - Any day of the week */ readonly weekDay?: string; } class LiteralSchedule extends Schedule { constructor(public readonly expressionString: string) { super(); } public _bind() {} } function fallback<T>(x: T | undefined, def: T): T { return x ?? def; } /** * Return the rate if the rate is whole number */ function maybeRate(interval: number, singular: string) { if (interval === 0 || !Number.isInteger(interval)) { return undefined; } return makeRate(interval, singular); } /** * Return 'rate(${interval} ${singular}(s))` for the interval */ function makeRate(interval: number, singular: string) { return interval === 1 ? `rate(1 ${singular})` : `rate(${interval} ${singular}s)`; }