From 816ba289e57a95e64c294badacedd378d44cd1b8 Mon Sep 17 00:00:00 2001 From: Roger Lam Date: Sat, 20 Aug 2016 00:46:54 -0700 Subject: [PATCH 01/85] Add failing test for name option in create --- lib/plugins/create/tests/create.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/plugins/create/tests/create.js b/lib/plugins/create/tests/create.js index 2c9e2bf7e..6130e10a2 100644 --- a/lib/plugins/create/tests/create.js +++ b/lib/plugins/create/tests/create.js @@ -42,6 +42,23 @@ describe('Create', () => { expect(() => create.create()).to.throw(Error); }); + it('should overwrite the name for the service if user passed name', () => { + const tmpDir = path.join(os.tmpdir(), (new Date()).getTime().toString()); + const cwd = process.cwd(); + fse.mkdirsSync(tmpDir); + process.chdir(tmpDir); + create.options.template = 'aws-nodejs'; + create.options.name = 'my_service'; + + return create.create().then(() => { + return create.serverless.yamlParser.parse(path.join(tmpDir, 'serverless.yml')).then((obj) => { + expect(obj.service).to.equal('my_service') + + process.chdir(cwd); + }); + }); + }); + it('should set servicePath based on cwd', () => { const tmpDir = path.join(os.tmpdir(), (new Date()).getTime().toString()); const cwd = process.cwd(); From 1fb8b0c2df2758ecd5207b331c633d61d5374e21 Mon Sep 17 00:00:00 2001 From: Sanghee Kim Date: Tue, 6 Sep 2016 18:11:34 -0400 Subject: [PATCH 02/85] follow pep8 - Python style guide checker --- lib/plugins/create/templates/aws-python/handler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/plugins/create/templates/aws-python/handler.py b/lib/plugins/create/templates/aws-python/handler.py index d06142357..fc62ea2d0 100644 --- a/lib/plugins/create/templates/aws-python/handler.py +++ b/lib/plugins/create/templates/aws-python/handler.py @@ -1,2 +1,5 @@ def hello(event, context): - return { "message": "Go Serverless v1.0! Your function executed successfully!", "event": event } + return { + "message": "Go Serverless v1.0! Your function executed successfully!", + "event": event + } From ef1433f9902d1452d1b2e8379815dde5914c10c7 Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Tue, 6 Sep 2016 15:53:55 -0700 Subject: [PATCH 03/85] Provide explicit relative path for '_mocha' in package.json test script configuration to support Windows. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79e1e71a1..49fe06809 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "serverless-run-python-handler": "./bin/serverless-run-python-handler" }, "scripts": { - "test": "istanbul cover _mocha tests/all -- -R spec --recursive", + "test": "istanbul cover node_modules/mocha/bin/_mocha tests/all -- -R spec --recursive", "lint": "eslint .", "integration-test": "mocha tests/integration_test" }, From 2fabfcc6f0f1f2e3a126855e7bba33062adf3c51 Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Tue, 6 Sep 2016 15:57:57 -0700 Subject: [PATCH 04/85] Add explicit 'node' prefix to command used to invoke serverless on Windows in unit tests. --- tests/classes/PluginManager.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/classes/PluginManager.js b/tests/classes/PluginManager.js index ceffbb05e..d77cd7ce7 100644 --- a/tests/classes/PluginManager.js +++ b/tests/classes/PluginManager.js @@ -765,8 +765,11 @@ describe('PluginManager', () => { describe('Plugin/CLI integration', () => { const serverlessInstance = new Serverless(); serverlessInstance.init(); - const serverlessExec = path.join(serverlessInstance.config.serverlessPath, - '..', 'bin', 'serverless'); + + // Cannot rely on shebang in severless.js to invoke script using NodeJS on Windows. Add 'node' to command. + const execPrefix = os.platform() === 'win32' ? 'node ' : null; + const serverlessExec = execPrefix + path.join(serverlessInstance.config.serverlessPath, + '..', 'bin', 'serverless'); const tmpDir = testUtils.getTmpDirPath(); fse.mkdirSync(tmpDir); const cwd = process.cwd(); From 0f1650bbdf28bc1fed20dab9197d69553fef5aa4 Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Tue, 6 Sep 2016 15:57:57 -0700 Subject: [PATCH 05/85] Add explicit 'node' prefix to command used to invoke serverless on Windows in unit tests. This will help fix #2044 --- tests/classes/PluginManager.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/classes/PluginManager.js b/tests/classes/PluginManager.js index ceffbb05e..d77cd7ce7 100644 --- a/tests/classes/PluginManager.js +++ b/tests/classes/PluginManager.js @@ -765,8 +765,11 @@ describe('PluginManager', () => { describe('Plugin/CLI integration', () => { const serverlessInstance = new Serverless(); serverlessInstance.init(); - const serverlessExec = path.join(serverlessInstance.config.serverlessPath, - '..', 'bin', 'serverless'); + + // Cannot rely on shebang in severless.js to invoke script using NodeJS on Windows. Add 'node' to command. + const execPrefix = os.platform() === 'win32' ? 'node ' : null; + const serverlessExec = execPrefix + path.join(serverlessInstance.config.serverlessPath, + '..', 'bin', 'serverless'); const tmpDir = testUtils.getTmpDirPath(); fse.mkdirSync(tmpDir); const cwd = process.cwd(); From a8f4639a41024978f4fd68e5161bd612fe4436b0 Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Tue, 6 Sep 2016 16:20:04 -0700 Subject: [PATCH 06/85] Adds missing 'os' variable to support testing OS version. --- tests/classes/PluginManager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/classes/PluginManager.js b/tests/classes/PluginManager.js index d77cd7ce7..3e96b2d80 100644 --- a/tests/classes/PluginManager.js +++ b/tests/classes/PluginManager.js @@ -10,6 +10,7 @@ const fse = require('fs-extra'); const execSync = require('child_process').execSync; const mockRequire = require('mock-require'); const testUtils = require('../../tests/utils'); +const os = require('os'); describe('PluginManager', () => { let pluginManager; From 810d52ece57abe0d0dae25bace5b94a38b2ad09a Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Tue, 6 Sep 2016 16:32:35 -0700 Subject: [PATCH 07/85] Use '' instead of null for non-windows command. --- tests/classes/PluginManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/classes/PluginManager.js b/tests/classes/PluginManager.js index 3e96b2d80..3bb21a0da 100644 --- a/tests/classes/PluginManager.js +++ b/tests/classes/PluginManager.js @@ -768,7 +768,7 @@ describe('PluginManager', () => { serverlessInstance.init(); // Cannot rely on shebang in severless.js to invoke script using NodeJS on Windows. Add 'node' to command. - const execPrefix = os.platform() === 'win32' ? 'node ' : null; + const execPrefix = os.platform() === 'win32' ? 'node ' : ''; const serverlessExec = execPrefix + path.join(serverlessInstance.config.serverlessPath, '..', 'bin', 'serverless'); const tmpDir = testUtils.getTmpDirPath(); From 049aec6bc0cdf4fa16273ffb87a88f9637f7f83e Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Tue, 6 Sep 2016 16:57:34 -0700 Subject: [PATCH 08/85] Shorten comment which was too long. --- tests/classes/PluginManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/classes/PluginManager.js b/tests/classes/PluginManager.js index 3bb21a0da..44b9bba34 100644 --- a/tests/classes/PluginManager.js +++ b/tests/classes/PluginManager.js @@ -767,7 +767,7 @@ describe('PluginManager', () => { const serverlessInstance = new Serverless(); serverlessInstance.init(); - // Cannot rely on shebang in severless.js to invoke script using NodeJS on Windows. Add 'node' to command. + // Cannot rely on shebang in severless.js to invoke script using NodeJS on Windows. const execPrefix = os.platform() === 'win32' ? 'node ' : ''; const serverlessExec = execPrefix + path.join(serverlessInstance.config.serverlessPath, '..', 'bin', 'serverless'); From 4544bf3d893415aed185fae24c1a0a70421b3374 Mon Sep 17 00:00:00 2001 From: Fabien Ruffin Date: Wed, 7 Sep 2016 15:36:45 +1000 Subject: [PATCH 09/85] Updated API Gateway integration to return 500 when Lambda functions time out --- lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js index 521925868..24c10cbb0 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js @@ -333,7 +333,7 @@ module.exports = { { StatusCode: 422, SelectionPattern: '.*\\[422\\].*' }, { StatusCode: 500, SelectionPattern: - '.*(Process\\s?exited\\s?before\\s?completing\\s?request|\\[500\\]).*' }, + '.*(Process\\s?exited\\s?before\\s?completing\\s?request|Task\\s?timed\\s?out\\s?after\\s?|\\[500\\]).*' }, { StatusCode: 502, SelectionPattern: '.*\\[502\\].*' }, { StatusCode: 504, SelectionPattern: '.*\\[504\\].*' } ); From a568d4f236dab0a97a4c15dc8ca0f7ded0c22ead Mon Sep 17 00:00:00 2001 From: Fabien Ruffin Date: Wed, 7 Sep 2016 15:45:47 +1000 Subject: [PATCH 10/85] Updated tests for API Gateway response integration --- .../aws/deploy/compile/events/apiGateway/tests/methods.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/methods.js b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/methods.js index 563eb5053..28d5da4fc 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/methods.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/methods.js @@ -665,7 +665,7 @@ describe('#compileMethods()', () => { awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersListGet.Properties.Integration.IntegrationResponses[6] ).to.deep.equal({ StatusCode: 500, - SelectionPattern: '.*(Process\\s?exited\\s?before\\s?completing\\s?request|\\[500\\]).*' }); + SelectionPattern: '.*(Process\\s?exited\\s?before\\s?completing\\s?request|Task\\s?timed\\s?out\\s?after\\s?|\\[500\\]).*' }); expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersListGet.Properties.Integration.IntegrationResponses[7] From 2f296c296f0ce1624d89e336f792c8648f7a6aa6 Mon Sep 17 00:00:00 2001 From: Fabien Ruffin Date: Wed, 7 Sep 2016 17:15:24 +1000 Subject: [PATCH 11/85] Fixed linting issues --- .../aws/deploy/compile/events/apiGateway/lib/methods.js | 3 ++- .../aws/deploy/compile/events/apiGateway/tests/methods.js | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js index 24c10cbb0..e150d834e 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/methods.js @@ -333,7 +333,8 @@ module.exports = { { StatusCode: 422, SelectionPattern: '.*\\[422\\].*' }, { StatusCode: 500, SelectionPattern: - '.*(Process\\s?exited\\s?before\\s?completing\\s?request|Task\\s?timed\\s?out\\s?after\\s?|\\[500\\]).*' }, + // eslint-disable-next-line max-len + '.*(Process\\s?exited\\s?before\\s?completing\\s?request|Task\\s?timed\\s?out\\s?|\\[500\\]).*' }, { StatusCode: 502, SelectionPattern: '.*\\[502\\].*' }, { StatusCode: 504, SelectionPattern: '.*\\[504\\].*' } ); diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/methods.js b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/methods.js index 28d5da4fc..ab169a668 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/methods.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/methods.js @@ -665,7 +665,9 @@ describe('#compileMethods()', () => { awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersListGet.Properties.Integration.IntegrationResponses[6] ).to.deep.equal({ StatusCode: 500, - SelectionPattern: '.*(Process\\s?exited\\s?before\\s?completing\\s?request|Task\\s?timed\\s?out\\s?after\\s?|\\[500\\]).*' }); + SelectionPattern: + // eslint-disable-next-line max-len + '.*(Process\\s?exited\\s?before\\s?completing\\s?request|Task\\s?timed\\s?out\\s?|\\[500\\]).*' }); expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Resources.ApiGatewayMethodUsersListGet.Properties.Integration.IntegrationResponses[7] From b9440f18b4717fe147063a374a84cb0c28afe271 Mon Sep 17 00:00:00 2001 From: Mohamed GHARSALLAH Date: Thu, 8 Sep 2016 13:08:10 +0200 Subject: [PATCH 12/85] Add support for s3 events filter rules --- .../aws/deploy/compile/events/s3/index.js | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/compile/events/s3/index.js b/lib/plugins/aws/deploy/compile/events/s3/index.js index 4deb9ea71..e8c4aa261 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/index.js +++ b/lib/plugins/aws/deploy/compile/events/s3/index.js @@ -23,6 +23,7 @@ class AwsCompileS3Events { if (event.s3) { let bucketName; let notificationEvent = 's3:ObjectCreated:*'; + let filter = {}; if (typeof event.s3 === 'object') { if (!event.s3.bucket) { @@ -38,6 +39,36 @@ class AwsCompileS3Events { if (event.s3.event) { notificationEvent = event.s3.event; } + notificationEvent = event.s3.event; + if (event.s3.rules) { + if (_.isArray(event.s3.rules)) { + const rules = []; + event.s3.rules.forEach(rule => { + if (_.isPlainObject(rule)) { + const name = Object.keys(rule)[0]; + const value = rule[name]; + rules.push({ Name: name, Value: value }); + } else { + const errorMessage = [ + `S3 filter rule ${rule} of function ${functionName} is not an object`, + ' The correct syntax is: { Name: Value }', + ' Please check the docs for more info.', + ].join(''); + throw new this.serverless.classes + .Error(errorMessage); + } + }); + filter = { Filter: { S3Key: { Rules: rules } } }; + } else { + const errorMessage = [ + `S3 filter rules of function ${functionName} is not an array`, + ' The correct syntax is: rules: [{ Name: Value }]', + ' Please check the docs for more info.', + ].join(''); + throw new this.serverless.classes + .Error(errorMessage); + } + } } else if (typeof event.s3 === 'string') { bucketName = event.s3; } else { @@ -56,7 +87,7 @@ class AwsCompileS3Events { // check if the bucket already defined // in another S3 event in the service if (bucketsLambdaConfigurations[bucketName]) { - const newLambdaConfiguration = { + let newLambdaConfiguration = { Event: notificationEvent, Function: { 'Fn::GetAtt': [ @@ -66,6 +97,11 @@ class AwsCompileS3Events { }, }; + // Assign 'filter' if not empty + newLambdaConfiguration = _.assign( + newLambdaConfiguration, + filter + ); bucketsLambdaConfigurations[bucketName] .push(newLambdaConfiguration); } else { @@ -80,6 +116,11 @@ class AwsCompileS3Events { }, }, ]; + // Assign 'filter' if not empty + bucketsLambdaConfigurations[bucketName][0] = _.assign( + bucketsLambdaConfigurations[bucketName][0], + filter + ); } s3EnabledFunctions.push(functionName); } From 5209b69c6eed1ff80e3362b7f9daa85a1861d069 Mon Sep 17 00:00:00 2001 From: Mohamed GHARSALLAH Date: Thu, 8 Sep 2016 13:08:31 +0200 Subject: [PATCH 13/85] Add unit tests --- .../deploy/compile/events/s3/tests/index.js | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) 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 13e8ded85..09506aef0 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/tests/index.js +++ b/lib/plugins/aws/deploy/compile/events/s3/tests/index.js @@ -51,6 +51,40 @@ describe('AwsCompileS3Events', () => { expect(() => awsCompileS3Events.compileS3Events()).to.throw(Error); }); + it('should throw an error if the "rules" property is not an array', () => { + awsCompileS3Events.serverless.service.functions = { + first: { + events: [ + { + s3: { + bucket: 'first-function-bucket', + rules: {}, + }, + }, + ], + }, + }; + + expect(() => awsCompileS3Events.compileS3Events()).to.throw(Error); + }); + + it('should throw an error if the "rules" property is invalid', () => { + awsCompileS3Events.serverless.service.functions = { + first: { + events: [ + { + s3: { + bucket: 'first-function-bucket', + rules: [[]], + }, + }, + ], + }, + }; + + expect(() => awsCompileS3Events.compileS3Events()).to.throw(Error); + }); + it('should create corresponding resources when S3 events are given', () => { awsCompileS3Events.serverless.service.functions = { first: { @@ -62,6 +96,9 @@ describe('AwsCompileS3Events', () => { s3: { bucket: 'first-function-bucket-two', event: 's3:ObjectCreated:Put', + rules: [ + { prefix: 'subfolder/' }, + ], }, }, ], @@ -92,6 +129,9 @@ describe('AwsCompileS3Events', () => { s3: { bucket: 'first-function-bucket-one', event: 's3:ObjectCreated:Put', + rules: [ + { prefix: 'subfolder/' }, + ], }, }, ], From cb5f9d831a55263d5a4ad94bdc1f753a508df74e Mon Sep 17 00:00:00 2001 From: Mohamed GHARSALLAH Date: Thu, 8 Sep 2016 13:08:43 +0200 Subject: [PATCH 14/85] Update documentation --- docs/02-providers/aws/events/02-s3.md | 17 +++++++++++++++++ .../aws/deploy/compile/events/s3/README.md | 18 +++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/02-providers/aws/events/02-s3.md b/docs/02-providers/aws/events/02-s3.md index 9d0485f70..b992d7376 100644 --- a/docs/02-providers/aws/events/02-s3.md +++ b/docs/02-providers/aws/events/02-s3.md @@ -31,6 +31,23 @@ functions: event: s3:ObjectRemoved:* ``` +## Setting filter rules + +This will create a bucket `photos`. The `users` function is called whenever an image with `.jpg` extension is uploaded to folder `uploads` in the bucket. Check out the [AWS documentation](http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#notification-how-to-filtering) to learn more about all the different filter types that can be configured. + +```yml +functions: + users: + handler: users.handler + rules: + - s3: + bucket: photos + event: s3:ObjectCreated:* + rules: + - prefix: uploads/ + - suffix: .jpg +``` + ## Triggering separate functions from the same bucket You're able to repeat the S3 event configuration in the same or separate functions so one bucket can call these functions. One caveat though is that you can't repeat the same configuration in both functions, e.g. the event type has to be different. diff --git a/lib/plugins/aws/deploy/compile/events/s3/README.md b/lib/plugins/aws/deploy/compile/events/s3/README.md index c86575600..1e19d8cb8 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/README.md +++ b/lib/plugins/aws/deploy/compile/events/s3/README.md @@ -17,7 +17,7 @@ the bucket name you've defined and an additional lambda notification configurati function and the `s3:objectCreated:*` events. The second possibility is to configure your S3 event more granular (like the bucket name or the event which this bucket -should listen to) with the help of key value pairs. +should listen to and or the filters rules) with the help of key value pairs. Take a look at the [Event syntax examples](#event-syntax-examples) below to see how you can setup S3 bucket events. @@ -58,3 +58,19 @@ functions: bucket: confidential-information event: s3:ObjectRemoved:* ``` + +We can also specify filter rules. + +```yml +# serverless.yml +functions: + mail: + handler: mail.removal + events: + - s3: + bucket: confidential-information + event: s3:ObjectRemoved:* + rules: + - prefix: inbox/ + - suffix: .eml +``` \ No newline at end of file From af3da9a127c14b1e61e89375e43f6b5415b997f7 Mon Sep 17 00:00:00 2001 From: Mohamed GHARSALLAH Date: Thu, 8 Sep 2016 13:35:54 +0200 Subject: [PATCH 15/85] Fix typo --- lib/plugins/aws/deploy/compile/events/s3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/compile/events/s3/README.md b/lib/plugins/aws/deploy/compile/events/s3/README.md index 1e19d8cb8..002e6cb1c 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/README.md +++ b/lib/plugins/aws/deploy/compile/events/s3/README.md @@ -17,7 +17,7 @@ the bucket name you've defined and an additional lambda notification configurati function and the `s3:objectCreated:*` events. The second possibility is to configure your S3 event more granular (like the bucket name or the event which this bucket -should listen to and or the filters rules) with the help of key value pairs. +should listen to) with the help of key value pairs. Take a look at the [Event syntax examples](#event-syntax-examples) below to see how you can setup S3 bucket events. From 290f5af21843a6d904b9e0417bc150a6d487c5ba Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Thu, 8 Sep 2016 17:39:30 -0700 Subject: [PATCH 16/85] Adds test to AWS Invoke to vierify that YAML can be used for event input. Verifies fix for #1994. --- lib/plugins/aws/invoke/tests/index.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/plugins/aws/invoke/tests/index.js b/lib/plugins/aws/invoke/tests/index.js index 87881b167..836fcc61f 100644 --- a/lib/plugins/aws/invoke/tests/index.js +++ b/lib/plugins/aws/invoke/tests/index.js @@ -93,6 +93,21 @@ describe('AwsInvoke', () => { }); }); + it('it should parse a yaml file if file path is provided', () => { + serverless.config.servicePath = testUtils.getTmpDirPath(); + const yamlContent = "testProp: testValue"; + + serverless.utils.writeFileSync(path + .join(serverless.config.servicePath, 'data.yml'), yamlContent); + awsInvoke.options.path = 'data.yml'; + + return awsInvoke.extendedValidate().then(() => { + expect(awsInvoke.options.data).to.deep.equal(data); + awsInvoke.options.path = false; + serverless.config.servicePath = true; + }); + }); + it('it should throw error if service path is not set', () => { serverless.config.servicePath = false; expect(() => awsInvoke.extendedValidate()).to.throw(Error); From f675132780f697eb64f34b410ebd9512eccd14a5 Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Thu, 8 Sep 2016 17:46:12 -0700 Subject: [PATCH 17/85] Provide data to verify YAML result. --- lib/plugins/aws/invoke/tests/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/plugins/aws/invoke/tests/index.js b/lib/plugins/aws/invoke/tests/index.js index 836fcc61f..e849278aa 100644 --- a/lib/plugins/aws/invoke/tests/index.js +++ b/lib/plugins/aws/invoke/tests/index.js @@ -102,7 +102,9 @@ describe('AwsInvoke', () => { awsInvoke.options.path = 'data.yml'; return awsInvoke.extendedValidate().then(() => { - expect(awsInvoke.options.data).to.deep.equal(data); + expect(awsInvoke.options.data).to.deep.equal({ + testProp: 'testValue', + }); awsInvoke.options.path = false; serverless.config.servicePath = true; }); From 21586c47f303c1e75616546f59d74a38e940df86 Mon Sep 17 00:00:00 2001 From: "Smith, Greg W" Date: Thu, 8 Sep 2016 17:49:25 -0700 Subject: [PATCH 18/85] Properly using single quotes only. --- lib/plugins/aws/invoke/tests/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/invoke/tests/index.js b/lib/plugins/aws/invoke/tests/index.js index e849278aa..0229844c8 100644 --- a/lib/plugins/aws/invoke/tests/index.js +++ b/lib/plugins/aws/invoke/tests/index.js @@ -95,7 +95,7 @@ describe('AwsInvoke', () => { it('it should parse a yaml file if file path is provided', () => { serverless.config.servicePath = testUtils.getTmpDirPath(); - const yamlContent = "testProp: testValue"; + const yamlContent = 'testProp: testValue'; serverless.utils.writeFileSync(path .join(serverless.config.servicePath, 'data.yml'), yamlContent); From 493de1e31055f76bd963cd8422327a8edd277906 Mon Sep 17 00:00:00 2001 From: Jon Sharratt Date: Sat, 10 Sep 2016 12:20:52 +0100 Subject: [PATCH 19/85] add support for dashes as a resource path --- .../compile/events/apiGateway/lib/resources.js | 6 +++++- .../events/apiGateway/tests/resources.js | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/resources.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/resources.js index 7a55ed267..6d9b04010 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/resources.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/resources.js @@ -43,7 +43,11 @@ module.exports = { }); }); - const capitalizeAlphaNumericPath = (path) => _.capitalize(path.replace(/[^0-9A-Za-z]/g, '')); + const capitalizeAlphaNumericPath = (path) => _.capitalize( + path + .replace(/-/g, 'Dash') + .replace(/[^0-9A-Za-z]/g, '') + ); // ['users', 'users/create', 'users/create/something'] this.resourcePaths.forEach(path => { diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/resources.js b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/resources.js index 450dfcf16..e182c47ca 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/resources.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/resources.js @@ -21,6 +21,9 @@ describe('#compileResources()', () => { method: 'POST', }, }, + { + http: 'GET bar/-', + }, { http: 'GET bar/foo', }, @@ -57,8 +60,17 @@ describe('#compileResources()', () => { it('should construct the correct resourcePaths array', () => awsCompileApigEvents .compileResources().then(() => { - const expectedResourcePaths = ['foo/bar', 'foo', 'bar/foo', 'bar', 'bar/{id}', - 'bar/{id}/foobar', 'bar/{foo_id}', 'bar/{foo_id}/foobar']; + const expectedResourcePaths = [ + 'foo/bar', + 'foo', + 'bar/-', + 'bar', + 'bar/foo', + 'bar/{id}', + 'bar/{id}/foobar', + 'bar/{foo_id}', + 'bar/{foo_id}/foobar', + ]; expect(awsCompileApigEvents.resourcePaths).to.deep.equal(expectedResourcePaths); }) ); @@ -66,6 +78,7 @@ describe('#compileResources()', () => { it('should construct the correct resourceLogicalIds object', () => awsCompileApigEvents .compileResources().then(() => { const expectedResourceLogicalIds = { + 'bar/-': 'ApiGatewayResourceBarDash', 'foo/bar': 'ApiGatewayResourceFooBar', foo: 'ApiGatewayResourceFoo', 'bar/{id}/foobar': 'ApiGatewayResourceBarIdFoobar', From 54728ef2b179da2e660e7a648973b93975396f3a Mon Sep 17 00:00:00 2001 From: rowan Date: Wed, 14 Sep 2016 10:58:39 +1000 Subject: [PATCH 20/85] Switch CloudFormation OnFailure behaviour to rollback instead of delete. Fixes #2087. --- lib/plugins/aws/deploy/lib/createStack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/lib/createStack.js b/lib/plugins/aws/deploy/lib/createStack.js index 27fc34253..3a37c7da5 100644 --- a/lib/plugins/aws/deploy/lib/createStack.js +++ b/lib/plugins/aws/deploy/lib/createStack.js @@ -10,7 +10,7 @@ module.exports = { const coreCloudFormationTemplate = this.loadCoreCloudFormationTemplate(); const params = { StackName: stackName, - OnFailure: 'DELETE', + OnFailure: 'ROLLBACK', Capabilities: [ 'CAPABILITY_IAM', ], From 54192f592ae2683de15a61a072f68a25ad268557 Mon Sep 17 00:00:00 2001 From: Dax Chen Date: Wed, 14 Sep 2016 15:36:16 +0800 Subject: [PATCH 21/85] Fix YML example code: strings should be quoted In YML, strings containing :, {, }, [, ], ,, &, *, #, ?, |, -, <, >, =, !, %, @, ` must be quoted. Without the quotes, deployments will fail at `AWS::ApiGateway::Method` step. I've been trying to use the custom request template after upgrading to v1.0, and finally found the correct syntax, lol. Let me know if any correction is needed, and if more/less examples are preferred. --- docs/02-providers/aws/events/01-apigateway.md | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/02-providers/aws/events/01-apigateway.md b/docs/02-providers/aws/events/01-apigateway.md index 5ffaf4360..c021e8bf5 100644 --- a/docs/02-providers/aws/events/01-apigateway.md +++ b/docs/02-providers/aws/events/01-apigateway.md @@ -71,12 +71,34 @@ functions: path: whatever request: template: - text/xhtml: { "stage" : "$context.stage" } - application/json: { "httpMethod" : "$context.httpMethod" } + text/xhtml: '{ "stage" : "$context.stage" }' + application/json: '{ "httpMethod" : "$context.httpMethod" }' ``` **Note:** The templates are defined as plain text here. However you can also reference an external file with the help of the `${file(templatefile)}` syntax. +**Note 2:** In .yml, strings containing `:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` `` must be quoted. + +If you want to map querystrings to the event object, you can use the `$input.params('hub.challenge')` syntax from API Gateway, as follows: + +```yml +functions: + create: + handler: posts.create + events: + - http: + method: get + path: whatever + request: + template: + application/json: '{ "foo" : "$input.params(''bar'')" }' +``` + +**Note:** Notice when using single-quoted strings, any single quote `'` inside its contents must be doubled (`''`) to escape it. +You can then access the query string `https://example.com/dev/whatever?bar=123` by `event.foo` in the lambda function. +If you want to spread a string into multiple lines, you can use the `>` or `|` syntax, but the following strings have to be all indented with the same amount, [read more about `>` syntax](http://stackoverflow.com/questions/3790454/in-yaml-how-do-i-break-a-string-over-multiple-lines). + + ### Pass Through Behavior API Gateway provides multiple ways to handle requests where the Content-Type header does not match any of the specified mapping templates. When this happens, the request payload will either be passed through the integration request *without transformation* or rejected with a `415 - Unsupported Media Type`, depending on the configuration. From e04226e922247c730e079b4f3eeeb4c88092443d Mon Sep 17 00:00:00 2001 From: Roger Lam Date: Mon, 19 Sep 2016 23:40:09 -0700 Subject: [PATCH 22/85] fix test --- lib/plugins/create/tests/create.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/plugins/create/tests/create.js b/lib/plugins/create/tests/create.js index 448f15324..b53ec2fc8 100644 --- a/lib/plugins/create/tests/create.js +++ b/lib/plugins/create/tests/create.js @@ -49,7 +49,6 @@ describe('Create', () => { }); it('should overwrite the name for the service if user passed name', () => { - const tmpDir = path.join(os.tmpdir(), (new Date()).getTime().toString()); const cwd = process.cwd(); fse.mkdirsSync(tmpDir); process.chdir(tmpDir); From 649ada4c2c803032f8df380a08887ca9fce4f9a3 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Mon, 12 Sep 2016 13:57:25 +0200 Subject: [PATCH 23/85] Add unique userId (uuid) for tracking functionality --- lib/classes/Utils.js | 13 ++++++++++++- npm-shrinkwrap.json | 42 +++++++++++++++++++++--------------------- package.json | 3 ++- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index b03055cf5..cfc2d5206 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -7,6 +7,7 @@ const BbPromise = require('bluebird'); const fse = BbPromise.promisifyAll(require('fs-extra')); const _ = require('lodash'); const fetch = require('node-fetch'); +const uuid = require('uuid'); class Utils { constructor(serverless) { @@ -147,6 +148,16 @@ class Utils { const loadedPlugins = serverless.pluginManager.plugins.map((plugin) => plugin.constructor.name); const events = []; + let userId = uuid.v1(); + + // create a new file with a uuid as the tracking id if not yet present + const trackingIdFilePath = path.join(serverless.config.serverlessPath, 'tracking-id'); + if (!this.fileExistsSync(trackingIdFilePath)) { + fs.writeFileSync(trackingIdFilePath, userId); + } else { + userId = fs.readFileSync(trackingIdFilePath).toString(); + } + if (serverless.service && serverless.service.functions) { _.forEach(serverless.service.functions, (func) => { if (func.events) { @@ -169,7 +180,7 @@ class Utils { const auth = `${writeKey}:`; const data = { - userId: 'anonymousUser', + userId, event: 'Serverless framework usage', properties: { commands: serverless.processedInput.commands, diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index ebab1c289..63d59b976 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.0.0-beta.2", + "version": "1.0.0-rc.1", "dependencies": { "abbrev": { "version": "1.0.9", @@ -136,7 +136,7 @@ }, "bl": { "version": "1.1.2", - "from": "bl@>=1.0.0 <2.0.0", + "from": "bl@>=1.1.2 <1.2.0", "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", "dependencies": { "readable-stream": { @@ -178,7 +178,7 @@ }, "builtin-modules": { "version": "1.1.1", - "from": "builtin-modules@>=1.0.0 <2.0.0", + "from": "builtin-modules@>=1.1.1 <2.0.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" }, "caller-id": { @@ -213,7 +213,7 @@ }, "chalk": { "version": "1.1.3", - "from": "chalk@>=1.1.1 <2.0.0", + "from": "chalk@>=1.1.0 <2.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" }, "circular-json": { @@ -327,12 +327,12 @@ }, "debug": { "version": "2.2.0", - "from": "debug@>=2.0.0 <3.0.0", + "from": "debug@>=2.2.0 <3.0.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" }, "decamelize": { "version": "1.2.0", - "from": "decamelize@>=1.1.1 <2.0.0", + "from": "decamelize@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" }, "deep-eql": { @@ -521,11 +521,6 @@ "from": "figures@>=1.3.5 <2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz" }, - "file-entry-cache": { - "version": "1.3.1", - "from": "file-entry-cache@>=1.3.1 <2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-1.3.1.tgz" - }, "find-up": { "version": "1.1.2", "from": "find-up@>=1.0.0 <2.0.0", @@ -558,7 +553,7 @@ }, "fs-extra": { "version": "0.26.7", - "from": "fs-extra@>=0.26.7 <0.27.0", + "from": "fs-extra@>=0.26.4 <0.27.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz" }, "fs.realpath": { @@ -637,7 +632,7 @@ }, "har-validator": { "version": "2.0.6", - "from": "har-validator@>=2.0.6 <2.1.0", + "from": "har-validator@>=2.0.2 <2.1.0", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz" }, "has-ansi": { @@ -652,7 +647,7 @@ }, "hawk": { "version": "3.1.3", - "from": "hawk@>=3.1.3 <3.2.0", + "from": "hawk@>=3.1.0 <3.2.0", "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" }, "hoek": { @@ -807,7 +802,7 @@ }, "js-yaml": { "version": "3.6.1", - "from": "js-yaml@>=3.6.1 <4.0.0", + "from": "js-yaml@>=3.5.5 <4.0.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz" }, "jsbn": { @@ -1074,7 +1069,7 @@ }, "node-uuid": { "version": "1.4.7", - "from": "node-uuid@>=1.4.7 <1.5.0", + "from": "node-uuid@>=1.4.2 <2.0.0", "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" }, "nopt": { @@ -1099,12 +1094,12 @@ }, "oauth-sign": { "version": "0.8.2", - "from": "oauth-sign@>=0.8.1 <0.9.0", + "from": "oauth-sign@>=0.8.0 <0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" }, "object-assign": { "version": "4.1.0", - "from": "object-assign@>=4.1.0 <5.0.0", + "from": "object-assign@>=4.0.1 <5.0.0", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" }, "once": { @@ -1353,7 +1348,7 @@ }, "semver-regex": { "version": "1.0.0", - "from": "semver-regex@>=1.0.0 <2.0.0", + "from": "semver-regex@latest", "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz" }, "set-blocking": { @@ -1425,7 +1420,7 @@ }, "stack-trace": { "version": "0.0.9", - "from": "stack-trace@>=0.0.7 <0.1.0", + "from": "stack-trace@>=0.0.0 <0.1.0", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" }, "string_decoder": { @@ -1622,6 +1617,11 @@ "from": "util-deprecate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" }, + "uuid": { + "version": "2.0.2", + "from": "uuid@latest", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.2.tgz" + }, "validate-npm-package-license": { "version": "3.0.1", "from": "validate-npm-package-license@>=3.0.1 <4.0.0", @@ -1649,7 +1649,7 @@ }, "wordwrap": { "version": "1.0.0", - "from": "wordwrap@>=1.0.0 <1.1.0", + "from": "wordwrap@>=0.0.2", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" }, "wrap-ansi": { diff --git a/package.json b/package.json index 79e1e71a1..d8fd6e4e1 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "replaceall": "^0.1.6", "semver-regex": "^1.0.0", "shelljs": "^0.6.0", - "traverse": "^0.6.6" + "traverse": "^0.6.6", + "uuid": "^2.0.2" } } From d1a16e283f4d42d3302f61dee3a44283ed48f462 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Mon, 12 Sep 2016 14:47:33 +0200 Subject: [PATCH 24/85] Add new tracking functionalities and group them by topic --- lib/classes/Utils.js | 60 +++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index cfc2d5206..6fd6210ec 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -145,9 +145,6 @@ class Utils { track(serverless) { const writeKey = 'XXXX'; // TODO: Replace me before release - const loadedPlugins = serverless.pluginManager.plugins.map((plugin) => plugin.constructor.name); - const events = []; - let userId = uuid.v1(); // create a new file with a uuid as the tracking id if not yet present @@ -158,40 +155,75 @@ class Utils { userId = fs.readFileSync(trackingIdFilePath).toString(); } + // prepare complex tracking data + const numberOfEventsPerType = []; + const eventNamesPerFunction = []; if (serverless.service && serverless.service.functions) { _.forEach(serverless.service.functions, (func) => { if (func.events) { + const funcEventsArray = []; + func.events.forEach((event) => { const name = Object.keys(event)[0]; - const alreadyPresentEvent = _.find(events, { name }); + funcEventsArray.push(name); + + const alreadyPresentEvent = _.find(numberOfEventsPerType, { name }); if (alreadyPresentEvent) { alreadyPresentEvent.count++; } else { - events.push({ + numberOfEventsPerType.push({ name, count: 1, }); } }); + + eventNamesPerFunction.push(funcEventsArray); } }); } + const hasCustomVariableSyntaxDefined = serverless.service + .defaults.variableSyntax !== '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}'; + const auth = `${writeKey}:`; const data = { userId, event: 'Serverless framework usage', properties: { - commands: serverless.processedInput.commands, - operatingSystem: process.platform, - loadedPlugins, - serverlessVersion: serverless.version, - numberOfFunctions: _.size(serverless.service.functions), - events, - totalNumberOfEvents: events.length, - provider: serverless.service.provider, - isRunInsideService: (!!serverless.config.servicePath), + command: { + name: serverless.processedInput.commands.join(' '), + isRunInService: (!!serverless.config.servicePath), + }, + service: { + numberOfCustomPlugins: _.size(serverless.service.plugins), + hasCustomResourcesDefined: (!!serverless.service.resources), + hasVariablesInCustomSectionDefined: (!!serverless.service.custom), + hasCustomVariableSyntaxDefined, + }, + provider: { + name: serverless.service.provider.name, + runtime: serverless.service.provider.runtime, + stage: serverless.service.provider.stage, + region: serverless.service.provider.region, + }, + functions: { + numberOfFunctions: _.size(serverless.service.functions), + // memorySizeAndTimeoutPerFunction + }, + events: { + numberOfEvents: numberOfEventsPerType.length, + numberOfEventsPerType, + eventNamesPerFunction, + }, + general: { + userId, + timestamp: new Date(), + operatingSystem: process.platform, + serverlessVersion: serverless.version, + nodeJsVersion: process.version, + }, }, }; From 3c84d6112e6efcc8cb7aa2ec982a65bd6c20fe87 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 13 Sep 2016 11:35:35 +0200 Subject: [PATCH 25/85] Add memorySize and timeout tracking functionality --- lib/classes/Utils.js | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index 6fd6210ec..10917f2e9 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -155,10 +155,32 @@ class Utils { userId = fs.readFileSync(trackingIdFilePath).toString(); } - // prepare complex tracking data + // function related information retrieval + const numberOfFunctions = _.size(serverless.service.functions); + + const memorySizeAndTimeoutPerFunction = []; + if (numberOfFunctions) { + _.forEach(serverless.service.functions, (func) => { + const memorySize = Number(func.memorySize) + || Number(this.serverless.service.provider.memorySize) + || 1024; + const timeout = Number(func.timeout) + || Number(this.serverless.service.provider.timeout) + || 6; + + const memorySizeAndTimeoutObject = { + memorySize, + timeout, + }; + + memorySizeAndTimeoutPerFunction.push(memorySizeAndTimeoutObject); + }); + } + + // event related information retrieval const numberOfEventsPerType = []; const eventNamesPerFunction = []; - if (serverless.service && serverless.service.functions) { + if (numberOfFunctions) { _.forEach(serverless.service.functions, (func) => { if (func.events) { const funcEventsArray = []; @@ -209,8 +231,8 @@ class Utils { region: serverless.service.provider.region, }, functions: { - numberOfFunctions: _.size(serverless.service.functions), - // memorySizeAndTimeoutPerFunction + numberOfFunctions, + memorySizeAndTimeoutPerFunction, }, events: { numberOfEvents: numberOfEventsPerType.length, From b69027b6860c7bdc3c8f2e701634004e8c00f55d Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 13 Sep 2016 13:57:00 +0200 Subject: [PATCH 26/85] Add tests for tracking functionality which check the existence of the trackingId --- lib/classes/Utils.js | 2 +- tests/classes/Utils.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index 10917f2e9..41d0333bf 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -249,7 +249,7 @@ class Utils { }, }; - fetch('https://api.segment.io/v1/track', { + return fetch('https://api.segment.io/v1/track', { headers: { Authorization: `Basic ${new Buffer(auth).toString('base64')}`, 'content-type': 'application/json', diff --git a/tests/classes/Utils.js b/tests/classes/Utils.js index 4cd5641c5..e67a2f3d1 100644 --- a/tests/classes/Utils.js +++ b/tests/classes/Utils.js @@ -3,6 +3,8 @@ const path = require('path'); const os = require('os'); const expect = require('chai').expect; +const fse = require('fs-extra'); +const fs = require('fs'); const Serverless = require('../../lib/Serverless'); const testUtils = require('../../tests/utils'); @@ -215,5 +217,40 @@ describe('Utils', () => { process.chdir(testDir); }); }); + + describe('#track()', () => { + let serverlessPath; + + beforeEach(() => { + serverless.init(); + + const tmpDirPath = testUtils.getTmpDirPath(); + fse.mkdirsSync(tmpDirPath); + + serverlessPath = tmpDirPath; + serverless.config.serverlessPath = tmpDirPath; + }); + + it('should create a new file with a tracking id if not found', () => { + const trackingIdFilePath = path.join(serverlessPath, 'tracking-id'); + + return serverless.utils.track(serverless).then(() => { + expect(fs.readFileSync(trackingIdFilePath).toString().length).to.be.above(1); + }); + }); + + it('should re-use an existing file which contains the tracking id if found', () => { + const trackingIdFilePath = path.join(serverlessPath, 'tracking-id'); + const trackingId = 'some-tracking-id'; + + // create a new file with a tracking id + fse.ensureFileSync(trackingIdFilePath); + fs.writeFileSync(trackingIdFilePath, trackingId); + + return serverless.utils.track(serverless).then(() => { + expect(fs.readFileSync(trackingIdFilePath).toString()).to.be.equal(trackingId); + }); + }); + }); }); From 3338d018d6d2848c4f2450e405a551ca1e8dd4f1 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 13 Sep 2016 14:06:58 +0200 Subject: [PATCH 27/85] Convert date object to string to get the timezone as well --- lib/classes/Utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index 41d0333bf..46bbab728 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -241,7 +241,7 @@ class Utils { }, general: { userId, - timestamp: new Date(), + timestamp: (new Date()).toString(), operatingSystem: process.platform, serverlessVersion: serverless.version, nodeJsVersion: process.version, From 3bb149f1dbf6ae2d48b2d5e0595a0a7927bff9b4 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 13 Sep 2016 14:14:13 +0200 Subject: [PATCH 28/85] Add some mock data to the serverless.service object in the track() tests So that parts of the data retrieval are run. --- tests/classes/Utils.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/classes/Utils.js b/tests/classes/Utils.js index e67a2f3d1..8ce13d1ae 100644 --- a/tests/classes/Utils.js +++ b/tests/classes/Utils.js @@ -224,11 +224,33 @@ describe('Utils', () => { beforeEach(() => { serverless.init(); + // create a new tmpDir for the serverlessPath const tmpDirPath = testUtils.getTmpDirPath(); fse.mkdirsSync(tmpDirPath); serverlessPath = tmpDirPath; serverless.config.serverlessPath = tmpDirPath; + + // add some mock data to the serverless service object + serverless.service.functions = { + foo: { + memorySize: 47, + timeout: 11, + events: [ + { + http: 'GET foo', + }, + ], + }, + bar: { + events: [ + { + http: 'GET foo', + s3: 'someBucketName', + }, + ], + }, + }; }); it('should create a new file with a tracking id if not found', () => { From 236d66c8b3e059b81c9c49f7c3ae2be09328348c Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 13 Sep 2016 14:21:07 +0200 Subject: [PATCH 29/85] Update docs for tracking to point to the code of the (evolving) track() method Instead of listing all the stuff we track (which will get outdated pretty quick). --- docs/usage-tracking.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/usage-tracking.md b/docs/usage-tracking.md index fd679ba28..b200679d9 100644 --- a/docs/usage-tracking.md +++ b/docs/usage-tracking.md @@ -14,16 +14,7 @@ of our users to improve Serverless in future releases. However you can always [d Our main goal is anonymity while tracking usage behavior. All the data is anonymized and won't reveal who you are or what the project you're working on is / looks like. -Here's a list about all the information we track: -- Entered command(s) -- Operating system -- Loaded plugins -- Serverless version -- Number of functions inside your service -- Total number of events inside your service -- Event types and how often they were defined -- Provider of your service -- If the command was executed inside a service +Please take a look at the [`track()` method](../lib/classes/Utils.js) in the `Utils` class to see what (and how) we track. ## How tracking is implemented From 1f8dc53aea1e502a0865550511d421cc6a4fceb9 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 20 Sep 2016 09:12:59 +0200 Subject: [PATCH 30/85] Add timezone information --- lib/classes/Utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index 46bbab728..4665fdd09 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -242,6 +242,7 @@ class Utils { general: { userId, timestamp: (new Date()).toString(), + timezone: (new Date()).toString().match(/([A-Z]+[\+-][0-9]+.*)/)[1], operatingSystem: process.platform, serverlessVersion: serverless.version, nodeJsVersion: process.version, From 7ca12674a47897f21b9d51ba618eb6aa2bd86895 Mon Sep 17 00:00:00 2001 From: Roger Lam Date: Tue, 20 Sep 2016 00:28:58 -0700 Subject: [PATCH 31/85] add additional test for when path and name are provided --- lib/plugins/create/tests/create.js | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/plugins/create/tests/create.js b/lib/plugins/create/tests/create.js index b53ec2fc8..d1e69514c 100644 --- a/lib/plugins/create/tests/create.js +++ b/lib/plugins/create/tests/create.js @@ -181,10 +181,13 @@ describe('Create', () => { process.chdir(tmpDir); create.options.path = 'my-new-service'; + create.options.name = null; // using the nodejs template (this test is completely be independent from the template) create.options.template = 'aws-nodejs'; + console.log("name is: " + create.options.name); + return create.create().then(() => { const serviceDir = path.join(tmpDir, create.options.path); @@ -198,10 +201,44 @@ describe('Create', () => { const serverlessYmlfileContent = fse .readFileSync(path.join(serviceDir, 'serverless.yml')).toString(); + console.log(serverlessYmlfileContent); expect((/service: my-new-service/).test(serverlessYmlfileContent)).to.equal(true); process.chdir(cwd); }); }); + + it('should create a custom renamed service in the directory if using the "path" and "name" option', () => { + const cwd = process.cwd(); + fse.mkdirsSync(tmpDir); + process.chdir(tmpDir); + + create.options.path = 'my-new-service'; + create.options.name = 'my-custom-new-service'; + + // using the nodejs template (this test is completely be independent from the template) + create.options.template = 'aws-nodejs'; + + console.log("name is: " + create.options.name); + + return create.create().then(() => { + const serviceDir = path.join(tmpDir, create.options.path); + + // check if files are created in the correct directory + expect(create.serverless.utils.fileExistsSync( + path.join(serviceDir, 'serverless.yml'))).to.be.equal(true); + expect(create.serverless.utils.fileExistsSync( + path.join(serviceDir, 'handler.js'))).to.be.equal(true); + + // check if the service was renamed + const serverlessYmlfileContent = fse + .readFileSync(path.join(serviceDir, 'serverless.yml')).toString(); + + console.log(serverlessYmlfileContent); + expect((/service: my-custom-new-service/).test(serverlessYmlfileContent)).to.equal(true); + + process.chdir(cwd); + }); + }); }); }); From 65489ad34c0620bffb3114da48626e583b6e9d2b Mon Sep 17 00:00:00 2001 From: Roger Lam Date: Tue, 20 Sep 2016 00:30:20 -0700 Subject: [PATCH 32/85] add ability to pass custom name through options --- lib/plugins/create/create.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js index 4d77a7c3c..50e9c06e0 100644 --- a/lib/plugins/create/create.js +++ b/lib/plugins/create/create.js @@ -36,6 +36,10 @@ class Create { usage: 'The path where the service should be created (e.g. --path my-service)', shortcut: 'p', }, + name: { + usage: 'Name for the service. Overwrites the default name of the created service.', + shortcut: 'n', + }, }, }, }; @@ -57,8 +61,9 @@ class Create { throw new this.serverless.classes.Error(errorMessage); } - // store the path option for the service if given + // store the custom options for the service if given const servicePath = this.options.path && this.options.path.length ? this.options.path : null; + const serviceName = this.options.name && this.options.name.length ? this.options.name : null; // create (if not yet present) and chdir into the directory for the service if (servicePath) { @@ -78,8 +83,8 @@ class Create { 'plugins', 'create', 'templates', this.options.template), this.serverless.config.servicePath); // rename the service if the user has provided a path via options - if (servicePath) { - const serviceName = servicePath.split(path.sep).pop(); + if (servicePath || serviceName) { + const newServiceName = serviceName || servicePath.split(path.sep).pop(); const serverlessYmlFilePath = path .join(this.serverless.config.servicePath, 'serverless.yml'); @@ -87,7 +92,7 @@ class Create { .readFileSync(serverlessYmlFilePath).toString(); serverlessYmlFileContent = serverlessYmlFileContent - .replace(/service: .+/, `service: ${serviceName}`); + .replace(/service: .+/, `service: ${newServiceName}`); fse.writeFileSync(serverlessYmlFilePath, serverlessYmlFileContent); } @@ -100,6 +105,11 @@ class Create { this.serverless.cli .log('NOTE: Please update the "service" property in serverless.yml with your service name'); } + // if (!this.options.name) { + // this.serverless.cli + // .log('NOTE: Please update the "service" property in serverless.yml with your service name'); + // } + return BbPromise.resolve(); } From 10a638b7442f1d98c1ac13a1c96334fc5f919692 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 20 Sep 2016 19:08:16 +0200 Subject: [PATCH 33/85] Update tracking functionality according to PR review --- lib/classes/Utils.js | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index 4665fdd09..d8aa02a7c 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -205,8 +205,34 @@ class Utils { }); } - const hasCustomVariableSyntaxDefined = serverless.service - .defaults.variableSyntax !== '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}'; + let hasCustomResourcesDefined = false; + // check if configuration in resources.Resources is defined + if ((serverless.service.resources && + serverless.service.resources.Resources && + Object.keys(serverless.service.resources.Resources).length)) { + hasCustomResourcesDefined = true; + } + // check if configuration in resources.Outputs is defined + if ((serverless.service.resources && + serverless.service.resources.Outputs && + Object.keys(serverless.service.resources.Outputs).length)) { + hasCustomResourcesDefined = true; + } + + let hasCustomVariableSyntaxDefined = false; + const defaultVariableSyntax = '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}'; + // check if the variableSyntax in the defaults section is defined + if (serverless.service.defaults && + serverless.service.defaults.variableSyntax && + serverless.service.defaults.variableSyntax !== defaultVariableSyntax) { + hasCustomVariableSyntaxDefined = true; + } + // check if the variableSyntax in the provider section is defined + if (serverless.service.provider && + serverless.service.provider.variableSyntax && + serverless.service.provider.variableSyntax !== defaultVariableSyntax) { + hasCustomVariableSyntaxDefined = true; + } const auth = `${writeKey}:`; @@ -220,7 +246,7 @@ class Utils { }, service: { numberOfCustomPlugins: _.size(serverless.service.plugins), - hasCustomResourcesDefined: (!!serverless.service.resources), + hasCustomResourcesDefined, hasVariablesInCustomSectionDefined: (!!serverless.service.custom), hasCustomVariableSyntaxDefined, }, @@ -241,7 +267,7 @@ class Utils { }, general: { userId, - timestamp: (new Date()).toString(), + timestamp: (new Date()).getTime(), timezone: (new Date()).toString().match(/([A-Z]+[\+-][0-9]+.*)/)[1], operatingSystem: process.platform, serverlessVersion: serverless.version, From 9a5c01815ea4bc596fab8f11c3684969744516f9 Mon Sep 17 00:00:00 2001 From: Roger Lam Date: Tue, 20 Sep 2016 10:56:27 -0700 Subject: [PATCH 34/85] remote comments and debug statements --- lib/plugins/create/create.js | 5 ----- lib/plugins/create/tests/create.js | 6 ------ 2 files changed, 11 deletions(-) diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js index 50e9c06e0..84e8d0212 100644 --- a/lib/plugins/create/create.js +++ b/lib/plugins/create/create.js @@ -105,11 +105,6 @@ class Create { this.serverless.cli .log('NOTE: Please update the "service" property in serverless.yml with your service name'); } - // if (!this.options.name) { - // this.serverless.cli - // .log('NOTE: Please update the "service" property in serverless.yml with your service name'); - // } - return BbPromise.resolve(); } diff --git a/lib/plugins/create/tests/create.js b/lib/plugins/create/tests/create.js index d1e69514c..be846a50a 100644 --- a/lib/plugins/create/tests/create.js +++ b/lib/plugins/create/tests/create.js @@ -186,8 +186,6 @@ describe('Create', () => { // using the nodejs template (this test is completely be independent from the template) create.options.template = 'aws-nodejs'; - console.log("name is: " + create.options.name); - return create.create().then(() => { const serviceDir = path.join(tmpDir, create.options.path); @@ -201,7 +199,6 @@ describe('Create', () => { const serverlessYmlfileContent = fse .readFileSync(path.join(serviceDir, 'serverless.yml')).toString(); - console.log(serverlessYmlfileContent); expect((/service: my-new-service/).test(serverlessYmlfileContent)).to.equal(true); process.chdir(cwd); @@ -219,8 +216,6 @@ describe('Create', () => { // using the nodejs template (this test is completely be independent from the template) create.options.template = 'aws-nodejs'; - console.log("name is: " + create.options.name); - return create.create().then(() => { const serviceDir = path.join(tmpDir, create.options.path); @@ -234,7 +229,6 @@ describe('Create', () => { const serverlessYmlfileContent = fse .readFileSync(path.join(serviceDir, 'serverless.yml')).toString(); - console.log(serverlessYmlfileContent); expect((/service: my-custom-new-service/).test(serverlessYmlfileContent)).to.equal(true); process.chdir(cwd); From 5cb7dc1734c864beb99d97254d39fba9203095bc Mon Sep 17 00:00:00 2001 From: Roger Lam Date: Tue, 20 Sep 2016 11:15:35 -0700 Subject: [PATCH 35/85] fix linting errors --- lib/plugins/create/tests/create.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/plugins/create/tests/create.js b/lib/plugins/create/tests/create.js index be846a50a..11775a503 100644 --- a/lib/plugins/create/tests/create.js +++ b/lib/plugins/create/tests/create.js @@ -55,13 +55,14 @@ describe('Create', () => { create.options.template = 'aws-nodejs'; create.options.name = 'my_service'; - return create.create().then(() => { - return create.serverless.yamlParser.parse(path.join(tmpDir, 'serverless.yml')).then((obj) => { - expect(obj.service).to.equal('my_service') - + return create.create().then(() => + create.serverless.yamlParser.parse( + path.join(tmpDir, 'serverless.yml') + ).then((obj) => { + expect(obj.service).to.equal('my_service'); process.chdir(cwd); - }); - }); + }) + ); }); it('should set servicePath based on cwd', () => { @@ -205,7 +206,8 @@ describe('Create', () => { }); }); - it('should create a custom renamed service in the directory if using the "path" and "name" option', () => { + it('should create a custom renamed service in the directory if using ' + + 'the "path" and "name" option', () => { const cwd = process.cwd(); fse.mkdirsSync(tmpDir); process.chdir(tmpDir); From 352a151f0e7fab51e443f3cc8c0e948d8e24ca4e Mon Sep 17 00:00:00 2001 From: Andrew McClenaghan Date: Wed, 21 Sep 2016 12:09:45 +1000 Subject: [PATCH 36/85] Remove reference to `readme.md` to help fix links in https://serverless.com/framework/docs/ --- docs/01-guide/05-event-sources.md | 2 +- docs/01-guide/07-removing-services.md | 4 ++-- docs/02-providers/aws/README.md | 2 +- docs/README.md | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/01-guide/05-event-sources.md b/docs/01-guide/05-event-sources.md index 0c6800d3a..5d4922b7b 100644 --- a/docs/01-guide/05-event-sources.md +++ b/docs/01-guide/05-event-sources.md @@ -68,7 +68,7 @@ You've successfully executed the function through the HTTP endpoint! Serverless provides more than just a HTTP event source. You can find the full list of all available event sources with corresponding examples in the provider specific docs: -* [AWS event documentation](../02-providers/aws/events/README.md). +* [AWS event documentation](../02-providers/aws/events/). ## Conclusion diff --git a/docs/01-guide/07-removing-services.md b/docs/01-guide/07-removing-services.md index 8995ec38d..18aee6d95 100644 --- a/docs/01-guide/07-removing-services.md +++ b/docs/01-guide/07-removing-services.md @@ -21,8 +21,8 @@ We've just removed the whole service from our provider with a simple `serverless ## What's next? -You can either dive deeper into our [Advanced Guides](./README.md#advanced-guides) or read through the provider specific documentation we provide: +You can either dive deeper into our [Advanced Guides](./#advanced-guides) or read through the provider specific documentation we provide: -* [AWS Documentation](../02-providers/aws/README.md) +* [AWS Documentation](../02-providers/aws/) Have fun with building your Serverless services and if you have feedback on the please let us know in [our Forum](forum.serverless.com) or [open an Issue in our Github repository](https://github.com/serverless/serverless/issues/new) for any bugs you might encounter or if you have an idea for a new feature. diff --git a/docs/02-providers/aws/README.md b/docs/02-providers/aws/README.md index bc28a8a6a..22bd41b18 100644 --- a/docs/02-providers/aws/README.md +++ b/docs/02-providers/aws/README.md @@ -6,7 +6,7 @@ layout: Doc # Serverless AWS Documentation -Check out the [Getting started guide](../../01-guide/README.md) and the [CLI reference](../../03-cli-reference/README.md) for an introduction to Serverless. +Check out the [Getting started guide](../../01-guide/) and the [CLI reference](../../03-cli-reference/) for an introduction to Serverless. ## Setup and configuration diff --git a/docs/README.md b/docs/README.md index 343a2532d..47a190cb3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,15 +8,15 @@ layout: Doc Welcome to the Serverless documentation. -- [Quick Start Guide](./01-guide/README.md) +- [Quick Start Guide](./01-guide/) - [Core Concepts](#concepts) -- [CLI Reference](./03-cli-reference/README.md) +- [CLI Reference](./03-cli-reference/) - [Providers](#providers) -- [Extending Serverless](./04-extending-serverless/README.md) +- [Extending Serverless](./04-extending-serverless/) - [Contributing to Serverless](#contributing) ## Providers -- [AWS Integration Docs](./02-providers/aws/README.md) +- [AWS Integration Docs](./02-providers/aws/) ## Concepts @@ -38,7 +38,7 @@ Serverless is used to build event driven architecture. Basically everything whic Events could be HTTP requests, events fired from a cloud storage (like a S3 bucket), scheduled events, etc. -- [AWS Events](./02-providers/aws/events/README.md) +- [AWS Events](./02-providers/aws/events/) ### Resources @@ -52,7 +52,7 @@ A *Serverless service* is a group of one or multiple functions and any resources Here you can read how to develop your own Serverless plugins. We'll get into details on how to write custom plugins to extend the functionality of Serverless. Furthermore we'll look into the way how you can use your plugin knowledge to integrate your own provider into the Serverless framework. -- [Building plugins](./04-extending-serverless/README.md) +- [Building plugins](./04-extending-serverless/) Connect with the community on [gitter](https://gitter.im/serverless/serverless) or in the [Forum](http://forum.serverless.com) From f732019d5b2bc362fcfa180ea9173ae0725788f7 Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Wed, 21 Sep 2016 12:38:10 +0200 Subject: [PATCH 37/85] Add any to list of allowed http methods --- .../aws/deploy/compile/events/apiGateway/lib/validate.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js index 249fdc952..cb2f77c12 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js @@ -43,11 +43,13 @@ module.exports = { throw new this.serverless.classes .Error(errorMessage); } - - if (['get', 'post', 'put', 'patch', 'options', 'head', 'delete'].indexOf(method) === -1) { + const allowedMethods = [ + 'get', 'post', 'put', 'patch', 'options', 'head', 'delete', 'any', + ]; + if (allowedMethods.indexOf(method) === -1) { const errorMessage = [ `Invalid APIG method "${method}" in function "${functionName}".`, - ' AWS supported methods are: get, post, put, patch, options, head, delete.', + ' AWS supported methods are: get, post, put, patch, options, head, delete, any.', ].join(''); throw new this.serverless.classes.Error(errorMessage); } From cb74b3b1bb0d3eb28da859253caaea2c5467ae37 Mon Sep 17 00:00:00 2001 From: kengos Date: Wed, 21 Sep 2016 22:36:13 +0900 Subject: [PATCH 38/85] add filename option to YAML.load --- lib/classes/Utils.js | 2 +- tests/classes/Utils.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index b03055cf5..6eef6587b 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -76,7 +76,7 @@ class Utils { if (filePath.endsWith('.json')) { contents = JSON.parse(contents); } else if (filePath.endsWith('.yml') || filePath.endsWith('.yaml')) { - contents = YAML.load(contents.toString()); + contents = YAML.load(contents.toString(), { filename: filePath }); } else { contents = contents.toString().trim(); } diff --git a/tests/classes/Utils.js b/tests/classes/Utils.js index 7711df8a6..08362e850 100644 --- a/tests/classes/Utils.js +++ b/tests/classes/Utils.js @@ -5,7 +5,13 @@ const os = require('os'); const expect = require('chai').expect; const Serverless = require('../../lib/Serverless'); const testUtils = require('../../tests/utils'); +const BbPromise = require('bluebird'); +const fse = BbPromise.promisifyAll(require('fs-extra')); +const writeFileSync = (filePath, contents) => { + fse.mkdirsSync(path.dirname(filePath)); + return fse.writeFileSync(filePath, contents); +}; const serverless = new Serverless(); describe('Utils', () => { @@ -107,6 +113,33 @@ describe('Utils', () => { expect(obj.foo).to.equal('bar'); }); + + it('should read a filename extension .yml', () => { + const tmpFilePath = testUtils.getTmpFilePath('anything.yml'); + + serverless.utils.writeFileSync(tmpFilePath, { foo: 'bar' }); + const obj = serverless.utils.readFileSync(tmpFilePath); + + expect(obj.foo).to.equal('bar'); + }); + + it('should read a filename extension .yaml', () => { + const tmpFilePath = testUtils.getTmpFilePath('anything.yaml'); + + serverless.utils.writeFileSync(tmpFilePath, { foo: 'bar' }); + const obj = serverless.utils.readFileSync(tmpFilePath); + + expect(obj.foo).to.equal('bar'); + }); + + it('should throw YAMLException with filename if yml file is invalid format', () => { + const tmpFilePath = testUtils.getTmpFilePath('invalid.yml'); + const contents = ': a'; + writeFileSync(tmpFilePath, contents); + expect(() => { + serverless.utils.readFileSync(tmpFilePath); + }).to.throw(new RegExp(`in "${tmpFilePath}"`)); + }); }); describe('#readFile()', () => { From 77f3133bf4b33bdc263f423f58087e81dbbbaccb Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Wed, 21 Sep 2016 18:53:44 +0200 Subject: [PATCH 39/85] Improve PR template for clearer validation data --- .github/PULL_REQUEST_TEMPLATE.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 92171cc87..b4e0f09f7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,7 @@ ## What did you implement: @@ -23,8 +22,14 @@ If this is a nontrivial change please briefly describe your implementation so it @@ -35,4 +40,7 @@ or AWS CLI commands to trigger something. - [ ] Fix linting errors - [ ] Make sure code coverage hasn't dropped - [ ] Provide verification config/commands/resources -- [ ] Leave a comment that this is ready for review once you've finished the implementation +- [ ] Change ready for review message below + + +***Is this ready for review?:*** NO From eaaeade956fdc04ff697bbe34fb2f39ba297e1e9 Mon Sep 17 00:00:00 2001 From: kengos Date: Thu, 22 Sep 2016 11:41:26 +0900 Subject: [PATCH 40/85] remove unnecessary requires and method --- tests/classes/Utils.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/classes/Utils.js b/tests/classes/Utils.js index 08362e850..56952475f 100644 --- a/tests/classes/Utils.js +++ b/tests/classes/Utils.js @@ -5,13 +5,7 @@ const os = require('os'); const expect = require('chai').expect; const Serverless = require('../../lib/Serverless'); const testUtils = require('../../tests/utils'); -const BbPromise = require('bluebird'); -const fse = BbPromise.promisifyAll(require('fs-extra')); -const writeFileSync = (filePath, contents) => { - fse.mkdirsSync(path.dirname(filePath)); - return fse.writeFileSync(filePath, contents); -}; const serverless = new Serverless(); describe('Utils', () => { @@ -134,8 +128,9 @@ describe('Utils', () => { it('should throw YAMLException with filename if yml file is invalid format', () => { const tmpFilePath = testUtils.getTmpFilePath('invalid.yml'); - const contents = ': a'; - writeFileSync(tmpFilePath, contents); + + serverless.utils.writeFileSync(tmpFilePath, ': a'); + expect(() => { serverless.utils.readFileSync(tmpFilePath); }).to.throw(new RegExp(`in "${tmpFilePath}"`)); From 5d08b6148a0db2bf5dac9a3e7c6fccd3685f7610 Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Thu, 22 Sep 2016 20:23:04 +0200 Subject: [PATCH 41/85] Bump to RC.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d8fd6e4e1..c9d7164c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "engines": { "node": ">=4.0" }, From b4a9e8c2ffad08320c7a77c76a43e27d9aebe1aa Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Thu, 22 Sep 2016 20:42:07 +0200 Subject: [PATCH 42/85] Bump dependencies --- docker-compose.yml | 2 +- npm-shrinkwrap.json | 32 +++++++++++--------------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 57fb343d4..7084ce367 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '2' services: serverless-node: - image: node:5.11.1 + image: node:latest working_dir: /app volumes: - .:/app diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 63d59b976..04d1609e1 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.0.0-rc.1", + "version": "1.0.0-rc.2", "dependencies": { "abbrev": { "version": "1.0.9", @@ -114,6 +114,11 @@ "from": "async@>=1.5.2 <2.0.0", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" }, + "asynckit": { + "version": "0.4.0", + "from": "asynckit@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + }, "aws-sdk": { "version": "2.5.3", "from": "aws-sdk@>=2.3.17 <3.0.0", @@ -521,6 +526,11 @@ "from": "figures@>=1.3.5 <2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz" }, + "file-entry-cache": { + "version": "2.0.0", + "from": "file-entry-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz" + }, "find-up": { "version": "1.1.2", "from": "find-up@>=1.0.0 <2.0.0", @@ -885,11 +895,6 @@ "from": "lcid@>=1.0.0 <2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz" }, - "lcov-parse": { - "version": "0.0.6", - "from": "lcov-parse@0.0.6", - "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.6.tgz" - }, "levn": { "version": "0.3.0", "from": "levn@>=0.3.0 <0.4.0", @@ -980,11 +985,6 @@ "from": "lodash.keys@>=3.0.0 <4.0.0", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz" }, - "log-driver": { - "version": "1.2.4", - "from": "log-driver@1.2.4", - "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.4.tgz" - }, "lolex": { "version": "1.3.2", "from": "lolex@1.3.2", @@ -1587,16 +1587,6 @@ "from": "uglify-to-browserify@>=1.0.0 <1.1.0", "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz" }, - "underscore": { - "version": "1.7.0", - "from": "underscore@>=1.7.0 <1.8.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" - }, - "underscore.string": { - "version": "2.4.0", - "from": "underscore.string@>=2.4.0 <2.5.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz" - }, "uri-js": { "version": "2.1.1", "from": "uri-js@>=2.1.1 <3.0.0", From 2bf6d447785a0fe642bfa3e0ed061022a7de090e Mon Sep 17 00:00:00 2001 From: Erik Erikson Date: Thu, 22 Sep 2016 12:28:35 -0700 Subject: [PATCH 43/85] Add Initial .editorconfig File Editor config allows IDEs to consistently behave according to the project's proscribed formatting requirements. This means that fewer spurious white space changes will be committed and that fewer generated white space characters will have to be eliminated by individual developers whose defaults are different than those of the Serverless project. --- .editorconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..49dafa5e2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true ; top-most EditorConfig file + +; Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true From 1b0e9aed6bb779a01ecf2b47b1f61face270a087 Mon Sep 17 00:00:00 2001 From: Erik Erikson Date: Thu, 22 Sep 2016 12:28:35 -0700 Subject: [PATCH 44/85] Add Initial .editorconfig File & Docs Editor config allows IDEs to consistently behave according to the project's proscribed formatting requirements. This means that fewer spurious white space changes will be committed and that fewer generated white space characters will have to be eliminated by individual developers whose defaults are different than those of the Serverless project. --- .editorconfig | 10 ++++++++++ CONTRIBUTING.md | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..49dafa5e2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true ; top-most EditorConfig file + +; Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 08c2f8a9d..6f90de2d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,7 +30,7 @@ Please follow these Issue guidelines for opening Issues: * Make sure your Issue is for a *feature request*, *bug report*, or *a discussion about a relevant topic*. For everything else, please use our [Discourse Forum](http://forum.serverless.com) ### Code Style -We aim for clean, consistent code style. We're using ESlint to check for codestyle issues using the Airbnb preset. If ESlint issues are found our build will fail and we can't merge the PR. +We aim for clean, consistent code style. We're using ESlint to check for codestyle issues using the Airbnb preset. If ESlint issues are found our build will fail and we can't merge the PR. To help reduce the effort of creating contributions with this style, an [.editorconfig file](http://editorconfig.org/) is provided that your editor may use to override any conflicting global defaults and automate a subset of the style settings. You may need to enable EditorConfig's use by changing a setting or installing a plugin. Using it is not compulsory. Please follow these Code Style guidelines when writing your unit tests: * In the root of our repo, use this command to check for styling issues: `npm run lint` From 73617d0a66d7a4cd390eff9a4ac00b036df0575c Mon Sep 17 00:00:00 2001 From: zorrofox Date: Fri, 23 Sep 2016 22:47:23 +0800 Subject: [PATCH 45/85] fix: #1973 deploy fails with unhelpful error message when service name is not a valid CF stack name --- lib/plugins/aws/deploy/lib/createStack.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/plugins/aws/deploy/lib/createStack.js b/lib/plugins/aws/deploy/lib/createStack.js index f8ba51d7f..de729ae5f 100644 --- a/lib/plugins/aws/deploy/lib/createStack.js +++ b/lib/plugins/aws/deploy/lib/createStack.js @@ -32,6 +32,16 @@ module.exports = { createStack() { const stackName = `${this.serverless.service.service}-${this.options.stage}`; + if (/^[a-zA-Z-]+/.test(stackName) || stackName.length > 128){ + const errorMessage = [ + 'The stack name "' + stackName + '" is not quallify. ', + 'A stack name can contain only alphanumeric', + ' (case sensitive) and hyphens. It must characters', + ' start with an alphabetic character and cannot', + ' be longer than 128 characters.' + ].join(''); + throw new this.serverless.classes.Error(errorMessage); + } this.serverless.service.provider .compiledCloudFormationTemplate = this.loadCoreCloudFormationTemplate(); From dbeaa54832983a09b3940e4d9edd8f4e95249e00 Mon Sep 17 00:00:00 2001 From: zorrofox Date: Fri, 23 Sep 2016 23:04:34 +0800 Subject: [PATCH 46/85] fix: #1973 deploy fails with unhelpful error message when service name is not a valid CF stack name --- lib/plugins/aws/deploy/lib/createStack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/lib/createStack.js b/lib/plugins/aws/deploy/lib/createStack.js index de729ae5f..c62e88ab2 100644 --- a/lib/plugins/aws/deploy/lib/createStack.js +++ b/lib/plugins/aws/deploy/lib/createStack.js @@ -32,7 +32,7 @@ module.exports = { createStack() { const stackName = `${this.serverless.service.service}-${this.options.stage}`; - if (/^[a-zA-Z-]+/.test(stackName) || stackName.length > 128){ + if (/^[a-zA-Z1-9-]+/.test(stackName) || stackName.length > 128){ const errorMessage = [ 'The stack name "' + stackName + '" is not quallify. ', 'A stack name can contain only alphanumeric', From b8e5bb4eca1b9d92e263e580b484b9efbbc7b8cf Mon Sep 17 00:00:00 2001 From: zorrofox Date: Fri, 23 Sep 2016 23:59:13 +0800 Subject: [PATCH 47/85] fix: #1973 deploy fails with unhelpful error message when service name is not a valid CF stack name --- lib/plugins/aws/deploy/lib/createStack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/lib/createStack.js b/lib/plugins/aws/deploy/lib/createStack.js index c62e88ab2..3b25bda07 100644 --- a/lib/plugins/aws/deploy/lib/createStack.js +++ b/lib/plugins/aws/deploy/lib/createStack.js @@ -32,7 +32,7 @@ module.exports = { createStack() { const stackName = `${this.serverless.service.service}-${this.options.stage}`; - if (/^[a-zA-Z1-9-]+/.test(stackName) || stackName.length > 128){ + if (/^[^a-zA-Z].+|.*[^a-zA-Z0-9\-].*/.test(stackName) || stackName.length > 128){ const errorMessage = [ 'The stack name "' + stackName + '" is not quallify. ', 'A stack name can contain only alphanumeric', From 244dd8092ad63da2b63dbc6abf038359f6567cfb Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Fri, 23 Sep 2016 18:14:44 +0200 Subject: [PATCH 48/85] Add --name docs --- docs/01-guide/02-creating-services.md | 4 ++-- docs/03-cli-reference/01-create.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/01-guide/02-creating-services.md b/docs/01-guide/02-creating-services.md index 9ea066723..eca5a3b16 100644 --- a/docs/01-guide/02-creating-services.md +++ b/docs/01-guide/02-creating-services.md @@ -12,10 +12,10 @@ You can create a service based on a specific template that specifies which provi To create a service with a `nodejs` runtime running on `aws` just pass the `aws-nodejs` template to the create command: ``` -serverless create --template aws-nodejs +serverless create --template aws-nodejs --name my-special-service ``` -This will create a service and generate `serverless.yml` and `handler.js` files in the current working directory. +This will create a service and generate `serverless.yml`, `handler.js` and `event.json` files in the current working directory and set the name of the service to `my-special-service` in `serverless.yml`. You can also check out the [create command docs](../03-cli-reference/01-create.md) for all the details and options. diff --git a/docs/03-cli-reference/01-create.md b/docs/03-cli-reference/01-create.md index 389d928aa..aa5b2410c 100644 --- a/docs/03-cli-reference/01-create.md +++ b/docs/03-cli-reference/01-create.md @@ -16,6 +16,7 @@ serverless create --template aws-nodejs ## Options - `--template` or `-t` The name of your new service. **Required**. - `--path` or `-p` The path where the service should be created. +- `--name` or `-n` the name of the service in `serverless.yml`. ## Provided lifecycle events - `create:create` @@ -36,7 +37,7 @@ Most commonly used templates: ### Creating a new service ``` -serverless create --template aws-nodejs +serverless create --template aws-nodejs --name my-special-service ``` This example will generate scaffolding for a service with `AWS` as a provider and `nodejs` as runtime. The scaffolding From eaf175da06f52753fe4051c259f34a8da6b4d370 Mon Sep 17 00:00:00 2001 From: zorrofox Date: Sat, 24 Sep 2016 00:22:19 +0800 Subject: [PATCH 49/85] fix: #1973 deploy fails with unhelpful error message when service name is not a valid CF stack name --- lib/plugins/aws/deploy/lib/createStack.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/plugins/aws/deploy/lib/createStack.js b/lib/plugins/aws/deploy/lib/createStack.js index 3b25bda07..d686997e8 100644 --- a/lib/plugins/aws/deploy/lib/createStack.js +++ b/lib/plugins/aws/deploy/lib/createStack.js @@ -32,14 +32,14 @@ module.exports = { createStack() { const stackName = `${this.serverless.service.service}-${this.options.stage}`; - if (/^[^a-zA-Z].+|.*[^a-zA-Z0-9\-].*/.test(stackName) || stackName.length > 128){ + if (/^[^a-zA-Z].+|.*[^a-zA-Z0-9\-].*/.test(stackName) || stackName.length > 128) { const errorMessage = [ - 'The stack name "' + stackName + '" is not quallify. ', - 'A stack name can contain only alphanumeric', - ' (case sensitive) and hyphens. It must characters', - ' start with an alphabetic character and cannot', - ' be longer than 128 characters.' - ].join(''); + `The stack name "${stackName}" is not quallify. `, + 'A stack name can contain only alphanumeric', + ' (case sensitive) and hyphens. It must characters', + ' start with an alphabetic character and cannot', + ' be longer than 128 characters.', + ].join(''); throw new this.serverless.classes.Error(errorMessage); } this.serverless.service.provider From 478209bfa6025248a1ef949f940a20fc80cca316 Mon Sep 17 00:00:00 2001 From: zorrofox Date: Sat, 24 Sep 2016 00:32:03 +0800 Subject: [PATCH 50/85] fix: #1973 deploy fails with unhelpful error message when service name is not a valid CF stack name --- lib/plugins/aws/deploy/lib/createStack.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/plugins/aws/deploy/lib/createStack.js b/lib/plugins/aws/deploy/lib/createStack.js index d686997e8..49c3bf94b 100644 --- a/lib/plugins/aws/deploy/lib/createStack.js +++ b/lib/plugins/aws/deploy/lib/createStack.js @@ -31,7 +31,6 @@ module.exports = { createStack() { const stackName = `${this.serverless.service.service}-${this.options.stage}`; - if (/^[^a-zA-Z].+|.*[^a-zA-Z0-9\-].*/.test(stackName) || stackName.length > 128) { const errorMessage = [ `The stack name "${stackName}" is not quallify. `, From a176b49a57eb92395c36cbcf607b1e0856eac128 Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Fri, 23 Sep 2016 21:47:50 +0200 Subject: [PATCH 51/85] Update PR Template with feedback from @pmuens --- .github/PULL_REQUEST_TEMPLATE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b4e0f09f7..4c321d3fd 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -26,10 +26,10 @@ to make it easy for us to verify this works. The easier you make it for us to review a PR, the faster we can review and merge it. Examples: -* Fully functioning serverless.yml to easily deploy changes -* Screenshot showing the difference between your output and the master -* AWS CLI commands to list AWS resources and show that the correct config is in place -* Anything else that comes to mind to help us evaluate +* serverless.yml - Fully functioning to easily deploy changes +* Screenshots - Showing the difference between your output and the master +* AWS CLI commands - To list AWS resources and show that the correct config is in place +* Other - Anything else that comes to mind to help us evaluate --> From b2f1d54d20556a67d3859a7cd619a2f02c0d5131 Mon Sep 17 00:00:00 2001 From: Doug Moscrop Date: Fri, 23 Sep 2016 18:04:47 -0400 Subject: [PATCH 52/85] Change describe to it because it contains a test --- tests/classes/PluginManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/classes/PluginManager.js b/tests/classes/PluginManager.js index b5e42701a..258766c15 100644 --- a/tests/classes/PluginManager.js +++ b/tests/classes/PluginManager.js @@ -763,7 +763,7 @@ describe('PluginManager', () => { }); }); - describe('Plugin/CLI integration', () => { + it('Plugin/CLI integration', () => { const serverlessInstance = new Serverless(); serverlessInstance.init(); From 8828042a32577707fa6014aadf3641338df2d68c Mon Sep 17 00:00:00 2001 From: Doug Moscrop Date: Sun, 25 Sep 2016 16:02:06 -0400 Subject: [PATCH 53/85] add Integration tests for CLI printing --help --- tests/classes/CLI.js | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/classes/CLI.js b/tests/classes/CLI.js index 94932add9..2f1ccf1a2 100644 --- a/tests/classes/CLI.js +++ b/tests/classes/CLI.js @@ -6,7 +6,12 @@ const expect = require('chai').expect; const CLI = require('../../lib/classes/CLI'); +const os = require('os'); +const fse = require('fs-extra'); +const exec = require('child_process').exec; +const path = require('path'); const Serverless = require('../../lib/Serverless'); +const testUtils = require('../../tests/utils'); describe('CLI', () => { let cli; @@ -262,4 +267,53 @@ describe('CLI', () => { expect(inputToBeProcessed).to.deep.equal(expectedObject); }); }); + + describe('integration tests', () => { + before(() => { + const tmpDir = testUtils.getTmpDirPath(); + + this.cwd = process.cwd(); + + fse.mkdirSync(tmpDir); + process.chdir(tmpDir); + + serverless = new Serverless(); + serverless.init(); + + // Cannot rely on shebang in severless.js to invoke script using NodeJS on Windows. + const execPrefix = os.platform() === 'win32' ? 'node ' : ''; + + this.serverlessExec = execPrefix + path.join(serverless.config.serverlessPath, + '..', 'bin', 'serverless'); + }); + + after(() => { + process.chdir(this.cwd); + }); + + it('prints general --help to stdout', (done) => { + exec(`${this.serverlessExec} --help`, (err, stdout) => { + if (err) { + done(err); + return; + } + + expect(stdout).to.contain('contextual help'); + done(); + }); + }); + + it('prints command --help to stdout', (done) => { + exec(`${this.serverlessExec} deploy --help`, (err, stdout) => { + if (err) { + done(err); + return; + } + + expect(stdout).to.contain('deploy'); + expect(stdout).to.contain('--stage'); + done(); + }); + }); + }); }); From 2904d6792b86204cd58fbc53a24906d4bdb7ab03 Mon Sep 17 00:00:00 2001 From: Doug Moscrop Date: Sun, 25 Sep 2016 16:50:56 -0400 Subject: [PATCH 54/85] add tracking-id to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9e1a89e4c..0bb65a865 100755 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ admin.env .env tmp .coveralls.yml +tracking-id From e7fedf4fd1eeeed39797c32fdb9e2eb53da54605 Mon Sep 17 00:00:00 2001 From: Sergio Arcos Date: Mon, 26 Sep 2016 15:09:41 +0900 Subject: [PATCH 55/85] wrong description --- lib/plugins/tracking/tracking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/tracking/tracking.js b/lib/plugins/tracking/tracking.js index f1e0dbea7..10115bdaa 100644 --- a/lib/plugins/tracking/tracking.js +++ b/lib/plugins/tracking/tracking.js @@ -43,7 +43,7 @@ class Tracking { } if (this.options.disable && !this.options.enable) { fs.writeFileSync(path.join(serverlessPath, trackingFileName), - 'Keep this file to enable tracking'); + 'Keep this file to disable tracking'); this.serverless.cli.log('Tracking successfully disabled'); } } From 41ef1d040ddcea3067777ebf679de84b4024a3c2 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Wed, 21 Sep 2016 13:32:13 +0200 Subject: [PATCH 56/85] Fix api key value displaying in info plugin --- .../compile/events/apiGateway/lib/apiKeys.js | 4 +- lib/plugins/aws/info/index.js | 67 +++++++++++++++---- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/apiKeys.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/apiKeys.js index 6e8dfa161..3f9edbe77 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/apiKeys.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/apiKeys.js @@ -39,9 +39,9 @@ module.exports = { _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, newApiKeyObject); - // Add API Key to Outputs section + // Add Id of API Key to Outputs section (Note: This is the Id, not the value!) const newOutput = { - Description: apiKey, + Description: `Id of API key "${apiKey}"`, Value: { Ref: `ApiGatewayApiKey${apiKeyNumber}`, }, diff --git a/lib/plugins/aws/info/index.js b/lib/plugins/aws/info/index.js index 26efcf3cc..e85796d87 100644 --- a/lib/plugins/aws/info/index.js +++ b/lib/plugins/aws/info/index.js @@ -51,12 +51,13 @@ class AwsInfo { this.options.stage, this.options.region) .then((result) => { + let outputs; + if (result) { - const outputs = result.Stacks[0].Outputs; + outputs = result.Stacks[0].Outputs; // Functions info.functions = []; - info.apiKeys = []; outputs.filter(x => x.OutputKey.match(/LambdaFunctionArn$/)) .forEach(x => { const functionInfo = {}; @@ -71,21 +72,23 @@ class AwsInfo { info.endpoint = x.OutputValue; }); - // API Keys - outputs.filter(x => x.OutputKey.match(/^ApiGatewayApiKey/)) - .forEach(x => { - const apiKeyInfo = {}; - apiKeyInfo.name = x.Description; - apiKeyInfo.value = x.OutputValue; - info.apiKeys.push(apiKeyInfo); - }); - // Resources info.resources = []; + + // API Keys + info.apiKeys = []; } - return BbPromise.resolve(info); + // create a gatheredData object which can be passed around ("[call] by reference") + const gatheredData = { + outputs, + info, + }; + + return BbPromise.resolve(gatheredData); }) + .then((gatheredData) => this.getApiKeyValues(gatheredData)) + .then((gatheredData) => BbPromise.resolve(gatheredData.info)) // resolve the info at the end .catch((e) => { let result; @@ -102,6 +105,46 @@ class AwsInfo { }); } + getApiKeyValues(gatheredData) { + const outputs = gatheredData.outputs; + const info = gatheredData.info; + + // check if there are API key outputs in the stacks Outputs section + let apiKeyOutputs; + if (outputs) { + apiKeyOutputs = outputs.filter((output) => output.OutputKey.match(/^ApiGatewayApiKey/)); + } + + if (apiKeyOutputs) { + return this.sdk.request('APIGateway', + 'getApiKeys', + { includeValues: true }, + this.options.stage, + this.options.region + ).then((apiKeys) => { + const items = apiKeys.items; + if (items) { + // extract the ids from the API key object + const apiKeyIds = []; + apiKeyOutputs.forEach((apiKeyOutput) => apiKeyIds.push(apiKeyOutput.OutputValue)); + + // filter out the API keys only created for this stack + const filteredItems = items.filter((item) => _.includes(apiKeyIds, item.id)); + + // iterate over all apiKeys and push the API key info and update the info object + filteredItems.forEach((item) => { + const apiKeyInfo = {}; + apiKeyInfo.name = item.name; + apiKeyInfo.value = item.value; + info.apiKeys.push(apiKeyInfo); + }); + } + return BbPromise.resolve(gatheredData); + }); + } + return BbPromise.resolve(gatheredData); + } + /** * Display service information */ From a5dcee1cad0aa5992393b54339bb44e3836c8d5e Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Wed, 21 Sep 2016 15:12:31 +0200 Subject: [PATCH 57/85] Add / fix tests for api key value retrieval --- .../events/apiGateway/tests/apiKeys.js | 2 +- lib/plugins/aws/info/index.js | 2 +- lib/plugins/aws/info/tests/index.js | 104 +++++++++++++++--- 3 files changed, 88 insertions(+), 20 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/apiKeys.js b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/apiKeys.js index fc721e7e4..868e0f8ec 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/apiKeys.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/apiKeys.js @@ -87,7 +87,7 @@ describe('#compileApiKeys()', () => { expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate .Outputs.ApiGatewayApiKey1Value.Description - ).to.equal('1234567890'); + ).to.equal('Id of API key "1234567890"'); expect( awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate diff --git a/lib/plugins/aws/info/index.js b/lib/plugins/aws/info/index.js index e85796d87..c1777437d 100644 --- a/lib/plugins/aws/info/index.js +++ b/lib/plugins/aws/info/index.js @@ -115,7 +115,7 @@ class AwsInfo { apiKeyOutputs = outputs.filter((output) => output.OutputKey.match(/^ApiGatewayApiKey/)); } - if (apiKeyOutputs) { + if (apiKeyOutputs.length) { return this.sdk.request('APIGateway', 'getApiKeys', { includeValues: true }, diff --git a/lib/plugins/aws/info/tests/index.js b/lib/plugins/aws/info/tests/index.js index fc7844823..53241d725 100644 --- a/lib/plugins/aws/info/tests/index.js +++ b/lib/plugins/aws/info/tests/index.js @@ -163,7 +163,7 @@ describe('AwsInfo', () => { it('should gather with correct params', () => awsInfo.gather() .then(() => { - expect(describeStackStub.calledOnce).to.equal(true); + expect(describeStackStub.called).to.equal(true); expect(describeStackStub.args[0][0]).to.equal('CloudFormation'); expect(describeStackStub.args[0][1]).to.equal('describeStacks'); expect(describeStackStub.args[0][2].StackName) @@ -218,23 +218,6 @@ describe('AwsInfo', () => { }); }); - it('should get api keys', () => { - const expectedApiKeys = [ - { - name: 'first', - value: 'xxx', - }, - { - name: 'second', - value: 'yyy', - }, - ]; - - return awsInfo.gather().then((info) => { - expect(info.apiKeys).to.deep.equal(expectedApiKeys); - }); - }); - it("should provide only general info when stack doesn't exist (ValidationError)", () => { awsInfo.sdk.request.restore(); @@ -267,6 +250,91 @@ 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(); + + const gatheredData = { + outputs: [ + { + OutputKey: 'ApiGatewayApiKey1Value', + OutputValue: '1234', + Description: 'Id for API key "foo"', + }, + { + OutputKey: 'ApiGatewayApiKey2Value', + OutputValue: '5678', + Description: 'Id for API key "bar"', + }, + ], + info: { + apiKeys: [], + }, + }; + + const apiKeyItems = { + items: [ + { + id: '4711', + name: 'SomeRandomIdInUsersAccount', + value: 'ShouldNotBeConsidered', + }, + { + id: '1234', + name: 'foo', + value: 'valueForKeyFoo', + }, + { + id: '5678', + name: 'bar', + value: 'valueForKeyBar', + }, + ], + }; + + const gatheredDataAfterKeyLookup = { + info: { + apiKeys: [ + { name: 'foo', value: 'valueForKeyFoo' }, + { name: 'bar', value: 'valueForKeyBar' }, + ], + }, + }; + + const getApiKeysStub = sinon + .stub(awsInfo.sdk, '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(); + }); + }); + + it('should resolve with the passed-in data if no API key retrieval is necessary', () => { + const gatheredData = { + outputs: [], + info: { + apiKeys: [], + }, + }; + + const getApiKeysStub = sinon + .stub(awsInfo.sdk, '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(); + }); + }); + }); + describe('#display()', () => { it('should format information message correctly', () => { serverless.cli = new CLI(serverless); From 40da6be2b2c19d44f8dcfa08114b550749352218 Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Thu, 22 Sep 2016 11:40:45 +0200 Subject: [PATCH 58/85] Remove dependency on API key id in outputs section --- .../compile/events/apiGateway/lib/apiKeys.js | 15 -------------- .../events/apiGateway/tests/apiKeys.js | 14 ------------- lib/plugins/aws/info/index.js | 20 ++++++------------- lib/plugins/aws/info/tests/index.js | 18 ++++++----------- 4 files changed, 12 insertions(+), 55 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/apiKeys.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/apiKeys.js index 3f9edbe77..31dae6232 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/apiKeys.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/apiKeys.js @@ -38,21 +38,6 @@ module.exports = { _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, newApiKeyObject); - - // Add Id of API Key to Outputs section (Note: This is the Id, not the value!) - const newOutput = { - Description: `Id of API key "${apiKey}"`, - Value: { - Ref: `ApiGatewayApiKey${apiKeyNumber}`, - }, - }; - - const newOutputObject = { - [`ApiGatewayApiKey${apiKeyNumber}Value`]: newOutput, - }; - - _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Outputs, - newOutputObject); }); } diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/apiKeys.js b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/apiKeys.js index 868e0f8ec..94e7ce305 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/apiKeys.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/apiKeys.js @@ -82,20 +82,6 @@ describe('#compileApiKeys()', () => { }) ); - it('should add api keys cf output template', () => awsCompileApigEvents - .compileApiKeys().then(() => { - expect( - awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate - .Outputs.ApiGatewayApiKey1Value.Description - ).to.equal('Id of API key "1234567890"'); - - expect( - awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate - .Outputs.ApiGatewayApiKey1Value.Value.Ref - ).to.equal('ApiGatewayApiKey1'); - }) - ); - it('throw error if apiKey property is not an array', () => { awsCompileApigEvents.serverless.service.provider.apiKeys = 2; expect(() => awsCompileApigEvents.compileApiKeys()).to.throw(Error); diff --git a/lib/plugins/aws/info/index.js b/lib/plugins/aws/info/index.js index c1777437d..dbbf6da54 100644 --- a/lib/plugins/aws/info/index.js +++ b/lib/plugins/aws/info/index.js @@ -106,30 +106,22 @@ class AwsInfo { } getApiKeyValues(gatheredData) { - const outputs = gatheredData.outputs; const info = gatheredData.info; - // check if there are API key outputs in the stacks Outputs section - let apiKeyOutputs; - if (outputs) { - apiKeyOutputs = outputs.filter((output) => output.OutputKey.match(/^ApiGatewayApiKey/)); - } + // check if the user has set api keys + const apiKeyNames = this.serverless.service.provider.apiKeys || []; - if (apiKeyOutputs.length) { + if (apiKeyNames.length) { return this.sdk.request('APIGateway', 'getApiKeys', { includeValues: true }, this.options.stage, this.options.region - ).then((apiKeys) => { - const items = apiKeys.items; + ).then((allApiKeys) => { + const items = allApiKeys.items; if (items) { - // extract the ids from the API key object - const apiKeyIds = []; - apiKeyOutputs.forEach((apiKeyOutput) => apiKeyIds.push(apiKeyOutput.OutputValue)); - // filter out the API keys only created for this stack - const filteredItems = items.filter((item) => _.includes(apiKeyIds, item.id)); + const filteredItems = items.filter((item) => _.includes(apiKeyNames, item.name)); // iterate over all apiKeys and push the API key info and update the info object filteredItems.forEach((item) => { diff --git a/lib/plugins/aws/info/tests/index.js b/lib/plugins/aws/info/tests/index.js index 53241d725..871cc261b 100644 --- a/lib/plugins/aws/info/tests/index.js +++ b/lib/plugins/aws/info/tests/index.js @@ -255,19 +255,11 @@ describe('AwsInfo', () => { // TODO: implement a pattern for stub restoring to get rid of this awsInfo.sdk.request.restore(); + // set the API Keys for the service + awsInfo.serverless.service.provider.apiKeys = ['foo', 'bar']; + const gatheredData = { - outputs: [ - { - OutputKey: 'ApiGatewayApiKey1Value', - OutputValue: '1234', - Description: 'Id for API key "foo"', - }, - { - OutputKey: 'ApiGatewayApiKey2Value', - OutputValue: '5678', - Description: 'Id for API key "bar"', - }, - ], + outputs: [], info: { apiKeys: [], }, @@ -315,6 +307,8 @@ describe('AwsInfo', () => { }); it('should resolve with the passed-in data if no API key retrieval is necessary', () => { + awsInfo.serverless.service.provider.apiKeys = null; + const gatheredData = { outputs: [], info: { From a2e6c598538855686fca93461bcae78eea80a41f Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Fri, 23 Sep 2016 12:24:26 +0200 Subject: [PATCH 59/85] Update info documentation and add api keys example to it --- docs/03-cli-reference/05-info.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/03-cli-reference/05-info.md b/docs/03-cli-reference/05-info.md index 0975bdb07..e8e97af66 100644 --- a/docs/03-cli-reference/05-info.md +++ b/docs/03-cli-reference/05-info.md @@ -24,7 +24,8 @@ serverless info ### AWS -On AWS the info plugin uses the `Outputs` section of the CloudFormation stack. Outputs will include Lambda function ARN's, a `ServiceEndpoint` for the API Gateway endpoint and user provided custom Outputs. +On AWS the info plugin uses the `Outputs` section of the CloudFormation stack and the AWS SDK to gather the necessary information. +See the example below for an example output. **Example:** @@ -35,6 +36,8 @@ Service Information service: my-serverless-service stage: dev region: us-east-1 +api keys: + myKey: some123valid456api789key1011for1213api1415gateway endpoints: GET - https://dxaynpuzd4.execute-api.us-east-1.amazonaws.com/dev/users functions: From d6ffe8962b7a39f7c2abcd261e31ed0c36c994b3 Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Fri, 23 Sep 2016 20:57:12 +0200 Subject: [PATCH 60/85] Add Var at the end of path variable CF name --- .../compile/events/apiGateway/lib/resources.js | 5 +++-- .../compile/events/apiGateway/tests/resources.js | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/resources.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/resources.js index 6d9b04010..1892be7fb 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/resources.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/resources.js @@ -43,9 +43,10 @@ module.exports = { }); }); - const capitalizeAlphaNumericPath = (path) => _.capitalize( - path + const capitalizeAlphaNumericPath = (path) => _.upperFirst( + _.capitalize(path) .replace(/-/g, 'Dash') + .replace(/\{(.*)\}/g, '$1Var') .replace(/[^0-9A-Za-z]/g, '') ); diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/resources.js b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/resources.js index e182c47ca..08ff6fe1c 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/resources.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/resources.js @@ -81,10 +81,10 @@ describe('#compileResources()', () => { 'bar/-': 'ApiGatewayResourceBarDash', 'foo/bar': 'ApiGatewayResourceFooBar', foo: 'ApiGatewayResourceFoo', - 'bar/{id}/foobar': 'ApiGatewayResourceBarIdFoobar', - 'bar/{id}': 'ApiGatewayResourceBarId', - 'bar/{foo_id}/foobar': 'ApiGatewayResourceBarFooidFoobar', - 'bar/{foo_id}': 'ApiGatewayResourceBarFooid', + 'bar/{id}/foobar': 'ApiGatewayResourceBarIdVarFoobar', + 'bar/{id}': 'ApiGatewayResourceBarIdVar', + 'bar/{foo_id}/foobar': 'ApiGatewayResourceBarFooidVarFoobar', + 'bar/{foo_id}': 'ApiGatewayResourceBarFooidVar', 'bar/foo': 'ApiGatewayResourceBarFoo', bar: 'ApiGatewayResourceBar', }; @@ -107,14 +107,14 @@ describe('#compileResources()', () => { .Resources.ApiGatewayResourceBar.Properties.ParentId['Fn::GetAtt'][1]) .to.equal('RootResourceId'); expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate - .Resources.ApiGatewayResourceBarId.Properties.ParentId.Ref) + .Resources.ApiGatewayResourceBarIdVar.Properties.ParentId.Ref) .to.equal('ApiGatewayResourceBar'); expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate - .Resources.ApiGatewayResourceBarFooid.Properties.ParentId.Ref) + .Resources.ApiGatewayResourceBarFooidVar.Properties.ParentId.Ref) .to.equal('ApiGatewayResourceBar'); expect(awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate - .Resources.ApiGatewayResourceBarFooidFoobar.Properties.ParentId.Ref) - .to.equal('ApiGatewayResourceBarFooid'); + .Resources.ApiGatewayResourceBarFooidVarFoobar.Properties.ParentId.Ref) + .to.equal('ApiGatewayResourceBarFooidVar'); }) ); From 47b9ec5f1e5171967a3f7d4898b3162920107a98 Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Mon, 26 Sep 2016 13:48:20 +0200 Subject: [PATCH 61/85] Add Var/Dash description to docs for APIG Resources --- docs/02-providers/aws/04-resource-names-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-providers/aws/04-resource-names-reference.md b/docs/02-providers/aws/04-resource-names-reference.md index c2ff1f148..efa6d6f05 100644 --- a/docs/02-providers/aws/04-resource-names-reference.md +++ b/docs/02-providers/aws/04-resource-names-reference.md @@ -28,7 +28,7 @@ We're also using the term `normalizedName` or similar terms in this guide. This |Lambda::Permission |
  • **Schedule**: {normalizedFunctionName}LambdaPermissionEventsRuleSchedule{index}
  • **S3**: {normalizedFunctionName}LambdaPermissionS3
  • **APIG**: {normalizedFunctionName}LambdaPermissionApiGateway
  • **SNS**: {normalizedFunctionName}LambdaPermission{normalizedTopicName}
  • |
    • **Schedule**: HelloLambdaPermissionEventsRuleSchedule1
    • **S3**: HelloLambdaPermissionS3
    • **APIG**: HelloLambdaPermissionApiGateway
    • **SNS**: HelloLambdaPermissionSometopic
    • | |Events::Rule | {normalizedFuntionName}EventsRuleSchedule{SequentialID} | HelloEventsRuleSchedule1 | |ApiGateway::RestApi | ApiGatewayRestApi | ApiGatewayRestApi | -|ApiGateway::Resource | ApiGatewayResource{normalizedPath} | ApiGatewayResourceUsers | +|ApiGateway::Resource | ApiGatewayResource{normalizedPath} |
      • ApiGatewayResourceUsers
      • ApiGatewayResourceUsers**Var** for paths containing a variable
      • ApiGatewayResource**Dash** if the path is just a `-`
      | |ApiGateway::Method | ApiGatewayResource{normalizedPath}{normalizedMethod} | ApiGatewayResourceUsersGet | |ApiGateway::Authorizer | {normalizedFunctionName}ApiGatewayAuthorizer | HelloApiGatewayAuthorizer | |ApiGateway::Deployment | ApiGatewayDeployment{randomNumber} | ApiGatewayDeployment12356789 | From 4490af73c07f8d511b69ebcc85238accb80428cc Mon Sep 17 00:00:00 2001 From: Doug Moscrop Date: Sat, 24 Sep 2016 11:21:19 -0400 Subject: [PATCH 62/85] fix Tests not passing on Windows --- lib/plugins/package/tests/zipService.js | 19 +++++++++++++------ tests/classes/Utils.js | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/plugins/package/tests/zipService.js b/lib/plugins/package/tests/zipService.js index 6c8032a22..723bab8f2 100644 --- a/lib/plugins/package/tests/zipService.js +++ b/lib/plugins/package/tests/zipService.js @@ -2,6 +2,7 @@ const expect = require('chai').expect; const fs = require('fs'); +const os = require('os'); const path = require('path'); const JsZip = require('jszip'); const _ = require('lodash'); @@ -140,13 +141,19 @@ describe('#zipService()', () => { }).then(unzippedData => { const unzippedFileData = unzippedData.files; - // binary file is set with chmod of 777 - expect(unzippedFileData['bin/some-binary'].unixPermissions) - .to.equal(Math.pow(2, 15) + 777); + if (os.platform() === 'win32') { + // chmod does not work right on windows. this is better than nothing? + expect(unzippedFileData['bin/some-binary'].unixPermissions) + .to.not.equal(unzippedFileData['bin/read-only'].unixPermissions); + } else { + // binary file is set with chmod of 777 + expect(unzippedFileData['bin/some-binary'].unixPermissions) + .to.equal(Math.pow(2, 15) + 777); - // read only file is set with chmod of 444 - expect(unzippedFileData['bin/read-only'].unixPermissions) - .to.equal(Math.pow(2, 15) + 444); + // read only file is set with chmod of 444 + expect(unzippedFileData['bin/read-only'].unixPermissions) + .to.equal(Math.pow(2, 15) + 444); + } }); }); diff --git a/tests/classes/Utils.js b/tests/classes/Utils.js index 45cf13926..afa2b766b 100644 --- a/tests/classes/Utils.js +++ b/tests/classes/Utils.js @@ -135,7 +135,7 @@ describe('Utils', () => { expect(() => { serverless.utils.readFileSync(tmpFilePath); - }).to.throw(new RegExp(`in "${tmpFilePath}"`)); + }).to.throw(new RegExp('YAMLException:.*invalid.yml')); }); }); From a793014bbc25cfc75abe0bd036049d8c556c2b7d Mon Sep 17 00:00:00 2001 From: Meaghan Harty Date: Thu, 22 Sep 2016 13:08:12 -0400 Subject: [PATCH 63/85] add aws-scala-sbt template for scala lambdas --- docker-compose.yml | 4 ++ lib/plugins/create/create.js | 1 + .../create/templates/aws-scala-sbt/build.sbt | 21 ++++++ .../create/templates/aws-scala-sbt/event.json | 5 ++ .../templates/aws-scala-sbt/serverless.yml | 70 +++++++++++++++++++ .../src/main/scala/hello/Handler.scala | 11 +++ .../src/main/scala/hello/Request.scala | 7 ++ .../src/main/scala/hello/Response.scala | 5 ++ lib/plugins/create/tests/create.js | 30 ++++++++ tests/templates/integration-test-template | 2 +- tests/templates/test_all_templates | 1 + 11 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 lib/plugins/create/templates/aws-scala-sbt/build.sbt create mode 100644 lib/plugins/create/templates/aws-scala-sbt/event.json create mode 100644 lib/plugins/create/templates/aws-scala-sbt/serverless.yml create mode 100644 lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Handler.scala create mode 100644 lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Request.scala create mode 100644 lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Response.scala diff --git a/docker-compose.yml b/docker-compose.yml index 57fb343d4..0242c04da 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,3 +28,7 @@ services: image: qlik/gradle volumes: - ./tmp/serverless-integration-test-aws-java-gradle:/app + aws-scala-sbt: + image: hseeberger/scala-sbt + volumes: + - ./tmp/serverless-integration-test-aws-scala-sbt:/app diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js index 4d77a7c3c..9f0e77bf4 100644 --- a/lib/plugins/create/create.js +++ b/lib/plugins/create/create.js @@ -10,6 +10,7 @@ const validTemplates = [ 'aws-python', 'aws-java-maven', 'aws-java-gradle', + 'aws-scala-sbt', ]; const humanReadableTemplateList = `${validTemplates.slice(0, -1) diff --git a/lib/plugins/create/templates/aws-scala-sbt/build.sbt b/lib/plugins/create/templates/aws-scala-sbt/build.sbt new file mode 100644 index 000000000..17cb52aa7 --- /dev/null +++ b/lib/plugins/create/templates/aws-scala-sbt/build.sbt @@ -0,0 +1,21 @@ +import sbt.Keys._ +import sbt._ +import sbtrelease.Version + +name := "hello" + +resolvers += Resolver.sonatypeRepo("public") +scalaVersion := "2.11.8" +releaseNextVersion := { ver => Version(ver).map(_.bumpMinor.string).getOrElse("Error") } +assemblyJarName in assembly := "hello.jar" + +libraryDependencies ++= Seq( + "com.amazonaws" % "aws-lambda-java-events" % "1.1.0", + "com.amazonaws" % "aws-lambda-java-core" % "1.1.0" +) + +scalacOptions ++= Seq( + "-unchecked", + "-deprecation", + "-feature", + "-Xfatal-warnings") diff --git a/lib/plugins/create/templates/aws-scala-sbt/event.json b/lib/plugins/create/templates/aws-scala-sbt/event.json new file mode 100644 index 000000000..2ac50a459 --- /dev/null +++ b/lib/plugins/create/templates/aws-scala-sbt/event.json @@ -0,0 +1,5 @@ +{ + "key3": "value3", + "key2": "value2", + "key1": "value1" +} diff --git a/lib/plugins/create/templates/aws-scala-sbt/serverless.yml b/lib/plugins/create/templates/aws-scala-sbt/serverless.yml new file mode 100644 index 000000000..ca47838b1 --- /dev/null +++ b/lib/plugins/create/templates/aws-scala-sbt/serverless.yml @@ -0,0 +1,70 @@ +# Welcome to Serverless! +# +# This file is the main config file for your service. +# It's very minimal at this point and uses default values. +# You can always add more config options for more control. +# We've included some commented out config examples here. +# Just uncomment any of them to get that config option. +# +# For full config options, check the docs: +# docs.serverless.com +# +# Happy Coding! + +service: aws-scala-sbt # NOTE: update this with your service name + +provider: + name: aws + runtime: java8 + +# you can overwrite defaults here +# stage: dev +# region: us-east-1 + +# you can add statements to the Lambda function's IAM Role here +# iamRoleStatements: +# - Effect: "Allow" +# Action: +# - "s3:ListBucket" +# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } +# - Effect: "Allow" +# Action: +# - "s3:PutObject" +# Resource: +# Fn::Join: +# - "" +# - - "arn:aws:s3:::" +# - "Ref" : "ServerlessDeploymentBucket" + +# you can add packaging information here +package: +# include: +# - include-me.java +# exclude: +# - exclude-me.java + artifact: target/scala-2.11/hello.jar + +functions: + hello: + handler: hello.Handler + +# you can add any of the following events +# events: +# - http: +# path: users/create +# method: get +# - s3: ${env:bucket} +# - schedule: rate(10 minutes) +# - sns: greeter-topic + +# you can add CloudFormation resource templates here +#resources: +# Resources: +# NewResource: +# Type: AWS::S3::Bucket +# Properties: +# BucketName: my-new-bucket +# Outputs: +# NewOutput: +# Description: "Description for the output" +# Value: "Some output value" diff --git a/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Handler.scala b/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Handler.scala new file mode 100644 index 000000000..ac3ac0b0f --- /dev/null +++ b/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Handler.scala @@ -0,0 +1,11 @@ +package hello + +import com.amazonaws.services.lambda.runtime.{Context, RequestHandler} + +class Handler extends RequestHandler[Request, Response] { + + def handleRequest(input: Request, context: Context): Response = { + return new Response("Go Serverless v1.0! Your function executed successfully!", input) + } + +} diff --git a/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Request.scala b/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Request.scala new file mode 100644 index 000000000..1724561e7 --- /dev/null +++ b/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Request.scala @@ -0,0 +1,7 @@ +package hello + +import scala.beans.BeanProperty + +class Request(@BeanProperty var key1: String, @BeanProperty var key2: String, @BeanProperty var key3: String) { + def this() = this("", "", "") +} diff --git a/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Response.scala b/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Response.scala new file mode 100644 index 000000000..dbed76047 --- /dev/null +++ b/lib/plugins/create/templates/aws-scala-sbt/src/main/scala/hello/Response.scala @@ -0,0 +1,5 @@ +package hello + +import scala.beans.BeanProperty + +case class Response(@BeanProperty message: String, @BeanProperty request: Request) diff --git a/lib/plugins/create/tests/create.js b/lib/plugins/create/tests/create.js index 908207067..8e6bde850 100644 --- a/lib/plugins/create/tests/create.js +++ b/lib/plugins/create/tests/create.js @@ -157,6 +157,36 @@ describe('Create', () => { }); }); + it('should generate scaffolding for "aws-scala-sbt" template', () => { + const cwd = process.cwd(); + fse.mkdirsSync(tmpDir); + process.chdir(tmpDir); + create.options.template = 'aws-scala-sbt'; + + return create.create().then(() => { + expect(create.serverless.utils.fileExistsSync(path.join(tmpDir, 'serverless.yml'))) + .to.be.equal(true); + expect(create.serverless.utils.fileExistsSync(path.join(tmpDir, 'event.json'))) + .to.be.equal(true); + expect(create.serverless.utils.fileExistsSync(path.join(tmpDir, 'build.sbt'))) + .to.be.equal(true); + expect(create.serverless.utils.fileExistsSync(path.join(tmpDir, 'src', 'main', 'scala', + 'hello', 'Handler.scala' + ))) + .to.be.equal(true); + expect(create.serverless.utils.fileExistsSync(path.join(tmpDir, 'src', 'main', 'scala', + 'hello', 'Request.scala' + ))) + .to.be.equal(true); + expect(create.serverless.utils.fileExistsSync(path.join(tmpDir, 'src', 'main', 'scala', + 'hello', 'Response.scala' + ))) + .to.be.equal(true); + + process.chdir(cwd); + }); + }); + // this test should live here because of process.cwd() which might cause trouble when using // nested dirs like its done here it('should create a renamed service in the directory if using the "path" option', () => { diff --git a/tests/templates/integration-test-template b/tests/templates/integration-test-template index 32ba93833..0ea20ed5f 100755 --- a/tests/templates/integration-test-template +++ b/tests/templates/integration-test-template @@ -19,7 +19,7 @@ serverless create --template $template echo "Overwriting Service Name" sed -i.bak s/${template}/sls-test-$template-$RANDOM/g $template_folder/serverless.yml -echo "Running Compose build for Teamplate" +echo "Running Compose build for Template" docker-compose build $template if [ ! -z "$2" ] diff --git a/tests/templates/test_all_templates b/tests/templates/test_all_templates index 4ce56adf0..ce51a2638 100755 --- a/tests/templates/test_all_templates +++ b/tests/templates/test_all_templates @@ -10,5 +10,6 @@ function integration-test { integration-test aws-java-gradle build integration-test aws-java-maven mvn package +integration-test aws-scala-sbt sbt assembly integration-test aws-nodejs integration-test aws-python From 3e93d244d3ec3968a8b60fc9f6a16aab71ec810f Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 27 Sep 2016 11:48:14 +0200 Subject: [PATCH 64/85] Add subdirectory for tmpDirs Add a subdirectory so that all directories which are generated for the tests are stored there. Furthermore old tests are updated so that they use the tmpDir name generated by the test utils functionality. This makes working with Docker way better as the tmpDirs are not created It the root directory. --- .gitignore | 7 ++++--- lib/plugins/aws/deploy/tests/uploadArtifacts.js | 3 ++- tests/classes/Service.js | 15 ++++++--------- tests/utils/index.js | 3 ++- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 0bb65a865..168ae1112 100755 --- a/.gitignore +++ b/.gitignore @@ -27,16 +27,17 @@ build/Release # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules -#IDE Stuff +# IDE stuff **/.idea -#OS STUFF +# OS stuff .DS_Store .tmp -#SERVERLESS STUFF +# Serverless stuff admin.env .env tmp .coveralls.yml tracking-id +tmpdirs-serverless diff --git a/lib/plugins/aws/deploy/tests/uploadArtifacts.js b/lib/plugins/aws/deploy/tests/uploadArtifacts.js index 94a25f613..ba20f7142 100644 --- a/lib/plugins/aws/deploy/tests/uploadArtifacts.js +++ b/lib/plugins/aws/deploy/tests/uploadArtifacts.js @@ -7,6 +7,7 @@ const BbPromise = require('bluebird'); const expect = require('chai').expect; const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); +const testUtils = require('../../../../../tests/utils'); describe('uploadArtifacts', () => { let serverless; @@ -61,7 +62,7 @@ describe('uploadArtifacts', () => { }); it('should upload the .zip file to the S3 bucket', () => { - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); + const tmpDirPath = testUtils.getTmpDirPath(); const artifactFilePath = path.join(tmpDirPath, 'artifact.zip'); serverless.utils.writeFileSync(artifactFilePath, 'artifact.zip file content'); diff --git a/tests/classes/Service.js b/tests/classes/Service.js index 4a29720b2..9add28609 100644 --- a/tests/classes/Service.js +++ b/tests/classes/Service.js @@ -7,6 +7,7 @@ const expect = require('chai').expect; const Service = require('../../lib/classes/Service'); const Utils = require('../../lib/classes/Utils'); const Serverless = require('../../lib/Serverless'); +const testUtils = require('../../tests/utils'); describe('Service', () => { describe('#constructor()', () => { @@ -101,6 +102,11 @@ describe('Service', () => { describe('#load()', () => { let serviceInstance; + let tmpDirPath; + + beforeEach(() => { + tmpDirPath = testUtils.getTmpDirPath(); + }); it('should resolve if no servicePath is found', () => { const serverless = new Serverless(); @@ -111,7 +117,6 @@ describe('Service', () => { it('should load from filesystem', () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYml = { service: 'new-service', provider: 'aws', @@ -164,7 +169,6 @@ describe('Service', () => { it('should make sure function name contains the default stage', () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYml = { service: 'new-service', provider: 'aws', @@ -206,7 +210,6 @@ describe('Service', () => { it('should support Serverless file with a .yaml extension', () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYaml = { service: 'my-service', provider: 'aws', @@ -238,7 +241,6 @@ describe('Service', () => { it('should support Serverless file with a .yml extension', () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYml = { service: 'my-service', provider: 'aws', @@ -268,7 +270,6 @@ describe('Service', () => { it('should throw error if service property is missing', () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYml = { provider: 'aws', functions: {}, @@ -290,7 +291,6 @@ describe('Service', () => { it('should throw error if provider property is missing', () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYml = { service: 'service-name', functions: {}, @@ -312,7 +312,6 @@ describe('Service', () => { it('should throw error if functions property is missing', () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYml = { service: 'service-name', provider: 'aws', @@ -334,7 +333,6 @@ describe('Service', () => { it("should throw error if a function's event is not an array", () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYml = { service: 'service-name', provider: 'aws', @@ -361,7 +359,6 @@ describe('Service', () => { it('should throw error if provider property is invalid', () => { const SUtils = new Utils(); - const tmpDirPath = path.join(os.tmpdir(), (new Date()).getTime().toString()); const serverlessYml = { service: 'service-name', provider: 'invalid', diff --git a/tests/utils/index.js b/tests/utils/index.js index e1207f80e..2a9713b41 100644 --- a/tests/utils/index.js +++ b/tests/utils/index.js @@ -4,7 +4,8 @@ const os = require('os'); const path = require('path'); const crypto = require('crypto'); -const getTmpDirPath = () => path.join(os.tmpdir(), crypto.randomBytes(8).toString('hex')); +const getTmpDirPath = () => path.join(os.tmpdir(), + 'tmpdirs-serverless', 'serverless', crypto.randomBytes(8).toString('hex')); const getTmpFilePath = (fileName) => path.join(getTmpDirPath(), fileName); From eb4d17d1b532803972bea9c9d526307f808bf41f Mon Sep 17 00:00:00 2001 From: Philipp Muens Date: Tue, 27 Sep 2016 12:04:15 +0200 Subject: [PATCH 65/85] Fix linting issues --- .eslintignore | 1 + lib/plugins/aws/deploy/tests/uploadArtifacts.js | 1 - tests/classes/Service.js | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.eslintignore b/.eslintignore index c14374115..412a51062 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ coverage node_modules tmp +tmpdirs-serverless diff --git a/lib/plugins/aws/deploy/tests/uploadArtifacts.js b/lib/plugins/aws/deploy/tests/uploadArtifacts.js index ba20f7142..2bd1f70a6 100644 --- a/lib/plugins/aws/deploy/tests/uploadArtifacts.js +++ b/lib/plugins/aws/deploy/tests/uploadArtifacts.js @@ -1,7 +1,6 @@ 'use strict'; const sinon = require('sinon'); -const os = require('os'); const path = require('path'); const BbPromise = require('bluebird'); const expect = require('chai').expect; diff --git a/tests/classes/Service.js b/tests/classes/Service.js index 9add28609..e39a80799 100644 --- a/tests/classes/Service.js +++ b/tests/classes/Service.js @@ -1,7 +1,6 @@ 'use strict'; const path = require('path'); -const os = require('os'); const YAML = require('js-yaml'); const expect = require('chai').expect; const Service = require('../../lib/classes/Service'); From d6bdef35ce9830837cc4d5d976e8fbf81daab0a7 Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Mon, 26 Sep 2016 12:48:32 +0200 Subject: [PATCH 66/85] Allow integration tests longer timeouts --- tests/classes/CLI.js | 6 ++++-- tests/classes/PluginManager.js | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/classes/CLI.js b/tests/classes/CLI.js index 2f1ccf1a2..7d1c05cea 100644 --- a/tests/classes/CLI.js +++ b/tests/classes/CLI.js @@ -291,7 +291,8 @@ describe('CLI', () => { process.chdir(this.cwd); }); - it('prints general --help to stdout', (done) => { + it('prints general --help to stdout', function (done) { + this.timeout(10000); exec(`${this.serverlessExec} --help`, (err, stdout) => { if (err) { done(err); @@ -303,7 +304,8 @@ describe('CLI', () => { }); }); - it('prints command --help to stdout', (done) => { + it('prints command --help to stdout', function (done) { + this.timeout(10000); exec(`${this.serverlessExec} deploy --help`, (err, stdout) => { if (err) { done(err); diff --git a/tests/classes/PluginManager.js b/tests/classes/PluginManager.js index 258766c15..459f6e644 100644 --- a/tests/classes/PluginManager.js +++ b/tests/classes/PluginManager.js @@ -763,7 +763,8 @@ describe('PluginManager', () => { }); }); - it('Plugin/CLI integration', () => { + it('Plugin/CLI integration', function () { + this.timeout(10000); const serverlessInstance = new Serverless(); serverlessInstance.init(); From 6e4b204f1633b5de51b6b3d31d3817bb387a9091 Mon Sep 17 00:00:00 2001 From: Florian Motlik Date: Mon, 26 Sep 2016 17:25:42 +0200 Subject: [PATCH 67/85] remove arrow syntax --- tests/classes/CLI.js | 6 +++--- tests/classes/PluginManager.js | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/classes/CLI.js b/tests/classes/CLI.js index 7d1c05cea..6695353f7 100644 --- a/tests/classes/CLI.js +++ b/tests/classes/CLI.js @@ -17,7 +17,7 @@ describe('CLI', () => { let cli; let serverless; - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback serverless = new Serverless({}); }); @@ -269,7 +269,7 @@ describe('CLI', () => { }); describe('integration tests', () => { - before(() => { + before(function () { const tmpDir = testUtils.getTmpDirPath(); this.cwd = process.cwd(); @@ -287,7 +287,7 @@ describe('CLI', () => { '..', 'bin', 'serverless'); }); - after(() => { + after(function () { // eslint-disable-line prefer-arrow-callback process.chdir(this.cwd); }); diff --git a/tests/classes/PluginManager.js b/tests/classes/PluginManager.js index 459f6e644..d146f9331 100644 --- a/tests/classes/PluginManager.js +++ b/tests/classes/PluginManager.js @@ -188,7 +188,7 @@ describe('PluginManager', () => { } } - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback serverless = new Serverless(); pluginManager = new PluginManager(serverless); }); @@ -349,7 +349,7 @@ describe('PluginManager', () => { }); describe('#loadAllPlugins()', () => { - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback mockRequire('ServicePluginMock1', ServicePluginMock1); mockRequire('ServicePluginMock2', ServicePluginMock2); }); @@ -396,7 +396,7 @@ describe('PluginManager', () => { expect(pluginManager.plugins[2]).to.be.instanceof(ServicePluginMock2); }); - afterEach(() => { + afterEach(function () { // eslint-disable-line prefer-arrow-callback mockRequire.stop('ServicePluginMock1'); mockRequire.stop('ServicePluginMock2'); }); @@ -411,7 +411,7 @@ describe('PluginManager', () => { }); describe('#loadServicePlugins()', () => { - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback mockRequire('ServicePluginMock1', ServicePluginMock1); mockRequire('ServicePluginMock2', ServicePluginMock2); }); @@ -427,7 +427,7 @@ describe('PluginManager', () => { expect(pluginManager.plugins).to.contain(servicePluginMock2); }); - afterEach(() => { + afterEach(function () { // eslint-disable-line prefer-arrow-callback mockRequire.stop('ServicePluginMock1'); mockRequire.stop('ServicePluginMock2'); }); @@ -443,7 +443,7 @@ describe('PluginManager', () => { }); describe('#getEvents()', () => { - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback const synchronousPluginMockInstance = new SynchronousPluginMock(); pluginManager.loadCommands(synchronousPluginMockInstance); }); @@ -481,7 +481,7 @@ describe('PluginManager', () => { }); describe('#getPlugins()', () => { - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback mockRequire('ServicePluginMock1', ServicePluginMock1); mockRequire('ServicePluginMock2', ServicePluginMock2); }); @@ -494,7 +494,7 @@ describe('PluginManager', () => { expect(pluginManager.getPlugins()[1]).to.be.instanceof(ServicePluginMock2); }); - afterEach(() => { + afterEach(function () { // eslint-disable-line prefer-arrow-callback mockRequire.stop('ServicePluginMock1'); mockRequire.stop('ServicePluginMock2'); }); @@ -692,7 +692,7 @@ describe('PluginManager', () => { }); describe('when using a synchronous hook function', () => { - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback pluginManager.addPlugin(SynchronousPluginMock); }); @@ -716,7 +716,7 @@ describe('PluginManager', () => { }); describe('when using a promise based hook function', () => { - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback pluginManager.addPlugin(PromisePluginMock); }); @@ -740,7 +740,7 @@ describe('PluginManager', () => { }); describe('when using provider specific plugins', () => { - beforeEach(() => { + beforeEach(function () { // eslint-disable-line prefer-arrow-callback pluginManager.setProvider('provider1'); pluginManager.addPlugin(Provider1PluginMock); From 7211c621ed7d6e8a97e937af99e0238d4275dcff Mon Sep 17 00:00:00 2001 From: Meaghan Harty Date: Tue, 27 Sep 2016 10:48:56 -0400 Subject: [PATCH 68/85] add project directory for sbt assembly and release --- .../create/templates/aws-scala-sbt/project/assembly.sbt | 3 +++ lib/plugins/create/templates/aws-scala-sbt/project/plugins.sbt | 1 + 2 files changed, 4 insertions(+) create mode 100644 lib/plugins/create/templates/aws-scala-sbt/project/assembly.sbt create mode 100644 lib/plugins/create/templates/aws-scala-sbt/project/plugins.sbt diff --git a/lib/plugins/create/templates/aws-scala-sbt/project/assembly.sbt b/lib/plugins/create/templates/aws-scala-sbt/project/assembly.sbt new file mode 100644 index 000000000..5b7f17a1c --- /dev/null +++ b/lib/plugins/create/templates/aws-scala-sbt/project/assembly.sbt @@ -0,0 +1,3 @@ +resolvers += Resolver.sonatypeRepo("public") + +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.0") \ No newline at end of file diff --git a/lib/plugins/create/templates/aws-scala-sbt/project/plugins.sbt b/lib/plugins/create/templates/aws-scala-sbt/project/plugins.sbt new file mode 100644 index 000000000..dd405c41f --- /dev/null +++ b/lib/plugins/create/templates/aws-scala-sbt/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.0") \ No newline at end of file From 2aab2a2ccce0e9f053c2e69d2e42dbdc456b1462 Mon Sep 17 00:00:00 2001 From: Doug Moscrop Date: Tue, 27 Sep 2016 11:49:51 -0400 Subject: [PATCH 69/85] fix #2226 non-aws providers throw an error --- lib/classes/Service.js | 2 +- tests/classes/Service.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/classes/Service.js b/lib/classes/Service.js index 24b506303..32fd3d620 100644 --- a/lib/classes/Service.js +++ b/lib/classes/Service.js @@ -69,7 +69,7 @@ class Service { }; } - if (['aws', 'azure', 'google', 'ibm'].indexOf(serverlessFile.provider.name)) { + if (['aws', 'azure', 'google', 'ibm'].indexOf(serverlessFile.provider.name) === -1) { const errorMessage = [ `Provider "${serverlessFile.provider.name}" is not supported.`, ' Valid values for provider are: aws, azure, google, ibm.', diff --git a/tests/classes/Service.js b/tests/classes/Service.js index e39a80799..520361f74 100644 --- a/tests/classes/Service.js +++ b/tests/classes/Service.js @@ -207,6 +207,37 @@ describe('Service', () => { }); }); + it('should support Serverless file with a non-aws provider', () => { + const SUtils = new Utils(); + const serverlessYaml = { + service: 'my-service', + provider: 'ibm', + functions: { + functionA: { + name: 'customFunctionName', + }, + }, + }; + + SUtils.writeFileSync(path.join(tmpDirPath, 'serverless.yaml'), + YAML.dump(serverlessYaml)); + + const serverless = new Serverless({ servicePath: tmpDirPath }); + serviceInstance = new Service(serverless); + + return serviceInstance.load().then(() => { + const expectedFunc = { + functionA: { + name: 'customFunctionName', + events: [], + }, + }; + expect(serviceInstance.service).to.be.equal('my-service'); + expect(serviceInstance.provider.name).to.deep.equal('ibm'); + expect(serviceInstance.functions).to.deep.equal(expectedFunc); + }); + }); + it('should support Serverless file with a .yaml extension', () => { const SUtils = new Utils(); const serverlessYaml = { From 3d6f22fb47c4322f92ecf5c5c75b9a1c51662a24 Mon Sep 17 00:00:00 2001 From: David Wells Date: Tue, 27 Sep 2016 10:09:03 -0700 Subject: [PATCH 70/85] Remove broken link --- docs/01-guide/09-installing-plugins.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/01-guide/09-installing-plugins.md b/docs/01-guide/09-installing-plugins.md index d6ecd98be..f7372c91e 100644 --- a/docs/01-guide/09-installing-plugins.md +++ b/docs/01-guide/09-installing-plugins.md @@ -52,5 +52,3 @@ plugins: ``` In this case `plugin1` is loaded before `plugin2`. - -[Next step > Removing your service](removing-a-service.md) From b69d1c0dc6cdeed86d05891223dd72221f36b971 Mon Sep 17 00:00:00 2001 From: Mohamed Gharsallah Date: Tue, 27 Sep 2016 21:30:15 +0100 Subject: [PATCH 71/85] check for event prop when s3 rules prop is defined --- .../aws/deploy/compile/events/s3/index.js | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/s3/index.js b/lib/plugins/aws/deploy/compile/events/s3/index.js index e8c4aa261..f1ef8ec11 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/index.js +++ b/lib/plugins/aws/deploy/compile/events/s3/index.js @@ -39,27 +39,18 @@ class AwsCompileS3Events { if (event.s3.event) { notificationEvent = event.s3.event; } - notificationEvent = event.s3.event; if (event.s3.rules) { - if (_.isArray(event.s3.rules)) { - const rules = []; - event.s3.rules.forEach(rule => { - if (_.isPlainObject(rule)) { - const name = Object.keys(rule)[0]; - const value = rule[name]; - rules.push({ Name: name, Value: value }); - } else { - const errorMessage = [ - `S3 filter rule ${rule} of function ${functionName} is not an object`, - ' The correct syntax is: { Name: Value }', - ' Please check the docs for more info.', - ].join(''); - throw new this.serverless.classes - .Error(errorMessage); - } - }); - filter = { Filter: { S3Key: { Rules: rules } } }; - } else { + if (!event.s3.event) { + const errorMessage = [ + `S3 filter rules of function ${functionName} requires "event" property`, + ' The correct syntax is: s3: bucketName OR an object with', + ' "bucket", "rules", and "event" property.', + ' Please check the docs for more info.', + ].join(''); + throw new this.serverless.classes + .Error(errorMessage); + } + if (!_.isArray(event.s3.rules)) { const errorMessage = [ `S3 filter rules of function ${functionName} is not an array`, ' The correct syntax is: rules: [{ Name: Value }]', @@ -68,6 +59,22 @@ class AwsCompileS3Events { throw new this.serverless.classes .Error(errorMessage); } + const rules = []; + event.s3.rules.forEach(rule => { + if (!_.isPlainObject(rule)) { + const errorMessage = [ + `S3 filter rule ${rule} of function ${functionName} is not an object`, + ' The correct syntax is: { Name: Value }', + ' Please check the docs for more info.', + ].join(''); + throw new this.serverless.classes + .Error(errorMessage); + } + const name = Object.keys(rule)[0]; + const value = rule[name]; + rules.push({ Name: name, Value: value }); + }); + filter = { Filter: { S3Key: { Rules: rules } } }; } } else if (typeof event.s3 === 'string') { bucketName = event.s3; From 9db30f620b374fd32526f9b03c24f695130012a1 Mon Sep 17 00:00:00 2001 From: Mohamed Gharsallah Date: Tue, 27 Sep 2016 21:30:55 +0100 Subject: [PATCH 72/85] Add unit tests --- .../deploy/compile/events/s3/tests/index.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) 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 09506aef0..db485adcd 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/tests/index.js +++ b/lib/plugins/aws/deploy/compile/events/s3/tests/index.js @@ -58,6 +58,7 @@ describe('AwsCompileS3Events', () => { { s3: { bucket: 'first-function-bucket', + event: 's3:ObjectCreated:Put', rules: {}, }, }, @@ -75,6 +76,7 @@ describe('AwsCompileS3Events', () => { { s3: { bucket: 'first-function-bucket', + event: 's3:ObjectCreated:Put', rules: [[]], }, }, @@ -85,6 +87,25 @@ describe('AwsCompileS3Events', () => { expect(() => awsCompileS3Events.compileS3Events()).to.throw(Error); }); + it('should throw an error if the "rules" is defined while the "event" is not', () => { + awsCompileS3Events.serverless.service.functions = { + first: { + events: [ + { + s3: { + bucket: 'first-function-bucket', + rules: [ + { prefix: 'subfolder/' }, + ], + }, + }, + ], + }, + }; + + expect(() => awsCompileS3Events.compileS3Events()).to.throw(Error); + }); + it('should create corresponding resources when S3 events are given', () => { awsCompileS3Events.serverless.service.functions = { first: { @@ -116,6 +137,11 @@ describe('AwsCompileS3Events', () => { expect(awsCompileS3Events.serverless.service.provider.compiledCloudFormationTemplate .Resources.FirstLambdaPermissionS3.Type ).to.equal('AWS::Lambda::Permission'); + expect(awsCompileS3Events.serverless.service.provider.compiledCloudFormationTemplate + .Resources.S3BucketFirstfunctionbuckettwo.Properties.NotificationConfiguration + .LambdaConfigurations[0].Filter).to.deep.equal({ + S3Key: { Rules: [{ Name: 'prefix', Value: 'subfolder/' }] }, + }); }); it('should create single bucket resource when the same bucket referenced repeatedly', () => { From a1563a3a7374f3ae7870dd5de3e42386aafa044c Mon Sep 17 00:00:00 2001 From: eL0ck Date: Sun, 11 Sep 2016 16:25:46 +1000 Subject: [PATCH 73/85] fix http event validation --- .../aws/deploy/compile/events/apiGateway/index.js | 3 ++- .../compile/events/apiGateway/lib/validate.js | 14 +++++++++++--- .../compile/events/apiGateway/tests/validate.js | 13 +++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/index.js b/lib/plugins/aws/deploy/compile/events/apiGateway/index.js index b2b8e2188..ccf2db649 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/index.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/index.js @@ -36,7 +36,8 @@ class AwsCompileApigEvents { _.forEach(this.serverless.service.functions, functionObj => { if (functionObj.events) { functionObj.events.forEach(event => { - if (event.http) noEndpoints = false; + // Allow events with empty http event to validate function + if ({}.hasOwnProperty.call(event, 'http')) noEndpoints = false; }); } }); diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js index cb2f77c12..0bb5621d4 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js @@ -13,10 +13,10 @@ module.exports = { let path; if (typeof event.http === 'object') { - method = event.http.method.toLowerCase(); + method = event.http.method; path = event.http.path; } else if (typeof event.http === 'string') { - method = event.http.split(' ')[0].toLowerCase(); + method = event.http.split(' ')[0]; path = event.http.split(' ')[1]; } @@ -49,10 +49,18 @@ module.exports = { if (allowedMethods.indexOf(method) === -1) { const errorMessage = [ `Invalid APIG method "${method}" in function "${functionName}".`, - ' AWS supported methods are: get, post, put, patch, options, head, delete, any.', + ` AWS supported methods are: ${allowedMethods.join(", ")}.`, ].join(''); throw new this.serverless.classes.Error(errorMessage); } + } else { + const errorMessage = [ + `Empty http event in function "${functionName}"`, + ' in serverless.yml.', + ' Check yaml indentation or', + ' read the docs for more options.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); } }); }); diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/validate.js b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/validate.js index 179b590cf..a6df3229f 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/tests/validate.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/tests/validate.js @@ -30,6 +30,19 @@ describe('#validate()', () => { }; }); + it('should reject an empty http event', () => { + awsCompileApigEvents.serverless.service.functions = { + first: { + events: [ + { + http: null, + }, + ], + }, + }; + expect(() => awsCompileApigEvents.validate()).to.throw(Error); + }); + it('should validate the http events "path" property', () => { awsCompileApigEvents.serverless.service.functions = { first: { From 228d52cc7cae2c66276ca0860eaf210fd7d2b206 Mon Sep 17 00:00:00 2001 From: eL0ck Date: Mon, 26 Sep 2016 10:07:42 +1000 Subject: [PATCH 74/85] remove redundant service list --- lib/classes/Service.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/classes/Service.js b/lib/classes/Service.js index 32fd3d620..7756e9a93 100644 --- a/lib/classes/Service.js +++ b/lib/classes/Service.js @@ -69,10 +69,11 @@ class Service { }; } - if (['aws', 'azure', 'google', 'ibm'].indexOf(serverlessFile.provider.name) === -1) { + const providers = ['aws', 'azure', 'google', 'ibm']; + if (providers.indexOf(serverlessFile.provider.name) === -1) { const errorMessage = [ `Provider "${serverlessFile.provider.name}" is not supported.`, - ' Valid values for provider are: aws, azure, google, ibm.', + ` Valid values for provider are: ${providers.join(', ')}.`, ' Please provide one of those values to the "provider" property in serverless.yml.', ].join(''); throw new SError(errorMessage); From dc0b252564289e2ff71eb6937c5bc32f90002f3f Mon Sep 17 00:00:00 2001 From: eL0ck Date: Mon, 26 Sep 2016 15:29:22 +1000 Subject: [PATCH 75/85] fixed merge error. Tests all passing --- .../aws/deploy/compile/events/apiGateway/lib/validate.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js index 0bb5621d4..d2e9e9c33 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js @@ -43,13 +43,15 @@ module.exports = { throw new this.serverless.classes .Error(errorMessage); } + method = method.toLowerCase(); + const allowedMethods = [ 'get', 'post', 'put', 'patch', 'options', 'head', 'delete', 'any', ]; if (allowedMethods.indexOf(method) === -1) { const errorMessage = [ `Invalid APIG method "${method}" in function "${functionName}".`, - ` AWS supported methods are: ${allowedMethods.join(", ")}.`, + ` AWS supported methods are: ${allowedMethods.join(', ')}.`, ].join(''); throw new this.serverless.classes.Error(errorMessage); } From 0f2510eac440f7d5fe4269e38de80fe788dbe0bd Mon Sep 17 00:00:00 2001 From: eL0ck Date: Mon, 26 Sep 2016 15:36:41 +1000 Subject: [PATCH 76/85] fixed inconsistent error message --- .../aws/deploy/compile/events/apiGateway/lib/validate.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js index d2e9e9c33..eaa5f64f6 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js @@ -59,8 +59,9 @@ module.exports = { const errorMessage = [ `Empty http event in function "${functionName}"`, ' in serverless.yml.', - ' Check yaml indentation or', - ' read the docs for more options.', + ' If you define an http event, make sure you pass a valid value for it,', + ' either as string syntax, or object syntax.', + ' Please check the docs for more options.', ].join(''); throw new this.serverless.classes.Error(errorMessage); } From 4fa7457f243e309556d38004fc7a0dd165f0b3f1 Mon Sep 17 00:00:00 2001 From: eL0ck Date: Mon, 26 Sep 2016 17:29:25 +1000 Subject: [PATCH 77/85] fixed. Was incorrectly catching non-http events as empty http events --- .../compile/events/apiGateway/lib/validate.js | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js index eaa5f64f6..cfc608ec4 100644 --- a/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js +++ b/lib/plugins/aws/deploy/compile/events/apiGateway/lib/validate.js @@ -8,10 +8,21 @@ module.exports = { // validate that path and method exists for each http event in service _.forEach(this.serverless.service.functions, (functionObject, functionName) => { functionObject.events.forEach(event => { - if (event.http) { + if ({}.hasOwnProperty.call(event, 'http')) { let method; let path; + if (!event.http) { + const errorMessage = [ + `Empty http event in function "${functionName}"`, + ' in serverless.yml.', + ' If you define an http event, make sure you pass a valid value for it,', + ' either as string syntax, or object syntax.', + ' Please check the docs for more options.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); + } + if (typeof event.http === 'object') { method = event.http.method; path = event.http.path; @@ -55,15 +66,6 @@ module.exports = { ].join(''); throw new this.serverless.classes.Error(errorMessage); } - } else { - const errorMessage = [ - `Empty http event in function "${functionName}"`, - ' in serverless.yml.', - ' If you define an http event, make sure you pass a valid value for it,', - ' either as string syntax, or object syntax.', - ' Please check the docs for more options.', - ].join(''); - throw new this.serverless.classes.Error(errorMessage); } }); }); From 5d07e285d5555651221522e5d927d2c6526c8ee6 Mon Sep 17 00:00:00 2001 From: Alexander Hansen Date: Wed, 28 Sep 2016 10:10:47 +0200 Subject: [PATCH 78/85] Updated docs: Changed the RestApiId logical name This was changed in commit: https://github.com/serverless/serverless/commit/a5e3c3197474f1eac2672802132ef7c0a26db6a4 But not changed in the docs. --- docs/02-providers/aws/events/01-apigateway.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/02-providers/aws/events/01-apigateway.md b/docs/02-providers/aws/events/01-apigateway.md index 94c9818ba..9ba2f13a1 100644 --- a/docs/02-providers/aws/events/01-apigateway.md +++ b/docs/02-providers/aws/events/01-apigateway.md @@ -356,12 +356,12 @@ resources: - RootResourceId PathPart: serverless # the endpoint in your API that is set as proxy RestApiId: - Ref: RestApiApigEvent + Ref: ApiGatewayRestApi ProxyMethod: ResourceId: Ref: ProxyResource RestApiId: - Ref: RestApiApigEvent + Ref: ApiGatewayRestApi Type: AWS::ApiGateway::Method Properties: HttpMethod: GET # the method of your proxy. Is it GET or POST or ... ? From 86ad6227a1fe0787f8311359b488b8cf31c67ea8 Mon Sep 17 00:00:00 2001 From: Austen Date: Wed, 28 Sep 2016 13:44:26 -0700 Subject: [PATCH 79/85] add plugins and services to readme --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index b6e23beb2..adb379873 100755 --- a/README.md +++ b/README.md @@ -54,6 +54,31 @@ Check out our in-depth [Guide to Serverless](./docs/01-guide/README.md) for more * An ecosystem of serverless services and plugins. * A passionate and welcoming community! +## Plugins (V1.0) + +Use these plugins to overwrite or extend the Framework's functionality... + +* [serverless-webpack](https://github.com/elastic-coders/serverless-webpack) - Bundle your lambdas with Webpack +* [serverless-alexa-plugin](https://github.com/rajington/serverless-alexa-plugin) - Support Alexa Lambda events +* [serverless-run-function](https://github.com/lithin/serverless-run-function-plugin) - Run functions locally +* [serverless-plugin-write-env-vars](https://github.com/silvermine/serverless-plugin-write-env-vars) +* [serverless-plugin-multiple-responses](https://github.com/silvermine/serverless-plugin-multiple-responses) +* [serverless-build](https://github.com/nfour/serverless-build-plugin) +* [serverless-scriptable](https://github.com/wei-xu-myob/serverless-scriptable-plugin) +* [serverless-plugin-stage-variables](https://github.com/svdgraaf/serverless-plugin-stage-variables) + +## Services & Projects (V1.0) + +Pre-written functions you can use instantly and example implementations... + +* [serverless-examples](https://github.com/andymac4182/serverless_example) +* [serverless-npm-registry](https://github.com/craftship/yith) +* [serverless-pokego](https://github.com/jch254/pokego-serverless) +* [serverless-pocket-app](https://github.com/s0enke/weekly2pocket) +* [serverless-quotebot](https://github.com/pmuens/quotebot) +* [serverless-slackbot](https://github.com/conveyal/trevorbot) +* [serverless-garden-aid](https://github.com/garden-aid/web-bff) + ## Contributing We love our contributors! Please read our [Contributing Document](CONTRIBUTING.md) to learn how you can start working on the Framework yourself. From a249a7d2d870e1ce142bb03f5fbb07fb8de49440 Mon Sep 17 00:00:00 2001 From: Austen Date: Wed, 28 Sep 2016 13:45:41 -0700 Subject: [PATCH 80/85] cleanup readme --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index adb379873..175e63856 100755 --- a/README.md +++ b/README.md @@ -10,8 +10,6 @@ Serverless is an MIT open-source project, actively maintained by a full-time, ve ## Links - - * [Guide to Serverless](./docs/01-guide/README.md) * [Features](#features) * [Documentation v.1](./docs/README.md) / [v.0](http://serverless.readme.io) @@ -146,7 +144,7 @@ Below are projects and plugins relating to version 0.5 and below. Note that thes You can read the v0.5.x documentation at [readme.io](https://serverless.readme.io/v0.5.0/docs). -## v0.5.x Projects +## Projects (v0.5.x) Serverless Projects are shareable and installable. You can publish them to npm and install them via the Serverless Framework CLI by using `$ serverless project install ` * [serverless-graphql](https://github.com/serverless/serverless-graphql) - Official Serverless boilerplate to kick start your project * [serverless-starter](https://github.com/serverless/serverless-starter) - A simple boilerplate for new projects (JavaScript) with a few architectural options @@ -156,7 +154,7 @@ Serverless Projects are shareable and installable. You can publish them to npm * [sc5-serverless-boilerplate](https://github.com/SC5/sc5-serverless-boilerplate) - A boilerplate for test driven development of REST endpoints * [MoonMail] (https://github.com/microapps/MoonMail) - Build your own email marketing infrastructure using Lambda + SES -## v0.5.x Plugins +## Plugins (v0.5.x) Serverless is composed of Plugins. A group of default Plugins ship with the Framework, and here are some others you can add to improve/help your workflow: * [Meta Sync](https://github.com/serverless/serverless-meta-sync) - Securely sync your the variables in your project's `_meta/variables` across your team. * [Offline](https://github.com/dherault/serverless-offline) - Emulate AWS Lambda and Api Gateway locally to speed up your development cycles. From a3e03fcfccd89458b324d72e16803a8106c476d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eslam=20=CE=BB=20Hefnawy?= Date: Thu, 29 Sep 2016 10:25:58 +0700 Subject: [PATCH 81/85] update license year --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index af47480d4..c63fb03f7 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Serverless, Inc. http://www.serverless.com +Copyright (c) 2016 Serverless, Inc. http://www.serverless.com The following license applies to all parts of this software except as documented below: From 9d04ca0a683bf0242488a7ea456d19059bb96a17 Mon Sep 17 00:00:00 2001 From: zorrofox Date: Fri, 30 Sep 2016 09:22:20 +0100 Subject: [PATCH 82/85] modify error message according to the review --- lib/plugins/aws/deploy/lib/createStack.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/plugins/aws/deploy/lib/createStack.js b/lib/plugins/aws/deploy/lib/createStack.js index 49c3bf94b..62cfcb4a4 100644 --- a/lib/plugins/aws/deploy/lib/createStack.js +++ b/lib/plugins/aws/deploy/lib/createStack.js @@ -33,11 +33,11 @@ module.exports = { const stackName = `${this.serverless.service.service}-${this.options.stage}`; if (/^[^a-zA-Z].+|.*[^a-zA-Z0-9\-].*/.test(stackName) || stackName.length > 128) { const errorMessage = [ - `The stack name "${stackName}" is not quallify. `, - 'A stack name can contain only alphanumeric', - ' (case sensitive) and hyphens. It must characters', - ' start with an alphabetic character and cannot', - ' be longer than 128 characters.', + `The stack service name "${stackName}" is not valid. `, + 'A service name should only contain alphanumeric', + ' (case sensitive) and hyphens. It should start', + ' with an alphabetic character and shouldn\'t', + ' exceed 128 characters.', ].join(''); throw new this.serverless.classes.Error(errorMessage); } From 6c3e5d8b4e78e4fa8b3b42b61c1b782ab3477763 Mon Sep 17 00:00:00 2001 From: Marcus Whybrow <~@marcus.codes> Date: Tue, 27 Sep 2016 10:44:37 +0100 Subject: [PATCH 83/85] [DOCS] Fix AWS::ApiGateway::Method format According to the AWS docs `ResourceId` and `RestApiId` should be members of `Properties` --- docs/02-providers/aws/events/01-apigateway.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/02-providers/aws/events/01-apigateway.md b/docs/02-providers/aws/events/01-apigateway.md index 319237254..d6912a598 100644 --- a/docs/02-providers/aws/events/01-apigateway.md +++ b/docs/02-providers/aws/events/01-apigateway.md @@ -380,12 +380,12 @@ resources: RestApiId: Ref: ApiGatewayRestApi ProxyMethod: - ResourceId: - Ref: ProxyResource - RestApiId: - Ref: ApiGatewayRestApi Type: AWS::ApiGateway::Method Properties: + ResourceId: + Ref: ProxyResource + RestApiId: + Ref: ApiGatewayRestApi HttpMethod: GET # the method of your proxy. Is it GET or POST or ... ? MethodResponses: - StatusCode: 200 From 645491e63bfbe99802fce74f2472cdf0e505ad28 Mon Sep 17 00:00:00 2001 From: Mohamed Gharsallah Date: Fri, 30 Sep 2016 14:41:59 +0100 Subject: [PATCH 84/85] remove not important s3 event verification --- lib/plugins/aws/deploy/compile/events/s3/index.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/plugins/aws/deploy/compile/events/s3/index.js b/lib/plugins/aws/deploy/compile/events/s3/index.js index f1ef8ec11..fc849c786 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/index.js +++ b/lib/plugins/aws/deploy/compile/events/s3/index.js @@ -40,16 +40,6 @@ class AwsCompileS3Events { notificationEvent = event.s3.event; } if (event.s3.rules) { - if (!event.s3.event) { - const errorMessage = [ - `S3 filter rules of function ${functionName} requires "event" property`, - ' The correct syntax is: s3: bucketName OR an object with', - ' "bucket", "rules", and "event" property.', - ' Please check the docs for more info.', - ].join(''); - throw new this.serverless.classes - .Error(errorMessage); - } if (!_.isArray(event.s3.rules)) { const errorMessage = [ `S3 filter rules of function ${functionName} is not an array`, From c0489ad754e5d191b48c50cb08d7fcd4d45af2c2 Mon Sep 17 00:00:00 2001 From: Mohamed Gharsallah Date: Fri, 30 Sep 2016 14:42:07 +0100 Subject: [PATCH 85/85] update tests --- .../deploy/compile/events/s3/tests/index.js | 19 ------------------- 1 file changed, 19 deletions(-) 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 db485adcd..9c504d946 100644 --- a/lib/plugins/aws/deploy/compile/events/s3/tests/index.js +++ b/lib/plugins/aws/deploy/compile/events/s3/tests/index.js @@ -87,25 +87,6 @@ describe('AwsCompileS3Events', () => { expect(() => awsCompileS3Events.compileS3Events()).to.throw(Error); }); - it('should throw an error if the "rules" is defined while the "event" is not', () => { - awsCompileS3Events.serverless.service.functions = { - first: { - events: [ - { - s3: { - bucket: 'first-function-bucket', - rules: [ - { prefix: 'subfolder/' }, - ], - }, - }, - ], - }, - }; - - expect(() => awsCompileS3Events.compileS3Events()).to.throw(Error); - }); - it('should create corresponding resources when S3 events are given', () => { awsCompileS3Events.serverless.service.functions = { first: {