From cc0a28cab8eb4c541904feec0c2054549f0320f6 Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Thu, 21 Feb 2019 11:03:01 +0100 Subject: [PATCH] feat(sns): Support raw message delivery (#1827) Adds a handler to enable raw message delivery on SNS topic subscriptions. --- packages/@aws-cdk/aws-sns/lib/subscription.ts | 16 +++- packages/@aws-cdk/aws-sns/lib/topic-base.ts | 23 +++--- packages/@aws-cdk/aws-sns/test/test.sns.ts | 74 +++++++++++++++++++ 3 files changed, 103 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-sns/lib/subscription.ts b/packages/@aws-cdk/aws-sns/lib/subscription.ts index fa4025af23714..f823d4d711347 100644 --- a/packages/@aws-cdk/aws-sns/lib/subscription.ts +++ b/packages/@aws-cdk/aws-sns/lib/subscription.ts @@ -22,6 +22,15 @@ export interface SubscriptionProps { * The topic to subscribe to. */ topic: ITopic; + + /** + * true if raw message delivery is enabled for the subscription. Raw messages are free of JSON formatting and can be + * sent to HTTP/S and Amazon SQS endpoints. For more information, see GetSubscriptionAttributes in the Amazon Simple + * Notification Service API Reference. + * + * @default false + */ + rawMessageDelivery?: boolean; } /** @@ -34,10 +43,15 @@ export class Subscription extends Construct { constructor(scope: Construct, id: string, props: SubscriptionProps) { super(scope, id); + if (props.rawMessageDelivery && ['http', 'https', 'sqs'].indexOf(props.protocol) < 0) { + throw new Error('Raw message delivery can only be enabled for HTTP/S and SQS subscriptions.'); + } + new CfnSubscription(this, 'Resource', { endpoint: props.endpoint, protocol: props.protocol, - topicArn: props.topic.topicArn + topicArn: props.topic.topicArn, + rawMessageDelivery: props.rawMessageDelivery, }); } diff --git a/packages/@aws-cdk/aws-sns/lib/topic-base.ts b/packages/@aws-cdk/aws-sns/lib/topic-base.ts index bc99502e63b54..14c1f21ee86a8 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic-base.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic-base.ts @@ -118,11 +118,12 @@ export abstract class TopicBase extends cdk.Construct implements ITopic { /** * Subscribe some endpoint to this topic */ - public subscribe(name: string, endpoint: string, protocol: SubscriptionProtocol): Subscription { + public subscribe(name: string, endpoint: string, protocol: SubscriptionProtocol, rawMessageDelivery?: boolean): Subscription { return new Subscription(this, name, { topic: this, endpoint, - protocol + protocol, + rawMessageDelivery, }); } @@ -134,8 +135,9 @@ export abstract class TopicBase extends cdk.Construct implements ITopic { * * @param name The subscription name * @param queue The target queue + * @param rawMessageDelivery Enable raw message delivery */ - public subscribeQueue(queue: sqs.IQueue): Subscription { + public subscribeQueue(queue: sqs.IQueue, rawMessageDelivery?: boolean): Subscription { if (!cdk.Construct.isConstruct(queue)) { throw new Error(`The supplied Queue object must be an instance of Construct`); } @@ -151,7 +153,8 @@ export abstract class TopicBase extends cdk.Construct implements ITopic { const sub = new Subscription(queue, subscriptionName, { topic: this, endpoint: queue.queueArn, - protocol: SubscriptionProtocol.Sqs + protocol: SubscriptionProtocol.Sqs, + rawMessageDelivery, }); // add a statement to the queue resource policy which allows this topic @@ -190,7 +193,7 @@ export abstract class TopicBase extends cdk.Construct implements ITopic { const sub = new Subscription(lambdaFunction, subscriptionName, { topic: this, endpoint: lambdaFunction.functionArn, - protocol: SubscriptionProtocol.Lambda + protocol: SubscriptionProtocol.Lambda, }); lambdaFunction.addPermission(this.node.id, { @@ -206,7 +209,7 @@ export abstract class TopicBase extends cdk.Construct implements ITopic { * * @param name A name for the subscription * @param emailAddress The email address to use. - * @param jsonFormat True if the email content should be in JSON format (default is false). + * @param options Options for the email delivery format. */ public subscribeEmail(name: string, emailAddress: string, options?: EmailSubscriptionOptions): Subscription { const protocol = (options && options.json ? SubscriptionProtocol.EmailJson : SubscriptionProtocol.Email); @@ -223,8 +226,9 @@ export abstract class TopicBase extends cdk.Construct implements ITopic { * * @param name A name for the subscription * @param url The URL to invoke + * @param rawMessageDelivery Enable raw message delivery */ - public subscribeUrl(name: string, url: string): Subscription { + public subscribeUrl(name: string, url: string, rawMessageDelivery?: boolean): Subscription { if (!url.startsWith('http://') && !url.startsWith('https://')) { throw new Error('URL must start with either http:// or https://'); } @@ -234,7 +238,8 @@ export abstract class TopicBase extends cdk.Construct implements ITopic { return new Subscription(this, name, { topic: this, endpoint: url, - protocol + protocol, + rawMessageDelivery, }); } @@ -353,5 +358,5 @@ export interface EmailSubscriptionOptions { * * @default Message text (false) */ - json?: boolean + json?: boolean; } diff --git a/packages/@aws-cdk/aws-sns/test/test.sns.ts b/packages/@aws-cdk/aws-sns/test/test.sns.ts index bcbf6398d3bbe..44cd9c15d3a94 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -128,6 +128,40 @@ export = { test.done(); }, + 'url subscription (with raw delivery)'(test: Test) { + const stack = new cdk.Stack(); + + const topic = new sns.Topic(stack, 'MyTopic', { + topicName: 'topicName', + displayName: 'displayName' + }); + + topic.subscribeUrl('appsubscription', 'https://foobar.com/', true); + + expect(stack).toMatch({ + "Resources": { + "MyTopic86869434": { + "Type": "AWS::SNS::Topic", + "Properties": { + "DisplayName": "displayName", + "TopicName": "topicName" + } + }, + "MyTopicappsubscription00FA69EA": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Endpoint": "https://foobar.com/", + "Protocol": "https", + "TopicArn": { "Ref": "MyTopic86869434" }, + "RawMessageDelivery": true + } + } + } + }); + + test.done(); + }, + 'queue subscription'(test: Test) { const stack = new cdk.Stack(); @@ -208,6 +242,35 @@ export = { test.done(); }, + 'queue subscription (with raw delivery)'(test: Test) { + const stack = new cdk.Stack(); + + const topic = new sns.Topic(stack, 'MyTopic', { + topicName: 'topicName', + displayName: 'displayName' + }); + + const queue = new sqs.Queue(stack, 'MyQueue'); + + topic.subscribeQueue(queue, true); + + expect(stack).to(haveResource('AWS::SNS::Subscription', { + "Endpoint": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + }, + "Protocol": "sqs", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "RawMessageDelivery": true + })); + + test.done(); + }, + 'lambda subscription'(test: Test) { const stack = new cdk.Stack(); @@ -504,6 +567,17 @@ export = { test.done(); }, + + 'invalid use of raw message delivery'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const topic = new sns.Topic(stack, 'Topic'); + + // THEN + test.throws(() => topic.subscribe('Nope', 'endpoint://location', sns.SubscriptionProtocol.Application, true), + /Raw message delivery can only be enabled for HTTP\/S and SQS subscriptions/); + test.done(); + } }, 'can add a policy to the topic'(test: Test) {