From 570f42c45141e4616c2410a85e1f169120631ee5 Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Sun, 21 May 2023 20:24:21 +0200 Subject: [PATCH 01/11] implementation of ScheduleInputTarget --- .../@aws-cdk/aws-scheduler-alpha/README.md | 41 ++++- .../@aws-cdk/aws-scheduler-alpha/lib/index.ts | 5 +- .../@aws-cdk/aws-scheduler-alpha/lib/input.ts | 120 +++++++++++++++ .../aws-scheduler-alpha/lib/schedule.ts | 63 ++++++++ .../aws-scheduler-alpha/lib/targets.ts | 57 +++++++ .../aws-scheduler-alpha/test/input.test.ts | 142 ++++++++++++++++++ .../test/schedule-expression.test.ts | 4 +- 7 files changed, 421 insertions(+), 11 deletions(-) create mode 100644 packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts create mode 100644 packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts create mode 100644 packages/@aws-cdk/aws-scheduler-alpha/lib/targets.ts create mode 100644 packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts diff --git a/packages/@aws-cdk/aws-scheduler-alpha/README.md b/packages/@aws-cdk/aws-scheduler-alpha/README.md index 171cecdf66854..0c2eeccae37b2 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/README.md +++ b/packages/@aws-cdk/aws-scheduler-alpha/README.md @@ -37,7 +37,15 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw ## Defining a schedule -TODO: Schedule is not yet implemented. See section in [L2 Event Bridge Scheduler RFC](https://github.com/aws/aws-cdk-rfcs/blob/master/text/0474-event-bridge-scheduler-l2.md) +TODO: Schedule is not yet fully implemented. See section in [L2 Event Bridge Scheduler RFC](https://github.com/aws/aws-cdk-rfcs/blob/master/text/0474-event-bridge-scheduler-l2.md) + +[comment]: <> (TODO: change for each PR that implements more functionality) + +Only an L2 class is created that wraps the L1 class and handles the following properties: + +- schedule +- target (only LambdaInvoke is supported for now) +- flexibleTimeWindow will be set to `{ mode: 'OFF' }` ### Schedule Expressions @@ -49,9 +57,7 @@ cron-based schedule you can specify a time zone in which EventBridge Scheduler e > ScheduleExpression should be used together with class Schedule, which is not yet implemented. -[comment]: <> (TODO: Switch to `ts` once Schedule is implemented) - -```text +```ts const rateBasedSchedule = new Schedule(this, 'Schedule', { scheduleExpression: ScheduleExpression.rate(Duration.minutes(10)), target, @@ -74,9 +80,7 @@ const cronBasedSchedule = new Schedule(this, 'Schedule', { A one-time schedule is a schedule that invokes a target only once. You configure a one-time schedule when by specifying the time of the day, date, and time zone in which EventBridge Scheduler evaluates the schedule. -[comment]: <> (TODO: Switch to `ts` once Schedule is implemented) - -```text +```ts const oneTimeSchedule = new Schedule(this, 'Schedule', { scheduleExpression: ScheduleExpression.at( new Date(2022, 10, 20, 19, 20, 23), @@ -95,10 +99,31 @@ TODO: Group is not yet implemented. See section in [L2 Event Bridge Scheduler RF TODO: Scheduler Targets Module is not yet implemented. See section in [L2 Event Bridge Scheduler RFC](https://github.com/aws/aws-cdk-rfcs/blob/master/text/0474-event-bridge-scheduler-l2.md) +Only LambdaInvoke target is added for now. + ### Input -TODO: Target Input is not yet implemented. See section in [L2 Event Bridge Scheduler RFC](https://github.com/aws/aws-cdk-rfcs/blob/master/text/0474-event-bridge-scheduler-l2.md) +Target can be invoked with a custom input. Class `ScheduleTargetInput` supports free form text input and JSON-formatted object input: +```ts +const input = ScheduleTargetInput.fromObject({ + 'QueueName': 'MyQueue' +}); +``` + +You can include context attributes in your target payload. EventBridge Scheduler will replace each keyword with +its respective value and deliver it to the target. See +[full list of supported context attributes](https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-schedule-context-attributes.html): + +1. `ContextAttribute.scheduleArn()` – The ARN of the schedule. +2. `ContextAttribute.scheduledTime()` – The time you specified for the schedule to invoke its target, for example, 2022-03-22T18:59:43Z. +3. `ContextAttribute.executionId()` – The unique ID that EventBridge Scheduler assigns for each attempted invocation of a target, for example, d32c5kddcf5bb8c3. +4. `ContextAttribute.attemptNumber()` – A counter that identifies the attempt number for the current invocation, for example, 1. + +```ts +const text = `Attempt number: ${ContextAttribute.attemptNumber}`; +const input = scheduler.ScheduleTargetInput.fromInput(text); +``` ### Specifying Execution Role diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/index.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/index.ts index c00ab258ae963..827fdd4a2b663 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/index.ts @@ -1 +1,4 @@ -export * from './schedule-expression'; \ No newline at end of file +export * from './schedule-expression'; +export * from './input'; +export * from './schedule'; +export * from './targets'; diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts new file mode 100644 index 0000000000000..a9201b125f489 --- /dev/null +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts @@ -0,0 +1,120 @@ +import { DefaultTokenResolver, IResolveContext, Stack, StringConcat, Token, Tokenization } from 'aws-cdk-lib'; +import { ISchedule } from './schedule'; + +/** + * The text, or well-formed JSON, passed to the target of the schedule. + */ +export abstract class ScheduleTargetInput { + /** + * Pass text to the target, it is possible to embed `ContextAttributes` and other + * cdk references. + * + * The target input value will be a single string: the string you pass + * here. Do not use this method to pass a complex value like a JSON object to + * a target. Use `ScheduleTargetInput.fromObject()` instead. + * + * @param text text to use as input for the target + */ + public static fromText(text: string): ScheduleTargetInput { + return new FieldAwareEventInput(text); + } + + /** + * Pass a JSON object to the target, it is possible to embed `ContextAttributes` and other + * cdk references. + * + * @param obj object to use to convert to JSON to use as input for the target + */ + public static fromObject(obj: any): ScheduleTargetInput { + return new FieldAwareEventInput(obj); + } + + protected constructor() { + } + + /** + * Return the input properties for this input object + */ + public abstract bind(schedule: ISchedule): string; +} + +class FieldAwareEventInput extends ScheduleTargetInput { + constructor(private readonly input: any) { + super(); + } + + public bind(schedule: ISchedule): string { + class Replacer extends DefaultTokenResolver { + constructor() { + super(new StringConcat()); + } + + public resolveToken(t: Token, _context: IResolveContext) { + return Token.asString(t); + } + } + + const stack = Stack.of(schedule); + return stack.toJsonString(Tokenization.resolve(this.input, { + scope: schedule, + resolver: new Replacer(), + })); + } +} + +/** + * Represents a field in the event pattern + * + * @see https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-schedule-context-attributes.html + */ +export class ContextAttribute { + /** + * The ARN of the schedule. + */ + public static get scheduleArn(): string { + return this.fromName('schedule-arn'); + } + + /** + * The time you specified for the schedule to invoke its target, for example, + * 2022-03-22T18:59:43Z. + */ + public static get scheduledTime(): string { + return this.fromName('scheduled-time'); + } + + /** + * The unique ID that EventBridge Scheduler assigns for each attempted invocation of + * a target, for example, d32c5kddcf5bb8c3. + */ + public static get executionId(): string { + return this.fromName('execution-id'); + } + + /** + * A counter that identifies the attempt number for the current invocation, for + * example, 1. + */ + public static get attemptNumber(): string { + return this.fromName('attempt-number'); + } + + /** + * Escape hatch for other ContextAttribute that might be resolved in future. + * + * @param name - name will replace xxx in + */ + public static fromName(name: string): string { + return new ContextAttribute(name).toString(); + } + + private constructor(public readonly name: string) { + } + + /** + * Convert the path to the field in the event pattern to JSON + */ + public toString() { + return ``; + } +} diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts new file mode 100644 index 0000000000000..74462df5382dd --- /dev/null +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts @@ -0,0 +1,63 @@ +import { IResource, Resource } from 'aws-cdk-lib'; +import { CfnSchedule } from 'aws-cdk-lib/aws-scheduler'; +import { Construct } from 'constructs'; +import { ScheduleExpression } from './schedule-expression'; + +/** + * DISCLAIMER: WORK IN PROGRESS, INTERFACE MIGHT CHANGE + * + * This unit is not yet finished. Only rudimentary Schedule is implemented in order + * to be able to create some sensible unit tests + */ + +export interface IScheduleTarget { + bind(_schedule: ISchedule): CfnSchedule.TargetProperty; +} + +/** + * Construction properties for `Schedule`. + */ +export interface ScheduleProps { + /** + * The expression that defines when the schedule runs. Can be either a `at`, `rate` + * or `cron` expression. + */ + readonly schedule: ScheduleExpression; + + /** + * The schedule's target details. + */ + readonly target: IScheduleTarget; + + /** + * The description you specify for the schedule. + * + * @default - no value + */ + readonly description?: string; +} + +/** + * Interface representing a created or an imported `Schedule`. + */ +export interface ISchedule extends IResource { + +} + +/** + * An EventBridge Schedule + */ +export class Schedule extends Resource implements ISchedule { + constructor(scope: Construct, id: string, props: ScheduleProps) { + super(scope, id); + + new CfnSchedule(this, 'Resource', { + flexibleTimeWindow: { mode: 'OFF' }, + scheduleExpression: props.schedule.expressionString, + scheduleExpressionTimezone: props.schedule.timeZone?.timezoneName, + target: { + ...props.target.bind(this), + }, + }); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/targets.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/targets.ts new file mode 100644 index 0000000000000..5fd8476131ee3 --- /dev/null +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/targets.ts @@ -0,0 +1,57 @@ +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { CfnSchedule } from 'aws-cdk-lib/aws-scheduler'; +import { ScheduleTargetInput } from './input'; +import { ISchedule } from './schedule'; + +/** + * DISCLAIMER: WORK IN PROGRESS, INTERFACE MIGHT CHANGE + * + * This unit is not yet finished. The LambaInvoke target is only implemented to be able + * to create some sensible unit tests. + */ + +export namespace targets { + export interface ScheduleTargetBaseProps { + readonly role?: iam.IRole; + readonly input?: ScheduleTargetInput; + } + + abstract class ScheduleTargetBase { + constructor( + private readonly baseProps: ScheduleTargetBaseProps, + protected readonly targetArn: string, + ) { + } + + protected abstract addTargetActionToRole(role: iam.IRole): void; + + protected bindBaseTargetConfig(_schedule: ISchedule): CfnSchedule.TargetProperty { + if (typeof this.baseProps.role === undefined) + throw Error("A role is needed (for now)"); + this.addTargetActionToRole(this.baseProps.role!); + return { + arn: this.targetArn, + roleArn: this.baseProps.role!.roleArn, + input: this.baseProps.input?.bind(_schedule), + }; + } + + bind(schedule: ISchedule): CfnSchedule.TargetProperty { + return this.bindBaseTargetConfig(schedule); + } + } + + export class LambdaInvoke extends ScheduleTargetBase { + constructor( + baseProps: ScheduleTargetBaseProps, + private readonly func: lambda.IFunction, + ) { + super(baseProps, func.functionArn); + } + + protected addTargetActionToRole(role: iam.IRole): void { + this.func.grantInvoke(role); + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts b/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts new file mode 100644 index 0000000000000..eb56e201d87e5 --- /dev/null +++ b/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts @@ -0,0 +1,142 @@ +import { Stack } from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import { ContextAttribute, Schedule, ScheduleExpression, ScheduleTargetInput, targets } from '../lib'; + +describe('schedule target input', () => { + let stack: Stack; + let role: iam.IRole; + let func: lambda.IFunction; + const expr = ScheduleExpression.at(new Date(Date.UTC(1969, 10, 20, 0, 0, 0))) + + beforeEach(() => { + stack = new Stack(); + role = iam.Role.fromRoleArn(stack, 'Role', 'arn:aws:iam::123456789012:role/johndoe'); + func = lambda.Function.fromFunctionArn(stack, 'Function', 'arn:aws:lambda:us-east-1:123456789012:function/somefunc'); + }); + + test('create an input from text', () => { + new Schedule(stack, 'MyScheduleDummy', { + schedule: expr, + target: new targets.LambdaInvoke({ + role, + input: ScheduleTargetInput.fromText('test'), + }, func), + }); + Template.fromStack(stack).hasResource('AWS::Scheduler::Schedule', { + Properties: { + Target: { + Input: '"test"', + }, + }, + }); + }); + + test('create an input from text with a ref inside', () => { + new Schedule(stack, 'MyScheduleDummy', { + schedule: expr, + target: new targets.LambdaInvoke({ + role, + input: ScheduleTargetInput.fromText(stack.account), + }, func), + }); + Template.fromStack(stack).hasResource('AWS::Scheduler::Schedule', { + Properties: { + Target: { + Input: { + 'Fn::Join': ["", ['"', { 'Ref': 'AWS::AccountId' }, '"']] + }, + }, + }, + }); + }); + + test('create an input from object', () => { + new Schedule(stack, 'MyScheduleDummy', { + schedule: expr, + target: new targets.LambdaInvoke({ + role, + input: ScheduleTargetInput.fromObject({ + test: 'test', + }), + }, func), + }); + Template.fromStack(stack).hasResource('AWS::Scheduler::Schedule', { + Properties: { + Target: { + Input: '{"test":"test"}', + }, + }, + }); + }); + + test('create an input from object with a ref', () => { + new Schedule(stack, 'MyScheduleDummy', { + schedule: expr, + target: new targets.LambdaInvoke({ + role, + input: ScheduleTargetInput.fromObject({ + test: stack.account, + }), + }, func), + }); + Template.fromStack(stack).hasResource('AWS::Scheduler::Schedule', { + Properties: { + Target: { + Input: { + 'Fn::Join': ['', [ + '{"test":"', + { 'Ref': 'AWS::AccountId' }, + '"}' + ]] + }, + }, + }, + }); + }); + + test('create an input with fromText with ContextrAttribute', () => { + new Schedule(stack, 'MyScheduleDummy', { + schedule: expr, + target: new targets.LambdaInvoke({ + role, + input: ScheduleTargetInput.fromText(`Test=${ContextAttribute.scheduleArn}`), + }, func), + }); + Template.fromStack(stack).hasResource('AWS::Scheduler::Schedule', { + Properties: { + Target: { + Input: '"Test="', + }, + }, + }); + }); + + test('create an input with fromObject with ContextrAttribute', () => { + new Schedule(stack, 'MyScheduleDummy', { + schedule: expr, + target: new targets.LambdaInvoke({ + role, + input: ScheduleTargetInput.fromObject({ + 'arn': ContextAttribute.scheduleArn, + 'att': ContextAttribute.attemptNumber, + 'xid': ContextAttribute.executionId, + 'tim': ContextAttribute.scheduledTime, + 'cus': ContextAttribute.fromName('escapehatch'), + }), + }, func), + }); + Template.fromStack(stack).hasResource('AWS::Scheduler::Schedule', { + Properties: { + Target: { + Input: '{"arn":"",' + + '"att":"",' + + '"xid":"",' + + '"tim":"",' + + '"cus":""}', + }, + }, + }); + }); +}); diff --git a/packages/@aws-cdk/aws-scheduler-alpha/test/schedule-expression.test.ts b/packages/@aws-cdk/aws-scheduler-alpha/test/schedule-expression.test.ts index 06fdb794ca600..dee74ae422854 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/test/schedule-expression.test.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/test/schedule-expression.test.ts @@ -94,7 +94,7 @@ describe('schedule expression', () => { }); test('one-time expression string has expected date', () => { - const x = ScheduleExpression.at(new Date(2022, 10, 20, 19, 20, 23)); + const x = ScheduleExpression.at(new Date(Date.UTC(2022, 10, 20, 19, 20, 23))); expect(x.expressionString).toEqual('at(2022-11-20T19:20:23)'); }); @@ -104,7 +104,7 @@ describe('schedule expression', () => { }); test('one-time expression has expected time zone if provided', () => { - const x = ScheduleExpression.at(new Date(2022, 10, 20, 19, 20, 23), TimeZone.EUROPE_LONDON); + const x = ScheduleExpression.at(new Date(Date.UTC(2022, 10, 20, 19, 20, 23)), TimeZone.EUROPE_LONDON); expect(x.expressionString).toEqual('at(2022-11-20T19:20:23)'); expect(x.timeZone).toEqual(TimeZone.EUROPE_LONDON); }); From d9dc10c9fd6a0f01aaee7dcb317753419cf295dd Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Fri, 16 Jun 2023 11:44:28 +0200 Subject: [PATCH 02/11] Update packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts Co-authored-by: Filipp Fediakov --- packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts index a9201b125f489..7b351252d39ed 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts @@ -9,9 +9,9 @@ export abstract class ScheduleTargetInput { * Pass text to the target, it is possible to embed `ContextAttributes` and other * cdk references. * - * The target input value will be a single string: the string you pass - * here. Do not use this method to pass a complex value like a JSON object to - * a target. Use `ScheduleTargetInput.fromObject()` instead. + * The target input value will be a single string that you pass. + * For passing complex values like JSON object to a target use method + * `ScheduleTargetInput.fromObject()` instead. * * @param text text to use as input for the target */ From 8af3db1d2cb8138b8d26ceadee69e9842d69b17a Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Fri, 16 Jun 2023 11:45:05 +0200 Subject: [PATCH 03/11] Update packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts Co-authored-by: Filipp Fediakov --- packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts b/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts index eb56e201d87e5..091ee41ae57ea 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts @@ -96,7 +96,7 @@ describe('schedule target input', () => { }); }); - test('create an input with fromText with ContextrAttribute', () => { + test('create an input with fromText with ContextAttribute', () => { new Schedule(stack, 'MyScheduleDummy', { schedule: expr, target: new targets.LambdaInvoke({ From 54d6e0f1ecef01c44c110e7b56ffc9df3c4ecc14 Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Fri, 16 Jun 2023 11:45:20 +0200 Subject: [PATCH 04/11] Update packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts Co-authored-by: Filipp Fediakov --- packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts index 7b351252d39ed..e510e1f9951d9 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts @@ -13,7 +13,7 @@ export abstract class ScheduleTargetInput { * For passing complex values like JSON object to a target use method * `ScheduleTargetInput.fromObject()` instead. * - * @param text text to use as input for the target + * @param text Text to use as the input for the target */ public static fromText(text: string): ScheduleTargetInput { return new FieldAwareEventInput(text); From 9c2f8d4df316f5974a659540480fd031da59a690 Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Sat, 17 Jun 2023 07:12:31 +0200 Subject: [PATCH 05/11] moved stuff to private --- .../@aws-cdk/aws-scheduler-alpha/lib/index.ts | 3 +- .../@aws-cdk/aws-scheduler-alpha/lib/input.ts | 8 +-- .../aws-scheduler-alpha/lib/private/index.ts | 2 + .../lib/private/schedule.ts | 57 +++++++++++++++++++ .../lib/{ => private}/targets.ts | 13 +++-- .../aws-scheduler-alpha/lib/schedule.ts | 57 +------------------ .../aws-scheduler-alpha/test/input.test.ts | 25 ++++---- 7 files changed, 85 insertions(+), 80 deletions(-) create mode 100644 packages/@aws-cdk/aws-scheduler-alpha/lib/private/index.ts create mode 100644 packages/@aws-cdk/aws-scheduler-alpha/lib/private/schedule.ts rename packages/@aws-cdk/aws-scheduler-alpha/lib/{ => private}/targets.ts (86%) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/index.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/index.ts index 827fdd4a2b663..c2ff54e61f61b 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/index.ts @@ -1,4 +1,3 @@ export * from './schedule-expression'; export * from './input'; -export * from './schedule'; -export * from './targets'; +export * from './schedule'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts index e510e1f9951d9..80fadffa47058 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts @@ -12,7 +12,7 @@ export abstract class ScheduleTargetInput { * The target input value will be a single string that you pass. * For passing complex values like JSON object to a target use method * `ScheduleTargetInput.fromObject()` instead. - * + * * @param text Text to use as the input for the target */ public static fromText(text: string): ScheduleTargetInput { @@ -22,7 +22,7 @@ export abstract class ScheduleTargetInput { /** * Pass a JSON object to the target, it is possible to embed `ContextAttributes` and other * cdk references. - * + * * @param obj object to use to convert to JSON to use as input for the target */ public static fromObject(obj: any): ScheduleTargetInput { @@ -64,7 +64,7 @@ class FieldAwareEventInput extends ScheduleTargetInput { /** * Represents a field in the event pattern - * + * * @see https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-schedule-context-attributes.html */ export class ContextAttribute { @@ -101,7 +101,7 @@ export class ContextAttribute { /** * Escape hatch for other ContextAttribute that might be resolved in future. - * + * * @param name - name will replace xxx in */ public static fromName(name: string): string { diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/private/index.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/private/index.ts new file mode 100644 index 0000000000000..acb4914fd0c93 --- /dev/null +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/private/index.ts @@ -0,0 +1,2 @@ +export * from './schedule'; +export * from './targets'; diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/private/schedule.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/private/schedule.ts new file mode 100644 index 0000000000000..0e2b33742d18f --- /dev/null +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/private/schedule.ts @@ -0,0 +1,57 @@ +import { Resource } from 'aws-cdk-lib'; +import { CfnSchedule } from 'aws-cdk-lib/aws-scheduler'; +import { Construct } from 'constructs'; +import { ISchedule } from '../schedule'; +import { ScheduleExpression } from '../schedule-expression'; + +/** + * DISCLAIMER: WORK IN PROGRESS, INTERFACE MIGHT CHANGE + * + * This unit is not yet finished. Only rudimentary Schedule is implemented in order + * to be able to create some sensible unit tests + */ + +export interface IScheduleTarget { + bind(_schedule: ISchedule): CfnSchedule.TargetProperty; +} + +/** + * Construction properties for `Schedule`. + */ +export interface ScheduleProps { + /** + * The expression that defines when the schedule runs. Can be either a `at`, `rate` + * or `cron` expression. + */ + readonly schedule: ScheduleExpression; + + /** + * The schedule's target details. + */ + readonly target: IScheduleTarget; + + /** + * The description you specify for the schedule. + * + * @default - no value + */ + readonly description?: string; +} + +/** + * An EventBridge Schedule + */ +export class Schedule extends Resource implements ISchedule { + constructor(scope: Construct, id: string, props: ScheduleProps) { + super(scope, id); + + new CfnSchedule(this, 'Resource', { + flexibleTimeWindow: { mode: 'OFF' }, + scheduleExpression: props.schedule.expressionString, + scheduleExpressionTimezone: props.schedule.timeZone?.timezoneName, + target: { + ...props.target.bind(this), + }, + }); + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/targets.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/private/targets.ts similarity index 86% rename from packages/@aws-cdk/aws-scheduler-alpha/lib/targets.ts rename to packages/@aws-cdk/aws-scheduler-alpha/lib/private/targets.ts index 5fd8476131ee3..1edff1b13db57 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/targets.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/private/targets.ts @@ -1,13 +1,13 @@ import * as iam from 'aws-cdk-lib/aws-iam'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import { CfnSchedule } from 'aws-cdk-lib/aws-scheduler'; -import { ScheduleTargetInput } from './input'; -import { ISchedule } from './schedule'; +import { ScheduleTargetInput } from '../input'; +import { ISchedule } from '../schedule'; /** * DISCLAIMER: WORK IN PROGRESS, INTERFACE MIGHT CHANGE - * - * This unit is not yet finished. The LambaInvoke target is only implemented to be able + * + * This unit is not yet finished. The LambaInvoke target is only implemented to be able * to create some sensible unit tests. */ @@ -27,8 +27,9 @@ export namespace targets { protected abstract addTargetActionToRole(role: iam.IRole): void; protected bindBaseTargetConfig(_schedule: ISchedule): CfnSchedule.TargetProperty { - if (typeof this.baseProps.role === undefined) - throw Error("A role is needed (for now)"); + if (typeof this.baseProps.role === undefined) { + throw Error('A role is needed (for now)'); + } this.addTargetActionToRole(this.baseProps.role!); return { arn: this.targetArn, diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts index 74462df5382dd..23bcd9406c0d2 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/schedule.ts @@ -1,41 +1,4 @@ -import { IResource, Resource } from 'aws-cdk-lib'; -import { CfnSchedule } from 'aws-cdk-lib/aws-scheduler'; -import { Construct } from 'constructs'; -import { ScheduleExpression } from './schedule-expression'; - -/** - * DISCLAIMER: WORK IN PROGRESS, INTERFACE MIGHT CHANGE - * - * This unit is not yet finished. Only rudimentary Schedule is implemented in order - * to be able to create some sensible unit tests - */ - -export interface IScheduleTarget { - bind(_schedule: ISchedule): CfnSchedule.TargetProperty; -} - -/** - * Construction properties for `Schedule`. - */ -export interface ScheduleProps { - /** - * The expression that defines when the schedule runs. Can be either a `at`, `rate` - * or `cron` expression. - */ - readonly schedule: ScheduleExpression; - - /** - * The schedule's target details. - */ - readonly target: IScheduleTarget; - - /** - * The description you specify for the schedule. - * - * @default - no value - */ - readonly description?: string; -} +import { IResource } from 'aws-cdk-lib'; /** * Interface representing a created or an imported `Schedule`. @@ -43,21 +6,3 @@ export interface ScheduleProps { export interface ISchedule extends IResource { } - -/** - * An EventBridge Schedule - */ -export class Schedule extends Resource implements ISchedule { - constructor(scope: Construct, id: string, props: ScheduleProps) { - super(scope, id); - - new CfnSchedule(this, 'Resource', { - flexibleTimeWindow: { mode: 'OFF' }, - scheduleExpression: props.schedule.expressionString, - scheduleExpressionTimezone: props.schedule.timeZone?.timezoneName, - target: { - ...props.target.bind(this), - }, - }); - } -} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts b/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts index 091ee41ae57ea..a49466e2929b6 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts @@ -2,13 +2,14 @@ import { Stack } from 'aws-cdk-lib'; import { Template } from 'aws-cdk-lib/assertions'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as lambda from 'aws-cdk-lib/aws-lambda'; -import { ContextAttribute, Schedule, ScheduleExpression, ScheduleTargetInput, targets } from '../lib'; +import { ContextAttribute, ScheduleExpression, ScheduleTargetInput } from '../lib'; +import { Schedule, targets } from '../lib/private'; describe('schedule target input', () => { let stack: Stack; let role: iam.IRole; let func: lambda.IFunction; - const expr = ScheduleExpression.at(new Date(Date.UTC(1969, 10, 20, 0, 0, 0))) + const expr = ScheduleExpression.at(new Date(Date.UTC(1969, 10, 20, 0, 0, 0))); beforeEach(() => { stack = new Stack(); @@ -45,7 +46,7 @@ describe('schedule target input', () => { Properties: { Target: { Input: { - 'Fn::Join': ["", ['"', { 'Ref': 'AWS::AccountId' }, '"']] + 'Fn::Join': ['', ['"', { Ref: 'AWS::AccountId' }, '"']], }, }, }, @@ -87,9 +88,9 @@ describe('schedule target input', () => { Input: { 'Fn::Join': ['', [ '{"test":"', - { 'Ref': 'AWS::AccountId' }, - '"}' - ]] + { Ref: 'AWS::AccountId' }, + '"}', + ]], }, }, }, @@ -119,18 +120,18 @@ describe('schedule target input', () => { target: new targets.LambdaInvoke({ role, input: ScheduleTargetInput.fromObject({ - 'arn': ContextAttribute.scheduleArn, - 'att': ContextAttribute.attemptNumber, - 'xid': ContextAttribute.executionId, - 'tim': ContextAttribute.scheduledTime, - 'cus': ContextAttribute.fromName('escapehatch'), + arn: ContextAttribute.scheduleArn, + att: ContextAttribute.attemptNumber, + xid: ContextAttribute.executionId, + tim: ContextAttribute.scheduledTime, + cus: ContextAttribute.fromName('escapehatch'), }), }, func), }); Template.fromStack(stack).hasResource('AWS::Scheduler::Schedule', { Properties: { Target: { - Input: '{"arn":"",' + + Input: '{"arn":"",' + '"att":"",' + '"xid":"",' + '"tim":"",' + From b7a6ba7d6038a4cd383d72557cac4e26fd7a72d6 Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Sat, 17 Jun 2023 09:01:26 +0200 Subject: [PATCH 06/11] Update packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts Co-authored-by: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> --- packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts b/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts index a49466e2929b6..adb6042ba3879 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/test/input.test.ts @@ -114,7 +114,7 @@ describe('schedule target input', () => { }); }); - test('create an input with fromObject with ContextrAttribute', () => { + test('create an input with fromObject with ContextAttribute', () => { new Schedule(stack, 'MyScheduleDummy', { schedule: expr, target: new targets.LambdaInvoke({ From e363170b0a454735b8cc2076945c9c515686c397 Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Sat, 17 Jun 2023 09:05:46 +0200 Subject: [PATCH 07/11] readme fix comment changed --- packages/@aws-cdk/aws-scheduler-alpha/README.md | 2 +- packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/README.md b/packages/@aws-cdk/aws-scheduler-alpha/README.md index 0c2eeccae37b2..f376bbfb2726b 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/README.md +++ b/packages/@aws-cdk/aws-scheduler-alpha/README.md @@ -122,7 +122,7 @@ its respective value and deliver it to the target. See ```ts const text = `Attempt number: ${ContextAttribute.attemptNumber}`; -const input = scheduler.ScheduleTargetInput.fromInput(text); +const input = ScheduleTargetInput.fromInput(text); ``` ### Specifying Execution Role diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts index 80fadffa47058..63a4f0b0d0ea1 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts @@ -6,8 +6,10 @@ import { ISchedule } from './schedule'; */ export abstract class ScheduleTargetInput { /** - * Pass text to the target, it is possible to embed `ContextAttributes` and other - * cdk references. + * Pass text to the target, it is possible to embed `ContextAttributes` + * that will be resolved to actual values while the CloudFormation is + * deployed or cdk Tokens that will be resolved when the CloudFormation + * templates are generated by CDK. * * The target input value will be a single string that you pass. * For passing complex values like JSON object to a target use method From 81793baa268ddec85638ccd4f608455d6650b3bb Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Mon, 19 Jun 2023 22:37:51 +0200 Subject: [PATCH 08/11] back to txt --- packages/@aws-cdk/aws-scheduler-alpha/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/README.md b/packages/@aws-cdk/aws-scheduler-alpha/README.md index f376bbfb2726b..0dd12e757fdc1 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/README.md +++ b/packages/@aws-cdk/aws-scheduler-alpha/README.md @@ -57,7 +57,9 @@ cron-based schedule you can specify a time zone in which EventBridge Scheduler e > ScheduleExpression should be used together with class Schedule, which is not yet implemented. -```ts +[comment]: <> (TODO: Switch to `ts` once Schedule is implemented) + +```text const rateBasedSchedule = new Schedule(this, 'Schedule', { scheduleExpression: ScheduleExpression.rate(Duration.minutes(10)), target, @@ -80,7 +82,9 @@ const cronBasedSchedule = new Schedule(this, 'Schedule', { A one-time schedule is a schedule that invokes a target only once. You configure a one-time schedule when by specifying the time of the day, date, and time zone in which EventBridge Scheduler evaluates the schedule. -```ts +[comment]: <> (TODO: Switch to `ts` once Schedule is implemented) + +```text const oneTimeSchedule = new Schedule(this, 'Schedule', { scheduleExpression: ScheduleExpression.at( new Date(2022, 10, 20, 19, 20, 23), From d7e4efe19b803bc6915dec8ecedeb5d3710d759d Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Wed, 21 Jun 2023 14:35:24 +0200 Subject: [PATCH 09/11] space --- packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts index 63a4f0b0d0ea1..682ce0687e374 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts +++ b/packages/@aws-cdk/aws-scheduler-alpha/lib/input.ts @@ -7,7 +7,7 @@ import { ISchedule } from './schedule'; export abstract class ScheduleTargetInput { /** * Pass text to the target, it is possible to embed `ContextAttributes` - * that will be resolved to actual values while the CloudFormation is + * that will be resolved to actual values while the CloudFormation is * deployed or cdk Tokens that will be resolved when the CloudFormation * templates are generated by CDK. * From b74d0ad1988cd7bd969541ab42ceb32f5b33d7b8 Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Thu, 22 Jun 2023 19:17:19 +0200 Subject: [PATCH 10/11] rosetta adjust --- .../@aws-cdk/aws-scheduler-alpha/rosetta/default.ts-fixture | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/rosetta/default.ts-fixture b/packages/@aws-cdk/aws-scheduler-alpha/rosetta/default.ts-fixture index 71131d04c63a3..776fd224ec9b1 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/rosetta/default.ts-fixture +++ b/packages/@aws-cdk/aws-scheduler-alpha/rosetta/default.ts-fixture @@ -7,7 +7,7 @@ import * as kms from 'aws-cdk-lib/aws-kms'; import * as sqs from 'aws-cdk-lib/aws-sqs'; import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch'; import { App, Stack, TimeZone, Duration } from 'aws-cdk-lib'; -import { ScheduleExpression } from '@aws-cdk/aws-scheduler-alpha'; +import { ScheduleExpression, ScheduleTargetInput, ContextAttribute } from '@aws-cdk/aws-scheduler-alpha'; class Fixture extends cdk.Stack { constructor(scope: Construct, id: string) { From 76ba2808d232548aaf736e0e8aa0566a60737211 Mon Sep 17 00:00:00 2001 From: Jacco Kulman Date: Thu, 22 Jun 2023 19:51:32 +0200 Subject: [PATCH 11/11] fromText --- packages/@aws-cdk/aws-scheduler-alpha/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-scheduler-alpha/README.md b/packages/@aws-cdk/aws-scheduler-alpha/README.md index 0dd12e757fdc1..7cc01e45a962a 100644 --- a/packages/@aws-cdk/aws-scheduler-alpha/README.md +++ b/packages/@aws-cdk/aws-scheduler-alpha/README.md @@ -126,7 +126,7 @@ its respective value and deliver it to the target. See ```ts const text = `Attempt number: ${ContextAttribute.attemptNumber}`; -const input = ScheduleTargetInput.fromInput(text); +const input = ScheduleTargetInput.fromText(text); ``` ### Specifying Execution Role