diff --git a/packages/aws-cdk-codebuild/test/integ.project-events.ts b/packages/aws-cdk-codebuild/test/integ.project-events.ts index ed0b72c62f585..c25282939351f 100644 --- a/packages/aws-cdk-codebuild/test/integ.project-events.ts +++ b/packages/aws-cdk-codebuild/test/integ.project-events.ts @@ -22,7 +22,7 @@ project.onStateChange('StateChange', topic); // The phase will be extracted from the "completed-phase" field of the event // details. project.onPhaseChange('PhaseChange').addTarget(topic, { - template: `Build phase changed to `, + textTemplate: `Build phase changed to `, pathsMap: { phase: '$.detail.completed-phase' } @@ -31,7 +31,7 @@ project.onPhaseChange('PhaseChange').addTarget(topic, { // trigger a build when a commit is pushed to the repo const onCommitRule = repo.onCommit('OnCommit', project, 'master'); onCommitRule.addTarget(topic, { - template: 'A commit was pushed to the repository on branch ', + textTemplate: 'A commit was pushed to the repository on branch ', pathsMap: { branch: '$.detail.referenceName', repo: '$.detail.repositoryName' diff --git a/packages/aws-cdk-codepipeline/test/integ.pipeline-events.ts b/packages/aws-cdk-codepipeline/test/integ.pipeline-events.ts index 92172f07dfaed..e5c6f42208ec1 100644 --- a/packages/aws-cdk-codepipeline/test/integ.pipeline-events.ts +++ b/packages/aws-cdk-codepipeline/test/integ.pipeline-events.ts @@ -18,13 +18,13 @@ const repository = new Repository(stack, 'CodeCommitRepo', { repositoryName: 'fo const project = new BuildProject(stack, 'BuildProject', { source: new CodePipelineSource() }); const sourceAction = new CodeCommitSource(sourceStage, 'CodeCommitSource', { artifactName: 'Source', repository }); -new CodeBuildAction(buildStage, 'CodeBuildAction', { source: sourceAction, project }); +new CodeBuildAction(buildStage, 'CodeBuildAction', { inputArtifact: sourceAction.artifact, project }); const topic = new Topic(stack, 'MyTopic'); topic.subscribeEmail('benisrae', 'benisrae@amazon.com'); pipeline.onStateChange('OnPipelineStateChange').addTarget(topic, { - template: 'Pipeline changed state to ', + textTemplate: 'Pipeline changed state to ', pathsMap: { pipeline: '$.detail.pipeline', state: '$.detail.state' diff --git a/packages/aws-cdk-events/lib/input-options.ts b/packages/aws-cdk-events/lib/input-options.ts index bae80ff62f2a0..1bf5a7e5da2bb 100644 --- a/packages/aws-cdk-events/lib/input-options.ts +++ b/packages/aws-cdk-events/lib/input-options.ts @@ -10,9 +10,38 @@ export interface TargetInputTemplate { * inputPathsMap to customize the data sent to the target. Enclose each * InputPathsMaps value in brackets: * - * The value will be serialized as a JSON object (JSON.stringify). + * The value passed here will be double-quoted to indicate it's a string value. + * This option is mutually exclusive with `jsonTemplate`. + * + * @example + * + * { + * textTemplate: 'Build started', + * pathsMap: { + * buildid: '$.detail.id' + * } + * } + */ + textTemplate?: any; + + /** + * Input template where you can use the values of the keys from + * inputPathsMap to customize the data sent to the target. Enclose each + * InputPathsMaps value in brackets: + * + * This option is mutually exclusive with `textTemplate`. + * + * @example + * + * { + * jsonTemplate: '{ "commands": }' , + * pathsMap: { + * commandsToRun: '$.detail.commands' + * } + * } + * */ - template?: any; + jsonTemplate?: any; /** * Map of JSON paths to be extracted from the event. These are key-value diff --git a/packages/aws-cdk-events/lib/rule.ts b/packages/aws-cdk-events/lib/rule.ts index eb3a72fbae353..45b372719f9ea 100644 --- a/packages/aws-cdk-events/lib/rule.ts +++ b/packages/aws-cdk-events/lib/rule.ts @@ -1,4 +1,4 @@ -import { Construct, resolve, Token } from 'aws-cdk'; +import { Construct, FnConcat, Token } from 'aws-cdk'; import { events } from 'aws-cdk-resources'; import { EventPattern } from './event-pattern'; import { TargetInputTemplate } from './input-options'; @@ -107,13 +107,37 @@ export class EventRule extends EventRuleRef { this.targets.push({ ...target.eventRuleTarget, - inputTransformer: inputOptions && { - inputTemplate: typeof(inputOptions.template) === 'string' - ? JSON.stringify(inputOptions.template) - : resolve(inputOptions.template), - inputPathsMap: inputOptions.pathsMap - }, + inputTransformer: renderTransformer(), }); + + function renderTransformer(): events.RuleResource.InputTransformerProperty | undefined { + if (!inputOptions) { + return undefined; + } + + if (inputOptions.jsonTemplate && inputOptions.textTemplate) { + throw new Error('"jsonTemplate" and "textTemplate" are mutually exclusive'); + } + + if (!inputOptions.jsonTemplate && !inputOptions.textTemplate) { + throw new Error('One of "jsonTemplate" or "textTemplate" are required'); + } + + let inputTemplate: any; + + if (inputOptions.jsonTemplate) { + inputTemplate = inputOptions.jsonTemplate; + } else if (typeof(inputOptions.textTemplate) === 'string') { + inputTemplate = JSON.stringify(inputOptions.textTemplate); + } else { + inputTemplate = new FnConcat('"', inputOptions.textTemplate, '"'); + } + + return { + inputPathsMap: inputOptions.pathsMap, + inputTemplate + }; + } } /** diff --git a/packages/aws-cdk-events/test/test.rule.ts b/packages/aws-cdk-events/test/test.rule.ts index 4e04f63d61994..aa524f421e585 100644 --- a/packages/aws-cdk-events/test/test.rule.ts +++ b/packages/aws-cdk-events/test/test.rule.ts @@ -1,4 +1,4 @@ -import { App, Arn, FnConcat, Stack } from 'aws-cdk'; +import { App, Arn, FnConcat, FnJoin, Stack } from 'aws-cdk'; import { expect } from 'aws-cdk-assert'; import { iam } from 'aws-cdk-resources'; import { Test } from 'nodeunit'; @@ -158,7 +158,7 @@ export = { }); rule.addTarget(t2, { - template: 'This is ', + textTemplate: 'This is ', pathsMap: { bla: '$.detail.bla' } @@ -201,26 +201,38 @@ export = { 'input template can contain tokens'(test: Test) { const stack = new Stack(); const t1: IEventRuleTarget = { - eventRuleTarget: { - id: 'T1', - arn: new Arn('ARN1'), - kinesisParameters: { partitionKeyPath: 'partitionKeyPath' } - } - }; - const t2: IEventRuleTarget = { - eventRuleTarget: { - id: 'T2', - arn: new Arn('ARN2'), - roleArn: new iam.RoleArn('IAM-ROLE-ARN') - } + eventRuleTarget: { id: 'T1', arn: new Arn('ARN1'), kinesisParameters: { partitionKeyPath: 'partitionKeyPath' } } }; + const t2: IEventRuleTarget = { eventRuleTarget: { id: 'T2', arn: new Arn('ARN2'), roleArn: new iam.RoleArn('IAM-ROLE-ARN') } }; + const t3: IEventRuleTarget = { eventRuleTarget: { id: 'T3', arn: new Arn('ARN3') } }; + const t4: IEventRuleTarget = { eventRuleTarget: { id: 'T4', arn: new Arn('ARN4') } }; + const rule = new EventRule(stack, 'EventRule'); + + // a plain string should just be stringified (i.e. double quotes added and escaped) + rule.addTarget(t2, { + textTemplate: 'Hello, "world"' + }); + + // tokens are used here (FnConcat), but this is a text template so we + // expect it to be wrapped in double quotes automatically for us. rule.addTarget(t1, { - template: new FnConcat('a', 'b') + textTemplate: new FnConcat('a', 'b') }); - rule.addTarget(t2, { - template: 'Hello, world' + + // jsonTemplate can be used to format JSON documents with replacements + rule.addTarget(t3, { + jsonTemplate: '{ "foo": }', + pathsMap: { + bar: '$.detail.bar' + } + }); + + // tokens can also used for JSON templates, but that means escaping is + // the responsibility of the user. + rule.addTarget(t4, { + jsonTemplate: new FnJoin(' ', '"', 'hello', '\"world\"', '"'), }); expect(stack).toMatch({ @@ -228,35 +240,71 @@ export = { "EventRule5A491D2C": { "Type": "AWS::Events::Rule", "Properties": { - "State": "ENABLED", - "Targets": [ - { - "Arn": "ARN1", - "Id": "T1", - "InputTransformer": { - "InputTemplate": { + "State": "ENABLED", + "Targets": [ + { + "Arn": "ARN2", + "Id": "T2", + "InputTransformer": { + "InputTemplate": "\"Hello, \\\"world\\\"\"" + }, + "RoleArn": "IAM-ROLE-ARN" + }, + { + "Arn": "ARN1", + "Id": "T1", + "InputTransformer": { + "InputTemplate": { "Fn::Join": [ "", [ - "a", - "b" + "\"", + { + "Fn::Join": [ + "", + [ + "a", + "b" + ] + ] + }, + "\"" ] ] + } + }, + "KinesisParameters": { + "PartitionKeyPath": "partitionKeyPath" } }, - "KinesisParameters": { - "PartitionKeyPath": "partitionKeyPath" - } - }, - { - "Arn": "ARN2", - "Id": "T2", + { + "Arn": "ARN3", + "Id": "T3", "InputTransformer": { - "InputTemplate": "\"Hello, world\"" - }, - "RoleArn": "IAM-ROLE-ARN" - } - ] + "InputPathsMap": { + "bar": "$.detail.bar" + }, + "InputTemplate": "{ \"foo\": }" + } + }, + { + "Arn": "ARN4", + "Id": "T4", + "InputTransformer": { + "InputTemplate": { + "Fn::Join": [ + " ", + [ + "\"", + "hello", + "\"world\"", + "\"" + ] + ] + } + } + } + ] } } } @@ -264,4 +312,4 @@ export = { test.done(); } -}; \ No newline at end of file +};