Repository: HyperBrain/serverless-aws-alias (js)

Issue:
Subscribe to existing SNS topic
I am trying to subscribe to an existing SNS topic with the serverless-aws-alias plugin installed. When using an existing SNS topic it doesn't look like the subscription is actually created. I was able to replicate this using serverless 1.25 and serverless-aws-alias 1.5.1.

When navigating to the lambda alias and looking at the SNS trigger a message is shown on the bottom stating

"A subscription for **arn** on the topic **topic name** could not be found". The console gives an option to fix or delete.

Under CloudFormation resources I do not see an AWS::SNS::Subscription defined either. 

It does work as expected with the alias plugin commented out in serverless.yml

Pull request description:
Added support to have an alias subscribe to an existing SNS topic #94
Closes #94 

This PR adds support for resources of type AWS::SNS::Subscription to be transferred to the alias stack. This resource will be present in the cloudformation template when a lambda subscribes to an existing SNS topic by ARN.



Patch:
diff --git a/lib/stackops/snsEvents.js b/lib/stackops/snsEvents.js
index 9cf626b..9f92316 100644
--- a/lib/stackops/snsEvents.js
+++ b/lib/stackops/snsEvents.js
@@ -46,6 +46,23 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
 		delete stageStack.Resources[name];
 	});
 
+	const snsSubscriptions =
+		_.assign({},
+			_.pickBy(stageStack.Resources, [ 'Type', 'AWS::SNS::Subscription' ]));
+
+	_.forOwn(snsSubscriptions, (subscription, name) => {
+
+		const functionNameRef = utils.findAllReferences(_.get(subscription.Properties, 'Endpoint'));
+		const functionName = _.replace(_.get(functionNameRef, '[0].ref', ''), /LambdaFunction$/, '');
+		const versionName = _.find(_.keys(versions), version => _.startsWith(version, functionName));
+		const aliasName = _.find(_.keys(aliases), alias => _.startsWith(alias, functionName));
+
+		subscription.Properties.Endpoint = { Ref: aliasName };
+		subscription.DependsOn = [ versionName, aliasName ];
+
+		delete stageStack.Resources[name];
+	});
+
 	// Fetch lambda permissions. These have to be updated later to allow the aliased functions.
 	const snsLambdaPermissions =
 			_.assign({},
@@ -71,6 +88,7 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
 
 	// Add all alias stack owned resources
 	aliasResources.push(snsTopics);
+	aliasResources.push(snsSubscriptions);
 	aliasResources.push(snsLambdaPermissions);
 
 	_.forEach(aliasResources, resource => _.assign(aliasStack.Resources, resource));


Test patch:
diff --git a/test/data/sns-stack.json b/test/data/sns-stack.json
index 55ebbd4..eb7c18a 100644
--- a/test/data/sns-stack.json
+++ b/test/data/sns-stack.json
@@ -250,6 +250,24 @@
         ]
       }
     },
+    "SNSTopicSubscriptionSlstestprojecttopic": {
+      "Type" : "AWS::SNS::Subscription",
+      "Properties": {
+        "Endpoint": {
+          "Fn::GetAtt": [
+            "Testfct1LambdaFunction",
+            "Arn"
+          ]
+        },
+        "Protocol": "lambda",
+        "TopicArn": {
+          "Fn::GetAtt": [
+            "SNSTopicSlstestprojecttopic",
+            "Arn"
+          ]
+        }
+      }
+    },
     "Testfct1LambdaPermissionSlstestprojecttopicSNS": {
       "Type": "AWS::Lambda::Permission",
       "Properties": {
diff --git a/test/stackops/snsEvents.test.js b/test/stackops/snsEvents.test.js
index 499be45..7321d1c 100644
--- a/test/stackops/snsEvents.test.js
+++ b/test/stackops/snsEvents.test.js
@@ -75,8 +75,10 @@ describe('SNS Events', () => {
 			return expect(awsAlias.aliasHandleSNSEvents({}, [], {})).to.be.fulfilled
 			.then(() => BbPromise.all([
 				expect(snsStack).to.not.have.a.nested.property('Resources.SNSTopicSlstestprojecttopic'),
+				expect(snsStack).to.not.have.a.nested.property('Resources.SNSTopicSubscriptionSlstestprojecttopic'),
 				expect(snsStack).to.not.have.a.nested.property('Resources.Testfct1LambdaPermissionSlstestprojecttopicSNS'),
 				expect(aliasStack).to.have.a.nested.property('Resources.SNSTopicSlstestprojecttopic'),
+				expect(aliasStack).to.have.a.nested.property('Resources.SNSTopicSubscriptionSlstestprojecttopic'),
 				expect(aliasStack).to.have.a.nested.property('Resources.Testfct1LambdaPermissionSlstestprojecttopicSNS'),
 			]));
 		});