Skip to content

Commit

Permalink
Merge 50d7b7c into ecdd175
Browse files Browse the repository at this point in the history
  • Loading branch information
jthomerson committed Jan 26, 2017
2 parents ecdd175 + 50d7b7c commit 469a7d3
Show file tree
Hide file tree
Showing 4 changed files with 396 additions and 34 deletions.
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,8 @@ functions:
By doing that, the `deploy` and `remove` commands in SLS will now subscribe and
unsubscribe your function to or from the specified topic. If you would like to
subscribe or unsubscribe the functions manually (outside of a deploy or remove
command), you can use `sls subscribeExternalSNS` or
`sls unsubscribeExternalSNS`.

**NOTE:** This plugin requires the AWS SDK (`require('aws-sdk')`), but does not
list it as a dependency. This allows you to provide the dependency your own
way, making sure not to bundle the SDK and all its dependencies with your
function code.
command), you can use `serverless subscribeExternalSNS` or
`serverless unsubscribeExternalSNS`.


## How do I contribute?
Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@
"homepage": "https://github.com/silvermine/serverless-plugin-external-sns-events#readme",
"dependencies": {
"class.extend": "0.9.2",
"q": "1.4.1",
"underscore": "1.8.3"
},
"devDependencies": {
"aws-sdk": "2.6.9",
"coveralls": "2.11.12",
"eslint": "3.3.1",
"eslint-config-silvermine": "1.0.0",
Expand All @@ -39,6 +37,7 @@
"istanbul": "0.4.4",
"mocha": "3.0.2",
"mocha-lcov-reporter": "1.2.0",
"sinon": "1.17.5"
"sinon": "1.17.5",
"q": "1.4.1"
}
}
59 changes: 37 additions & 22 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
'use strict';

var _ = require('underscore'),
Q = require('q'),
AWS = require('aws-sdk'),
Class = require('class.extend');

module.exports = Class.extend({

init: function(serverless, opts) {
this._serverless = serverless;
this._opts = opts;
this.provider = serverless ? serverless.getProvider('aws') : null;

this.hooks = {
'deploy:compileEvents': this._loopEvents.bind(this, this.addEventPermission),
Expand All @@ -21,9 +20,11 @@ module.exports = Class.extend({

this.commands = {
subscribeExternalSNS: {
usage: 'Adds subscriptions to any SNS Topics defined by externalSNS.',
lifecycleEvents: [ 'subscribe' ],
},
unsubscribeExternalSNS: {
usage: 'Removes subscriptions from any SNS Topics defined by externalSNS.',
lifecycleEvents: [ 'unsubscribe' ],
},
};
Expand Down Expand Up @@ -54,66 +55,78 @@ module.exports = Class.extend({
FunctionName: { 'Fn::GetAtt': [ fnRef, 'Arn' ] },
Action: 'lambda:InvokeFunction',
Principal: 'sns.amazonaws.com',
SourceArn: { 'Fn::Join': [ ':', [ 'arn:aws:sns', { 'Ref': 'AWS::Region' }, { 'Ref': 'AWS::AccountId' }, topicName ] ] }
},
};

this._serverless.service.provider.compiledCloudFormationTemplate.Resources[permRef] = permission;
},

subscribeFunction: function(fnName, fnDef, topicName) {
var self = this,
sns = new AWS.SNS();
var self = this;

if (this._opts.noDeploy) {
return this._serverless.cli.log(
this._serverless.cli.log(
'Not subscribing ' + fnDef.name + ' to ' + topicName + ' because of the noDeploy flag'
);
return;
}

this._serverless.cli.log('Need to subscribe ' + fnDef.name + ' to ' + topicName);

return this._getSubscriptionInfo(fnName, fnDef, topicName)
return this._getSubscriptionInfo(fnDef, topicName)
.then(function(info) {
var params;

if (info.SubscriptionArn) {
return self._serverless.cli.log('Function ' + info.FunctionArn + ' is already subscribed to ' + info.TopicArn);
self._serverless.cli.log('Function ' + info.FunctionArn + ' is already subscribed to ' + info.TopicArn);
return;
}

return Q.ninvoke(sns, 'subscribe', { TopicArn: info.TopicArn, Protocol: 'lambda', Endpoint: info.FunctionArn })
params = {
TopicArn: info.TopicArn,
Protocol: 'lambda',
Endpoint: info.FunctionArn,
};
return self.provider.request('SNS', 'subscribe', params, self._opts.stage, self._opts.region)
.then(function() {
return self._serverless.cli.log('Function ' + info.FunctionArn + ' is now subscribed to ' + info.TopicArn);
self._serverless.cli.log('Function ' + info.FunctionArn + ' is now subscribed to ' + info.TopicArn);
return;
});
});
},

unsubscribeFunction: function(fnName, fnDef, topicName) {
var self = this,
sns = new AWS.SNS();
var self = this;

this._serverless.cli.log('Need to unsubscribe ' + fnDef.name + ' from ' + topicName);

return this._getSubscriptionInfo(fnName, fnDef, topicName)
return this._getSubscriptionInfo(fnDef, topicName)
.then(function(info) {
var params = { SubscriptionArn: info.SubscriptionArn };

if (!info.SubscriptionArn) {
return self._serverless.cli.log('Function ' + info.FunctionArn + ' is not subscribed to ' + info.TopicArn);
self._serverless.cli.log('Function ' + info.FunctionArn + ' is not subscribed to ' + info.TopicArn);
return;
}

return Q.ninvoke(sns, 'unsubscribe', { SubscriptionArn: info.SubscriptionArn })
return self.provider.request('SNS', 'unsubscribe', params, self._opts.stage, self._opts.region)
.then(function() {
return self._serverless.cli.log(
self._serverless.cli.log(
'Function ' + info.FunctionArn + ' is no longer subscribed to ' + info.TopicArn +
' (deleted ' + info.SubscriptionArn + ')'
);
return;
});
});
},

_getSubscriptionInfo: function(fnName, fnDef, topicName) {
_getSubscriptionInfo: function(fnDef, topicName) {
var self = this,
sns = new AWS.SNS(),
lambda = new AWS.Lambda(),
fnArn, acctID, region, topicArn;
fnArn, acctID, region, topicArn, params;

params = { FunctionName: fnDef.name };

return Q.ninvoke(lambda, 'getFunction', { FunctionName: fnDef.name })
return this.provider.request('Lambda', 'getFunction', params, this._opts.stage, this._opts.region)
.then(function(fn) {
fnArn = fn.Configuration.FunctionArn;
// NOTE: assumes that the topic is in the same account and region at this point
Expand All @@ -125,7 +138,9 @@ module.exports = Class.extend({
self._serverless.cli.log('Topic ARN: ' + topicArn);

// NOTE: does not support NextToken and paginating through subscriptions at this point
return Q.ninvoke(sns, 'listSubscriptionsByTopic', { TopicArn: topicArn });
return self.provider.request('SNS', 'listSubscriptionsByTopic',
{ TopicArn: topicArn }, self._opts.stage, self._opts.region
);
})
.then(function(resp) {
var existing = _.findWhere(resp.Subscriptions, { Protocol: 'lambda', Endpoint: fnArn }) || {};
Expand Down

0 comments on commit 469a7d3

Please sign in to comment.