Skip to content

Commit bc9f3de

Browse files
authored
feat(scheduler): ScheduleTargetInput (#25663)
This PR contains implementation of ScheduleTargetInput. While a schedule is the main resource in Amazon EventBridge Scheduler, this PR adds ScheduleTargetInput on which ScheduleTargetBase depends. Every Schedule has a target that determines what extra information is sent to the target when the schedule is triggered. Also 4 ContextAttributes can be used that will be resolved at trigger-time. To be able to create sensible unit tests, also the a start is made to add the `Schedule` and the `LambdaInvoke` target as described in the RFC. Implementation is based on RFC: https://github.com/aws/aws-cdk-rfcs/blob/master/text/0474-event-bridge-scheduler-l2.md Also added a small fix to 2 of the unit tests of the previous PR for this module. Advances #23394 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 1ccfc78 commit bc9f3de

File tree

9 files changed

+425
-4
lines changed

9 files changed

+425
-4
lines changed

packages/@aws-cdk/aws-scheduler-alpha/README.md

+31-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,15 @@ This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aw
3737
3838
## Defining a schedule
3939

40-
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)
40+
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)
41+
42+
[comment]: <> (TODO: change for each PR that implements more functionality)
43+
44+
Only an L2 class is created that wraps the L1 class and handles the following properties:
45+
46+
- schedule
47+
- target (only LambdaInvoke is supported for now)
48+
- flexibleTimeWindow will be set to `{ mode: 'OFF' }`
4149

4250
### Schedule Expressions
4351

@@ -95,10 +103,31 @@ TODO: Group is not yet implemented. See section in [L2 Event Bridge Scheduler RF
95103

96104
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)
97105

106+
Only LambdaInvoke target is added for now.
107+
98108
### Input
99109

100-
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)
110+
Target can be invoked with a custom input. Class `ScheduleTargetInput` supports free form text input and JSON-formatted object input:
111+
112+
```ts
113+
const input = ScheduleTargetInput.fromObject({
114+
'QueueName': 'MyQueue'
115+
});
116+
```
117+
118+
You can include context attributes in your target payload. EventBridge Scheduler will replace each keyword with
119+
its respective value and deliver it to the target. See
120+
[full list of supported context attributes](https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-schedule-context-attributes.html):
101121

122+
1. `ContextAttribute.scheduleArn()` – The ARN of the schedule.
123+
2. `ContextAttribute.scheduledTime()` – The time you specified for the schedule to invoke its target, for example, 2022-03-22T18:59:43Z.
124+
3. `ContextAttribute.executionId()` – The unique ID that EventBridge Scheduler assigns for each attempted invocation of a target, for example, d32c5kddcf5bb8c3.
125+
4. `ContextAttribute.attemptNumber()` – A counter that identifies the attempt number for the current invocation, for example, 1.
126+
127+
```ts
128+
const text = `Attempt number: ${ContextAttribute.attemptNumber}`;
129+
const input = ScheduleTargetInput.fromText(text);
130+
```
102131

103132
### Specifying Execution Role
104133

Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
export * from './schedule-expression';
1+
export * from './schedule-expression';
2+
export * from './input';
3+
export * from './schedule';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { DefaultTokenResolver, IResolveContext, Stack, StringConcat, Token, Tokenization } from 'aws-cdk-lib';
2+
import { ISchedule } from './schedule';
3+
4+
/**
5+
* The text, or well-formed JSON, passed to the target of the schedule.
6+
*/
7+
export abstract class ScheduleTargetInput {
8+
/**
9+
* Pass text to the target, it is possible to embed `ContextAttributes`
10+
* that will be resolved to actual values while the CloudFormation is
11+
* deployed or cdk Tokens that will be resolved when the CloudFormation
12+
* templates are generated by CDK.
13+
*
14+
* The target input value will be a single string that you pass.
15+
* For passing complex values like JSON object to a target use method
16+
* `ScheduleTargetInput.fromObject()` instead.
17+
*
18+
* @param text Text to use as the input for the target
19+
*/
20+
public static fromText(text: string): ScheduleTargetInput {
21+
return new FieldAwareEventInput(text);
22+
}
23+
24+
/**
25+
* Pass a JSON object to the target, it is possible to embed `ContextAttributes` and other
26+
* cdk references.
27+
*
28+
* @param obj object to use to convert to JSON to use as input for the target
29+
*/
30+
public static fromObject(obj: any): ScheduleTargetInput {
31+
return new FieldAwareEventInput(obj);
32+
}
33+
34+
protected constructor() {
35+
}
36+
37+
/**
38+
* Return the input properties for this input object
39+
*/
40+
public abstract bind(schedule: ISchedule): string;
41+
}
42+
43+
class FieldAwareEventInput extends ScheduleTargetInput {
44+
constructor(private readonly input: any) {
45+
super();
46+
}
47+
48+
public bind(schedule: ISchedule): string {
49+
class Replacer extends DefaultTokenResolver {
50+
constructor() {
51+
super(new StringConcat());
52+
}
53+
54+
public resolveToken(t: Token, _context: IResolveContext) {
55+
return Token.asString(t);
56+
}
57+
}
58+
59+
const stack = Stack.of(schedule);
60+
return stack.toJsonString(Tokenization.resolve(this.input, {
61+
scope: schedule,
62+
resolver: new Replacer(),
63+
}));
64+
}
65+
}
66+
67+
/**
68+
* Represents a field in the event pattern
69+
*
70+
* @see https://docs.aws.amazon.com/scheduler/latest/UserGuide/managing-schedule-context-attributes.html
71+
*/
72+
export class ContextAttribute {
73+
/**
74+
* The ARN of the schedule.
75+
*/
76+
public static get scheduleArn(): string {
77+
return this.fromName('schedule-arn');
78+
}
79+
80+
/**
81+
* The time you specified for the schedule to invoke its target, for example,
82+
* 2022-03-22T18:59:43Z.
83+
*/
84+
public static get scheduledTime(): string {
85+
return this.fromName('scheduled-time');
86+
}
87+
88+
/**
89+
* The unique ID that EventBridge Scheduler assigns for each attempted invocation of
90+
* a target, for example, d32c5kddcf5bb8c3.
91+
*/
92+
public static get executionId(): string {
93+
return this.fromName('execution-id');
94+
}
95+
96+
/**
97+
* A counter that identifies the attempt number for the current invocation, for
98+
* example, 1.
99+
*/
100+
public static get attemptNumber(): string {
101+
return this.fromName('attempt-number');
102+
}
103+
104+
/**
105+
* Escape hatch for other ContextAttribute that might be resolved in future.
106+
*
107+
* @param name - name will replace xxx in <aws.scheduler.xxx>
108+
*/
109+
public static fromName(name: string): string {
110+
return new ContextAttribute(name).toString();
111+
}
112+
113+
private constructor(public readonly name: string) {
114+
}
115+
116+
/**
117+
* Convert the path to the field in the event pattern to JSON
118+
*/
119+
public toString() {
120+
return `<aws.scheduler.${this.name}>`;
121+
}
122+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './schedule';
2+
export * from './targets';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Resource } from 'aws-cdk-lib';
2+
import { CfnSchedule } from 'aws-cdk-lib/aws-scheduler';
3+
import { Construct } from 'constructs';
4+
import { ISchedule } from '../schedule';
5+
import { ScheduleExpression } from '../schedule-expression';
6+
7+
/**
8+
* DISCLAIMER: WORK IN PROGRESS, INTERFACE MIGHT CHANGE
9+
*
10+
* This unit is not yet finished. Only rudimentary Schedule is implemented in order
11+
* to be able to create some sensible unit tests
12+
*/
13+
14+
export interface IScheduleTarget {
15+
bind(_schedule: ISchedule): CfnSchedule.TargetProperty;
16+
}
17+
18+
/**
19+
* Construction properties for `Schedule`.
20+
*/
21+
export interface ScheduleProps {
22+
/**
23+
* The expression that defines when the schedule runs. Can be either a `at`, `rate`
24+
* or `cron` expression.
25+
*/
26+
readonly schedule: ScheduleExpression;
27+
28+
/**
29+
* The schedule's target details.
30+
*/
31+
readonly target: IScheduleTarget;
32+
33+
/**
34+
* The description you specify for the schedule.
35+
*
36+
* @default - no value
37+
*/
38+
readonly description?: string;
39+
}
40+
41+
/**
42+
* An EventBridge Schedule
43+
*/
44+
export class Schedule extends Resource implements ISchedule {
45+
constructor(scope: Construct, id: string, props: ScheduleProps) {
46+
super(scope, id);
47+
48+
new CfnSchedule(this, 'Resource', {
49+
flexibleTimeWindow: { mode: 'OFF' },
50+
scheduleExpression: props.schedule.expressionString,
51+
scheduleExpressionTimezone: props.schedule.timeZone?.timezoneName,
52+
target: {
53+
...props.target.bind(this),
54+
},
55+
});
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as iam from 'aws-cdk-lib/aws-iam';
2+
import * as lambda from 'aws-cdk-lib/aws-lambda';
3+
import { CfnSchedule } from 'aws-cdk-lib/aws-scheduler';
4+
import { ScheduleTargetInput } from '../input';
5+
import { ISchedule } from '../schedule';
6+
7+
/**
8+
* DISCLAIMER: WORK IN PROGRESS, INTERFACE MIGHT CHANGE
9+
*
10+
* This unit is not yet finished. The LambaInvoke target is only implemented to be able
11+
* to create some sensible unit tests.
12+
*/
13+
14+
export namespace targets {
15+
export interface ScheduleTargetBaseProps {
16+
readonly role?: iam.IRole;
17+
readonly input?: ScheduleTargetInput;
18+
}
19+
20+
abstract class ScheduleTargetBase {
21+
constructor(
22+
private readonly baseProps: ScheduleTargetBaseProps,
23+
protected readonly targetArn: string,
24+
) {
25+
}
26+
27+
protected abstract addTargetActionToRole(role: iam.IRole): void;
28+
29+
protected bindBaseTargetConfig(_schedule: ISchedule): CfnSchedule.TargetProperty {
30+
if (typeof this.baseProps.role === undefined) {
31+
throw Error('A role is needed (for now)');
32+
}
33+
this.addTargetActionToRole(this.baseProps.role!);
34+
return {
35+
arn: this.targetArn,
36+
roleArn: this.baseProps.role!.roleArn,
37+
input: this.baseProps.input?.bind(_schedule),
38+
};
39+
}
40+
41+
bind(schedule: ISchedule): CfnSchedule.TargetProperty {
42+
return this.bindBaseTargetConfig(schedule);
43+
}
44+
}
45+
46+
export class LambdaInvoke extends ScheduleTargetBase {
47+
constructor(
48+
baseProps: ScheduleTargetBaseProps,
49+
private readonly func: lambda.IFunction,
50+
) {
51+
super(baseProps, func.functionArn);
52+
}
53+
54+
protected addTargetActionToRole(role: iam.IRole): void {
55+
this.func.grantInvoke(role);
56+
}
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { IResource } from 'aws-cdk-lib';
2+
3+
/**
4+
* Interface representing a created or an imported `Schedule`.
5+
*/
6+
export interface ISchedule extends IResource {
7+
8+
}

packages/@aws-cdk/aws-scheduler-alpha/rosetta/default.ts-fixture

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as kms from 'aws-cdk-lib/aws-kms';
77
import * as sqs from 'aws-cdk-lib/aws-sqs';
88
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
99
import { App, Stack, TimeZone, Duration } from 'aws-cdk-lib';
10-
import { ScheduleExpression } from '@aws-cdk/aws-scheduler-alpha';
10+
import { ScheduleExpression, ScheduleTargetInput, ContextAttribute } from '@aws-cdk/aws-scheduler-alpha';
1111

1212
class Fixture extends cdk.Stack {
1313
constructor(scope: Construct, id: string) {

0 commit comments

Comments
 (0)