diff --git a/docs/04-extending-serverless/01-creating-plugins.md b/docs/04-extending-serverless/01-creating-plugins.md index 867ecb251..3c052ce5e 100644 --- a/docs/04-extending-serverless/01-creating-plugins.md +++ b/docs/04-extending-serverless/01-creating-plugins.md @@ -362,7 +362,11 @@ Plugins can be provider specific which means that they are bound to a provider. **Note:** Binding a plugin to a provider is optional. Serverless will always consider your plugin if you don't specify a `provider`. -The provider definition should be added inside the plugins constructor: +The provider definition should be added inside the plugins constructor and can be the name of the provider (as a string) or the provider plugin instance. + +### String representation + +The string representation simply tells Serverless that this plugin should only be loaded if the provider defined in the service matches the one of the plugin. ```javascript 'use strict'; @@ -402,7 +406,52 @@ class ProviderDeploy { module.exports = ProviderDeploy; ``` -The plugins functionality will now only be executed when the Serverless services provider matches the provider name which is defined inside the plugins constructor. +### The provider plugin instance + +If you want to get access to provider specific utilities such as the SDK you can get the provider plugin instance with the help of the +`this.serverless.getProvider('providerName')` function and set it to the plugins `provider` property. +You can access the wrapped methods via `this.provider.`. + +The usage of the plugins `provider` property will load the plugin only if the `provider` matches the one defined in the service. + +```javascript +'use strict'; + +class ProviderInvoke { + constructor(serverless, options) { + this.serverless = serverless; + this.options = options; + + // get access to the provider plugin instance here + this.provider = this.serverless.getProvider('providerName'); + + this.commands = { + invoke: { + lifecycleEvents: [ + 'function' + ], + options: { + function: { + usage: 'Specify the function you want to invoke (e.g. "--function myFunction")', + required: true + } + } + }, + }; + + this.hooks = { + 'invoke:function': this.invokeFunction.bind(this) + } + } + + invokeFunction() { + // use the "request" method from the provider plugin + return this.provider.request('function', 'invoke', this.options.function); + } +} + +module.exports = ProviderInvoke; +``` ## Plugin registration process diff --git a/docs/04-extending-serverless/02-creating-provider-plugins.md b/docs/04-extending-serverless/02-creating-provider-plugins.md index 6288424a8..ca11ca0f4 100644 --- a/docs/04-extending-serverless/02-creating-provider-plugins.md +++ b/docs/04-extending-serverless/02-creating-provider-plugins.md @@ -9,6 +9,38 @@ layout: Doc Integrating different infrastructure providers happens through the standard plugin system. Take a look at the ["building plugins"](./01-creating-plugins.md) documentation to understand how the plugin system works. +## Provider plugins + +We'd recommend that you encapsulate provider related utilities into an own plugin and call it `Provider`. +You can register this provider plugin in Serverless with the help of the `serverless.setProvider(, )` function. +Furthermore your provider plugin needs to provide a static method which returns the providers name. + +Here's a simple skeleton which shows this in detail and can be used as a boilerplate to get started: + +```javascript +class MyProvider { + static getProviderName() { + return 'providerName'; + } + + constructor(serverless) { + this.serverless = serverless; + this.sdk = TheProvidersSDK; + this.provider = this; // only load plugin in a "providerName" service context + this.serverless.setProvider('providerName', this); + } + + // this method can be easily re-used by other plugins + useSDK(command) { + return this.sdk(command); + } +} + +module.exports = MyProvider; +``` + +After doing this other plugins can simply call `this.serverless.getProvider('providerName')` and access all the methods you've defined in your provider plugin. + ## Provider specific plugins You can add the providers name inside the constructor of your plugin. This makes it possible to only execute your plugins logic when the Serverless service uses the provider you've specified in your plugin. diff --git a/lib/Serverless.js b/lib/Serverless.js index cd4fa678e..61675bad9 100644 --- a/lib/Serverless.js +++ b/lib/Serverless.js @@ -20,6 +20,8 @@ class Serverless { let configObject = config; configObject = configObject || {}; + this.providers = {}; + this.version = Version; this.yamlParser = new YamlParser(this); @@ -58,10 +60,6 @@ class Serverless { return this.service.load(this.processedInput.options) .then(() => { - // set the provider of the service (so that the PluginManager takes care to - // execute the correct provider specific plugins) - this.pluginManager.setProvider(this.service.provider.name); - // load all plugins this.pluginManager.loadAllPlugins(this.service.plugins); @@ -90,6 +88,14 @@ class Serverless { return this.pluginManager.run(this.processedInput.commands); } + setProvider(name, provider) { + this.providers[name] = provider; + } + + getProvider(name) { + return this.providers[name] ? this.providers[name] : false; + } + getVersion() { return this.version; } diff --git a/tests/classes/Serverless.js b/lib/Serverless.test.js similarity index 83% rename from tests/classes/Serverless.js rename to lib/Serverless.test.js index c37ae03db..3d9f44ede 100644 --- a/tests/classes/Serverless.js +++ b/lib/Serverless.test.js @@ -1,18 +1,18 @@ 'use strict'; const expect = require('chai').expect; -const Serverless = require('../../lib/Serverless'); +const Serverless = require('./Serverless'); const semverRegex = require('semver-regex'); const path = require('path'); const YAML = require('js-yaml'); -const YamlParser = require('../../lib/classes/YamlParser'); -const PluginManager = require('../../lib/classes/PluginManager'); -const Utils = require('../../lib/classes/Utils'); -const Service = require('../../lib/classes/Service'); -const CLI = require('../../lib/classes/CLI'); -const Error = require('../../lib/classes/Error').SError; -const testUtils = require('../../tests/utils'); +const YamlParser = require('../lib/classes/YamlParser'); +const PluginManager = require('../lib/classes/PluginManager'); +const Utils = require('../lib/classes/Utils'); +const Service = require('../lib/classes/Service'); +const CLI = require('../lib/classes/CLI'); +const Error = require('../lib/classes/Error').SError; +const testUtils = require('../tests/utils'); describe('Serverless', () => { let serverless; @@ -37,6 +37,10 @@ describe('Serverless', () => { expect(Object.keys(serverless.config)).to.include('servicePath'); }); + it('should set an empty providers object', () => { + expect(serverless.providers).to.deep.equal({}); + }); + it('should set the Serverless version', () => { expect(serverless.version.length).to.be.at.least(1); }); @@ -106,7 +110,7 @@ describe('Serverless', () => { describe('#init()', () => { it('should create a new CLI instance', () => { serverless.init(); - expect(serverless.cli).to.be.instanceOf(CLI); + expect(serverless.cli).to.be.instanceof(CLI); }); // note: we just test that the processedInput variable is set (not the content of it) @@ -194,6 +198,34 @@ describe('Serverless', () => { }); }); + describe('#setProvider()', () => { + class ProviderMock {} + + it('should set the provider object in the provider object', () => { + const myProvider = new ProviderMock(); + + serverless.setProvider('myProvider', myProvider); + + expect(serverless.providers.myProvider).to.equal(myProvider); + }); + }); + + describe('#getProvider()', () => { + class ProviderMock {} + let myProvider; + + beforeEach(() => { + myProvider = new ProviderMock(); + serverless.setProvider('myProvider', myProvider); + }); + + it('should return the provider object', () => { + const retrivedProvider = serverless.getProvider('myProvider'); + + expect(retrivedProvider).to.deep.equal(myProvider); + }); + }); + describe('#getVersion()', () => { it('should get the correct Serverless version', () => { expect(semverRegex().test(serverless.getVersion())).to.equal(true); diff --git a/tests/classes/CLI.js b/lib/classes/CLI.test.js similarity index 100% rename from tests/classes/CLI.js rename to lib/classes/CLI.test.js diff --git a/tests/classes/Config.js b/lib/classes/Config.test.js similarity index 100% rename from tests/classes/Config.js rename to lib/classes/Config.test.js diff --git a/lib/classes/PluginManager.js b/lib/classes/PluginManager.js index ef436481c..27bec57c0 100644 --- a/lib/classes/PluginManager.js +++ b/lib/classes/PluginManager.js @@ -7,7 +7,6 @@ const _ = require('lodash'); class PluginManager { constructor(serverless) { this.serverless = serverless; - this.provider = null; this.cliOptions = {}; this.cliCommands = []; @@ -17,10 +16,6 @@ class PluginManager { this.hooks = {}; } - setProvider(provider) { - this.provider = provider; - } - setCliOptions(options) { this.cliOptions = options; } @@ -32,8 +27,19 @@ class PluginManager { addPlugin(Plugin) { const pluginInstance = new Plugin(this.serverless, this.cliOptions); + let pluginProvider = null; + // check if plugin is provider agnostic + if (pluginInstance.provider) { + if (typeof pluginInstance.provider === 'string') { + pluginProvider = pluginInstance.provider; + } else if (typeof pluginInstance.provider === 'object') { + pluginProvider = pluginInstance.provider.constructor.getProviderName(); + } + } + // ignore plugins that specify a different provider than the current one - if (pluginInstance.provider && (pluginInstance.provider !== this.provider)) { + if (pluginProvider + && (pluginProvider !== this.serverless.service.provider.name)) { return; } diff --git a/tests/classes/PluginManager.js b/lib/classes/PluginManager.test.js similarity index 93% rename from tests/classes/PluginManager.js rename to lib/classes/PluginManager.test.js index bd51119fc..aaa49c08e 100644 --- a/tests/classes/PluginManager.js +++ b/lib/classes/PluginManager.test.js @@ -198,10 +198,6 @@ describe('PluginManager', () => { expect(pluginManager.serverless).to.deep.equal(serverless); }); - it('should create a nullified provider variable', () => { - expect(pluginManager.provider).to.equal(null); - }); - it('should create an empty cliOptions object', () => { expect(pluginManager.cliOptions).to.deep.equal({}); }); @@ -219,15 +215,6 @@ describe('PluginManager', () => { }); }); - describe('#setProvider()', () => { - it('should set the provider variable', () => { - const provider = 'provider1'; - pluginManager.setProvider(provider); - - expect(pluginManager.provider).to.equal(provider); - }); - }); - describe('#setCliOptions()', () => { it('should set the cliOptions object', () => { const options = { foo: 'bar' }; @@ -286,7 +273,7 @@ describe('PluginManager', () => { it('should add a plugin instance to the plugins array', () => { pluginManager.addPlugin(SynchronousPluginMock); - expect(pluginManager.plugins[0]).to.be.an.instanceof(SynchronousPluginMock); + expect(pluginManager.plugins[0]).to.be.instanceof(SynchronousPluginMock); }); it('should load the plugin commands', () => { @@ -294,6 +281,51 @@ describe('PluginManager', () => { expect(pluginManager.commands).to.have.property('deploy'); }); + + it('should skip service related plugins which not match the services provider', () => { + pluginManager.serverless.service.provider.name = 'someProvider'; + class Plugin { + constructor() { + this.provider = 'someOtherProvider'; + } + } + + pluginManager.addPlugin(Plugin); + + expect(pluginManager.plugins.length).to.equal(0); + }); + + it('should add service related plugins when provider property is the providers name', () => { + pluginManager.serverless.service.provider.name = 'someProvider'; + class Plugin { + constructor() { + this.provider = 'someProvider'; + } + } + + pluginManager.addPlugin(Plugin); + + expect(pluginManager.plugins[0]).to.be.an.instanceOf(Plugin); + }); + + it('should add service related plugins when provider propery is provider plugin', () => { + pluginManager.serverless.service.provider.name = 'someProvider'; + class ProviderPlugin { + static getProviderName() { + return 'someProvider'; + } + } + const providerPlugin = new ProviderPlugin(); + class Plugin { + constructor() { + this.provider = providerPlugin; + } + } + + pluginManager.addPlugin(Plugin); + + expect(pluginManager.plugins[0]).to.be.an.instanceOf(Plugin); + }); }); describe('#loadAllPlugins()', () => { @@ -670,7 +702,7 @@ describe('PluginManager', () => { describe('when using provider specific plugins', () => { beforeEach(function () { // eslint-disable-line prefer-arrow-callback - pluginManager.setProvider('provider1'); + pluginManager.serverless.service.provider.name = 'provider1'; pluginManager.addPlugin(Provider1PluginMock); pluginManager.addPlugin(Provider2PluginMock); diff --git a/tests/classes/Service.js b/lib/classes/Service.test.js similarity index 100% rename from tests/classes/Service.js rename to lib/classes/Service.test.js diff --git a/tests/classes/Utils.js b/lib/classes/Utils.test.js similarity index 100% rename from tests/classes/Utils.js rename to lib/classes/Utils.test.js diff --git a/tests/classes/Variables.js b/lib/classes/Variables.test.js similarity index 99% rename from tests/classes/Variables.js rename to lib/classes/Variables.test.js index 15e303920..bfef5ecd2 100644 --- a/tests/classes/Variables.js +++ b/lib/classes/Variables.test.js @@ -7,7 +7,7 @@ const Variables = require('../../lib/classes/Variables'); const Utils = require('../../lib/classes/Utils'); const Serverless = require('../../lib/Serverless'); const sinon = require('sinon'); -const testUtils = require('../utils'); +const testUtils = require('../../tests/utils'); describe('Variables', () => { describe('#constructor()', () => { diff --git a/tests/classes/YamlParser.js b/lib/classes/YamlParser.test.js similarity index 100% rename from tests/classes/YamlParser.js rename to lib/classes/YamlParser.test.js diff --git a/lib/plugins/Plugins.json b/lib/plugins/Plugins.json index a5c6bf6f9..d213b41e7 100644 --- a/lib/plugins/Plugins.json +++ b/lib/plugins/Plugins.json @@ -9,6 +9,7 @@ "./logs/logs.js", "./remove/remove.js", "./slstats/slstats.js", + "./aws/provider/awsProvider.js", "./aws/deploy/index.js", "./aws/invoke/index.js", "./aws/info/index.js", diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/index.js b/lib/plugins/aws/deploy/compile/events/apiGateway/index.js index ccf2db649..26f7c7fcf 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/index.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/index.js @@ -16,7 +16,7 @@ class AwsCompileApigEvents { constructor(serverless, options) { this.serverless = serverless; this.options = options; - this.provider = 'aws'; + this.provider = this.serverless.getProvider('aws'); Object.assign( this, diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/index.js b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/index.js index 4c7301b28..b4dc4183e 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/index.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/index.js @@ -3,6 +3,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); const BbPromise = require('bluebird'); +const AwsProvider = require('../../../../../provider/awsProvider'); const AwsCompileApigEvents = require('../index'); const Serverless = require('../../../../../../../Serverless'); @@ -39,7 +40,7 @@ describe('AwsCompileApigEvents', () => { stage: 'dev', region: 'us-east-1', }; - + serverless.setProvider('aws', new AwsProvider(serverless)); const awsCompileApigEvents = new AwsCompileApigEvents(serverless, options); describe('#constructor()', () => { @@ -47,8 +48,8 @@ describe('AwsCompileApigEvents', () => { it('should have hooks', () => expect(awsCompileApigEvents.hooks).to.be.not.empty); - it('should set the provider variable to "aws"', () => expect(awsCompileApigEvents.provider) - .to.equal('aws')); + it('should set the provider variable to be an instanceof AwsProvider', () => + expect(awsCompileApigEvents.provider).to.be.instanceof(AwsProvider)); it('should run promise chain in order', () => { const validateStub = sinon diff --git a/lib/plugins/aws/deploy/compile/events/s3/index.js b/lib/plugins/aws/deploy/compile/events/s3/index.js index fc849c786..11c8bd76f 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/index.js +++ b/lib/plugins/aws/deploy/compile/events/s3/index.js @@ -5,7 +5,7 @@ const _ = require('lodash'); class AwsCompileS3Events { constructor(serverless) { this.serverless = serverless; - this.provider = 'aws'; + this.provider = this.serverless.getProvider('aws'); this.hooks = { 'deploy:compileEvents': this.compileS3Events.bind(this), diff --git a/lib/plugins/aws/deploy/compile/events/s3/tests/index.js b/lib/plugins/aws/deploy/compile/events/s3/tests/index.js index 9c504d946..cf33a0a29 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/tests/index.js +++ b/lib/plugins/aws/deploy/compile/events/s3/tests/index.js @@ -1,6 +1,7 @@ 'use strict'; const expect = require('chai').expect; +const AwsProvider = require('../../../../../provider/awsProvider'); const AwsCompileS3Events = require('../index'); const Serverless = require('../../../../../../../Serverless'); @@ -11,13 +12,14 @@ describe('AwsCompileS3Events', () => { beforeEach(() => { serverless = new Serverless(); serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} }; + serverless.setProvider('aws', new AwsProvider(serverless)); awsCompileS3Events = new AwsCompileS3Events(serverless); awsCompileS3Events.serverless.service.service = 'new-service'; }); describe('#constructor()', () => { - it('should set the provider variable to "aws"', () => expect(awsCompileS3Events.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsCompileS3Events.provider).to.be.instanceof(AwsProvider)); }); describe('#compileS3Events()', () => { diff --git a/lib/plugins/aws/deploy/compile/events/schedule/index.js b/lib/plugins/aws/deploy/compile/events/schedule/index.js index 5e06de37c..b5f72782b 100644 --- a/lib/plugins/aws/deploy/compile/events/schedule/index.js +++ b/lib/plugins/aws/deploy/compile/events/schedule/index.js @@ -5,7 +5,7 @@ const _ = require('lodash'); class AwsCompileScheduledEvents { constructor(serverless) { this.serverless = serverless; - this.provider = 'aws'; + this.provider = this.serverless.getProvider('aws'); this.hooks = { 'deploy:compileEvents': this.compileScheduledEvents.bind(this), diff --git a/lib/plugins/aws/deploy/compile/events/schedule/tests/index.js b/lib/plugins/aws/deploy/compile/events/schedule/tests/index.js index e71842dfa..633435650 100644 --- a/lib/plugins/aws/deploy/compile/events/schedule/tests/index.js +++ b/lib/plugins/aws/deploy/compile/events/schedule/tests/index.js @@ -1,6 +1,7 @@ 'use strict'; const expect = require('chai').expect; +const AwsProvider = require('../../../../../provider/awsProvider'); const AwsCompileScheduledEvents = require('../index'); const Serverless = require('../../../../../../../Serverless'); @@ -11,13 +12,14 @@ describe('AwsCompileScheduledEvents', () => { beforeEach(() => { serverless = new Serverless(); serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} }; + serverless.setProvider('aws', new AwsProvider(serverless)); awsCompileScheduledEvents = new AwsCompileScheduledEvents(serverless); awsCompileScheduledEvents.serverless.service.service = 'new-service'; }); describe('#constructor()', () => { - it('should set the provider variable to "aws"', () => expect(awsCompileScheduledEvents.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsCompileScheduledEvents.provider).to.be.instanceof(AwsProvider)); }); describe('#compileScheduledEvents()', () => { diff --git a/lib/plugins/aws/deploy/compile/events/sns/index.js b/lib/plugins/aws/deploy/compile/events/sns/index.js index c935bebab..4576949b7 100644 --- a/lib/plugins/aws/deploy/compile/events/sns/index.js +++ b/lib/plugins/aws/deploy/compile/events/sns/index.js @@ -5,7 +5,7 @@ const _ = require('lodash'); class AwsCompileSNSEvents { constructor(serverless) { this.serverless = serverless; - this.provider = 'aws'; + this.provider = this.serverless.getProvider('aws'); this.hooks = { 'deploy:compileEvents': this.compileSNSEvents.bind(this), diff --git a/lib/plugins/aws/deploy/compile/events/sns/tests/index.js b/lib/plugins/aws/deploy/compile/events/sns/tests/index.js index f9021c06c..a0807480d 100644 --- a/lib/plugins/aws/deploy/compile/events/sns/tests/index.js +++ b/lib/plugins/aws/deploy/compile/events/sns/tests/index.js @@ -1,6 +1,7 @@ 'use strict'; const expect = require('chai').expect; +const AwsProvider = require('../../../../../provider/awsProvider'); const AwsCompileSNSEvents = require('../index'); const Serverless = require('../../../../../../../Serverless'); @@ -11,12 +12,13 @@ describe('AwsCompileSNSEvents', () => { beforeEach(() => { serverless = new Serverless(); serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} }; + serverless.setProvider('aws', new AwsProvider(serverless)); awsCompileSNSEvents = new AwsCompileSNSEvents(serverless); }); describe('#constructor()', () => { - it('should set the provider variable to "aws"', () => expect(awsCompileSNSEvents.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsCompileSNSEvents.provider).to.be.instanceof(AwsProvider)); }); describe('#compileSNSEvents()', () => { diff --git a/lib/plugins/aws/deploy/compile/events/stream/index.js b/lib/plugins/aws/deploy/compile/events/stream/index.js index 861103da5..7f4378a0f 100644 --- a/lib/plugins/aws/deploy/compile/events/stream/index.js +++ b/lib/plugins/aws/deploy/compile/events/stream/index.js @@ -5,7 +5,7 @@ const _ = require('lodash'); class AwsCompileStreamEvents { constructor(serverless) { this.serverless = serverless; - this.provider = 'aws'; + this.provider = this.serverless.getProvider('aws'); this.hooks = { 'deploy:compileEvents': this.compileStreamEvents.bind(this), diff --git a/lib/plugins/aws/deploy/compile/events/stream/tests/index.js b/lib/plugins/aws/deploy/compile/events/stream/tests/index.js index 0dcabb175..9b1f03ac9 100644 --- a/lib/plugins/aws/deploy/compile/events/stream/tests/index.js +++ b/lib/plugins/aws/deploy/compile/events/stream/tests/index.js @@ -1,6 +1,7 @@ 'use strict'; const expect = require('chai').expect; +const AwsProvider = require('../../../../../provider/awsProvider'); const AwsCompileStreamEvents = require('../index'); const Serverless = require('../../../../../../../Serverless'); @@ -21,13 +22,14 @@ describe('AwsCompileStreamEvents', () => { }, }, }; + serverless.setProvider('aws', new AwsProvider(serverless)); awsCompileStreamEvents = new AwsCompileStreamEvents(serverless); awsCompileStreamEvents.serverless.service.service = 'new-service'; }); describe('#constructor()', () => { - it('should set the provider variable to "aws"', () => expect(awsCompileStreamEvents.provider) - .to.equal('aws')); + it('should set the provider variable to be an instance of AwsProvider', () => + expect(awsCompileStreamEvents.provider).to.be.instanceof(AwsProvider)); }); describe('#compileStreamEvents()', () => { diff --git a/lib/plugins/aws/deploy/compile/functions/index.js b/lib/plugins/aws/deploy/compile/functions/index.js index bbdaf3fce..943d0ae2d 100644 --- a/lib/plugins/aws/deploy/compile/functions/index.js +++ b/lib/plugins/aws/deploy/compile/functions/index.js @@ -7,7 +7,7 @@ class AwsCompileFunctions { constructor(serverless, options) { this.serverless = serverless; this.options = options; - this.provider = 'aws'; + this.provider = this.serverless.getProvider('aws'); this.compileFunctions = this.compileFunctions.bind(this); this.compileFunction = this.compileFunction.bind(this); diff --git a/lib/plugins/aws/deploy/compile/functions/tests/index.js b/lib/plugins/aws/deploy/compile/functions/tests/index.js index 1f2ce8787..893b0fbd5 100644 --- a/lib/plugins/aws/deploy/compile/functions/tests/index.js +++ b/lib/plugins/aws/deploy/compile/functions/tests/index.js @@ -2,6 +2,7 @@ const path = require('path'); const expect = require('chai').expect; +const AwsProvider = require('../../../../provider/awsProvider'); const AwsCompileFunctions = require('../index'); const Serverless = require('../../../../../../Serverless'); @@ -17,6 +18,7 @@ describe('AwsCompileFunctions', () => { stage: 'dev', region: 'us-east-1', }; + serverless.setProvider('aws', new AwsProvider(serverless)); awsCompileFunctions = new AwsCompileFunctions(serverless, options); awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate = { Resources: {}, @@ -34,8 +36,8 @@ describe('AwsCompileFunctions', () => { }); describe('#constructor()', () => { - it('should set the provider variable to "aws"', () => expect(awsCompileFunctions.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsCompileFunctions.provider).to.be.instanceof(AwsProvider)); }); describe('#compileFunctions()', () => { diff --git a/lib/plugins/aws/deploy/index.js b/lib/plugins/aws/deploy/index.js index 6cb1b4840..53cbf0a5d 100644 --- a/lib/plugins/aws/deploy/index.js +++ b/lib/plugins/aws/deploy/index.js @@ -12,14 +12,11 @@ const uploadArtifacts = require('./lib/uploadArtifacts'); const updateStack = require('./lib/updateStack'); const configureStack = require('./lib/configureStack'); -const SDK = require('../'); - class AwsDeploy { constructor(serverless, options) { this.serverless = serverless; this.options = options; - this.provider = 'aws'; - this.sdk = new SDK(serverless); + this.provider = this.serverless.getProvider('aws'); Object.assign( this, diff --git a/lib/plugins/aws/deploy/lib/cleanupS3Bucket.js b/lib/plugins/aws/deploy/lib/cleanupS3Bucket.js index ad92b5a37..7aa4196b6 100644 --- a/lib/plugins/aws/deploy/lib/cleanupS3Bucket.js +++ b/lib/plugins/aws/deploy/lib/cleanupS3Bucket.js @@ -9,7 +9,7 @@ module.exports = { const directoriesToKeepCount = 4; const serviceStage = `${this.serverless.service.service}/${this.options.stage}`; - return this.sdk.request('S3', + return this.provider.request('S3', 'listObjectsV2', { Bucket: this.bucketName, @@ -62,7 +62,7 @@ module.exports = { if (objectsToRemove && objectsToRemove.length) { this.serverless.cli.log('Removing old service versions...'); - return this.sdk.request('S3', + return this.provider.request('S3', 'deleteObjects', { Bucket: this.bucketName, diff --git a/lib/plugins/aws/deploy/lib/configureStack.js b/lib/plugins/aws/deploy/lib/configureStack.js index 2c96df68c..4518a31de 100644 --- a/lib/plugins/aws/deploy/lib/configureStack.js +++ b/lib/plugins/aws/deploy/lib/configureStack.js @@ -79,7 +79,7 @@ module.exports = { if (bucketName) { return BbPromise.bind(this) .then(() => this.validateS3BucketName(bucketName)) - .then(() => this.sdk.request('S3', + .then(() => this.provider.request('S3', 'getBucketLocation', { Bucket: bucketName, diff --git a/lib/plugins/aws/deploy/lib/createStack.js b/lib/plugins/aws/deploy/lib/createStack.js index daae1c2de..73fa76863 100644 --- a/lib/plugins/aws/deploy/lib/createStack.js +++ b/lib/plugins/aws/deploy/lib/createStack.js @@ -27,7 +27,7 @@ module.exports = { Tags: Object.keys(stackTags).map((key) => ({ Key: key, Value: stackTags[key] })), }; - return this.sdk.request('CloudFormation', + return this.provider.request('CloudFormation', 'createStack', params, this.options.stage, @@ -55,7 +55,7 @@ module.exports = { return BbPromise.resolve(); } - return this.sdk.request('CloudFormation', + return this.provider.request('CloudFormation', 'describeStackResources', { StackName: stackName }, this.options.stage, diff --git a/lib/plugins/aws/deploy/lib/setBucketName.js b/lib/plugins/aws/deploy/lib/setBucketName.js index 0b2995718..8765ac656 100644 --- a/lib/plugins/aws/deploy/lib/setBucketName.js +++ b/lib/plugins/aws/deploy/lib/setBucketName.js @@ -12,7 +12,7 @@ module.exports = { return BbPromise.resolve(); } - return this.sdk.getServerlessDeploymentBucketName(this.options.stage, this.options.region) + return this.provider.getServerlessDeploymentBucketName(this.options.stage, this.options.region) .then((bucketName) => { this.bucketName = bucketName; }); diff --git a/lib/plugins/aws/deploy/lib/updateStack.js b/lib/plugins/aws/deploy/lib/updateStack.js index c2e6b312e..819a3032d 100644 --- a/lib/plugins/aws/deploy/lib/updateStack.js +++ b/lib/plugins/aws/deploy/lib/updateStack.js @@ -39,7 +39,7 @@ module.exports = { }); } - return this.sdk.request('CloudFormation', + return this.provider.request('CloudFormation', 'updateStack', params, this.options.stage, diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.js index 5ce7bce69..e53d14b89 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.js @@ -19,7 +19,7 @@ module.exports = { ContentType: 'application/json', }; - return this.sdk.request('S3', + return this.provider.request('S3', 'putObject', params, this.options.stage, @@ -42,7 +42,7 @@ module.exports = { ContentType: 'application/zip', }; - return this.sdk.request('S3', + return this.provider.request('S3', 'putObject', params, this.options.stage, diff --git a/lib/plugins/aws/deploy/tests/cleanupS3Bucket.js b/lib/plugins/aws/deploy/tests/cleanupS3Bucket.js index 61097ea46..0352fbfc6 100644 --- a/lib/plugins/aws/deploy/tests/cleanupS3Bucket.js +++ b/lib/plugins/aws/deploy/tests/cleanupS3Bucket.js @@ -3,6 +3,7 @@ const sinon = require('sinon'); const BbPromise = require('bluebird'); const expect = require('chai').expect; +const AwsProvider = require('../../provider/awsProvider'); const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); @@ -13,6 +14,7 @@ describe('cleanupS3Bucket', () => { beforeEach(() => { serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); serverless.service.service = 'cleanupS3Bucket'; const options = { stage: 'dev', @@ -31,7 +33,7 @@ describe('cleanupS3Bucket', () => { }; const listObjectsStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve(serviceObjects)); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve(serviceObjects)); return awsDeploy.getObjectsToRemove().then(() => { expect(listObjectsStub.calledOnce).to.be.equal(true); @@ -40,7 +42,7 @@ describe('cleanupS3Bucket', () => { expect(listObjectsStub.args[0][2].Bucket).to.be.equal(awsDeploy.bucketName); expect(listObjectsStub.args[0][2].Prefix).to.be.equal(`${s3Key}`); expect(listObjectsStub.calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); @@ -63,7 +65,7 @@ describe('cleanupS3Bucket', () => { }; const listObjectsStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve(serviceObjects)); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve(serviceObjects)); return awsDeploy.getObjectsToRemove().then((objectsToRemove) => { expect(objectsToRemove).to.not @@ -104,7 +106,7 @@ describe('cleanupS3Bucket', () => { expect(listObjectsStub.args[0][2].Bucket).to.be.equal(awsDeploy.bucketName); expect(listObjectsStub.args[0][2].Prefix).to.be.equal(`${s3Key}`); expect(listObjectsStub.calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); @@ -121,7 +123,7 @@ describe('cleanupS3Bucket', () => { }; const listObjectsStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve(serviceObjects)); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve(serviceObjects)); return awsDeploy.getObjectsToRemove().then((objectsToRemove) => { expect(objectsToRemove.length).to.equal(0); @@ -131,7 +133,7 @@ describe('cleanupS3Bucket', () => { expect(listObjectsStub.args[0][2].Bucket).to.be.equal(awsDeploy.bucketName); expect(listObjectsStub.args[0][2].Prefix).to.be.equal(`${s3Key}`); expect(listObjectsStub.calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); @@ -150,7 +152,7 @@ describe('cleanupS3Bucket', () => { }; const listObjectsStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve(serviceObjects)); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve(serviceObjects)); return awsDeploy.getObjectsToRemove().then((objectsToRemove) => { expect(objectsToRemove.length).to.equal(0); @@ -160,7 +162,7 @@ describe('cleanupS3Bucket', () => { expect(listObjectsStub.args[0][2].Bucket).to.be.equal(awsDeploy.bucketName); expect(listObjectsStub.args[0][2].Prefix).to.be.equal(`${s3Key}`); expect(listObjectsStub.calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); }); @@ -170,13 +172,13 @@ describe('cleanupS3Bucket', () => { beforeEach(() => { deleteObjectsStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); }); it('should resolve if no service objects are found in the S3 bucket', () => awsDeploy .removeObjects().then(() => { expect(deleteObjectsStub.calledOnce).to.be.equal(false); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }) ); @@ -195,7 +197,7 @@ describe('cleanupS3Bucket', () => { expect(deleteObjectsStub.args[0][2].Bucket).to.be.equal(awsDeploy.bucketName); expect(deleteObjectsStub.args[0][2].Delete.Objects).to.be.equal(objectsToRemove); expect(deleteObjectsStub.calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); }); diff --git a/lib/plugins/aws/deploy/tests/configureStack.js b/lib/plugins/aws/deploy/tests/configureStack.js index c5944e67d..386044c25 100644 --- a/lib/plugins/aws/deploy/tests/configureStack.js +++ b/lib/plugins/aws/deploy/tests/configureStack.js @@ -4,34 +4,37 @@ const sinon = require('sinon'); const BbPromise = require('bluebird'); const path = require('path'); const expect = require('chai').expect; - +const AwsProvider = require('../../provider/awsProvider'); const Serverless = require('../../../../Serverless'); -const AwsSdk = require('../'); +const validate = require('../../lib/validate'); +const configureStack = require('../lib/configureStack'); describe('#configureStack', () => { - let awsSdk; let serverless; + const awsPlugin = {}; beforeEach(() => { serverless = new Serverless(); - const options = { + awsPlugin.serverless = serverless; + awsPlugin.provider = new AwsProvider(serverless); + awsPlugin.options = { stage: 'dev', region: 'us-east-1', }; - awsSdk = new AwsSdk(serverless, options); - awsSdk.serverless.cli = new serverless.classes.CLI(); + + Object.assign(awsPlugin, configureStack, validate); }); it('should validate the region for the given S3 bucket', () => { const bucketName = 'com.serverless.deploys'; const getBucketLocationStub = sinon - .stub(awsSdk.sdk, 'request').returns( - BbPromise.resolve({ LocationConstraint: awsSdk.options.region }) + .stub(awsPlugin.provider, 'request').returns( + BbPromise.resolve({ LocationConstraint: awsPlugin.options.region }) ); - awsSdk.serverless.service.provider.deploymentBucket = bucketName; - return awsSdk.configureStack() + awsPlugin.serverless.service.provider.deploymentBucket = bucketName; + return awsPlugin.configureStack() .then(() => { expect(getBucketLocationStub.args[0][0]).to.equal('S3'); expect(getBucketLocationStub.args[0][1]).to.equal('getBucketLocation'); @@ -43,12 +46,12 @@ describe('#configureStack', () => { const bucketName = 'com.serverless.deploys'; const createStackStub = sinon - .stub(awsSdk.sdk, 'request').returns( + .stub(awsPlugin.provider, 'request').returns( BbPromise.resolve({ LocationConstraint: 'us-west-1' }) ); - awsSdk.serverless.service.provider.deploymentBucket = 'com.serverless.deploys'; - return awsSdk.configureStack() + awsPlugin.serverless.service.provider.deploymentBucket = 'com.serverless.deploys'; + return awsPlugin.configureStack() .catch((err) => { expect(createStackStub.args[0][0]).to.equal('S3'); expect(createStackStub.args[0][1]).to.equal('getBucketLocation'); @@ -60,7 +63,7 @@ describe('#configureStack', () => { it('should merge the IamRoleLambdaExecution template into the CloudFormation template', () => { - const IamRoleLambdaExecutionTemplate = awsSdk.serverless.utils.readFileSync( + const IamRoleLambdaExecutionTemplate = awsPlugin.serverless.utils.readFileSync( path.join( __dirname, '..', @@ -69,55 +72,55 @@ describe('#configureStack', () => { ) ); - return awsSdk.configureStack() + return awsPlugin.configureStack() .then(() => { - expect(awsSdk.serverless.service.provider.compiledCloudFormationTemplate + expect(awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Resources.IamRoleLambdaExecution ).to.deep.equal(IamRoleLambdaExecutionTemplate.IamRoleLambdaExecution); }); }); it('should merge IamPolicyLambdaExecution template into the CloudFormation template', () => - awsSdk.configureStack() + awsPlugin.configureStack() .then(() => { // we check for the type here because a deep equality check will error out due to // the updates which are made after the merge (they are tested in a separate test) - expect(awsSdk.serverless.service.provider.compiledCloudFormationTemplate + expect(awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Resources.IamPolicyLambdaExecution.Type ).to.deep.equal('AWS::IAM::Policy'); }) ); it('should update the necessary variables for the IamPolicyLambdaExecution', () => - awsSdk.configureStack() + awsPlugin.configureStack() .then(() => { - expect(awsSdk.serverless.service.provider.compiledCloudFormationTemplate + expect(awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Resources .IamPolicyLambdaExecution .Properties .PolicyName ).to.equal( `${ - awsSdk.options.stage + awsPlugin.options.stage }-${ - awsSdk.serverless.service.service + awsPlugin.serverless.service.service }-lambda` ); - expect(awsSdk.serverless.service.provider.compiledCloudFormationTemplate + expect(awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Resources .IamPolicyLambdaExecution .Properties .PolicyDocument .Statement[0] .Resource - ).to.equal(`arn:aws:logs:${awsSdk.options.region}:*:*`); + ).to.equal(`arn:aws:logs:${awsPlugin.options.region}:*:*`); }) ); it('should add custom IAM policy statements', () => { - awsSdk.serverless.service.provider.name = 'aws'; - awsSdk.serverless.service.provider.iamRoleStatements = [ + awsPlugin.serverless.service.provider.name = 'aws'; + awsPlugin.serverless.service.provider.iamRoleStatements = [ { Effect: 'Allow', Action: [ @@ -128,20 +131,20 @@ describe('#configureStack', () => { ]; - return awsSdk.configureStack() + return awsPlugin.configureStack() .then(() => { - expect(awsSdk.serverless.service.provider.compiledCloudFormationTemplate + expect(awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Resources.IamPolicyLambdaExecution.Properties.PolicyDocument.Statement[1] - ).to.deep.equal(awsSdk.serverless.service.provider.iamRoleStatements[0]); + ).to.deep.equal(awsPlugin.serverless.service.provider.iamRoleStatements[0]); }); }); it('should use a custom bucket if specified', () => { const bucketName = 'com.serverless.deploys'; - awsSdk.serverless.service.provider.deploymentBucket = bucketName; + awsPlugin.serverless.service.provider.deploymentBucket = bucketName; - const coreCloudFormationTemplate = awsSdk.serverless.utils.readFileSync( + const coreCloudFormationTemplate = awsPlugin.serverless.utils.readFileSync( path.join( __dirname, '..', @@ -149,44 +152,44 @@ describe('#configureStack', () => { 'core-cloudformation-template.json' ) ); - awsSdk.serverless.service.provider + awsPlugin.serverless.service.provider .compiledCloudFormationTemplate = coreCloudFormationTemplate; sinon - .stub(awsSdk.sdk, 'request') - .returns(BbPromise.resolve({ LocationConstraint: awsSdk.options.region })); + .stub(awsPlugin.provider, 'request') + .returns(BbPromise.resolve({ LocationConstraint: awsPlugin.options.region })); - return awsSdk.configureStack() + return awsPlugin.configureStack() .then(() => { expect( - awsSdk.serverless.service.provider.compiledCloudFormationTemplate + awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Outputs.ServerlessDeploymentBucketName.Value ).to.equal(bucketName); // eslint-disable-next-line no-unused-expressions expect( - awsSdk.serverless.service.provider.compiledCloudFormationTemplate + awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Resources.ServerlessDeploymentBucket ).to.not.exist; }); }); it('should not add IamPolicyLambdaExecution', () => { - awsSdk.serverless.service.provider.iamRoleARN = 'some:aws:arn:xxx:*:*'; + awsPlugin.serverless.service.provider.iamRoleARN = 'some:aws:arn:xxx:*:*'; - return awsSdk.configureStack() + return awsPlugin.configureStack() .then(() => expect( - awsSdk.serverless.service.provider.compiledCloudFormationTemplate + awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Resources.IamPolicyLambdaExecution ).to.not.exist); }); it('should not add IamRole', () => { - awsSdk.serverless.service.provider.iamRoleARN = 'some:aws:arn:xxx:*:*'; + awsPlugin.serverless.service.provider.iamRoleARN = 'some:aws:arn:xxx:*:*'; - return awsSdk.configureStack() + return awsPlugin.configureStack() .then(() => expect( - awsSdk.serverless.service.provider.compiledCloudFormationTemplate + awsPlugin.serverless.service.provider.compiledCloudFormationTemplate .Resources.IamRoleLambdaExecution ).to.not.exist); }); diff --git a/lib/plugins/aws/deploy/tests/createStack.js b/lib/plugins/aws/deploy/tests/createStack.js index 51b9d1e27..69970f657 100644 --- a/lib/plugins/aws/deploy/tests/createStack.js +++ b/lib/plugins/aws/deploy/tests/createStack.js @@ -4,6 +4,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); const path = require('path'); const BbPromise = require('bluebird'); +const AwsProvider = require('../../provider/awsProvider'); const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); const testUtils = require('../../../../../tests/utils'); @@ -25,6 +26,7 @@ describe('createStack', () => { beforeEach(() => { const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); serverless.utils.writeFileSync(serverlessYmlPath, serverlessYml); serverless.config.servicePath = tmpDirPath; const options = { @@ -49,7 +51,7 @@ describe('createStack', () => { .compiledCloudFormationTemplate = coreCloudFormationTemplate; const createStackStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); return awsDeploy.create().then(() => { expect(createStackStub.args[0][0]).to.equal('CloudFormation'); @@ -69,7 +71,7 @@ describe('createStack', () => { awsDeploy.serverless.service.provider.stackTags = { STAGE: 'overridden', tag1: 'value1' }; const createStackStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); return awsDeploy.create().then(() => { expect(createStackStub.args[0][2].Tags) @@ -77,14 +79,14 @@ describe('createStack', () => { { Key: 'STAGE', Value: 'overridden' }, { Key: 'tag1', Value: 'value1' }, ]); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); }); describe('#createStack()', () => { it('should store the core CloudFormation template in the provider object', () => { - sinon.stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + sinon.stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); const coreCloudFormationTemplate = awsDeploy.serverless.utils.readFileSync( path.join(__dirname, @@ -110,7 +112,7 @@ describe('createStack', () => { const createStub = sinon .stub(awsDeploy, 'create').returns(BbPromise.resolve()); - sinon.stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + sinon.stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); return awsDeploy.createStack().then(() => { expect(createStub.called).to.be.equal(false); @@ -136,11 +138,11 @@ describe('createStack', () => { const writeCreateTemplateToDiskStub = sinon .stub(awsDeploy, 'writeCreateTemplateToDisk').returns(BbPromise.resolve()); - sinon.stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + sinon.stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); return awsDeploy.createStack().then((res) => { expect(writeCreateTemplateToDiskStub.calledOnce).to.be.equal(true); - expect(awsDeploy.sdk.request.called).to.be.equal(true); + expect(awsDeploy.provider.request.called).to.be.equal(true); expect(res).to.equal('alreadyCreated'); }); }); @@ -150,7 +152,7 @@ describe('createStack', () => { message: 'Something went wrong.', }; - sinon.stub(awsDeploy.sdk, 'request').returns(BbPromise.reject(errorMock)); + sinon.stub(awsDeploy.provider, 'request').returns(BbPromise.reject(errorMock)); const createStub = sinon .stub(awsDeploy, 'create').returns(BbPromise.resolve()); @@ -167,7 +169,7 @@ describe('createStack', () => { message: 'does not exist', }; - sinon.stub(awsDeploy.sdk, 'request').returns(BbPromise.reject(errorMock)); + sinon.stub(awsDeploy.provider, 'request').returns(BbPromise.reject(errorMock)); const createStub = sinon .stub(awsDeploy, 'create').returns(BbPromise.resolve()); diff --git a/lib/plugins/aws/deploy/tests/index.js b/lib/plugins/aws/deploy/tests/index.js index d5f3de785..8f67b8dce 100644 --- a/lib/plugins/aws/deploy/tests/index.js +++ b/lib/plugins/aws/deploy/tests/index.js @@ -1,5 +1,6 @@ 'use strict'; +const AwsProvider = require('../../provider/awsProvider'); const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); const expect = require('chai').expect; @@ -14,7 +15,7 @@ describe('AwsDeploy', () => { stage: 'dev', region: 'us-east-1', }; - + serverless.setProvider('aws', new AwsProvider(serverless)); awsDeploy = new AwsDeploy(serverless, options); awsDeploy.serverless.cli = new serverless.classes.CLI(); }); @@ -22,8 +23,8 @@ describe('AwsDeploy', () => { describe('#constructor()', () => { it('should have hooks', () => expect(awsDeploy.hooks).to.be.not.empty); - it('should set the provider variable to "aws"', () => expect(awsDeploy.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsDeploy.provider).to.be.instanceof(AwsProvider)); }); describe('hooks', () => { diff --git a/lib/plugins/aws/deploy/tests/setBucketName.js b/lib/plugins/aws/deploy/tests/setBucketName.js index 6652d3497..0cc069327 100644 --- a/lib/plugins/aws/deploy/tests/setBucketName.js +++ b/lib/plugins/aws/deploy/tests/setBucketName.js @@ -2,6 +2,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); +const AwsProvider = require('../../provider/awsProvider'); const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); const BbPromise = require('bluebird'); @@ -13,6 +14,7 @@ describe('#setBucketName()', () => { beforeEach(() => { serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); const options = { stage: 'dev', region: 'us-east-1', @@ -20,7 +22,7 @@ describe('#setBucketName()', () => { awsDeploy = new AwsDeploy(serverless, options); getServerlessDeploymentBucketNameStub = sinon - .stub(awsDeploy.sdk, 'getServerlessDeploymentBucketName') + .stub(awsDeploy.provider, 'getServerlessDeploymentBucketName') .returns(BbPromise.resolve('bucket-name')); }); @@ -30,7 +32,7 @@ describe('#setBucketName()', () => { expect(getServerlessDeploymentBucketNameStub.calledOnce).to.be.equal(true); expect(getServerlessDeploymentBucketNameStub .calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.getServerlessDeploymentBucketName.restore(); + awsDeploy.provider.getServerlessDeploymentBucketName.restore(); }) ); @@ -39,7 +41,7 @@ describe('#setBucketName()', () => { return awsDeploy.setBucketName().then(() => { expect(getServerlessDeploymentBucketNameStub.calledOnce).to.be.equal(false); - awsDeploy.sdk.getServerlessDeploymentBucketName.restore(); + awsDeploy.provider.getServerlessDeploymentBucketName.restore(); }); }); diff --git a/lib/plugins/aws/deploy/tests/updateStack.js b/lib/plugins/aws/deploy/tests/updateStack.js index 6f0a1aa37..199368aef 100644 --- a/lib/plugins/aws/deploy/tests/updateStack.js +++ b/lib/plugins/aws/deploy/tests/updateStack.js @@ -4,6 +4,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); const path = require('path'); const BbPromise = require('bluebird'); +const AwsProvider = require('../../provider/awsProvider'); const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); const testUtils = require('../../../../../tests/utils'); @@ -15,6 +16,7 @@ describe('updateStack', () => { beforeEach(() => { serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); const options = { stage: 'dev', region: 'us-east-1', @@ -34,7 +36,7 @@ describe('updateStack', () => { beforeEach(() => { updateStackStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); }); it('should update the stack', () => awsDeploy.update() @@ -51,7 +53,7 @@ describe('updateStack', () => { .to.deep.equal([{ Key: 'STAGE', Value: awsDeploy.options.stage }]); expect(updateStackStub.calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }) ); @@ -74,7 +76,7 @@ describe('updateStack', () => { .to.equal( '{"Statement":[{"Effect":"Allow","Principal":"*","Action":"Update:*","Resource":"*"}]}' ); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); }); diff --git a/lib/plugins/aws/deploy/tests/uploadArtifacts.js b/lib/plugins/aws/deploy/tests/uploadArtifacts.js index 2bd1f70a6..146af269d 100644 --- a/lib/plugins/aws/deploy/tests/uploadArtifacts.js +++ b/lib/plugins/aws/deploy/tests/uploadArtifacts.js @@ -4,6 +4,7 @@ const sinon = require('sinon'); const path = require('path'); const BbPromise = require('bluebird'); const expect = require('chai').expect; +const AwsProvider = require('../../provider/awsProvider'); const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); const testUtils = require('../../../../../tests/utils'); @@ -14,6 +15,7 @@ describe('uploadArtifacts', () => { beforeEach(() => { serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); const options = { stage: 'dev', region: 'us-east-1', @@ -29,7 +31,7 @@ describe('uploadArtifacts', () => { awsDeploy.serverless.service.provider.compiledCloudFormationTemplate = { key: 'value' }; const putObjectStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); return awsDeploy.uploadCloudFormationFile().then(() => { expect(putObjectStub.calledOnce).to.be.equal(true); @@ -44,19 +46,19 @@ describe('uploadArtifacts', () => { .provider.compiledCloudFormationTemplate)); expect(putObjectStub.calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); }); describe('#uploadZipFile()', () => { it('should throw for null artifact paths', () => { - sinon.stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + sinon.stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); expect(() => awsDeploy.uploadZipFile(null)).to.throw(Error); }); it('should throw for empty artifact paths', () => { - sinon.stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + sinon.stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); expect(() => awsDeploy.uploadZipFile('')).to.throw(Error); }); @@ -68,7 +70,7 @@ describe('uploadArtifacts', () => { const artifactFileBuffer = serverless.utils.readFileSync(artifactFilePath); const putObjectStub = sinon - .stub(awsDeploy.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); return awsDeploy.uploadZipFile(artifactFilePath).then(() => { expect(putObjectStub.calledOnce).to.be.equal(true); @@ -82,7 +84,7 @@ describe('uploadArtifacts', () => { .to.be.equal(artifactFileBuffer.toString()); expect(putObjectStub.calledWith(awsDeploy.options.stage, awsDeploy.options.region)); - awsDeploy.sdk.request.restore(); + awsDeploy.provider.request.restore(); }); }); }); diff --git a/lib/plugins/aws/deployFunction/index.js b/lib/plugins/aws/deployFunction/index.js index 770ce3cf7..4075dfd64 100644 --- a/lib/plugins/aws/deployFunction/index.js +++ b/lib/plugins/aws/deployFunction/index.js @@ -2,7 +2,6 @@ const BbPromise = require('bluebird'); const fs = require('fs'); -const SDK = require('../'); const validate = require('../lib/validate'); // The Package plugin which is used to zip the service @@ -12,8 +11,7 @@ class AwsDeployFunction { constructor(serverless, options) { this.serverless = serverless; this.options = options || {}; - this.provider = 'aws'; - this.sdk = new SDK(serverless); + this.provider = this.serverless.getProvider('aws'); this.pkg = new Package(this.serverless, this.options); @@ -38,7 +36,7 @@ class AwsDeployFunction { FunctionName: this.options.functionObj.name, }; - this.sdk.request( + this.provider.request( 'Lambda', 'getFunction', params, @@ -69,7 +67,7 @@ class AwsDeployFunction { ZipFile: data, }; - return this.sdk.request( + return this.provider.request( 'Lambda', 'updateFunctionCode', params, diff --git a/lib/plugins/aws/deployFunction/tests/index.js b/lib/plugins/aws/deployFunction/tests/index.js index 89d42de5a..d1489d7cb 100644 --- a/lib/plugins/aws/deployFunction/tests/index.js +++ b/lib/plugins/aws/deployFunction/tests/index.js @@ -5,7 +5,8 @@ const sinon = require('sinon'); const path = require('path'); const fs = require('fs'); const Package = require('../../../package'); -const AwsDeployFunction = require('../'); +const AwsProvider = require('../../provider/awsProvider'); +const AwsDeployFunction = require('../index'); const Serverless = require('../../../../Serverless'); const BbPromise = require('bluebird'); const testUtils = require('../../../../../tests/utils'); @@ -44,14 +45,15 @@ describe('AwsDeployFunction', () => { }, }; serverless.init(); + serverless.setProvider('aws', new AwsProvider(serverless)); awsDeployFunction = new AwsDeployFunction(serverless, options); }); describe('#constructor()', () => { it('should have hooks', () => expect(awsDeployFunction.hooks).to.be.not.empty); - it('should set the provider variable to "aws"', () => expect(awsDeployFunction.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsDeployFunction.provider).to.be.instanceof(AwsProvider)); it('should set an empty options object if no options are given', () => { const awsDeployFunctionWithEmptyOptions = new AwsDeployFunction(serverless); @@ -60,6 +62,8 @@ describe('AwsDeployFunction', () => { }); it('should run promise chain in order', () => { + const validateStub = sinon + .stub(awsDeployFunction, 'validate').returns(BbPromise.resolve()); const checkIfFunctionExistsStub = sinon .stub(awsDeployFunction, 'checkIfFunctionExists').returns(BbPromise.resolve()); const zipFunctionStub = sinon @@ -70,13 +74,15 @@ describe('AwsDeployFunction', () => { .stub(awsDeployFunction, 'cleanup').returns(BbPromise.resolve()); return awsDeployFunction.hooks['deploy:function:deploy']().then(() => { - expect(checkIfFunctionExistsStub.calledOnce).to.be.equal(true); + expect(validateStub.calledOnce).to.equal(true); + expect(checkIfFunctionExistsStub.calledAfter(validateStub)) + .to.equal(true); expect(zipFunctionStub.calledAfter(checkIfFunctionExistsStub)) - .to.be.equal(true); + .to.equal(true); expect(deployFunctionStub.calledAfter(zipFunctionStub)) - .to.be.equal(true); + .to.equal(true); expect(cleanupStub.calledAfter(deployFunctionStub)) - .to.be.equal(true); + .to.equal(true); awsDeployFunction.checkIfFunctionExists.restore(); awsDeployFunction.zipFunction.restore(); @@ -94,7 +100,7 @@ describe('AwsDeployFunction', () => { it('should check if the function is deployed', () => { const getFunctionStub = sinon - .stub(awsDeployFunction.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsDeployFunction.provider, 'request').returns(BbPromise.resolve()); awsDeployFunction.serverless.service.functions = { first: { @@ -111,7 +117,7 @@ describe('AwsDeployFunction', () => { expect(getFunctionStub.args[0][0]).to.be.equal('Lambda'); expect(getFunctionStub.args[0][1]).to.be.equal('getFunction'); expect(getFunctionStub.args[0][2].FunctionName).to.be.equal('first'); - awsDeployFunction.sdk.request.restore(); + awsDeployFunction.provider.request.restore(); }); }); }); @@ -145,7 +151,7 @@ describe('AwsDeployFunction', () => { awsDeployFunction.options.functionObj.artifact = artifactFilePath; const updateFunctionCodeStub = sinon - .stub(awsDeployFunction.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsDeployFunction.provider, 'request').returns(BbPromise.resolve()); return awsDeployFunction.deployFunction().then(() => { const data = fs.readFileSync(artifactFilePath); @@ -158,7 +164,7 @@ describe('AwsDeployFunction', () => { expect(updateFunctionCodeStub.args[0][1]).to.be.equal('updateFunctionCode'); expect(updateFunctionCodeStub.args[0][2].FunctionName).to.be.equal('first'); expect(updateFunctionCodeStub.args[0][2].ZipFile).to.deep.equal(data); - awsDeployFunction.sdk.request.restore(); + awsDeployFunction.provider.request.restore(); }); }); }); diff --git a/lib/plugins/aws/info/index.js b/lib/plugins/aws/info/index.js index ab0213c81..915c87462 100644 --- a/lib/plugins/aws/info/index.js +++ b/lib/plugins/aws/info/index.js @@ -2,16 +2,14 @@ const BbPromise = require('bluebird'); const validate = require('../lib/validate'); -const SDK = require('../'); const chalk = require('chalk'); const _ = require('lodash'); class AwsInfo { constructor(serverless, options) { this.serverless = serverless; + this.provider = this.serverless.getProvider('aws'); this.options = options || {}; - this.provider = 'aws'; - this.sdk = new SDK(serverless); Object.assign(this, validate); this.hooks = { @@ -37,7 +35,7 @@ class AwsInfo { * Gather information about the service */ gather() { - const stackName = this.sdk.getStackName(this.options.stage); + const stackName = this.provider.getStackName(this.options.stage); const info = { service: this.serverless.service.service, stage: this.options.stage, @@ -45,7 +43,7 @@ class AwsInfo { }; // Get info from CloudFormation Outputs - return this.sdk.request('CloudFormation', + return this.provider.request('CloudFormation', 'describeStacks', { StackName: stackName }, this.options.stage, @@ -113,7 +111,7 @@ class AwsInfo { const apiKeyNames = this.serverless.service.provider.apiKeys || []; if (apiKeyNames.length) { - return this.sdk.request('APIGateway', + return this.provider.request('APIGateway', 'getApiKeys', { includeValues: true }, this.options.stage, diff --git a/lib/plugins/aws/info/tests/index.js b/lib/plugins/aws/info/tests/index.js index 3481b1450..f12d45f8f 100644 --- a/lib/plugins/aws/info/tests/index.js +++ b/lib/plugins/aws/info/tests/index.js @@ -3,6 +3,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); const AwsInfo = require('../'); +const AwsProvider = require('../../provider/awsProvider'); const Serverless = require('../../../../Serverless'); const CLI = require('../../../../classes/CLI'); const BbPromise = require('bluebird'); @@ -10,6 +11,7 @@ const chalk = require('chalk'); describe('AwsInfo', () => { const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); serverless.service.functions = { function1: { events: [ @@ -41,8 +43,8 @@ describe('AwsInfo', () => { describe('#constructor()', () => { it('should have hooks', () => expect(awsInfo.hooks).to.be.not.empty); - it('should set the provider variable to "aws"', () => expect(awsInfo.provider) - .to.equal('aws')); + it('should set the provider variable to the AwsProvider instance', () => + expect(awsInfo.provider).to.be.instanceof(AwsProvider)); it('should set an empty options object if no options are given', () => { const awsInfoWithEmptyOptions = new AwsInfo(serverless); @@ -158,7 +160,7 @@ describe('AwsInfo', () => { ], }; - const describeStackStub = sinon.stub(awsInfo.sdk, 'request') + const describeStackStub = sinon.stub(awsInfo.provider, 'request') .returns(BbPromise.resolve(describeStacksResponse)); it('should gather with correct params', () => awsInfo.gather() @@ -167,7 +169,7 @@ describe('AwsInfo', () => { expect(describeStackStub.args[0][0]).to.equal('CloudFormation'); expect(describeStackStub.args[0][1]).to.equal('describeStacks'); expect(describeStackStub.args[0][2].StackName) - .to.equal(awsInfo.sdk.getStackName(awsInfo.options.stage)); + .to.equal(awsInfo.provider.getStackName(awsInfo.options.stage)); expect(describeStackStub .calledWith(awsInfo.options.stage, awsInfo.options.region)); }) @@ -219,7 +221,7 @@ describe('AwsInfo', () => { }); it("should provide only general info when stack doesn't exist (ValidationError)", () => { - awsInfo.sdk.request.restore(); + awsInfo.provider.request.restore(); serverless.service.service = 'my-first'; const validationError = { @@ -227,7 +229,7 @@ describe('AwsInfo', () => { message: 'Stack with id not-created-service does not exist', }; - sinon.stub(awsInfo.sdk, 'request').returns(BbPromise.reject(validationError)); + sinon.stub(awsInfo.provider, 'request').returns(BbPromise.reject(validationError)); const expectedInfo = { service: 'my-first', @@ -241,8 +243,8 @@ describe('AwsInfo', () => { }); it('should throw a ServerlessError when AWS sdk throws an error', () => { - awsInfo.sdk.request.restore(); - sinon.stub(awsInfo.sdk, 'request').returns(BbPromise.reject(Error)); + awsInfo.provider.request.restore(); + sinon.stub(awsInfo.provider, 'request').returns(BbPromise.reject(Error)); return awsInfo.gather().catch((e) => { expect(e.name).to.equal('ServerlessError'); @@ -253,7 +255,7 @@ describe('AwsInfo', () => { describe('#getApiKeyValues()', () => { it('should return the api keys in the info object', () => { // TODO: implement a pattern for stub restoring to get rid of this - awsInfo.sdk.request.restore(); + awsInfo.provider.request.restore(); // set the API Keys for the service awsInfo.serverless.service.provider.apiKeys = ['foo', 'bar']; @@ -295,14 +297,14 @@ describe('AwsInfo', () => { }; const getApiKeysStub = sinon - .stub(awsInfo.sdk, 'request') + .stub(awsInfo.provider, 'request') .returns(BbPromise.resolve(apiKeyItems)); return awsInfo.getApiKeyValues(gatheredData).then((result) => { expect(getApiKeysStub.calledOnce).to.equal(true); expect(result.info.apiKeys).to.deep.equal(gatheredDataAfterKeyLookup.info.apiKeys); - awsInfo.sdk.request.restore(); + awsInfo.provider.request.restore(); }); }); @@ -317,14 +319,14 @@ describe('AwsInfo', () => { }; const getApiKeysStub = sinon - .stub(awsInfo.sdk, 'request') + .stub(awsInfo.provider, 'request') .returns(BbPromise.resolve()); return awsInfo.getApiKeyValues(gatheredData).then((result) => { expect(getApiKeysStub.calledOnce).to.equal(false); expect(result).to.deep.equal(gatheredData); - awsInfo.sdk.request.restore(); + awsInfo.provider.request.restore(); }); }); }); diff --git a/lib/plugins/aws/invoke/index.js b/lib/plugins/aws/invoke/index.js index 3782cdbe9..9d2ef8bcf 100644 --- a/lib/plugins/aws/invoke/index.js +++ b/lib/plugins/aws/invoke/index.js @@ -3,7 +3,6 @@ const BbPromise = require('bluebird'); const chalk = require('chalk'); const path = require('path'); -const SDK = require('../'); const moment = require('moment'); const validate = require('../lib/validate'); @@ -11,8 +10,7 @@ class AwsInvoke { constructor(serverless, options) { this.serverless = serverless; this.options = options || {}; - this.provider = 'aws'; - this.sdk = new SDK(serverless); + this.provider = this.serverless.getProvider('aws'); Object.assign(this, validate); @@ -58,7 +56,8 @@ class AwsInvoke { Payload: new Buffer(JSON.stringify(this.options.data || {})), }; - return this.sdk.request('Lambda', 'invoke', params, this.options.stage, this.options.region); + return this.provider + .request('Lambda', 'invoke', params, this.options.stage, this.options.region); } log(invocationReply) { diff --git a/lib/plugins/aws/invoke/tests/index.js b/lib/plugins/aws/invoke/tests/index.js index 0229844c8..381f24aee 100644 --- a/lib/plugins/aws/invoke/tests/index.js +++ b/lib/plugins/aws/invoke/tests/index.js @@ -4,12 +4,14 @@ const expect = require('chai').expect; const sinon = require('sinon'); const path = require('path'); const AwsInvoke = require('../'); +const AwsProvider = require('../../provider/awsProvider'); const Serverless = require('../../../../Serverless'); const BbPromise = require('bluebird'); const testUtils = require('../../../../../tests/utils'); describe('AwsInvoke', () => { const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); const options = { stage: 'dev', region: 'us-east-1', @@ -20,8 +22,8 @@ describe('AwsInvoke', () => { describe('#constructor()', () => { it('should have hooks', () => expect(awsInvoke.hooks).to.be.not.empty); - it('should set the provider variable to "aws"', () => expect(awsInvoke.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsInvoke.provider).to.be.instanceof(AwsProvider)); it('should run promise chain in order', () => { const validateStub = sinon @@ -136,7 +138,7 @@ describe('AwsInvoke', () => { describe('#invoke()', () => { let invokeStub; beforeEach(() => { - invokeStub = sinon.stub(awsInvoke.sdk, 'request').returns(BbPromise.resolve()); + invokeStub = sinon.stub(awsInvoke.provider, 'request').returns(BbPromise.resolve()); awsInvoke.serverless.service.service = 'new-service'; awsInvoke.options = { stage: 'dev', @@ -155,7 +157,7 @@ describe('AwsInvoke', () => { expect(invokeStub.args[0][2].InvocationType).to.be.equal('RequestResponse'); expect(invokeStub.args[0][2].LogType).to.be.equal('None'); expect(typeof invokeStub.args[0][2].Payload).to.not.be.equal('undefined'); - awsInvoke.sdk.request.restore(); + awsInvoke.provider.request.restore(); }) ); @@ -169,7 +171,7 @@ describe('AwsInvoke', () => { expect(invokeStub.args[0][2].InvocationType).to.be.equal('RequestResponse'); expect(invokeStub.args[0][2].LogType).to.be.equal('Tail'); expect(typeof invokeStub.args[0][2].Payload).to.not.be.equal('undefined'); - awsInvoke.sdk.request.restore(); + awsInvoke.provider.request.restore(); }); }); @@ -183,7 +185,7 @@ describe('AwsInvoke', () => { expect(invokeStub.args[0][2].InvocationType).to.be.equal('OtherType'); expect(invokeStub.args[0][2].LogType).to.be.equal('None'); expect(typeof invokeStub.args[0][2].Payload).to.not.be.equal('undefined'); - awsInvoke.sdk.request.restore(); + awsInvoke.provider.request.restore(); }); }); }); diff --git a/lib/plugins/aws/lib/monitorStack.js b/lib/plugins/aws/lib/monitorStack.js index d17f363b9..8d08aa96f 100644 --- a/lib/plugins/aws/lib/monitorStack.js +++ b/lib/plugins/aws/lib/monitorStack.js @@ -35,7 +35,7 @@ module.exports = { const params = { StackName: cfData.StackId, }; - return this.sdk.request('CloudFormation', + return this.provider.request('CloudFormation', 'describeStackEvents', params, this.options.stage, diff --git a/lib/plugins/aws/logs/index.js b/lib/plugins/aws/logs/index.js index 03174be5d..0e1779691 100644 --- a/lib/plugins/aws/logs/index.js +++ b/lib/plugins/aws/logs/index.js @@ -3,7 +3,6 @@ const BbPromise = require('bluebird'); const chalk = require('chalk'); const _ = require('lodash'); -const SDK = require('../'); const os = require('os'); const moment = require('moment'); const validate = require('../lib/validate'); @@ -12,8 +11,7 @@ class AwsLogs { constructor(serverless, options) { this.serverless = serverless; this.options = options || {}; - this.provider = 'aws'; - this.sdk = new SDK(serverless); + this.provider = this.serverless.getProvider('aws'); Object.assign(this, validate); @@ -45,7 +43,7 @@ class AwsLogs { orderBy: 'LastEventTime', }; - return this.sdk + return this.provider .request('CloudWatchLogs', 'describeLogStreams', params, @@ -120,7 +118,7 @@ class AwsLogs { } } - return this.sdk + return this.provider .request('CloudWatchLogs', 'filterLogEvents', params, diff --git a/lib/plugins/aws/logs/tests/index.js b/lib/plugins/aws/logs/tests/index.js index 09e1723ce..e193315a5 100644 --- a/lib/plugins/aws/logs/tests/index.js +++ b/lib/plugins/aws/logs/tests/index.js @@ -2,6 +2,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); +const AwsProvider = require('../../provider/awsProvider'); const AwsLogs = require('../'); const Serverless = require('../../../../Serverless'); const BbPromise = require('bluebird'); @@ -17,6 +18,7 @@ describe('AwsLogs', () => { function: 'first', }; serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); awsLogs = new AwsLogs(serverless, options); }); @@ -29,8 +31,8 @@ describe('AwsLogs', () => { expect(awsLogsWithEmptyOptions.options).to.deep.equal({}); }); - it('should set the provider variable to "aws"', () => expect(awsLogs.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsLogs.provider).to.be.instanceof(AwsProvider)); it('should run promise chain in order', () => { const validateStub = sinon @@ -114,7 +116,7 @@ describe('AwsLogs', () => { }, ], }; - const getLogStreamsStub = sinon.stub(awsLogs.sdk, 'request').returns( + const getLogStreamsStub = sinon.stub(awsLogs.provider, 'request').returns( BbPromise.resolve(replyMock) ); @@ -137,20 +139,20 @@ describe('AwsLogs', () => { expect(logStreamNames[1]) .to.be.equal('2016/07/28/[$LATEST]83f5206ab2a8488290349b9c1fbfe2ba'); - awsLogs.sdk.request.restore(); + awsLogs.provider.request.restore(); }); }); it('should throw error if no log streams found', () => { - sinon.stub(awsLogs.sdk, 'request').returns(BbPromise.resolve()); + sinon.stub(awsLogs.provider, 'request').returns(BbPromise.resolve()); return awsLogs.getLogStreams() .then(() => { expect(1).to.equal(2); - awsLogs.sdk.request.restore(); + awsLogs.provider.request.restore(); }).catch(e => { expect(e.name).to.be.equal('ServerlessError'); - awsLogs.sdk.request.restore(); + awsLogs.provider.request.restore(); }); }); }); @@ -175,7 +177,7 @@ describe('AwsLogs', () => { '2016/07/28/[$LATEST]83f5206ab2a8488290349b9c1fbfe2ba', '2016/07/28/[$LATEST]83f5206ab2a8488290349b9c1fbfe2ba', ]; - const filterLogEventsStub = sinon.stub(awsLogs.sdk, 'request').returns( + const filterLogEventsStub = sinon.stub(awsLogs.provider, 'request').returns( BbPromise.resolve(replyMock) ); awsLogs.serverless.service.service = 'new-service'; @@ -201,7 +203,7 @@ describe('AwsLogs', () => { expect(filterLogEventsStub.args[0][2].logStreamNames).to.deep.equal(logStreamNamesMock); expect(filterLogEventsStub.args[0][2].filterPattern).to.be.equal('error'); expect(typeof filterLogEventsStub.args[0][2].startTime).to.be.equal('number'); - awsLogs.sdk.request.restore(); + awsLogs.provider.request.restore(); }); }); @@ -224,7 +226,7 @@ describe('AwsLogs', () => { '2016/07/28/[$LATEST]83f5206ab2a8488290349b9c1fbfe2ba', '2016/07/28/[$LATEST]83f5206ab2a8488290349b9c1fbfe2ba', ]; - const filterLogEventsStub = sinon.stub(awsLogs.sdk, 'request').returns( + const filterLogEventsStub = sinon.stub(awsLogs.provider, 'request').returns( BbPromise.resolve(replyMock) ); awsLogs.serverless.service.service = 'new-service'; @@ -250,7 +252,7 @@ describe('AwsLogs', () => { expect(filterLogEventsStub.args[0][2].logStreamNames).to.deep.equal(logStreamNamesMock); expect(filterLogEventsStub.args[0][2].filterPattern).to.be.equal('error'); expect(typeof filterLogEventsStub.args[0][2].startTime).to.be.equal('number'); - awsLogs.sdk.request.restore(); + awsLogs.provider.request.restore(); }); }); }); diff --git a/lib/plugins/aws/index.js b/lib/plugins/aws/provider/awsProvider.js similarity index 96% rename from lib/plugins/aws/index.js rename to lib/plugins/aws/provider/awsProvider.js index c3eb9ca80..6efa52be4 100644 --- a/lib/plugins/aws/index.js +++ b/lib/plugins/aws/provider/awsProvider.js @@ -70,11 +70,16 @@ const impl = { }, }; -class SDK { +class AwsProvider { + static getProviderName() { + return 'aws'; + } + constructor(serverless) { - // Defaults - this.sdk = AWS; this.serverless = serverless; + this.sdk = AWS; + this.provider = this; // only load plugin in an AWS service context + this.serverless.setProvider('aws', this); // Use HTTPS Proxy (Optional) const proxy = process.env.proxy @@ -187,4 +192,4 @@ class SDK { } } -module.exports = SDK; +module.exports = AwsProvider; diff --git a/lib/plugins/aws/tests/index.js b/lib/plugins/aws/provider/awsProvider.test.js similarity index 76% rename from lib/plugins/aws/tests/index.js rename to lib/plugins/aws/provider/awsProvider.test.js index 553d097cf..e4b9370f0 100644 --- a/lib/plugins/aws/tests/index.js +++ b/lib/plugins/aws/provider/awsProvider.test.js @@ -4,11 +4,11 @@ const sinon = require('sinon'); const BbPromise = require('bluebird'); const expect = require('chai').expect; const Serverless = require('../../../Serverless'); -const AwsSdk = require('../'); +const AwsProvider = require('./awsProvider'); const proxyquire = require('proxyquire'); -describe('AWS SDK', () => { - let awsSdk; +describe('AwsProvider', () => { + let awsProvider; let serverless; beforeEach(() => { @@ -17,24 +17,34 @@ describe('AWS SDK', () => { region: 'us-east-1', }; serverless = new Serverless(options); - awsSdk = new AwsSdk(serverless, options); - awsSdk.serverless.cli = new serverless.classes.CLI(); + awsProvider = new AwsProvider(serverless, options); + awsProvider.serverless.cli = new serverless.classes.CLI(); + }); + + describe('#getProviderName()', () => { + it('should return the provider name', () => { + expect(AwsProvider.getProviderName()).to.equal('aws'); + }); }); describe('#constructor()', () => { - it('should set AWS instance', () => { - expect(typeof awsSdk.sdk).to.not.equal('undefined'); + it('should set Serverless instance', () => { + expect(typeof awsProvider.serverless).to.not.equal('undefined'); }); - it('should set Serverless instance', () => { - expect(typeof awsSdk.serverless).to.not.equal('undefined'); + it('should set AWS instance', () => { + expect(typeof awsProvider.sdk).to.not.equal('undefined'); + }); + + it('should set the provider property', () => { + expect(awsProvider.provider).to.equal(awsProvider); }); it('should set AWS proxy', () => { process.env.proxy = 'http://a.b.c.d:n'; - const newAwsSdk = new AwsSdk(serverless); + const newAwsProvider = new AwsProvider(serverless); - expect(typeof newAwsSdk.sdk.config.httpOptions.agent).to.not.equal('undefined'); + expect(typeof newAwsProvider.sdk.config.httpOptions.agent).to.not.equal('undefined'); // clear env delete process.env.proxy; @@ -42,9 +52,9 @@ describe('AWS SDK', () => { it('should set AWS timeout', () => { process.env.AWS_CLIENT_TIMEOUT = '120000'; - const newAwsSdk = new AwsSdk(serverless); + const newAwsProvider = new AwsProvider(serverless); - expect(typeof newAwsSdk.sdk.config.httpOptions.timeout).to.not.equal('undefined'); + expect(typeof newAwsProvider.sdk.config.httpOptions.timeout).to.not.equal('undefined'); // clear env delete process.env.AWS_CLIENT_TIMEOUT; @@ -65,10 +75,10 @@ describe('AWS SDK', () => { }; } } - awsSdk.sdk = { + awsProvider.sdk = { S3: FakeS3, }; - awsSdk.serverless.service.environment = { + awsProvider.serverless.service.environment = { vars: {}, stages: { dev: { @@ -80,7 +90,7 @@ describe('AWS SDK', () => { }, }; - return awsSdk.request('S3', 'putObject', {}, 'dev', 'us-east-1').then(data => { + return awsProvider.request('S3', 'putObject', {}, 'dev', 'us-east-1').then(data => { expect(data.called).to.equal(true); }); }); @@ -110,10 +120,10 @@ describe('AWS SDK', () => { }; } } - awsSdk.sdk = { + awsProvider.sdk = { S3: FakeS3, }; - awsSdk.request('S3', 'error', {}, 'dev', 'us-east-1') + awsProvider.request('S3', 'error', {}, 'dev', 'us-east-1') .then(data => { // eslint-disable-next-line no-unused-expressions expect(data).to.exist; @@ -142,10 +152,10 @@ describe('AWS SDK', () => { }; } } - awsSdk.sdk = { + awsProvider.sdk = { S3: FakeS3, }; - awsSdk.request('S3', 'error', {}, 'dev', 'us-east-1') + awsProvider.request('S3', 'error', {}, 'dev', 'us-east-1') .then(() => done('Should not succeed')) .catch(() => done()); }); @@ -168,10 +178,10 @@ describe('AWS SDK', () => { }; } } - awsSdk.sdk = { + awsProvider.sdk = { S3: FakeS3, }; - awsSdk.request('S3', 'error', {}, 'dev', 'us-east-1') + awsProvider.request('S3', 'error', {}, 'dev', 'us-east-1') .then(() => done('Should not succeed')) .catch((err) => { expect(err.message).to.contain('https://git.io/viZAC'); @@ -188,36 +198,36 @@ describe('AWS SDK', () => { return config; }; const awsStub = sinon.stub().returns(); - const AwsSdkProxyquired = proxyquire('../index.js', { + const AwsProviderProxyquired = proxyquire('./awsProvider.js', { 'aws-sdk': awsStub, }); - let newAwsSdk; + let newAwsProvider; beforeEach(() => { - newAwsSdk = new AwsSdkProxyquired(serverless); + newAwsProvider = new AwsProviderProxyquired(serverless); }); it('should set region for credentials', () => { - const credentials = newAwsSdk.getCredentials('teststage', 'testregion'); + const credentials = newAwsProvider.getCredentials('teststage', 'testregion'); expect(credentials.region).to.equal('testregion'); }); it('should get credentials from provider', () => { serverless.service.provider.profile = 'notDefault'; - const credentials = newAwsSdk.getCredentials(); + const credentials = newAwsProvider.getCredentials(); expect(credentials.credentials.profile).to.equal('notDefault'); }); it('should not set credentials if empty profile is set', () => { serverless.service.provider.profile = ''; - const credentials = mockCreds(newAwsSdk.getCredentials('teststage', 'testregion')); + const credentials = mockCreds(newAwsProvider.getCredentials('teststage', 'testregion')); expect(credentials).to.eql({ region: 'testregion' }); }); it('should not set credentials if credentials is an empty object', () => { serverless.service.provider.credentials = {}; - const credentials = mockCreds(newAwsSdk.getCredentials('teststage', 'testregion')); + const credentials = mockCreds(newAwsProvider.getCredentials('teststage', 'testregion')); expect(credentials).to.eql({ region: 'testregion' }); }); @@ -227,7 +237,7 @@ describe('AWS SDK', () => { secretAccessKey: undefined, sessionToken: undefined, }; - const credentials = mockCreds(newAwsSdk.getCredentials('teststage', 'testregion')); + const credentials = mockCreds(newAwsProvider.getCredentials('teststage', 'testregion')); expect(credentials).to.eql({ region: 'testregion' }); }); @@ -237,7 +247,7 @@ describe('AWS SDK', () => { secretAccessKey: '', sessionToken: '', }; - const credentials = mockCreds(newAwsSdk.getCredentials('teststage', 'testregion')); + const credentials = mockCreds(newAwsProvider.getCredentials('teststage', 'testregion')); expect(credentials).to.eql({ region: 'testregion' }); }); @@ -255,7 +265,7 @@ describe('AWS SDK', () => { secretAccessKey: 'secretAccessKey', sessionToken: 'sessionToken', }; - const credentials = newAwsSdk.getCredentials('teststage', 'testregion'); + const credentials = newAwsProvider.getCredentials('teststage', 'testregion'); expect(credentials.credentials).to.deep.eql(serverless.service.provider.credentials); process.env.AWS_ACCESS_KEY_ID = tmpAccessKeyID; @@ -277,7 +287,7 @@ describe('AWS SDK', () => { process.env.AWS_ACCESS_KEY_ID = testVal.accessKeyId; process.env.AWS_SECRET_ACCESS_KEY = testVal.secretAccessKey; process.env.AWS_SESSION_TOKEN = testVal.sessionToken; - const credentials = newAwsSdk.getCredentials('teststage', 'testregion'); + const credentials = newAwsProvider.getCredentials('teststage', 'testregion'); process.env.AWS_ACCESS_KEY_ID = prevVal.accessKeyId; process.env.AWS_SECRET_ACCESS_KEY = prevVal.secretAccessKey; process.env.AWS_SESSION_TOKEN = prevVal.sessionToken; @@ -298,7 +308,7 @@ describe('AWS SDK', () => { process.env.AWS_TESTSTAGE_ACCESS_KEY_ID = testVal.accessKeyId; process.env.AWS_TESTSTAGE_SECRET_ACCESS_KEY = testVal.secretAccessKey; process.env.AWS_TESTSTAGE_SESSION_TOKEN = testVal.sessionToken; - const credentials = newAwsSdk.getCredentials('teststage', 'testregion'); + const credentials = newAwsProvider.getCredentials('teststage', 'testregion'); process.env.AWS_TESTSTAGE_ACCESS_KEY_ID = prevVal.accessKeyId; process.env.AWS_TESTSTAGE_SECRET_ACCESS_KEY = prevVal.secretAccessKey; process.env.AWS_TESTSTAGE_SESSION_TOKEN = prevVal.sessionToken; @@ -307,26 +317,26 @@ describe('AWS SDK', () => { it('should not set credentials if profile is not set', () => { serverless.service.provider.profile = undefined; - const credentials = mockCreds(newAwsSdk.getCredentials('teststage', 'testregion')); + const credentials = mockCreds(newAwsProvider.getCredentials('teststage', 'testregion')); expect(credentials).to.eql({ region: 'testregion' }); }); it('should not set credentials if empty profile is set', () => { serverless.service.provider.profile = ''; - const credentials = mockCreds(newAwsSdk.getCredentials('teststage', 'testregion')); + const credentials = mockCreds(newAwsProvider.getCredentials('teststage', 'testregion')); expect(credentials).to.eql({ region: 'testregion' }); }); it('should get credentials from provider declared profile', () => { serverless.service.provider.profile = 'notDefault'; - const credentials = newAwsSdk.getCredentials(); + const credentials = newAwsProvider.getCredentials(); expect(credentials.credentials.profile).to.equal('notDefault'); }); it('should get credentials from environment declared for-all-stages profile', () => { const prevVal = process.env.AWS_PROFILE; process.env.AWS_PROFILE = 'notDefault'; - const credentials = newAwsSdk.getCredentials(); + const credentials = newAwsProvider.getCredentials(); process.env.AWS_PROFILE = prevVal; expect(credentials.credentials.profile).to.equal('notDefault'); }); @@ -334,7 +344,7 @@ describe('AWS SDK', () => { it('should get credentials from environment declared stage-specific profile', () => { const prevVal = process.env.AWS_TESTSTAGE_PROFILE; process.env.AWS_TESTSTAGE_PROFILE = 'notDefault'; - const credentials = newAwsSdk.getCredentials('teststage', 'testregion'); + const credentials = newAwsProvider.getCredentials('teststage', 'testregion'); process.env.AWS_TESTSTAGE_PROFILE = prevVal; expect(credentials.credentials.profile).to.equal('notDefault'); }); @@ -348,25 +358,25 @@ describe('AWS SDK', () => { }; const describeStackResourcesStub = sinon - .stub(awsSdk, 'request') + .stub(awsProvider, 'request') .returns(BbPromise.resolve({ StackResourceDetail: { PhysicalResourceId: 'serverlessDeploymentBucketName', }, })); - return awsSdk.getServerlessDeploymentBucketName(options.stage, options.region) + return awsProvider.getServerlessDeploymentBucketName(options.stage, options.region) .then((bucketName) => { expect(describeStackResourcesStub.calledOnce).to.be.equal(true); expect(describeStackResourcesStub.calledWith(options.stage, options.region)); expect(describeStackResourcesStub.args[0][0]).to.equal('CloudFormation'); expect(describeStackResourcesStub.args[0][1]).to.equal('describeStackResource'); expect(describeStackResourcesStub.args[0][2].StackName) - .to.equal(`${awsSdk.serverless.service.service}-${options.stage}`); + .to.equal(`${awsProvider.serverless.service.service}-${options.stage}`); expect(bucketName).to.equal('serverlessDeploymentBucketName'); - awsSdk.request.restore(); + awsProvider.request.restore(); }); }); }); @@ -375,7 +385,7 @@ describe('AWS SDK', () => { it('should return the stack name', () => { serverless.service.service = 'myservice'; - expect(awsSdk.getStackName('dev')).to.equal('myservice-dev'); + expect(awsProvider.getStackName('dev')).to.equal('myservice-dev'); }); }); }); diff --git a/lib/plugins/aws/remove/index.js b/lib/plugins/aws/remove/index.js index 90d1149c6..e7ca3ca5b 100644 --- a/lib/plugins/aws/remove/index.js +++ b/lib/plugins/aws/remove/index.js @@ -6,14 +6,11 @@ const monitorStack = require('../lib/monitorStack'); const emptyS3Bucket = require('./lib/bucket'); const removeStack = require('./lib/stack'); -const SDK = require('../'); - class AwsRemove { constructor(serverless, options) { this.serverless = serverless; this.options = options || {}; - this.provider = 'aws'; - this.sdk = new SDK(serverless); + this.provider = this.serverless.getProvider('aws'); Object.assign(this, validate, emptyS3Bucket, removeStack, monitorStack); diff --git a/lib/plugins/aws/remove/lib/bucket.js b/lib/plugins/aws/remove/lib/bucket.js index b0037c9e0..b20f7b755 100644 --- a/lib/plugins/aws/remove/lib/bucket.js +++ b/lib/plugins/aws/remove/lib/bucket.js @@ -4,7 +4,7 @@ const BbPromise = require('bluebird'); module.exports = { setServerlessDeploymentBucketName() { - return this.sdk.getServerlessDeploymentBucketName(this.options.stage, this.options.region) + return this.provider.getServerlessDeploymentBucketName(this.options.stage, this.options.region) .then((bucketName) => { this.bucketName = bucketName; }); @@ -15,7 +15,7 @@ module.exports = { this.serverless.cli.log('Getting all objects in S3 bucket...'); const serviceStage = `${this.serverless.service.service}/${this.options.stage}`; - return this.sdk.request('S3', 'listObjectsV2', { + return this.provider.request('S3', 'listObjectsV2', { Bucket: this.bucketName, Prefix: `serverless/${serviceStage}`, }, this.options.stage, this.options.region).then((result) => { @@ -33,7 +33,7 @@ module.exports = { deleteObjects() { this.serverless.cli.log('Removing objects in S3 bucket...'); if (this.objectsInBucket.length) { - return this.sdk.request('S3', 'deleteObjects', { + return this.provider.request('S3', 'deleteObjects', { Bucket: this.bucketName, Delete: { Objects: this.objectsInBucket, diff --git a/lib/plugins/aws/remove/lib/stack.js b/lib/plugins/aws/remove/lib/stack.js index fafe95e0a..243dd4e2a 100644 --- a/lib/plugins/aws/remove/lib/stack.js +++ b/lib/plugins/aws/remove/lib/stack.js @@ -13,7 +13,7 @@ module.exports = { StackId: stackName, }; - return this.sdk.request('CloudFormation', + return this.provider.request('CloudFormation', 'deleteStack', params, this.options.stage, diff --git a/lib/plugins/aws/remove/tests/bucket.js b/lib/plugins/aws/remove/tests/bucket.js index 4ff8de3f1..a06593d0c 100644 --- a/lib/plugins/aws/remove/tests/bucket.js +++ b/lib/plugins/aws/remove/tests/bucket.js @@ -2,12 +2,14 @@ const expect = require('chai').expect; const sinon = require('sinon'); +const AwsProvider = require('../../provider/awsProvider'); const AwsRemove = require('../index'); const Serverless = require('../../../../Serverless'); const BbPromise = require('bluebird'); describe('emptyS3Bucket', () => { const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); let awsRemove; @@ -23,7 +25,7 @@ describe('emptyS3Bucket', () => { describe('#setServerlessDeploymentBucketName()', () => { it('should store the name of the Serverless deployment bucket in the "this" variable', () => { const getServerlessDeploymentBucketNameStub = sinon - .stub(awsRemove.sdk, 'getServerlessDeploymentBucketName') + .stub(awsRemove.provider, 'getServerlessDeploymentBucketName') .returns(BbPromise.resolve('new-service-dev-us-east-1-12345678')); return awsRemove.setServerlessDeploymentBucketName().then(() => { @@ -31,14 +33,14 @@ describe('emptyS3Bucket', () => { expect(getServerlessDeploymentBucketNameStub.calledOnce).to.be.equal(true); expect(getServerlessDeploymentBucketNameStub .calledWith(awsRemove.options.stage, awsRemove.options.region)); - awsRemove.sdk.getServerlessDeploymentBucketName.restore(); + awsRemove.provider.getServerlessDeploymentBucketName.restore(); }); }); }); describe('#listObjects()', () => { it('should resolve if no objects are present', () => { - const listObjectsStub = sinon.stub(awsRemove.sdk, 'request') + const listObjectsStub = sinon.stub(awsRemove.provider, 'request') .returns(BbPromise.resolve()); return awsRemove.listObjects().then(() => { @@ -49,12 +51,12 @@ describe('emptyS3Bucket', () => { expect(listObjectsStub.args[0][2].Bucket) .to.be.equal(awsRemove.bucketName); expect(awsRemove.objectsInBucket.length).to.equal(0); - awsRemove.sdk.request.restore(); + awsRemove.provider.request.restore(); }); }); it('should push objects to the array if present', () => { - const listObjectsStub = sinon.stub(awsRemove.sdk, 'request') + const listObjectsStub = sinon.stub(awsRemove.provider, 'request') .returns(BbPromise.resolve({ Contents: [ { Key: 'object1' }, @@ -71,7 +73,7 @@ describe('emptyS3Bucket', () => { .to.be.equal(awsRemove.bucketName); expect(awsRemove.objectsInBucket[0]).to.deep.equal({ Key: 'object1' }); expect(awsRemove.objectsInBucket[1]).to.deep.equal({ Key: 'object2' }); - awsRemove.sdk.request.restore(); + awsRemove.provider.request.restore(); }); }); }); @@ -80,7 +82,7 @@ describe('emptyS3Bucket', () => { it('should delete all objects in the S3 bucket', () => { awsRemove.objectsInBucket = [{ Key: 'foo' }]; - const deleteObjectsStub = sinon.stub(awsRemove.sdk, 'request') + const deleteObjectsStub = sinon.stub(awsRemove.provider, 'request') .returns(BbPromise.resolve()); return awsRemove.deleteObjects().then(() => { @@ -90,7 +92,7 @@ describe('emptyS3Bucket', () => { .to.be.equal(awsRemove.bucketName); expect(deleteObjectsStub.args[0][2].Delete.Objects) .to.be.equal(awsRemove.objectsInBucket); - awsRemove.sdk.request.restore(); + awsRemove.provider.request.restore(); }); }); diff --git a/lib/plugins/aws/remove/tests/index.js b/lib/plugins/aws/remove/tests/index.js index 6a725aa27..db6543ea7 100644 --- a/lib/plugins/aws/remove/tests/index.js +++ b/lib/plugins/aws/remove/tests/index.js @@ -3,7 +3,8 @@ const expect = require('chai').expect; const BbPromise = require('bluebird'); const sinon = require('sinon'); -const AwsRemove = require('../'); +const AwsProvider = require('../../provider/awsProvider'); +const AwsRemove = require('../index'); const Serverless = require('../../../../Serverless'); describe('AwsRemove', () => { @@ -12,14 +13,15 @@ describe('AwsRemove', () => { stage: 'dev', region: 'us-east-1', }; + serverless.setProvider('aws', new AwsProvider(serverless)); const awsRemove = new AwsRemove(serverless, options); awsRemove.serverless.cli = new serverless.classes.CLI(); describe('#constructor()', () => { it('should have hooks', () => expect(awsRemove.hooks).to.be.not.empty); - it('should set the provider variable to "aws"', () => expect(awsRemove.provider) - .to.equal('aws')); + it('should set the provider variable to an instance of AwsProvider', () => + expect(awsRemove.provider).to.be.instanceof(AwsProvider)); it('should have access to the serverless instance', () => { expect(awsRemove.serverless).to.deep.equal(serverless); diff --git a/lib/plugins/aws/remove/tests/stack.js b/lib/plugins/aws/remove/tests/stack.js index d703edead..ddaa35640 100644 --- a/lib/plugins/aws/remove/tests/stack.js +++ b/lib/plugins/aws/remove/tests/stack.js @@ -2,12 +2,14 @@ const expect = require('chai').expect; const sinon = require('sinon'); +const AwsProvider = require('../../provider/awsProvider'); const AwsRemove = require('../index'); const Serverless = require('../../../../Serverless'); const BbPromise = require('bluebird'); describe('removeStack', () => { const serverless = new Serverless(); + serverless.setProvider('aws', new AwsProvider(serverless)); let awsRemove; @@ -23,12 +25,12 @@ describe('removeStack', () => { describe('#remove()', () => { it('should remove a stack', () => { const removeStackStub = sinon - .stub(awsRemove.sdk, 'request').returns(BbPromise.resolve()); + .stub(awsRemove.provider, 'request').returns(BbPromise.resolve()); return awsRemove.remove().then(() => { expect(removeStackStub.calledOnce).to.be.equal(true); expect(removeStackStub.calledWith(awsRemove.options.stage, awsRemove.options.region)); - awsRemove.sdk.request.restore(); + awsRemove.provider.request.restore(); }); }); }); diff --git a/lib/plugins/aws/tests/monitorStack.js b/lib/plugins/aws/tests/monitorStack.js index d07ebf812..35a1fcf8d 100644 --- a/lib/plugins/aws/tests/monitorStack.js +++ b/lib/plugins/aws/tests/monitorStack.js @@ -4,7 +4,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); const BbPromise = require('bluebird'); const Serverless = require('../../../Serverless'); -const SDK = require('../index'); +const AwsProvider = require('../provider/awsProvider'); const CLI = require('../../../classes/CLI'); const monitorStack = require('../lib/monitorStack'); @@ -14,7 +14,7 @@ describe('monitorStack', () => { beforeEach(() => { awsPlugin.serverless = serverless; - awsPlugin.sdk = new SDK(serverless); + awsPlugin.provider = new AwsProvider(serverless); awsPlugin.serverless.cli = new CLI(serverless); awsPlugin.options = { stage: 'dev', @@ -27,28 +27,28 @@ describe('monitorStack', () => { describe('#monitorStack()', () => { it('should skip monitoring if the --noDeploy option is specified', () => { awsPlugin.options.noDeploy = true; - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); const cfDataMock = { StackId: 'new-service-dev', }; return awsPlugin.monitorStack('update', cfDataMock, 10).then(() => { expect(describeStackEventsStub.callCount).to.be.equal(0); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); it('should skip monitoring if the stack was already created', () => { - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); return awsPlugin.monitorStack('update', 'alreadyCreated', 10).then(() => { expect(describeStackEventsStub.callCount).to.be.equal(0); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); it('should keep monitoring until CREATE_COMPLETE stack status', () => { - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); const cfDataMock = { StackId: 'new-service-dev', }; @@ -87,12 +87,12 @@ describe('monitorStack', () => { awsPlugin.options.region )); expect(stackStatus).to.be.equal('CREATE_COMPLETE'); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); it('should keep monitoring until UPDATE_COMPLETE stack status', () => { - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); const cfDataMock = { StackId: 'new-service-dev', }; @@ -131,12 +131,12 @@ describe('monitorStack', () => { awsPlugin.options.region )); expect(stackStatus).to.be.equal('UPDATE_COMPLETE'); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); it('should keep monitoring until DELETE_COMPLETE stack status', () => { - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); const cfDataMock = { StackId: 'new-service-dev', }; @@ -175,12 +175,12 @@ describe('monitorStack', () => { awsPlugin.options.region )); expect(stackStatus).to.be.equal('DELETE_COMPLETE'); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); it('should keep monitoring until DELETE_COMPLETE or stack not found catch', () => { - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); const cfDataMock = { StackId: 'new-service-dev', }; @@ -211,13 +211,13 @@ describe('monitorStack', () => { awsPlugin.options.region )); expect(stackStatus).to.be.equal('DELETE_COMPLETE'); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); it('should output all stack events information with the --verbose option', () => { awsPlugin.options.verbose = true; - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); const cfDataMock = { StackId: 'new-service-dev', }; @@ -283,12 +283,12 @@ describe('monitorStack', () => { awsPlugin.options.stage, awsPlugin.options.region )); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); it('should catch describeStackEvents error if stack was not in deleting state', () => { - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); const cfDataMock = { StackId: 'new-service-dev', }; @@ -307,12 +307,12 @@ describe('monitorStack', () => { awsPlugin.options.stage, awsPlugin.options.region )); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); it('should throw an error and exit immediataley if statck status is *_FAILED', () => { - const describeStackEventsStub = sinon.stub(awsPlugin.sdk, 'request'); + const describeStackEventsStub = sinon.stub(awsPlugin.provider, 'request'); const cfDataMock = { StackId: 'new-service-dev', }; @@ -380,7 +380,7 @@ describe('monitorStack', () => { awsPlugin.options.stage, awsPlugin.options.region )); - awsPlugin.sdk.request.restore(); + awsPlugin.provider.request.restore(); }); }); }); diff --git a/package.json b/package.json index 33a4947a8..3b8027879 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "sls": "./bin/serverless" }, "scripts": { - "test": "istanbul cover node_modules/mocha/bin/_mocha tests/all -- -R spec --recursive", + "test": "istanbul cover -x '**/*.test.js' node_modules/mocha/bin/_mocha tests/all -- -R spec --recursive", "lint": "eslint .", "simple-integration-test": "mocha tests/integration/simple-integration-test", "complex-integration-test": "mocha tests/integration/all" diff --git a/tests/all.js b/tests/all.js index e15c40095..0fcb26610 100644 --- a/tests/all.js +++ b/tests/all.js @@ -1,14 +1,14 @@ 'use strict'; // Serverless Core Tests -require('./classes/Serverless'); -require('./classes/PluginManager'); -require('./classes/Utils'); -require('./classes/Config'); -require('./classes/Service'); -require('./classes/Variables'); -require('./classes/YamlParser'); -require('./classes/CLI'); +require('../lib/Serverless.test'); +require('../lib/classes/PluginManager.test'); +require('../lib/classes/Utils.test'); +require('../lib/classes/Config.test'); +require('../lib/classes/Service.test'); +require('../lib/classes/Variables.test'); +require('../lib/classes/YamlParser.test'); +require('../lib/classes/CLI.test'); // Core Plugins Tests require('../lib/plugins/create/tests/create'); @@ -22,7 +22,7 @@ require('../lib/plugins/package/tests/all'); require('../lib/plugins/slstats/tests/slstats'); // AWS Plugins Tests -require('../lib/plugins/aws/tests'); +require('../lib/plugins/aws/provider/awsProvider.test'); require('../lib/plugins/aws/tests/validate'); require('../lib/plugins/aws/tests/monitorStack'); require('../lib/plugins/aws/info/tests');