Skip to content

Commit 5e91a0a

Browse files
authored
fix(aws-events): ergonomics improvements to CloudWatch Events (#1570)
Fixes the following things for CloudWatch events: * Support newlines in CloudWatch Events textTemplate as intended, by making a newline-separated list of JSON strings. Fixes #1514. * `jsonTemplate` now accepts arbitrary objects. They will be JSONified automatically. Fixes #1198. * Explicitly implement `IEventRuleTarget` on stepfunctions StateMachine so that Java/.NET users can trigger StateMachines using CloudWatch Events. Fixes part of #1275.
1 parent 9128390 commit 5e91a0a

File tree

7 files changed

+190
-9
lines changed

7 files changed

+190
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"app": "node index"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import events = require('@aws-cdk/aws-events');
2+
import sns = require('@aws-cdk/aws-sns');
3+
import cdk = require('@aws-cdk/cdk');
4+
5+
const app = new cdk.App();
6+
7+
class CloudWatchEventsExample extends cdk.Stack {
8+
constructor(scope: cdk.App, id: string) {
9+
super(scope, id);
10+
11+
const topic = new sns.Topic(this, 'TestTopic');
12+
13+
const event = new events.EventRule(this, 'Rule', {
14+
scheduleExpression: 'rate(1 minute)'
15+
});
16+
17+
event.addTarget(topic, {
18+
textTemplate: 'one line\nsecond line'
19+
});
20+
}
21+
}
22+
23+
new CloudWatchEventsExample(app, 'CWE-Example');
24+
25+
app.run();

examples/cdk-examples-typescript/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"@aws-cdk/aws-rds": "^0.22.0",
3838
"@aws-cdk/aws-s3": "^0.22.0",
3939
"@aws-cdk/aws-sns": "^0.22.0",
40+
"@aws-cdk/aws-events": "^0.22.0",
4041
"@aws-cdk/aws-sqs": "^0.22.0",
4142
"@aws-cdk/cdk": "^0.22.0",
4243
"@aws-cdk/runtime-values": "^0.22.0"

packages/@aws-cdk/aws-events/lib/rule.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export class EventRule extends Construct implements IEventRule {
116116
*/
117117
public addTarget(target?: IEventRuleTarget, inputOptions?: TargetInputTemplate) {
118118
if (!target) { return; }
119+
const self = this;
119120

120121
const targetProps = target.asEventRuleTarget(this.ruleArn, this.node.uniqueId);
121122

@@ -145,11 +146,15 @@ export class EventRule extends Construct implements IEventRule {
145146
let inputTemplate: any;
146147

147148
if (inputOptions.jsonTemplate) {
148-
inputTemplate = inputOptions.jsonTemplate;
149-
} else if (typeof(inputOptions.textTemplate) === 'string') {
150-
inputTemplate = JSON.stringify(inputOptions.textTemplate);
149+
inputTemplate = typeof inputOptions.jsonTemplate === 'string'
150+
? inputOptions.jsonTemplate
151+
: self.node.stringifyJson(inputOptions.jsonTemplate);
151152
} else {
152-
inputTemplate = `"${inputOptions.textTemplate}"`;
153+
inputTemplate = typeof(inputOptions.textTemplate) === 'string'
154+
// Newline separated list of JSON-encoded strings
155+
? inputOptions.textTemplate.split('\n').map(x => self.node.stringifyJson(x)).join('\n')
156+
// Some object, stringify it, then stringify the string for proper escaping
157+
: self.node.stringifyJson(self.node.stringifyJson(inputOptions.textTemplate));
153158
}
154159

155160
return {

packages/@aws-cdk/aws-events/test/test.rule.ts

+119-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect, haveResource } from '@aws-cdk/assert';
1+
import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert';
22
import cdk = require('@aws-cdk/cdk');
33
import { Stack } from '@aws-cdk/cdk';
44
import { Test } from 'nodeunit';
@@ -351,5 +351,122 @@ export = {
351351
test.deepEqual(importedRule.ruleArn, 'arn:of:rule');
352352

353353
test.done();
354-
}
354+
},
355+
356+
'json template': {
357+
'can just be a JSON object'(test: Test) {
358+
// GIVEN
359+
const stack = new Stack();
360+
const rule = new EventRule(stack, 'Rule', {
361+
scheduleExpression: 'rate(1 minute)'
362+
});
363+
364+
// WHEN
365+
rule.addTarget(new SomeTarget(), {
366+
jsonTemplate: { SomeObject: 'withAValue' },
367+
});
368+
369+
// THEN
370+
expect(stack).to(haveResourceLike('AWS::Events::Rule', {
371+
Targets: [
372+
{
373+
InputTransformer: {
374+
InputTemplate: "{\"SomeObject\":\"withAValue\"}"
375+
},
376+
}
377+
]
378+
}));
379+
test.done();
380+
},
381+
},
382+
383+
'text templates': {
384+
'strings with newlines are serialized to a newline-delimited list of JSON strings'(test: Test) {
385+
// GIVEN
386+
const stack = new Stack();
387+
const rule = new EventRule(stack, 'Rule', {
388+
scheduleExpression: 'rate(1 minute)'
389+
});
390+
391+
// WHEN
392+
rule.addTarget(new SomeTarget(), {
393+
textTemplate: 'I have\nmultiple lines',
394+
});
395+
396+
// THEN
397+
expect(stack).to(haveResourceLike('AWS::Events::Rule', {
398+
Targets: [
399+
{
400+
InputTransformer: {
401+
InputTemplate: "\"I have\"\n\"multiple lines\""
402+
},
403+
}
404+
]
405+
}));
406+
407+
test.done();
408+
},
409+
410+
'escaped newlines are not interpreted as newlines'(test: Test) {
411+
// GIVEN
412+
const stack = new Stack();
413+
const rule = new EventRule(stack, 'Rule', {
414+
scheduleExpression: 'rate(1 minute)'
415+
});
416+
417+
// WHEN
418+
rule.addTarget(new SomeTarget(), {
419+
textTemplate: 'this is not\\na real newline',
420+
});
421+
422+
// THEN
423+
expect(stack).to(haveResourceLike('AWS::Events::Rule', {
424+
Targets: [
425+
{
426+
InputTransformer: {
427+
InputTemplate: "\"this is not\\\\na real newline\""
428+
},
429+
}
430+
]
431+
}));
432+
433+
test.done();
434+
},
435+
436+
'can use Tokens in text templates'(test: Test) {
437+
// GIVEN
438+
const stack = new Stack();
439+
const rule = new EventRule(stack, 'Rule', {
440+
scheduleExpression: 'rate(1 minute)'
441+
});
442+
443+
const world = new cdk.Token(() => 'world');
444+
445+
// WHEN
446+
rule.addTarget(new SomeTarget(), {
447+
textTemplate: `hello ${world}`,
448+
});
449+
450+
// THEN
451+
expect(stack).to(haveResourceLike('AWS::Events::Rule', {
452+
Targets: [
453+
{
454+
InputTransformer: {
455+
InputTemplate: "\"hello world\""
456+
},
457+
}
458+
]
459+
}));
460+
461+
test.done();
462+
}
463+
},
355464
};
465+
466+
class SomeTarget implements IEventRuleTarget {
467+
public asEventRuleTarget() {
468+
return {
469+
id: 'T1', arn: 'ARN1', kinesisParameters: { partitionKeyPath: 'partitionKeyPath' }
470+
};
471+
}
472+
}

packages/@aws-cdk/aws-stepfunctions/lib/state-machine.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export interface StateMachineProps {
4040
/**
4141
* Define a StepFunctions State Machine
4242
*/
43-
export class StateMachine extends cdk.Construct implements IStateMachine {
43+
export class StateMachine extends cdk.Construct implements IStateMachine, events.IEventRuleTarget {
4444
/**
4545
* Import a state machine
4646
*/

packages/@aws-cdk/aws-stepfunctions/test/test.state-machine-resources.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { expect, haveResource } from '@aws-cdk/assert';
1+
import { expect, haveResource, haveResourceLike } from '@aws-cdk/assert';
2+
import events = require('@aws-cdk/aws-events');
23
import iam = require('@aws-cdk/aws-iam');
34
import cdk = require('@aws-cdk/cdk');
45
import { Test } from 'nodeunit';
@@ -134,7 +135,36 @@ export = {
134135
});
135136

136137
test.done();
137-
}
138+
},
139+
140+
'State machine can be used as Event Rule target'(test: Test) {
141+
// GIVEN
142+
const stack = new cdk.Stack();
143+
const rule = new events.EventRule(stack, 'Rule', {
144+
scheduleExpression: 'rate(1 minute)'
145+
});
146+
const stateMachine = new stepfunctions.StateMachine(stack, 'SM', {
147+
definition: new stepfunctions.Wait(stack, 'Hello', { })
148+
});
149+
150+
// WHEN
151+
rule.addTarget(stateMachine, {
152+
jsonTemplate: { SomeParam: 'SomeValue' },
153+
});
154+
155+
// THEN
156+
expect(stack).to(haveResourceLike('AWS::Events::Rule', {
157+
Targets: [
158+
{
159+
InputTransformer: {
160+
InputTemplate: "{\"SomeParam\":\"SomeValue\"}"
161+
},
162+
}
163+
]
164+
}));
165+
166+
test.done();
167+
},
138168
};
139169

140170
class FakeResource implements stepfunctions.IStepFunctionsTaskResource {

0 commit comments

Comments
 (0)