From 44a67691718702c5ff579781becd8122a1b325c6 Mon Sep 17 00:00:00 2001 From: ac360 Date: Wed, 3 Feb 2016 08:43:39 -0800 Subject: [PATCH 01/29] ServerlessModule: begin removing this as a class and concept --- lib/Serverless.js | 1 - lib/ServerlessComponent.js | 42 ++--- lib/ServerlessEndpoint.js | 26 +--- lib/ServerlessFunction.js | 22 +-- lib/ServerlessModule.js | 311 ------------------------------------- lib/ServerlessPlugin.js | 34 ---- lib/ServerlessState.js | 57 +------ 7 files changed, 27 insertions(+), 466 deletions(-) delete mode 100644 lib/ServerlessModule.js diff --git a/lib/Serverless.js b/lib/Serverless.js index 0cb5aed75..004b3f4a9 100644 --- a/lib/Serverless.js +++ b/lib/Serverless.js @@ -52,7 +52,6 @@ class Serverless { Meta: require('./ServerlessMeta'), Project: require('./ServerlessProject'), Component: require('./ServerlessComponent'), - Module: require('./ServerlessModule'), Function: require('./ServerlessFunction'), Endpoint: require('./ServerlessEndpoint') }; diff --git a/lib/ServerlessComponent.js b/lib/ServerlessComponent.js index c408377cd..ab22a109c 100644 --- a/lib/ServerlessComponent.js +++ b/lib/ServerlessComponent.js @@ -31,7 +31,7 @@ class ServerlessComponent { _this.name = _this._config.component || 'component' + SUtils.generateShortId(6); _this.runtime = config.runtime || 'nodejs'; _this.custom = {}; - _this.modules = {}; + _this.functions = {}; _this.templates = {}; } @@ -81,9 +81,9 @@ class ServerlessComponent { throw new SError('Component could not be loaded because it does not exist in your project: ' + _this._config.sPath); } - componentJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-component.json')); - componentJson.modules = {}; - componentContents = fs.readdirSync(_this._config.fullPath); + componentJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-component.json')); + componentJson.functions = {}; + componentContents = fs.readdirSync(_this._config.fullPath); return componentContents; }) @@ -91,17 +91,17 @@ class ServerlessComponent { if (!SUtils.fileExistsSync(path.join( _this._config.fullPath, componentContents[i], - 's-module.json'))) return; + 's-function.json'))) return; - let module = new _this._S.classes.Module(_this._S, { + let func = new _this._S.classes.Function(_this._S, { component: _this._config.component, module: m }); - return module.load() + return func.load() .then(function(instance) { - componentJson.modules[m]= instance; - return componentJson.modules[m]; + componentJson.functions[m]= instance; + return componentJson.functions[m]; }); }) .then(function() { @@ -129,17 +129,17 @@ class ServerlessComponent { let _this = this; // Instantiate Components - for (let prop in data.modules) { + for (let prop in data.functions) { - if (data.modules[prop] instanceof _this._S.classes.Module) { + if (data.functions[prop] instanceof _this._S.classes.Function) { throw new SError('You cannot pass subclasses into the set method, only object literals'); } - let instance = new _this._S.classes.Module(_this._S, { + let instance = new _this._S.classes.Function(_this._S, { component: _this._config.component, module: prop }); - data.modules[prop] = instance.set(data.modules[prop]); + data.functions[prop] = instance.set(data.functions[prop]); } // Merge in @@ -154,8 +154,8 @@ class ServerlessComponent { get() { let clone = _.cloneDeep(this); - for (let prop in this.modules) { - clone.modules[prop] = this.modules[prop].get(); + for (let prop in this.functions) { + clone.functions[prop] = this.functions[prop].get(); } return SUtils.exportClassData(clone); } @@ -179,9 +179,9 @@ class ServerlessComponent { // Populate let clone = _this.get(); clone = SUtils.populate(_this._S.state.getMeta(), this.getTemplates(), clone, options.stage, options.region); - clone.modules = {}; - for (let prop in _this.modules) { - clone.modules[prop] = _this.modules[prop].getPopulated(options); + clone.functions = {}; + for (let prop in _this.functions) { + clone.functions[prop] = _this.functions[prop].getPopulated(options); } return clone; @@ -225,10 +225,10 @@ class ServerlessComponent { // Save all nested modules if (options && options.deep) { return BbPromise.try(function () { - return Object.keys(_this.modules); + return Object.keys(_this.functions); }) .each(function(m) { - return _this.modules[m].save(); + return _this.functions[m].save(); }) } }) @@ -244,7 +244,7 @@ class ServerlessComponent { let clone = _this.get(); // Strip properties - if (clone.modules) delete clone.modules; + if (clone.functions) delete clone.functions; if (clone.templates) delete clone.templates; // Write file diff --git a/lib/ServerlessEndpoint.js b/lib/ServerlessEndpoint.js index 72a5b61a9..5e75a7671 100644 --- a/lib/ServerlessEndpoint.js +++ b/lib/ServerlessEndpoint.js @@ -2,7 +2,7 @@ /** * Serverless Endpoint Class - * - options.path format is: "moduleFolder/functionFolder#functionName" + * - options.path format is: "component/functionParentFolders(ifAny)/functionFolder@endpointPath~endpointMethod */ const SError = require('./ServerlessError'), @@ -21,7 +21,7 @@ class ServerlessEndpoint { constructor(Serverless, config) { // Validate required attributes - if (!config.component || !config.module || !config.function || !config.endpointPath || !config.endpointMethod) throw new SError('Missing required config.component, config.module, config.function, config.endpointMethod, config.endpointPath'); + if (!config.component || !config.function || !config.endpointPath || !config.endpointMethod) throw new SError('Missing required config.component, config.module, config.function, config.endpointMethod, config.endpointPath'); // Private properties let _this = this; @@ -61,7 +61,7 @@ class ServerlessEndpoint { if (!config) return; // Set sPath - if (config.component || config.module || config.function || config.endpointPath || config.endpointMethod) { + if (config.component || config.function || config.endpointPath || config.endpointMethod) { this._config.component = config.component; this._config.module = config.module; this._config.function = config.function; @@ -178,7 +178,6 @@ class ServerlessEndpoint { return _.merge( this.getProject().getTemplates(), this.getComponent().getTemplates(), - this.getModule().getTemplates(), this.getFunction().getTemplates(), _.cloneDeep(this.templates) ); @@ -250,25 +249,6 @@ class ServerlessEndpoint { throw new SError('Could not find component for endpoint'); } - /** - * Get Module - * - Returns reference to the instance - */ - - getModule() { - - let modules = this._S.state.getModules({ - component: this._config.component, - module: this._config.module - }); - - if (modules.length === 1) { - return modules[0]; - } - - throw new SError('Could not find module for endpoint'); - } - /** * Get Function * - Returns reference to the instance diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index aa857cce7..dc183f35e 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -22,7 +22,7 @@ class ServerlessFunction { constructor(Serverless, config) { // Validate required attributes - if (!config.component || !config.module || !config.function) throw new SError('Missing required config.component, config.module or config.function'); + if (!config.component || !config.function) throw new SError('Missing required config.component, config.module or config.function'); let _this = this; _this._S = Serverless; @@ -220,7 +220,6 @@ class ServerlessFunction { return _.merge( this.getProject().getTemplates(), this.getComponent().getTemplates(), - this.getModule().getTemplates(), _.cloneDeep(this.templates) ); } @@ -345,25 +344,6 @@ class ServerlessFunction { throw new SError('Could not find component for endpoint'); } - - /** - * Get Module - * - Returns reference to the instance - */ - - getModule() { - - let modules = this._S.state.getModules({ - component: this._config.component, - module: this._config.module - }); - - if (modules.length === 1) { - return modules[0]; - } - - throw new SError('Could not find module for endpoint'); - } } module.exports = ServerlessFunction; diff --git a/lib/ServerlessModule.js b/lib/ServerlessModule.js deleted file mode 100644 index 42542928e..000000000 --- a/lib/ServerlessModule.js +++ /dev/null @@ -1,311 +0,0 @@ -'use strict'; - -/** - * Serverless Module Class - * - options.path format is: "moduleFolder" - */ - -const SError = require('./ServerlessError'), - SUtils = require('./utils/index'), - BbPromise = require('bluebird'), - path = require('path'), - _ = require('lodash'), - fs = require('fs'); - -class ServerlessModule { - - /** - * Constructor - */ - - constructor(Serverless, config) { - - // Validate required attributes - if (!config.component || !config.module) throw new SError('Missing required config.component or config.module'); - - let _this = this; - _this._S = Serverless; - _this._config = {}; - _this.updateConfig(config); - - // Default Properties - _this.name = _this._config.module || 'module' + SUtils.generateShortId(6); - _this.profile = 'aws-v' + require('../package.json').version; - _this.custom = {}; - _this.functions = {}; - _this.cloudFormation = { - resources: {}, - lambdaIamPolicyDocumentStatements: [] - }; - _this.templates = {}; - } - - /** - * Update Config - * - Takes config.component and config.module - */ - - updateConfig(config) { - - if (!config) return; - - // Set sPath - if (config.component || config.module) { - this._config.component = config.component; - this._config.module = config.module; - this._config.sPath = SUtils.buildSPath({ - component: config.component, - module: config.module - }); - } - // Make full path - if (this._S.config.projectPath && this._config.sPath) { - let parse = SUtils.parseSPath(this._config.sPath); - this._config.fullPath = path.join(this._S.config.projectPath, parse.component, parse.module); - } - } - - /** - * Load - * - Load from source (i.e., file system); - * - Returns promise - */ - - load() { - - let _this = this, - moduleJson, - moduleContents; - - return BbPromise.try(function() { - - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Module could not be loaded because no project path has been set on Serverless instance'); - - // Validate: Check module exists - if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-module.json'))) { - throw new SError('Module could not be loaded because it does not exist in your project: ' + _this._config.sPath); - } - - moduleJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-module.json')); - moduleJson.functions = {}; - moduleContents = fs.readdirSync(_this._config.fullPath); - - return moduleContents; - }) - .each(function(f, i) { - - if (!SUtils.fileExistsSync(path.join( - _this._config.fullPath, moduleContents[i], - 's-function.json'))) return; - - let func = new _this._S.classes.Function(_this._S, { - component: _this._config.component, - module: _this._config.module, - function: f - }); - return func.load() - .then(function(instance) { - moduleJson.functions[f] = instance; - return moduleJson.functions[f]; - }); - }) - .then(function() { - - // Get templates - if (_this._config.fullPath && SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-templates.json'))) { - moduleJson.templates = require(path.join(_this._config.fullPath, 's-templates.json')); - } - }) - .then(function() { - - // Merge - _.assign(_this, moduleJson); - return _this; - }); - } - - /** - * Set - * - Set data - * - Accepts a data object - */ - - set(data) { - let _this = this; - - // Instantiate Components - for (let prop in data.functions) { - - if (data.functions[prop] instanceof _this._S.classes.Function) { - throw new SError('You cannot pass subclasses into the set method, only object literals'); - } - - let instance = new _this._S.classes.Function(_this._S, { - component: _this._config.component, - module: _this._config.module, - function: prop - }); - data.functions[prop] = instance.set(data.functions[prop]); - } - - // Merge in - _this = _.extend(_this, data); - return _this; - } - - /** - * Get - * - Return data - */ - - get() { - let clone = _.cloneDeep(this); - for (let prop in this.functions) { - clone.functions[prop] = this.functions[prop].get(); - } - return SUtils.exportClassData(clone); - } - - /** - * getPopulated - * - Fill in templates then variables - */ - - getPopulated(options) { - - let _this = this; - - options = options || {}; - - // Validate: Check Stage & Region - if (!options.stage || !options.region) throw new SError('Both "stage" and "region" params are required'); - - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Module could not be populated because no project path has been set on Serverless instance'); - - // Populate - let clone = _this.get(); - clone = SUtils.populate(_this._S.state.getMeta(), _this.getTemplates(), clone, options.stage, options.region); - clone.functions = {}; - for (let prop in _this.functions) { - clone.functions[prop] = _this.functions[prop].getPopulated(options); - } - - return clone; - } - - /** - * Get Templates - * - Returns clone of templates - * - Inherits parent templates - */ - - getTemplates() { - return _.merge( - this.getProject().getTemplates(), - this.getComponent().getTemplates(), - _.cloneDeep(this.templates) - ); - } - - /** - * Save - * - Saves data to file system - * - Returns promise - */ - - save(options) { - - let _this = this; - - return new BbPromise.try(function() { - - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Module could not be saved because no project path has been set on Serverless instance'); - - // Create if does not exist - if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-module.json'))) { - return _this._create(); - } - }) - .then(function() { - - // Save all nested functions - if (options && options.deep) { - return BbPromise.try(function () { - return Object.keys(_this.functions); - }) - .each(function(f) { - return _this.functions[f].save(); - }) - } - }) - .then(function() { - - // If templates, save templates - if (_this.templates && Object.keys(_this.templates).length) { - return SUtils.writeFile(path.join(_this._config.fullPath, 's-templates.json'), _this.templates); - } - }) - .then(function() { - - let clone = _this.get(); - - // Strip properties - if (clone.functions) delete clone.functions; - if (clone.templates) delete clone.templates; - - // Write file - return SUtils.writeFile(path.join(_this._config.fullPath, 's-module.json'), - JSON.stringify(clone, null, 2)); - }) - .then(function() { - return _this; - }) - } - - /** - * Create (scaffolding) - * - Returns promise - */ - - _create() { - - let _this = this; - - // Make folder - fs.mkdirSync(_this._config.fullPath); - - return BbPromise.resolve(); - } - - /** - * Get Project - * - Returns reference to the instance - */ - - getProject() { - return this._S.state.project; - } - - /** - * Get Component - * - Returns reference to parent component instance - */ - - getComponent() { - - let components = this._S.state.getComponents({ - component: this._config.component - }); - - if (components.length === 1) { - return components[0]; - } - - throw new SError('Could not find component for module'); - } -} - -module.exports = ServerlessModule; diff --git a/lib/ServerlessPlugin.js b/lib/ServerlessPlugin.js index 6070c69fb..b7d0fa490 100644 --- a/lib/ServerlessPlugin.js +++ b/lib/ServerlessPlugin.js @@ -232,40 +232,6 @@ class ServerlessPlugin { }); } - - cliPromptSelectModule(message, module, component) { - - let _this = this; - - if (module || !_this.S.config.interactive) return BbPromise.resolve(module); - - if(!_this.S.state.project.components[component]) { - return BbPromise.reject(new SError('Component ' + component + ' does not exist in your project.', SError.errorCodes.UNKNOWN)); - } - - let moduleChoices = Object.keys(_this.S.state.project.components[component].modules), - choices = []; - - if(moduleChoices.length === 0) { - return BbPromise.reject(new SError('Your component has no modules.', SError.errorCodes.UNKNOWN)); - } - - if(moduleChoices.length === 1) return BbPromise.resolve(moduleChoices[0]); - - moduleChoices.forEach(function(r) { - choices.push({ - key: '', - value: r, - label: r - }); - }); - - return _this.cliPromptSelect(message, choices, false) - .then(results => { - return results[0].value; - }); - - } } module.exports = ServerlessPlugin; diff --git a/lib/ServerlessState.js b/lib/ServerlessState.js index 73641caec..181c985bb 100644 --- a/lib/ServerlessState.js +++ b/lib/ServerlessState.js @@ -77,12 +77,10 @@ class ServerlessState { this.project = data; } else if (data instanceof this._S.classes.Component) { this.project.components[data.name] = data; - } else if (data instanceof this._S.classes.Module) { - this.project.components[data._config.component].modules[data.name] = data; } else if (data instanceof this._S.classes.Function) { - this.project.components[data._config.component].modules[data._config.module].functions[data.name] = data; + this.project.components[data._config.component].functions[data.name] = data; } else if (data instanceof this._S.classes.Endpoint) { - let func = this.project.components[data._config.component].modules[data._config.module].functions[data._config.function]; + let func = this.project.components[data._config.component].functions[data._config.function]; let added = false; for (let i = 0; i < func.endpoints.length; i++) { if (func.endpoints[i].path === data.path && func.endpoints[i].method === data.method) { @@ -219,57 +217,6 @@ class ServerlessState { return foundComponents; } - /** - * Get Modules - * - Returns an array of this state's modules instances - * - Options: component, module - * - options.paths is an array of serverless paths like this: ['component/moduleOne', 'component/moduleTwo'] - */ - - getModules(options) { - - let _this = this, - allModules = [], - foundModules = []; - - // Get all - for (let i = 0; i < Object.keys(_this.project.components).length; i++) { - let component = _this.project.components[Object.keys(_this.project.components)[i]]; - if (!component.modules) continue; - for (let j = 0; j < Object.keys(component.modules).length; j++) { - allModules.push(component.modules[Object.keys(component.modules)[j]]); - } - } - - // Return if no options specified - if (!options) return allModules; - if (options && options == { returnPaths: true }) return allModules.map(function(d) { return d._config.sPath }); - - // If options specified, loop through and find the ones specified - for (let i = 0; i < allModules.length; i++) { - let module = allModules[i]; - - if (options.component && options.module) { - if (module._config.component == options.component && module._config.module == options.module) { - foundModules.push(options.returnPaths ? module._config.sPath : module); - } - continue; - } - if (options.component) { - if (module._config.component == options.component) { - foundModules.push(options.returnPaths ? module._config.sPath : module); - } - continue; - } - if (options.paths && options.paths.indexOf(module._config.sPath) !== -1) { - foundModules.push(module); - continue; - } - } - - return foundModules; - } - /** * Get Functions * - Returns an array of this state's function instances From 9cec71c8c7db9d6d4a1df7c4540a073f34ecf29a Mon Sep 17 00:00:00 2001 From: ac360 Date: Wed, 3 Feb 2016 09:30:59 -0800 Subject: [PATCH 02/29] ServerlessFunction: Keep removing module concept --- lib/ServerlessFunction.js | 50 +++++++++++++++++++------------- lib/utils/index.js | 60 ++++++++++++--------------------------- 2 files changed, 49 insertions(+), 61 deletions(-) diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index dc183f35e..83fbd51f9 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -2,7 +2,8 @@ /** * Serverless Function Class - * - options.path format is: "moduleFolder/functionFolder#functionName" + * - options.path format is: "componentPath/functionFolder + * - */ const SError = require('./ServerlessError'), @@ -22,7 +23,7 @@ class ServerlessFunction { constructor(Serverless, config) { // Validate required attributes - if (!config.component || !config.function) throw new SError('Missing required config.component, config.module or config.function'); + if (!config.component || !config.function) throw new SError('Missing required config.component or config.function'); let _this = this; _this._S = Serverless; @@ -31,7 +32,10 @@ class ServerlessFunction { // Default properties _this.name = _this._config.function || 'function' + SUtils.generateShortId(6); - _this.handler = path.posix.join(_this._config.module, _this._config.function, 'handler.handler'); + _this.handler = path.posix.join( + _this._config.cPath ? _this._config.cPath.split('/').join(path.sep) : null, + _this._config.function, + 'handler.handler'); _this.timeout = 6; _this.memorySize = 1024; _this.custom = { @@ -41,9 +45,9 @@ class ServerlessFunction { _this.endpoints = []; _this.endpoints.push(new _this._S.classes.Endpoint(_this._S, { component: _this._config.component, - module: _this._config.module, + cPath: _this._config.cPath, function: _this._config.function, - endpointPath: _this._config.module + '/' + _this._config.function, + endpointPath: _this._config.cPath ? _this._config.cPath + '/' + _this._config.function : _this._config.function, endpointMethod: 'GET' })); _this.templates = {}; @@ -66,11 +70,11 @@ class ServerlessFunction { } let instance = new _this._S.classes.Endpoint(_this._S, { - component: _this._config.component, - module: _this._config.module, - function: _this.name, - endpointPath: _this._config.module + '/' + _this.name, - endpointMethod: data.endpoints[i].method + component: _this._config.component, + cPath: _this._config.cPath, + function: _this.name, + endpointPath: _this._config.cPath ? _this._config.cPath + '/' + _this._config.function : _this._config.function, + endpointMethod: data.endpoints[i].method }); data.endpoints[i] = instance.set(data.endpoints[i]); } @@ -82,21 +86,24 @@ class ServerlessFunction { /** * Update Config - * - Takes config.component, config.module, config.function + * - Takes config.component, config.cPath, config.function */ updateConfig(config) { if (!config) return; + // Check cPath separators + if (config.cPath.indexOf('\\') !== -1) throw new SError('Cannot use "\\" in config.cPath'); + // Set sPath - if (config.component || config.module || config.function) { - this._config.component = config.component; - this._config.module = config.module; - this._config.function = config.function; - this._config.sPath = SUtils.buildSPath({ + if (config.component || config.cPath || config.function) { + this._config.component = config.component; + this._config.function = config.function; + this._config.cPath = config.cPath ? config.cPath : null; + this._config.sPath = SUtils.buildSPath({ component: config.component, - module: config.module, + cPath: config.cPath, function: config.function }); } @@ -104,7 +111,12 @@ class ServerlessFunction { // Make full path if (this._S.config.projectPath && this._config.sPath) { let parse = SUtils.parseSPath(this._config.sPath); - this._config.fullPath = path.join(this._S.config.projectPath, parse.component, parse.module, parse.function); + this._config.fullPath = path.join( + this._S.config.projectPath, + parse.component, + parse.cPath ? parse.cPath.split('/').join(path.sep) : null, + parse.function + ); } } @@ -138,7 +150,7 @@ class ServerlessFunction { // Add Endpoint Class Instances functionJson.endpoints[i] = new _this._S.classes.Endpoint(_this._S, { component: _this._config.component, - module: _this._config.module, + cPath: _this._config.cPath, function: functionJson.name, endpointPath: e.path, endpointMethod: e.method diff --git a/lib/utils/index.js b/lib/utils/index.js index 5f0f66fdc..d3975de4d 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -59,7 +59,7 @@ exports.exportClassData = function(data) { exports.buildSPath = function(data) { let path = ''; if (data.component) path = path + data.component.trim(); - if (data.module) path = path + '/' + data.module.trim(); + if (data.cPath) path = path + '/' + data.cPath.trim(); if (data.function) path = path + '/' + data.function.trim(); if (data.endpointPath) path = path + '@' + data.endpointPath.trim(); if (data.endpointMethod) path = path + '~' + data.endpointMethod.trim(); @@ -71,48 +71,24 @@ exports.buildSPath = function(data) { */ exports.parseSPath = function(path) { - let parsed = {}; - parsed.component = path.split('/')[0] || null; - parsed.module = path.split('/')[1] || null; - parsed.function = path.split('/')[2] ? path.split('/')[2].split('@')[0] : null; - parsed.urlPath = path.split('@')[1] ? path.split('@')[1].split('~')[0] : null; - parsed.urlMethod = path.split('~')[1] || null; - return parsed; -}; - -/** - * Validate sPath - */ - -exports.validateSPath = function(projectPath, sPath, type) { - - // Validate Syntax - if (type.indexOf('component') > -1) { - if (!sPath) throw new SError('Invalid path'); - } else if (type.indexOf('module') > -1) { - let pathArray = sPath.split('/'); - if (!pathArray[0] || !pathArray[1] || pathArray[2] || sPath.indexOf('@') > -1 || sPath.indexOf('~') > -1) { - throw new SError('Invalid path'); - } - } else if (type.indexOf('function') > -1) { - - // Check path contents - let pathArray = sPath.split('/'); - if (!pathArray[0] || !pathArray[1] || !pathArray[2]) { - throw new SError('Invalid path'); - } - - // Validate Existence - let parsed = this.parseSPath(sPath); - if (!this.fileExistsSync(path.join(projectPath, parsed.component, parsed.module, parsed.function, 's-function.json'))) { - throw new SError('Function path does not exist: ', sPath); - } - - } else if (type.indexOf('endpoint') > -1) { - let pathArray = sPath.split('/'); - if (!pathArray[0] || !pathArray[1] || !pathArray[2] || sPath.indexOf('@') == -1 || sPath.indexOf('~') == -1) { - throw new SError('Invalid path'); + let pathArray = path.split('/'); + if (pathArray.length < 1) { + return { component: pathArray[0] } + } else { + let parsed = { + component: pathArray[0], + function: pathArray[pathArray.length - 1].split('@')[0], + urlPath: pathArray[pathArray.length - 1].split('@')[1].split('~')[0], + urlMethod: pathArray[pathArray.length - 1].split('@')[1].split('~')[1], + event: pathArray[pathArray.length - 1].split('#')[1] + }; + pathArray.shift(); + pathArray.pop(); + // Check for any cPath + if (pathArray.length) { + parsed.cPath = pathArray.join('/'); } + return parsed; } }; From e2f0531c53e6cc9f02d465992d5a90b2dbfd1bfa Mon Sep 17 00:00:00 2001 From: ac360 Date: Wed, 3 Feb 2016 10:35:49 -0800 Subject: [PATCH 03/29] Continue removing modules --- lib/ServerlessComponent.js | 57 +++++++++++++++++++++++++++++--------- lib/ServerlessEndpoint.js | 20 +++++++------ lib/ServerlessFunction.js | 6 ++-- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/lib/ServerlessComponent.js b/lib/ServerlessComponent.js index ab22a109c..cff022686 100644 --- a/lib/ServerlessComponent.js +++ b/lib/ServerlessComponent.js @@ -71,6 +71,22 @@ class ServerlessComponent { componentJson, componentContents; + // Helper to instantiate functions + let loadFn = function(cPath, fnName) { + let func = new _this._S.classes.Function(_this._S, { + component: _this._config.component, + cPath: cPath ? cPath : null, + function: fnName + }); + + return func.load() + .then(function(instance) { + componentJson.functions[m]= instance; + return componentJson.functions[m]; + }); + }; + + // Load component, then traverse component dirs to get all functions return BbPromise.try(function() { // Validate: Check project path is set @@ -87,22 +103,37 @@ class ServerlessComponent { return componentContents; }) - .each(function(m, i) { + .each(function(f, i) { + + // Skip node_modules and lib + if (['node_modules', 'lib'].indexOf(f.trim() == -1) return; + + // If s-function.json doesn't exist, look in 2 subfolders + if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], 's-function.json'))) { + + let sfOne = fs.readdirSync(path.join(_this._config.fullPath, componentContents[i])); + + return BbPromise.resolve(sfOne) + .each(function(sf1, i) { + + if (!SUtils.fileExistsSync(path.join( + _this._config.fullPath, + componentContents[i], + sf1, + 's-function.json'))) return; + }); + + } + + + + + + + - if (!SUtils.fileExistsSync(path.join( - _this._config.fullPath, componentContents[i], - 's-function.json'))) return; - let func = new _this._S.classes.Function(_this._S, { - component: _this._config.component, - module: m - }); - return func.load() - .then(function(instance) { - componentJson.functions[m]= instance; - return componentJson.functions[m]; - }); }) .then(function() { diff --git a/lib/ServerlessEndpoint.js b/lib/ServerlessEndpoint.js index 5e75a7671..cb3b9fe5f 100644 --- a/lib/ServerlessEndpoint.js +++ b/lib/ServerlessEndpoint.js @@ -21,7 +21,7 @@ class ServerlessEndpoint { constructor(Serverless, config) { // Validate required attributes - if (!config.component || !config.function || !config.endpointPath || !config.endpointMethod) throw new SError('Missing required config.component, config.module, config.function, config.endpointMethod, config.endpointPath'); + if (!config.component || !config.function || !config.endpointPath || !config.endpointMethod) throw new SError('Missing required config.component, config.function, config.endpointMethod, config.endpointPath'); // Private properties let _this = this; @@ -63,15 +63,15 @@ class ServerlessEndpoint { // Set sPath if (config.component || config.function || config.endpointPath || config.endpointMethod) { this._config.component = config.component; - this._config.module = config.module; + this._config.cPath = config.cPath ? config.cPath : null; this._config.function = config.function; this._config.endpointPath = config.endpointPath; this._config.endpointMethod = config.endpointMethod; this._config.type = config.type ? config.type : 'AWS'; this._config.sPath = SUtils.buildSPath({ - component: config.component, - module: config.module, - function: config.function, + component: config.component, + cPath: config.cPath, + function: config.function, endpointPath: config.endpointPath, endpointMethod: config.endpointMethod }); @@ -80,7 +80,12 @@ class ServerlessEndpoint { // Make full path if (this._S.config.projectPath && this._config.sPath) { let parse = SUtils.parseSPath(this._config.sPath); - this._config.fullPath = path.join(this._S.config.projectPath, parse.component, parse.module, parse.function); + this._config.fullPath = path.join( + this._S.config.projectPath, + parse.component, + parse.cPath ? parse.cPath.split('/').join(path.sep) : null, + parse.function + ); } } @@ -129,7 +134,6 @@ class ServerlessEndpoint { */ set(data) { - // Merge _.assign(this, data); return this; @@ -258,7 +262,7 @@ class ServerlessEndpoint { let functions = this._S.state.getFunctions({ component: this._config.component, - module: this._config.module, + cPath: this._config.cPath, function: this._config.function }); diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index 83fbd51f9..13a0cfe2e 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -2,8 +2,10 @@ /** * Serverless Function Class - * - options.path format is: "componentPath/functionFolder - * - + * - options.path format is: "component/cPath/functionFolder" + * - cPath is for component path. You can nest functions in any amounts of subfolders (these subfolders are the cPath) + * - But you must use '/' to divide subfolders. E.g., restApi/data/users/show + * - In the example, data/users is the component path. The 'cPath' */ const SError = require('./ServerlessError'), From 1381fbcc08e631fc1162c665d28c6951d2a03004 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Wed, 3 Feb 2016 12:27:39 -0800 Subject: [PATCH 04/29] ServerlessComponent: change loading --- lib/ServerlessComponent.js | 588 +++++++++++++++++++------------------ lib/ServerlessFunction.js | 2 +- 2 files changed, 297 insertions(+), 293 deletions(-) diff --git a/lib/ServerlessComponent.js b/lib/ServerlessComponent.js index cff022686..8c0c47828 100644 --- a/lib/ServerlessComponent.js +++ b/lib/ServerlessComponent.js @@ -5,338 +5,342 @@ */ const SError = require('./ServerlessError'), - SUtils = require('./utils/index'), - BbPromise = require('bluebird'), - path = require('path'), - _ = require('lodash'), - fs = require('fs'); + SUtils = require('./utils/index'), + BbPromise = require('bluebird'), + path = require('path'), + _ = require('lodash'), + fs = require('fs'); class ServerlessComponent { - /** - * Constructor - */ + /** + * Constructor + */ - constructor(Serverless, config) { + constructor(Serverless, config) { - // Validate required attributes - if (!config.component) throw new SError('Missing required config.component'); + // Validate required attributes + if (!config.component) throw new SError('Missing required config.component'); - let _this = this; - _this._S = Serverless; - _this._config = {}; - _this.updateConfig(config); + let _this = this; + _this._S = Serverless; + _this._config = {}; + _this.updateConfig(config); - // Default Properties - _this.name = _this._config.component || 'component' + SUtils.generateShortId(6); - _this.runtime = config.runtime || 'nodejs'; - _this.custom = {}; - _this.functions = {}; - _this.templates = {}; - } - - /** - * Update Config - * - Takes config.component - */ - - updateConfig(config) { - - if (!config) return; - - // Set sPath - if (config.component) { - this._config.component = config.component; - this._config.sPath = SUtils.buildSPath({ - component: config.component - }); + // Default Properties + _this.name = _this._config.component || 'component' + SUtils.generateShortId(6); + _this.runtime = config.runtime || 'nodejs'; + _this.custom = {}; + _this.functions = {}; + _this.templates = {}; } - // Make full path - if (this._S.config.projectPath && this._config.sPath) { - let parse = SUtils.parseSPath(this._config.sPath); - this._config.fullPath = path.join(this._S.config.projectPath, parse.component); - } - } + /** + * Update Config + * - Takes config.component + */ - /** - * Load - * - Load from source (i.e., file system); - * - Returns promise - */ + updateConfig(config) { - load() { + if (!config) return; - let _this = this, - componentJson, - componentContents; - - // Helper to instantiate functions - let loadFn = function(cPath, fnName) { - let func = new _this._S.classes.Function(_this._S, { - component: _this._config.component, - cPath: cPath ? cPath : null, - function: fnName - }); - - return func.load() - .then(function(instance) { - componentJson.functions[m]= instance; - return componentJson.functions[m]; - }); - }; - - // Load component, then traverse component dirs to get all functions - return BbPromise.try(function() { - - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Component could not be loaded because no project path has been set on Serverless instance'); - - // Validate: Check component exists - if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-component.json'))) { - throw new SError('Component could not be loaded because it does not exist in your project: ' + _this._config.sPath); + // Set sPath + if (config.component) { + this._config.component = config.component; + this._config.sPath = SUtils.buildSPath({ + component: config.component + }); } - componentJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-component.json')); - componentJson.functions = {}; - componentContents = fs.readdirSync(_this._config.fullPath); + // Make full path + if (this._S.config.projectPath && this._config.sPath) { + let parse = SUtils.parseSPath(this._config.sPath); + this._config.fullPath = path.join(this._S.config.projectPath, parse.component); + } + } - return componentContents; - }) - .each(function(f, i) { + /** + * Load + * - Load from source (i.e., file system); + * - Returns promise + */ - // Skip node_modules and lib - if (['node_modules', 'lib'].indexOf(f.trim() == -1) return; + load() { - // If s-function.json doesn't exist, look in 2 subfolders - if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], 's-function.json'))) { + let _this = this, + skip = ['node_modules', 'lib'], + componentJson, + componentContents; - let sfOne = fs.readdirSync(path.join(_this._config.fullPath, componentContents[i])); - - return BbPromise.resolve(sfOne) - .each(function(sf1, i) { - - if (!SUtils.fileExistsSync(path.join( - _this._config.fullPath, - componentContents[i], - sf1, - 's-function.json'))) return; + // Helper to instantiate functions + let loadFn = function(fnName, fnKey, cPath) { + let func = new _this._S.classes.Function(_this._S, { + component: _this._config.component, + cPath: cPath ? cPath : null, + function: fnName }); - } + return func.load() + .then(function(instance) { + componentJson.functions[fnKey]= instance; + return componentJson.functions[fnKey]; + }); + }; + // Load component, then traverse component dirs to get all functions + return BbPromise.try(function() { + // Validate: Check project path is set + if (!_this._S.config.projectPath) throw new SError('Component could not be loaded because no project path has been set on Serverless instance'); + // Validate: Check component exists + if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-component.json'))) { + throw new SError('Component could not be loaded because it does not exist in your project: ' + _this._config.sPath); + } - - - - - - - }) - .then(function() { - - // Get templates - if (_this._config.fullPath && SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-templates.json'))) { - componentJson.templates = require(path.join(_this._config.fullPath, 's-templates.json')); - } - }) - .then(function() { - - // Merge - _.assign(_this, componentJson); - return _this; - }); - } - - /** - * Set - * - Set data - * - Accepts a data object - */ - - set(data) { - let _this = this; - - // Instantiate Components - for (let prop in data.functions) { - - if (data.functions[prop] instanceof _this._S.classes.Function) { - throw new SError('You cannot pass subclasses into the set method, only object literals'); - } - - let instance = new _this._S.classes.Function(_this._S, { - component: _this._config.component, - module: prop - }); - data.functions[prop] = instance.set(data.functions[prop]); - } - - // Merge in - _this = _.extend(_this, data); - return _this; - } - - /** - * Get - * - Returns clone of data - */ - - get() { - let clone = _.cloneDeep(this); - for (let prop in this.functions) { - clone.functions[prop] = this.functions[prop].get(); - } - return SUtils.exportClassData(clone); - } - - /** - * getPopulated - * - Fill in templates then variables - */ - - getPopulated(options) { - let _this = this; - - options = options || {}; - - // Validate: Check Stage & Region - if (!options.stage || !options.region) throw new SError('Both "stage" and "region" params are required'); - - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Component could not be populated because no project path has been set on Serverless instance'); - - // Populate - let clone = _this.get(); - clone = SUtils.populate(_this._S.state.getMeta(), this.getTemplates(), clone, options.stage, options.region); - clone.functions = {}; - for (let prop in _this.functions) { - clone.functions[prop] = _this.functions[prop].getPopulated(options); - } - - return clone; - } - - /** - * Get Templates - * - Returns clone of templates - * - Inherits parent templates - */ - - getTemplates() { - return _.merge( - this.getProject().getTemplates(), - _.cloneDeep(this.templates) - ); - } - - /** - * Save - * - Saves data to file system - * - Returns promise - */ - - save(options) { - - let _this = this; - - return new BbPromise.try(function() { - - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Component could not be saved because no project path has been set on Serverless instance'); - - // Create if does not exist - if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-component.json'))) { - return _this._create(); - } - }) - .then(function() { - - // Save all nested modules - if (options && options.deep) { - return BbPromise.try(function () { - return Object.keys(_this.functions); + componentJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-component.json')); + componentJson.functions = {}; + componentContents = fs.readdirSync(_this._config.fullPath); + return componentContents; }) - .each(function(m) { - return _this.functions[m].save(); + .each(function(f, i) { + + // Skip node_modules and lib + if (skip.indexOf(f.trim() == -1) return; + + // If s-function.json doesn't exist, look in 2 subfolders + if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], 's-function.json'))) { + let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], 's-function.json')); + return loadFn(fnJson.name, fnJson.name, null); + } else { + + // Loop through 1st level subfolders looking for more functions + return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, componentContents[i]))) + .each(function(sf1, i) { + + // If s-function.json doesn't exist, look in 2 subfolders + if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], sf1, 's-function.json'))) { + let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], sf1, 's-function.json')); + return loadFn(fnJson.name, fnJson.name, sf1 + '/' + fnJson.name); + } else { + + // Loop through 2nd level subfolders looking for more functions + return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, componentContents[i], sf1))) + .each(function(sf2, i) { + + // If s-function.json doesn't exist, look in 2 subfolders + if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], sf1, sf2, 's-function.json'))) { + let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], sf1, sf2, 's-function.json')); + return loadFn(fnJson.name, fnJson.name, sf1 + '/' + sf2 + '/' + fnJson.name); + } + }); + } + }); + } }) + .then(function() { + + // Get templates + if (_this._config.fullPath && SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-templates.json'))) { + componentJson.templates = require(path.join(_this._config.fullPath, 's-templates.json')); + } + }) + .then(function() { + + // Merge + _.assign(_this, componentJson); + return _this; + }); + } + + /** + * Set + * - Set data + * - Accepts a data object + */ + + set(data) { + let _this = this; + + // Instantiate Components + for (let prop in data.functions) { + + if (data.functions[prop] instanceof _this._S.classes.Function) { + throw new SError('You cannot pass subclasses into the set method, only object literals'); + } + + let instance = new _this._S.classes.Function(_this._S, { + component: _this._config.component, + function: data.functions[prop].name, + cPath: prop.indexOf('/') !== -1 ? prop : null + }); + data.functions[prop] = instance.set(data.functions[prop]); } - }) - .then(function() { - // If templates, save templates - if (_this.templates && Object.keys(_this.templates).length) { - return SUtils.writeFile(path.join(_this._config.fullPath, 's-templates.json'), _this.templates); - } - }) - .then(function() { - - let clone = _this.get(); - - // Strip properties - if (clone.functions) delete clone.functions; - if (clone.templates) delete clone.templates; - - // Write file - return SUtils.writeFile(path.join(_this._config.fullPath, 's-component.json'), - JSON.stringify(clone, null, 2)); - }) - .then(function() { + // Merge in + _this = _.extend(_this, data); return _this; - }) - } + } - /** - * Create (scaffolding) - * - Returns promise - */ + /** + * Get + * - Returns clone of data + */ - _create() { + get() { + let clone = _.cloneDeep(this); + for (let prop in this.functions) { + clone.functions[prop] = this.functions[prop].get(); + } + return SUtils.exportClassData(clone); + } - let _this = this, - writeDeferred = []; + /** + * getPopulated + * - Fill in templates then variables + */ - return BbPromise.try(function() { + getPopulated(options) { + let _this = this; - writeDeferred.push( - fs.mkdirSync(_this._config.fullPath), - fs.mkdirSync(path.join(_this._config.fullPath, 'lib')) + options = options || {}; + + // Validate: Check Stage & Region + if (!options.stage || !options.region) throw new SError('Both "stage" and "region" params are required'); + + // Validate: Check project path is set + if (!_this._S.config.projectPath) throw new SError('Component could not be populated because no project path has been set on Serverless instance'); + + // Populate + let clone = _this.get(); + clone = SUtils.populate(_this._S.state.getMeta(), this.getTemplates(), clone, options.stage, options.region); + clone.functions = {}; + for (let prop in _this.functions) { + clone.functions[prop] = _this.functions[prop].getPopulated(options); + } + + return clone; + } + + /** + * Get Templates + * - Returns clone of templates + * - Inherits parent templates + */ + + getTemplates() { + return _.merge( + this.getProject().getTemplates(), + _.cloneDeep(this.templates) ); - // Runtime: nodejs - if (_this.runtime === 'nodejs') { - let packageJsonTemplate = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'package.json')), - libJs = fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'index.js')); + } - writeDeferred.push( - SUtils.writeFile(path.join(_this._config.fullPath, 'lib', 'index.js'), libJs), - SUtils.writeFile(path.join(_this._config.fullPath, 'package.json'), JSON.stringify(packageJsonTemplate, null, 2)) - ); - } else if (_this.runtime === 'python2.7') { - let requirements = fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', 'requirements.txt')), - initPy = fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', '__init__.py')), - blankInitPy = fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', 'blank__init__.py')); + /** + * Save + * - Saves data to file system + * - Returns promise + */ - writeDeferred.push( - fs.mkdirSync(path.join(_this._config.fullPath, 'vendored')), - SUtils.writeFile(path.join(_this._config.fullPath, 'lib', '__init__.py'), initPy), - SUtils.writeFile(path.join(_this._config.fullPath, 'vendored', '__init__.py'), blankInitPy), - SUtils.writeFile(path.join(_this._config.fullPath, 'requirements.txt'), requirements) - ); - } + save(options) { - return BbPromise.all(writeDeferred); - }); - } + let _this = this; - /** - * Get Project - * - Returns reference to the instance - */ + return new BbPromise.try(function() { - getProject() { - return this._S.state.project; - } + // Validate: Check project path is set + if (!_this._S.config.projectPath) throw new SError('Component could not be saved because no project path has been set on Serverless instance'); + + // Create if does not exist + if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-component.json'))) { + return _this._create(); + } + }) + .then(function() { + + // Save all nested modules + if (options && options.deep) { + return BbPromise.try(function () { + return Object.keys(_this.functions); + }) + .each(function(fnKey) { + return _this.functions[fnKey].save(); + }) + } + }) + .then(function() { + + // If templates, save templates + if (_this.templates && Object.keys(_this.templates).length) { + return SUtils.writeFile(path.join(_this._config.fullPath, 's-templates.json'), _this.templates); + } + }) + .then(function() { + + let clone = _this.get(); + + // Strip properties + if (clone.functions) delete clone.functions; + if (clone.templates) delete clone.templates; + + // Write file + return SUtils.writeFile(path.join(_this._config.fullPath, 's-component.json'), + JSON.stringify(clone, null, 2)); + }) + .then(function() { + return _this; + }) + } + + /** + * Create (scaffolding) + * - Returns promise + */ + + _create() { + + let _this = this, + writeDeferred = []; + + return BbPromise.try(function() { + + writeDeferred.push( + fs.mkdirSync(_this._config.fullPath), + fs.mkdirSync(path.join(_this._config.fullPath, 'lib')) + ); + // Runtime: nodejs + if (_this.runtime === 'nodejs') { + let packageJsonTemplate = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'package.json')), + libJs = fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'index.js')); + + writeDeferred.push( + SUtils.writeFile(path.join(_this._config.fullPath, 'lib', 'index.js'), libJs), + SUtils.writeFile(path.join(_this._config.fullPath, 'package.json'), JSON.stringify(packageJsonTemplate, null, 2)) + ); + } else if (_this.runtime === 'python2.7') { + let requirements = fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', 'requirements.txt')), + initPy = fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', '__init__.py')), + blankInitPy = fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', 'blank__init__.py')); + + writeDeferred.push( + fs.mkdirSync(path.join(_this._config.fullPath, 'vendored')), + SUtils.writeFile(path.join(_this._config.fullPath, 'lib', '__init__.py'), initPy), + SUtils.writeFile(path.join(_this._config.fullPath, 'vendored', '__init__.py'), blankInitPy), + SUtils.writeFile(path.join(_this._config.fullPath, 'requirements.txt'), requirements) + ); + } + + return BbPromise.all(writeDeferred); + }); + } + + /** + * Get Project + * - Returns reference to the instance + */ + + getProject() { + return this._S.state.project; + } } module.exports = ServerlessComponent; \ No newline at end of file diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index 13a0cfe2e..a6d597ffe 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -3,7 +3,7 @@ /** * Serverless Function Class * - options.path format is: "component/cPath/functionFolder" - * - cPath is for component path. You can nest functions in any amounts of subfolders (these subfolders are the cPath) + * - cPath is optional. It specifies component path. You can nest functions in any amounts of subfolders (these subfolders are the cPath) * - But you must use '/' to divide subfolders. E.g., restApi/data/users/show * - In the example, data/users is the component path. The 'cPath' */ From 33e108161d516d4f1c2c471f0567d36ff48fb6a5 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Wed, 3 Feb 2016 13:49:05 -0800 Subject: [PATCH 05/29] ServerlessComponent: continue work on function loading --- lib/ServerlessComponent.js | 48 +++++++-------- lib/ServerlessFunction.js | 4 +- lib/ServerlessState.js | 20 +++---- lib/utils/index.js | 27 +++++---- tests/all.js | 43 +++++++------- .../function1 => function0}/event.json | 0 .../function1 => function0}/handler.js | 0 .../nodejscomponent/function0/s-function.json | 34 +++++++++++ .../function1 => function0}/s-templates.json | 0 .../function3 => group1/function1}/event.json | 0 .../group1/function1/handler.js | 12 ++++ .../function1/s-function.json | 0 .../group1/function1/s-templates.json | 7 +++ .../{module1 => group1}/function2/event.json | 0 .../{module1 => group1}/function2/handler.js | 0 .../function2/s-function.json | 0 .../function2/s-templates.json | 0 .../group1/function3/event.json | 3 + .../{module1 => group1}/function3/handler.js | 0 .../function3/s-function.json | 0 .../group1/group2/function4/event.json | 3 + .../group1/group2/function4/handler.js | 10 ++++ .../group1/group2/function4/s-function.json | 59 +++++++++++++++++++ .../{module1 => group1}/s-module.json | 0 .../{module1 => group1}/s-templates.json | 0 tests/tests/actions/EndpointDeploy.js | 2 +- tests/tests/actions/FunctionCreate.js | 4 +- tests/tests/actions/FunctionDeploy.js | 6 +- tests/tests/actions/FunctionRun.js | 2 +- tests/tests/classes/ServerlessEndpointTest.js | 6 +- tests/tests/classes/ServerlessFunctionTest.js | 4 +- tests/tests/classes/ServerlessModuleTest.js | 4 +- tests/tests/classes/ServerlessStateTest.js | 34 ++--------- 33 files changed, 214 insertions(+), 118 deletions(-) rename tests/test-prj/nodejscomponent/{module1/function1 => function0}/event.json (100%) rename tests/test-prj/nodejscomponent/{module1/function1 => function0}/handler.js (100%) create mode 100644 tests/test-prj/nodejscomponent/function0/s-function.json rename tests/test-prj/nodejscomponent/{module1/function1 => function0}/s-templates.json (100%) rename tests/test-prj/nodejscomponent/{module1/function3 => group1/function1}/event.json (100%) create mode 100644 tests/test-prj/nodejscomponent/group1/function1/handler.js rename tests/test-prj/nodejscomponent/{module1 => group1}/function1/s-function.json (100%) create mode 100644 tests/test-prj/nodejscomponent/group1/function1/s-templates.json rename tests/test-prj/nodejscomponent/{module1 => group1}/function2/event.json (100%) rename tests/test-prj/nodejscomponent/{module1 => group1}/function2/handler.js (100%) rename tests/test-prj/nodejscomponent/{module1 => group1}/function2/s-function.json (100%) rename tests/test-prj/nodejscomponent/{module1 => group1}/function2/s-templates.json (100%) create mode 100644 tests/test-prj/nodejscomponent/group1/function3/event.json rename tests/test-prj/nodejscomponent/{module1 => group1}/function3/handler.js (100%) rename tests/test-prj/nodejscomponent/{module1 => group1}/function3/s-function.json (100%) create mode 100644 tests/test-prj/nodejscomponent/group1/group2/function4/event.json create mode 100644 tests/test-prj/nodejscomponent/group1/group2/function4/handler.js create mode 100644 tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json rename tests/test-prj/nodejscomponent/{module1 => group1}/s-module.json (100%) rename tests/test-prj/nodejscomponent/{module1 => group1}/s-templates.json (100%) diff --git a/lib/ServerlessComponent.js b/lib/ServerlessComponent.js index 8c0c47828..fe67ab42a 100644 --- a/lib/ServerlessComponent.js +++ b/lib/ServerlessComponent.js @@ -68,12 +68,13 @@ class ServerlessComponent { load() { let _this = this, - skip = ['node_modules', 'lib'], + reserved = ['node_modules', 'lib', '.DS_Store'], componentJson, componentContents; // Helper to instantiate functions - let loadFn = function(fnName, fnKey, cPath) { + let loadFn = function(fnName, cPath) { + console.log("Loading Function...", fnName, cPath) let func = new _this._S.classes.Function(_this._S, { component: _this._config.component, cPath: cPath ? cPath : null, @@ -82,7 +83,7 @@ class ServerlessComponent { return func.load() .then(function(instance) { - componentJson.functions[fnKey]= instance; + componentJson.functions[cPath ? cPath + '/' + fnName : fnName]= instance; return componentJson.functions[fnKey]; }); }; @@ -103,37 +104,32 @@ class ServerlessComponent { componentContents = fs.readdirSync(_this._config.fullPath); return componentContents; }) - .each(function(f, i) { + .each(function(sf, i) { - // Skip node_modules and lib - if (skip.indexOf(f.trim() == -1) return; + let fPath1 = path.join(_this._config.fullPath, sf); + + // Skip reserved names and files + if (reserved.indexOf(sf.trim()) !== -1 || !fs.lstatSync(fPath1).isDirectory()) return; // If s-function.json doesn't exist, look in 2 subfolders - if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], 's-function.json'))) { - let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], 's-function.json')); - return loadFn(fnJson.name, fnJson.name, null); + if (SUtils.fileExistsSync(path.join(fPath1, 's-function.json'))) { + let fnJson = SUtils.readAndParseJsonSync(path.join(fPath1, 's-function.json')); + return loadFn(fnJson.name, null); } else { // Loop through 1st level subfolders looking for more functions return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, componentContents[i]))) - .each(function(sf1, i) { + .each(function(sf2, i) { + + let fPath2 = path.join(fPath1, sf2); + + // Skip reserved names and files + if (reserved.indexOf(sf2.trim()) !== -1 || !fs.lstatSync(fPath2).isDirectory()) return; // If s-function.json doesn't exist, look in 2 subfolders - if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], sf1, 's-function.json'))) { - let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], sf1, 's-function.json')); - return loadFn(fnJson.name, fnJson.name, sf1 + '/' + fnJson.name); - } else { - - // Loop through 2nd level subfolders looking for more functions - return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, componentContents[i], sf1))) - .each(function(sf2, i) { - - // If s-function.json doesn't exist, look in 2 subfolders - if (SUtils.fileExistsSync(path.join(_this._config.fullPath, componentContents[i], sf1, sf2, 's-function.json'))) { - let fnJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, componentContents[i], sf1, sf2, 's-function.json')); - return loadFn(fnJson.name, fnJson.name, sf1 + '/' + sf2 + '/' + fnJson.name); - } - }); + if (SUtils.fileExistsSync(path.join(fPath2, 's-function.json'))) { + let fnJson = SUtils.readAndParseJsonSync(path.join(fPath2, 's-function.json')); + return loadFn(fnJson.name, fnJson.name, sf2 + '/' + fnJson.name); } }); } @@ -257,7 +253,7 @@ class ServerlessComponent { }) .then(function() { - // Save all nested modules + // Save all nested functions if (options && options.deep) { return BbPromise.try(function () { return Object.keys(_this.functions); diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index a6d597ffe..9e92c78b4 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -26,7 +26,7 @@ class ServerlessFunction { // Validate required attributes if (!config.component || !config.function) throw new SError('Missing required config.component or config.function'); - + console.log("ServerlessFunction Constructor", config); let _this = this; _this._S = Serverless; _this._config = {}; @@ -310,7 +310,7 @@ class ServerlessFunction { writeDeferred.push( fs.mkdirSync(_this._config.fullPath), SUtils.writeFile(path.join(_this._config.fullPath, 'event.json'), '{}') - ) + ); if (_this.getRuntime() === 'nodejs') { writeDeferred.push( SUtils.writeFile(path.join(_this._config.fullPath, 'handler.js'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'handler.js'))) diff --git a/lib/ServerlessState.js b/lib/ServerlessState.js index 181c985bb..ddc4e225f 100644 --- a/lib/ServerlessState.js +++ b/lib/ServerlessState.js @@ -69,7 +69,7 @@ class ServerlessState { /** * Set Asset * - Add or replace an asset to the state - * - Accepts a class instance of: Project, Component, Module, Function, Endpoint + * - Accepts a class instance of: Project, Component, Function, Endpoint */ setAsset(data) { @@ -90,7 +90,7 @@ class ServerlessState { } if (!added) func.endpoints.push(data); } else { - return new SError('State.setAsset() failed because you did not submit an instance of a Project, Component, Module, Function or Endpoint class.'); + return new SError('State.setAsset() failed because you did not submit an instance of a Project, Component, Function or Endpoint class.'); } } @@ -220,8 +220,8 @@ class ServerlessState { /** * Get Functions * - Returns an array of this state's function instances - * - Options: paths, component, module, function - * - options.paths is an array of Serverless paths like this: ['component/moduleOne/functionOne', 'component/moduleOne/functionOne'] + * - Options: paths, component, function + * - options.paths is an array of Serverless paths like this: ['component/functionOne', 'component/subfolder1/subfolder2/functionTwo'] */ getFunctions(options) { @@ -233,13 +233,9 @@ class ServerlessState { // Get all for (let i = 0; i < Object.keys(_this.project.components).length; i++) { let component = _this.project.components[Object.keys(_this.project.components)[i]]; - if (!component.modules) continue; - for (let j = 0; j < Object.keys(component.modules).length; j++) { - let module = component.modules[Object.keys(component.modules)[j]]; - if (!module.functions) continue; - for (let k = 0; k < Object.keys(module.functions).length; k++) { - allFunctions.push(module.functions[Object.keys(module.functions)[k]]); - } + console.log("here", component.functions) + for (let j = 0; j < Object.keys(component.functions).length; j++) { + allFunctions.push(component.functions[Object.keys(component.functions)[j]]); } } @@ -251,7 +247,7 @@ class ServerlessState { for (let i = 0; i < allFunctions.length; i++) { let func = allFunctions[i]; - if (options.component && options.module && options.function) { + if (options.component && options.cPath && options.function) { if (func._config.component == options.component && func._config.module == options.module && func.name == options.function) { foundFunctions.push(options.returnPaths ? func._config.sPath : func); } diff --git a/lib/utils/index.js b/lib/utils/index.js index d3975de4d..9cd816ac9 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -70,23 +70,24 @@ exports.buildSPath = function(data) { * Parse sPath */ -exports.parseSPath = function(path) { - let pathArray = path.split('/'); - if (pathArray.length < 1) { - return { component: pathArray[0] } +exports.parseSPath = function(sPath) { + console.log("parseSPath", sPath) + let pArray = sPath.split('/'); + if (pArray.length < 1) { + return { component: pArray[0] } } else { let parsed = { - component: pathArray[0], - function: pathArray[pathArray.length - 1].split('@')[0], - urlPath: pathArray[pathArray.length - 1].split('@')[1].split('~')[0], - urlMethod: pathArray[pathArray.length - 1].split('@')[1].split('~')[1], - event: pathArray[pathArray.length - 1].split('#')[1] + component: pArray[0], + function: pArray[1] ? pArray[pArray.length - 1].split('@')[0] : null, + urlPath: pArray[1] ? pArray[pArray.length - 1].split('@')[1] ? pArray[pArray.length - 1].split('@')[1].split('~')[0] : null : null, + urlMethod: pArray[1] ? pArray[pArray.length - 1].split('@')[1] ? pArray[pArray.length - 1].split('@')[1].split('~')[1] : null : null, + event: pArray[1] ? pArray[pArray.length - 1].split('#')[1] : null }; - pathArray.shift(); - pathArray.pop(); + pArray.shift(); + pArray.pop(); // Check for any cPath - if (pathArray.length) { - parsed.cPath = pathArray.join('/'); + if (pArray.length) { + parsed.cPath = pArray.join('/'); } return parsed; } diff --git a/tests/all.js b/tests/all.js index aef4907c5..bed6fffd9 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,27 +10,26 @@ describe('All Tests', function() { }); after(function() {}); - require('./tests/classes/ServerlessEndpointTest'); - require('./tests/classes/ServerlessFunctionTest'); - require('./tests/classes/ServerlessModuleTest'); - require('./tests/classes/ServerlessComponentTest'); - require('./tests/classes/ServerlessProjectTest'); require('./tests/classes/ServerlessStateTest'); - require('./tests/actions/TestPluginCustom'); - require('./tests/actions/TestDefaultActionHook'); - require('./tests/actions/StageCreate'); - require('./tests/actions/RegionCreate'); - require('./tests/actions/ComponentCreate'); - require('./tests/actions/ModuleCreate'); - require('./tests/actions/FunctionCreate'); - require('./tests/actions/EnvList'); - require('./tests/actions/EnvGet'); - require('./tests/actions/EnvSetUnset'); - require('./tests/actions/ResourcesDeploy'); - require('./tests/actions/FunctionRun'); - require('./tests/actions/FunctionDeploy'); - require('./tests/actions/EndpointDeploy'); - require('./tests/actions/ProjectInit'); - require('./tests/actions/ProjectInstall'); - require('./tests/actions/ProjectLifeCycle.js'); + //require('./tests/classes/ServerlessProjectTest'); + //require('./tests/classes/ServerlessComponentTest'); + //require('./tests/classes/ServerlessFunctionTest'); + //require('./tests/classes/ServerlessEndpointTest'); + //require('./tests/actions/TestPluginCustom'); + //require('./tests/actions/TestDefaultActionHook'); + //require('./tests/actions/StageCreate'); + //require('./tests/actions/RegionCreate'); + //require('./tests/actions/ComponentCreate'); + //require('./tests/actions/ModuleCreate'); + //require('./tests/actions/FunctionCreate'); + //require('./tests/actions/EnvList'); + //require('./tests/actions/EnvGet'); + //require('./tests/actions/EnvSetUnset'); + //require('./tests/actions/ResourcesDeploy'); + //require('./tests/actions/FunctionRun'); + //require('./tests/actions/FunctionDeploy'); + //require('./tests/actions/EndpointDeploy'); + //require('./tests/actions/ProjectInit'); + //require('./tests/actions/ProjectInstall'); + //require('./tests/actions/ProjectLifeCycle.js'); }); \ No newline at end of file diff --git a/tests/test-prj/nodejscomponent/module1/function1/event.json b/tests/test-prj/nodejscomponent/function0/event.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function1/event.json rename to tests/test-prj/nodejscomponent/function0/event.json diff --git a/tests/test-prj/nodejscomponent/module1/function1/handler.js b/tests/test-prj/nodejscomponent/function0/handler.js similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function1/handler.js rename to tests/test-prj/nodejscomponent/function0/handler.js diff --git a/tests/test-prj/nodejscomponent/function0/s-function.json b/tests/test-prj/nodejscomponent/function0/s-function.json new file mode 100644 index 000000000..dce1e2274 --- /dev/null +++ b/tests/test-prj/nodejscomponent/function0/s-function.json @@ -0,0 +1,34 @@ +{ + "name" : "function0", + "custom": { + "envVars": [], + "excludePatterns": [] + }, + "handler": "module1/function0/handler.handler", + "timeout": 6, + "memorySize": 1024, + "events": [], + "endpoints": [ + { + "path": "module1/function0", + "method": "GET", + "authorizationType": "${endpointVariable}", + "apiKeyRequired": false, + "requestParameters": "$${endpointTemplate}", + "requestTemplates": "$${apiRequestTemplate}", + "responses": { + "default": { + "statusCode": "200", + "responseParameters": {}, + "responseModels": {}, + "responseTemplates": { + "application/json": "" + } + }, + "400": { + "statusCode": "400" + } + } + } + ] +} \ No newline at end of file diff --git a/tests/test-prj/nodejscomponent/module1/function1/s-templates.json b/tests/test-prj/nodejscomponent/function0/s-templates.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function1/s-templates.json rename to tests/test-prj/nodejscomponent/function0/s-templates.json diff --git a/tests/test-prj/nodejscomponent/module1/function3/event.json b/tests/test-prj/nodejscomponent/group1/function1/event.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function3/event.json rename to tests/test-prj/nodejscomponent/group1/function1/event.json diff --git a/tests/test-prj/nodejscomponent/group1/function1/handler.js b/tests/test-prj/nodejscomponent/group1/function1/handler.js new file mode 100644 index 000000000..919263934 --- /dev/null +++ b/tests/test-prj/nodejscomponent/group1/function1/handler.js @@ -0,0 +1,12 @@ +'use strict'; + +// Load ENV +var ServerlessHelpers = require('serverless-helpers-js'); +ServerlessHelpers.loadEnv(); + +// Lambda Handler +module.exports.handler = function(event, context) { + return context.done(null, { + message: '"functionOne" lambda function has run successfully' + }); +}; diff --git a/tests/test-prj/nodejscomponent/module1/function1/s-function.json b/tests/test-prj/nodejscomponent/group1/function1/s-function.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function1/s-function.json rename to tests/test-prj/nodejscomponent/group1/function1/s-function.json diff --git a/tests/test-prj/nodejscomponent/group1/function1/s-templates.json b/tests/test-prj/nodejscomponent/group1/function1/s-templates.json new file mode 100644 index 000000000..0e74f88de --- /dev/null +++ b/tests/test-prj/nodejscomponent/group1/function1/s-templates.json @@ -0,0 +1,7 @@ +{ + "apiRequestTemplate": { + "application/json": { + "pathParams" : "$input.path('$.id1')" + } + } +} \ No newline at end of file diff --git a/tests/test-prj/nodejscomponent/module1/function2/event.json b/tests/test-prj/nodejscomponent/group1/function2/event.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function2/event.json rename to tests/test-prj/nodejscomponent/group1/function2/event.json diff --git a/tests/test-prj/nodejscomponent/module1/function2/handler.js b/tests/test-prj/nodejscomponent/group1/function2/handler.js similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function2/handler.js rename to tests/test-prj/nodejscomponent/group1/function2/handler.js diff --git a/tests/test-prj/nodejscomponent/module1/function2/s-function.json b/tests/test-prj/nodejscomponent/group1/function2/s-function.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function2/s-function.json rename to tests/test-prj/nodejscomponent/group1/function2/s-function.json diff --git a/tests/test-prj/nodejscomponent/module1/function2/s-templates.json b/tests/test-prj/nodejscomponent/group1/function2/s-templates.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function2/s-templates.json rename to tests/test-prj/nodejscomponent/group1/function2/s-templates.json diff --git a/tests/test-prj/nodejscomponent/group1/function3/event.json b/tests/test-prj/nodejscomponent/group1/function3/event.json new file mode 100644 index 000000000..63e2427f0 --- /dev/null +++ b/tests/test-prj/nodejscomponent/group1/function3/event.json @@ -0,0 +1,3 @@ +{ + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJ1X2FkN2Y5MGMwLTNiZWItMTFlNS05MDVjLWUzNzViNDljNDhkMiIsImlhdCI6MTQzODgzMTk3NSwiZXhwIjoxNDM5NDM2Nzc1LCJpc3MiOiJKQVdTIn0.FlISdHxUrFdrDkovrq_RRwxHqlzaSL3GShr27xlvcB0" +} \ No newline at end of file diff --git a/tests/test-prj/nodejscomponent/module1/function3/handler.js b/tests/test-prj/nodejscomponent/group1/function3/handler.js similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function3/handler.js rename to tests/test-prj/nodejscomponent/group1/function3/handler.js diff --git a/tests/test-prj/nodejscomponent/module1/function3/s-function.json b/tests/test-prj/nodejscomponent/group1/function3/s-function.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/function3/s-function.json rename to tests/test-prj/nodejscomponent/group1/function3/s-function.json diff --git a/tests/test-prj/nodejscomponent/group1/group2/function4/event.json b/tests/test-prj/nodejscomponent/group1/group2/function4/event.json new file mode 100644 index 000000000..63e2427f0 --- /dev/null +++ b/tests/test-prj/nodejscomponent/group1/group2/function4/event.json @@ -0,0 +1,3 @@ +{ + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJ1X2FkN2Y5MGMwLTNiZWItMTFlNS05MDVjLWUzNzViNDljNDhkMiIsImlhdCI6MTQzODgzMTk3NSwiZXhwIjoxNDM5NDM2Nzc1LCJpc3MiOiJKQVdTIn0.FlISdHxUrFdrDkovrq_RRwxHqlzaSL3GShr27xlvcB0" +} \ No newline at end of file diff --git a/tests/test-prj/nodejscomponent/group1/group2/function4/handler.js b/tests/test-prj/nodejscomponent/group1/group2/function4/handler.js new file mode 100644 index 000000000..e56224823 --- /dev/null +++ b/tests/test-prj/nodejscomponent/group1/group2/function4/handler.js @@ -0,0 +1,10 @@ +'use strict'; + +// Load ENV +var ServerlessHelpers = require('serverless-helpers-js'); +ServerlessHelpers.loadEnv(); + +// Lambda Handler +module.exports.handler = function(event, context) { + return context.done(null, { message: '"functionThree" lambda function has run successfully' }); +}; diff --git a/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json b/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json new file mode 100644 index 000000000..226a62d6d --- /dev/null +++ b/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json @@ -0,0 +1,59 @@ +{ + "name": "function4", + "custom": { + "envVars": [], + "excludePatterns": [] + }, + "handler": "module1/function4/handler.handler", + "timeout": 6, + "memorySize": 1024, + "events": [], + "endpoints": [ + { + "path": "module1/function4", + "method": "GET", + "authorizationType": "none", + "apiKeyRequired": false, + "requestParameters": {}, + "requestTemplates": { + "application/json": "{\"access_token\":\"$input.params('access_token')\",\"body\":\"$input.json('$')\"}" + }, + "responses": { + "default": { + "statusCode": "200", + "responseParameters": {}, + "responseModels": {}, + "responseTemplates": { + "application/json": "" + } + }, + "400": { + "statusCode": "400" + } + } + }, + { + "path": "module1/function4", + "method": "POST", + "authorizationType": "none", + "apiKeyRequired": false, + "requestTemplates": { + "application/json": "{\"access_token\":\"$input.params('access_token')\",\"body\":\"$input.json('$')\"}" + }, + "requestParameters": {}, + "responses": { + "default": { + "statusCode": "200", + "responseParameters": {}, + "responseModels": {}, + "responseTemplates": { + "application/json": "" + } + }, + "400": { + "statusCode": "400" + } + } + } + ] +} \ No newline at end of file diff --git a/tests/test-prj/nodejscomponent/module1/s-module.json b/tests/test-prj/nodejscomponent/group1/s-module.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/s-module.json rename to tests/test-prj/nodejscomponent/group1/s-module.json diff --git a/tests/test-prj/nodejscomponent/module1/s-templates.json b/tests/test-prj/nodejscomponent/group1/s-templates.json similarity index 100% rename from tests/test-prj/nodejscomponent/module1/s-templates.json rename to tests/test-prj/nodejscomponent/group1/s-templates.json diff --git a/tests/tests/actions/EndpointDeploy.js b/tests/tests/actions/EndpointDeploy.js index 53a7ab536..437e47a50 100644 --- a/tests/tests/actions/EndpointDeploy.js +++ b/tests/tests/actions/EndpointDeploy.js @@ -78,7 +78,7 @@ describe('Test Action: Endpoint Deploy', function() { stage: config.stage, region: config.region, paths: [ - 'nodejscomponent/module1/function1@module1/function1~GET' + 'nodejscomponent/group1/function1@group1/function1~GET' ] }; diff --git a/tests/tests/actions/FunctionCreate.js b/tests/tests/actions/FunctionCreate.js index 4007e8395..8b7e2764b 100644 --- a/tests/tests/actions/FunctionCreate.js +++ b/tests/tests/actions/FunctionCreate.js @@ -57,7 +57,7 @@ describe('Test action: Function Create', function() { let evt = { options: { component: 'nodejscomponent', - module: 'module1', + module: 'group1', function: 'new' } }; @@ -65,7 +65,7 @@ describe('Test action: Function Create', function() { serverless.actions.functionCreate(evt) .then(function(evt) { validateEvent(evt); - let functionJson = utils.readAndParseJsonSync(path.join(serverless.config.projectPath, 'nodejscomponent', 'module1', 'function1', 's-function.json')); + let functionJson = utils.readAndParseJsonSync(path.join(serverless.config.projectPath, 'nodejscomponent', 'group1', 'function1', 's-function.json')); assert.equal(true, typeof functionJson.name != 'undefined'); assert.equal(true, functionJson.endpoints.length); done(); diff --git a/tests/tests/actions/FunctionDeploy.js b/tests/tests/actions/FunctionDeploy.js index 3c4c6586b..472c3336b 100644 --- a/tests/tests/actions/FunctionDeploy.js +++ b/tests/tests/actions/FunctionDeploy.js @@ -108,7 +108,7 @@ describe('Test Action: Function Deploy', function() { stage: config.stage, region: config.region, paths: [ - 'nodejscomponent/module1/function1' + 'nodejscomponent/group1/function1' ] }; @@ -132,7 +132,7 @@ describe('Test Action: Function Deploy', function() { // stage: config.stage, // region: config.region, // paths: [ - // 'nodejscomponent/module1/function1' + // 'nodejscomponent/group1/function1' // ] // }; // @@ -167,7 +167,7 @@ describe('Test Action: Function Deploy', function() { // stage: config.stage, // region: config.region, // paths: [ - // 'nodejscomponent/module1/function1' + // 'nodejscomponent/group1/function1' // ] // }; // diff --git a/tests/tests/actions/FunctionRun.js b/tests/tests/actions/FunctionRun.js index e7f151acf..0b58d3009 100644 --- a/tests/tests/actions/FunctionRun.js +++ b/tests/tests/actions/FunctionRun.js @@ -58,7 +58,7 @@ describe('Test Action: Function Run', function() { this.timeout(0); let options = { - path: 'nodejscomponent/module1/function1' + path: 'nodejscomponent/group1/function1' }; serverless.actions.functionRun(options) diff --git a/tests/tests/classes/ServerlessEndpointTest.js b/tests/tests/classes/ServerlessEndpointTest.js index 13492b6c9..79739cd08 100644 --- a/tests/tests/classes/ServerlessEndpointTest.js +++ b/tests/tests/classes/ServerlessEndpointTest.js @@ -35,9 +35,9 @@ describe('Test Serverless Endpoint Class', function() { // Instantiate Class instance = new serverless.classes.Endpoint(serverless, { component: 'nodejscomponent', - module: 'module1', + module: 'group1', function: 'function1', - endpointPath: 'module1/function1', + endpointPath: 'group1/function1', endpointMethod: 'GET' }); @@ -95,7 +95,7 @@ describe('Test Serverless Endpoint Class', function() { it('Create new and save', function(done) { let endpoint = new serverless.classes.Endpoint(serverless, { component: 'nodejscomponent', - module: 'module1', + module: 'group1', function: 'function1', endpointPath: 'test', endpointMethod: 'GET' diff --git a/tests/tests/classes/ServerlessFunctionTest.js b/tests/tests/classes/ServerlessFunctionTest.js index 2e7c578d7..f0ce3862e 100644 --- a/tests/tests/classes/ServerlessFunctionTest.js +++ b/tests/tests/classes/ServerlessFunctionTest.js @@ -35,7 +35,7 @@ describe('Test Serverless Function Class', function() { // Instantiate Class instance = new serverless.classes.Function(serverless, { component: 'nodejscomponent', - module: 'module1', + module: 'group1', function: 'function1' }); @@ -94,7 +94,7 @@ describe('Test Serverless Function Class', function() { it('Create new and save', function(done) { let func = new serverless.classes.Function(serverless, { component: 'nodejscomponent', - module: 'module1', + module: 'group1', function: 'function4' }); diff --git a/tests/tests/classes/ServerlessModuleTest.js b/tests/tests/classes/ServerlessModuleTest.js index 6bd1aa508..ac797604f 100644 --- a/tests/tests/classes/ServerlessModuleTest.js +++ b/tests/tests/classes/ServerlessModuleTest.js @@ -36,7 +36,7 @@ describe('Test Serverless Module Class', function() { // Instantiate Class instance = new serverless.classes.Module(serverless, { component: 'nodejscomponent', - module: 'module1' + module: 'group1' }); done(); @@ -94,7 +94,7 @@ describe('Test Serverless Module Class', function() { it('Create new and save', function(done) { let module = new serverless.classes.Module(serverless, { component: 'nodejscomponent', - module: 'module1' + module: 'group1' }); module.save() diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index 4d4f757b2..a6944ddfc 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -142,30 +142,6 @@ describe('Test Serverless State Class', function() { done(); }); - it('Get modules w/o paths', function(done) { - let modules = instance.getModules(); - assert.equal(true, modules[0].name === 'module1'); - done(); - }); - - it('Get modules w paths', function(done) { - let modules = instance.getModules({ paths: ['nodejscomponent/module1'] }); - assert.equal(true, modules[0].name === 'module1'); - done(); - }); - - it('Get modules by component and module', function(done) { - let modules = instance.getModules({ component: 'nodejscomponent', module: 'module1' }); - assert.equal(true, modules[0].name === 'module1'); - done(); - }); - - it('Get modules by component', function(done) { - let modules = instance.getModules({ component: 'nodejscomponent' }); - assert.equal(true, modules.length === 1); - done(); - }); - it('Get functions w/o paths', function(done) { let functions = instance.getFunctions(); assert.equal(true, functions.length === 3); @@ -173,13 +149,13 @@ describe('Test Serverless State Class', function() { }); it('Get functions w paths', function(done) { - let functions = instance.getFunctions({ paths: ['nodejscomponent/module1/function1'] }); + let functions = instance.getFunctions({ paths: ['nodejscomponent/group1/function1'] }); assert.equal(true, functions.length === 1); done(); }); it('Get functions by component, module and function', function(done) { - let functions = instance.getFunctions({ component: 'nodejscomponent', module: 'module1', function: 'function1' }); + let functions = instance.getFunctions({ component: 'nodejscomponent', module: 'group1', function: 'function1' }); assert.equal(true, functions.length === 1); done(); }); @@ -191,19 +167,19 @@ describe('Test Serverless State Class', function() { }); it('Get endpoints w paths', function(done) { - let endpoints = instance.getEndpoints({ paths: ['nodejscomponent/module1/function1@module1/function1~GET'] }); + let endpoints = instance.getEndpoints({ paths: ['nodejscomponent/group1/function1@group1/function1~GET'] }); assert.equal(true, endpoints.length === 1); done(); }); it('Get endpoints by component, module, function, path and method', function(done) { - let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'module1', function: 'function3', endpointPath: 'module1/function3', endpointMethod: 'POST' }); + let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'group1', function: 'function3', endpointPath: 'group1/function3', endpointMethod: 'POST' }); assert.equal(true, endpoints.length === 1); done(); }); it('Get endpoints by component, module and function', function(done) { - let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'module1', function: 'function1' }); + let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'group1', function: 'function1' }); assert.equal(true, endpoints.length === 1); done(); }); From ed29fa175e16077825ae38a1545f09c5167bf33c Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Wed, 3 Feb 2016 14:25:58 -0800 Subject: [PATCH 06/29] Remove configs from classes and encourage use of sPath only --- lib/ServerlessComponent.js | 31 ++- lib/ServerlessEndpoint.js | 8 +- lib/ServerlessFunction.js | 199 ++++++++---------- .../nodejscomponent/function0/s-function.json | 4 +- .../group1/function1/s-function.json | 4 +- .../group1/function2/s-function.json | 4 +- .../group1/function3/s-function.json | 4 +- .../group1/group2/function4/s-function.json | 6 +- 8 files changed, 119 insertions(+), 141 deletions(-) diff --git a/lib/ServerlessComponent.js b/lib/ServerlessComponent.js index fe67ab42a..f7dd41cf4 100644 --- a/lib/ServerlessComponent.js +++ b/lib/ServerlessComponent.js @@ -20,7 +20,7 @@ class ServerlessComponent { constructor(Serverless, config) { // Validate required attributes - if (!config.component) throw new SError('Missing required config.component'); + if (!config.component || !config.sPath) throw new SError('config.sPath is required'); let _this = this; _this._S = Serverless; @@ -44,18 +44,18 @@ class ServerlessComponent { if (!config) return; + // Temp fix to support config.component + if (config.component && !config.sPath) config.sPath = config.component; + // Set sPath - if (config.component) { + if (config.sPath || config.component) { this._config.component = config.component; - this._config.sPath = SUtils.buildSPath({ - component: config.component - }); + this._config.sPath = config.sPath; } // Make full path if (this._S.config.projectPath && this._config.sPath) { - let parse = SUtils.parseSPath(this._config.sPath); - this._config.fullPath = path.join(this._S.config.projectPath, parse.component); + this._config.fullPath = path.join(this._S.config.projectPath, this._config.sPath); } } @@ -67,24 +67,21 @@ class ServerlessComponent { load() { - let _this = this, + let _this = this, reserved = ['node_modules', 'lib', '.DS_Store'], componentJson, componentContents; // Helper to instantiate functions - let loadFn = function(fnName, cPath) { - console.log("Loading Function...", fnName, cPath) + let loadFn = function(sPath) { + console.log("Loading Function...", sPath) let func = new _this._S.classes.Function(_this._S, { - component: _this._config.component, - cPath: cPath ? cPath : null, - function: fnName + sPath: sPath }); return func.load() .then(function(instance) { - componentJson.functions[cPath ? cPath + '/' + fnName : fnName]= instance; - return componentJson.functions[fnKey]; + componentJson.functions[sPath] = instance; }); }; @@ -114,7 +111,7 @@ class ServerlessComponent { // If s-function.json doesn't exist, look in 2 subfolders if (SUtils.fileExistsSync(path.join(fPath1, 's-function.json'))) { let fnJson = SUtils.readAndParseJsonSync(path.join(fPath1, 's-function.json')); - return loadFn(fnJson.name, null); + return loadFn(_this.sPath + '/' + fnJson.name); } else { // Loop through 1st level subfolders looking for more functions @@ -129,7 +126,7 @@ class ServerlessComponent { // If s-function.json doesn't exist, look in 2 subfolders if (SUtils.fileExistsSync(path.join(fPath2, 's-function.json'))) { let fnJson = SUtils.readAndParseJsonSync(path.join(fPath2, 's-function.json')); - return loadFn(fnJson.name, fnJson.name, sf2 + '/' + fnJson.name); + return loadFn(_this.sPath + '/' + sf + '/' + fnJson.name); } }); } diff --git a/lib/ServerlessEndpoint.js b/lib/ServerlessEndpoint.js index cb3b9fe5f..571e16510 100644 --- a/lib/ServerlessEndpoint.js +++ b/lib/ServerlessEndpoint.js @@ -69,9 +69,9 @@ class ServerlessEndpoint { this._config.endpointMethod = config.endpointMethod; this._config.type = config.type ? config.type : 'AWS'; this._config.sPath = SUtils.buildSPath({ - component: config.component, - cPath: config.cPath, - function: config.function, + component: config.component, + cPath: config.cPath, + function: config.function, endpointPath: config.endpointPath, endpointMethod: config.endpointMethod }); @@ -83,7 +83,7 @@ class ServerlessEndpoint { this._config.fullPath = path.join( this._S.config.projectPath, parse.component, - parse.cPath ? parse.cPath.split('/').join(path.sep) : null, + parse.cPath ? parse.cPath.split('/').join(path.sep) : '', parse.function ); } diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index 9e92c78b4..4be15588d 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -2,19 +2,17 @@ /** * Serverless Function Class - * - options.path format is: "component/cPath/functionFolder" - * - cPath is optional. It specifies component path. You can nest functions in any amounts of subfolders (these subfolders are the cPath) - * - But you must use '/' to divide subfolders. E.g., restApi/data/users/show - * - In the example, data/users is the component path. The 'cPath' + * - config.sPath is required + * - config.component, config.module, config.function will be DEPRECATED soon. Do not use! */ const SError = require('./ServerlessError'), - SUtils = require('./utils/index'), - BbPromise = require('bluebird'), - async = require('async'), - path = require('path'), - fs = require('fs'), - _ = require('lodash'); + SUtils = require('./utils/index'), + BbPromise = require('bluebird'), + async = require('async'), + path = require('path'), + fs = require('fs'), + _ = require('lodash'); class ServerlessFunction { @@ -25,7 +23,7 @@ class ServerlessFunction { constructor(Serverless, config) { // Validate required attributes - if (!config.component || !config.function) throw new SError('Missing required config.component or config.function'); + if ((!config.component || !config.module || !config.function) && !config.sPath) throw new SError('Missing required config.sPath'); console.log("ServerlessFunction Constructor", config); let _this = this; _this._S = Serverless; @@ -35,9 +33,9 @@ class ServerlessFunction { // Default properties _this.name = _this._config.function || 'function' + SUtils.generateShortId(6); _this.handler = path.posix.join( - _this._config.cPath ? _this._config.cPath.split('/').join(path.sep) : null, - _this._config.function, - 'handler.handler'); + _this._config.cPath ? _this._config.cPath.split('/').join(path.sep) : '', + _this._config.function, + 'handler.handler'); _this.timeout = 6; _this.memorySize = 1024; _this.custom = { @@ -46,11 +44,7 @@ class ServerlessFunction { }; _this.endpoints = []; _this.endpoints.push(new _this._S.classes.Endpoint(_this._S, { - component: _this._config.component, - cPath: _this._config.cPath, - function: _this._config.function, - endpointPath: _this._config.cPath ? _this._config.cPath + '/' + _this._config.function : _this._config.function, - endpointMethod: 'GET' + sPath: _this._config.sPath + '@' + _this._config.sPath + '~GET' })); _this.templates = {}; } @@ -95,30 +89,17 @@ class ServerlessFunction { if (!config) return; - // Check cPath separators - if (config.cPath.indexOf('\\') !== -1) throw new SError('Cannot use "\\" in config.cPath'); - // Set sPath - if (config.component || config.cPath || config.function) { - this._config.component = config.component; - this._config.function = config.function; - this._config.cPath = config.cPath ? config.cPath : null; - this._config.sPath = SUtils.buildSPath({ - component: config.component, - cPath: config.cPath, - function: config.function - }); + if (config.component || config.module || config.function) { + this._config.sPath = config.component + '/' + config.module + '/' + config.function; + } + if (config.sPath) { + this._config.sPath = config.sPath; } // Make full path if (this._S.config.projectPath && this._config.sPath) { - let parse = SUtils.parseSPath(this._config.sPath); - this._config.fullPath = path.join( - this._S.config.projectPath, - parse.component, - parse.cPath ? parse.cPath.split('/').join(path.sep) : null, - parse.function - ); + this._config.fullPath = path.join(this._S.config.projectPath, this._config.sPath); } } @@ -131,52 +112,52 @@ class ServerlessFunction { load() { let _this = this, - functionJson; + functionJson; return BbPromise.try(function() { - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Function could not be loaded because no project path has been set on Serverless instance'); + // Validate: Check project path is set + if (!_this._S.config.projectPath) throw new SError('Function could not be loaded because no project path has been set on Serverless instance'); - // Validate: Check function exists - if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-function.json'))) { - throw new SError('Function could not be loaded because it does not exist in your project: ' + _this._config.sPath); - } + // Validate: Check function exists + if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-function.json'))) { + throw new SError('Function could not be loaded because it does not exist in your project: ' + _this._config.sPath); + } - functionJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-function.json')); + functionJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-function.json')); - return functionJson.endpoints; - }) - .each(function(e, i) { + return functionJson.endpoints; + }) + .each(function(e, i) { - // Add Endpoint Class Instances - functionJson.endpoints[i] = new _this._S.classes.Endpoint(_this._S, { - component: _this._config.component, - cPath: _this._config.cPath, - function: functionJson.name, - endpointPath: e.path, - endpointMethod: e.method - }); - - return functionJson.endpoints[i].load() - .then(function(instance) { - functionJson.endpoints[i] = instance; - return functionJson.endpoints[i]; + // Add Endpoint Class Instances + functionJson.endpoints[i] = new _this._S.classes.Endpoint(_this._S, { + component: _this._config.component, + cPath: _this._config.cPath, + function: functionJson.name, + endpointPath: e.path, + endpointMethod: e.method }); - }) - .then(function() { - // Get templates - if (_this._config.fullPath && SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-templates.json'))) { - functionJson.templates = require(path.join(_this._config.fullPath, 's-templates.json')); - } - }) - .then(function() { + return functionJson.endpoints[i].load() + .then(function(instance) { + functionJson.endpoints[i] = instance; + return functionJson.endpoints[i]; + }); + }) + .then(function() { - // Merge - _.assign(_this, functionJson); - return _this; - }); + // Get templates + if (_this._config.fullPath && SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-templates.json'))) { + functionJson.templates = require(path.join(_this._config.fullPath, 's-templates.json')); + } + }) + .then(function() { + + // Merge + _.assign(_this, functionJson); + return _this; + }); } /** @@ -232,9 +213,9 @@ class ServerlessFunction { getTemplates() { return _.merge( - this.getProject().getTemplates(), - this.getComponent().getTemplates(), - _.cloneDeep(this.templates) + this.getProject().getTemplates(), + this.getComponent().getTemplates(), + _.cloneDeep(this.templates) ); } @@ -258,39 +239,39 @@ class ServerlessFunction { return _this._create(); } }) - .then(function() { + .then(function() { - // Save all nested endpoints - if (options && options.deep) { - return BbPromise.try(function () { - return _this.endpoints; - }) - .each(function(endpoint) { - return endpoint.save(); - }) - } - }) - .then(function() { + // Save all nested endpoints + if (options && options.deep) { + return BbPromise.try(function () { + return _this.endpoints; + }) + .each(function(endpoint) { + return endpoint.save(); + }) + } + }) + .then(function() { - // If templates, save templates - if (_this.templates && Object.keys(_this.templates).length) { - return SUtils.writeFile(path.join(_this._config.fullPath, 's-templates.json'), _this.templates); - } - }) - .then(function() { + // If templates, save templates + if (_this.templates && Object.keys(_this.templates).length) { + return SUtils.writeFile(path.join(_this._config.fullPath, 's-templates.json'), _this.templates); + } + }) + .then(function() { - let clone = _this.get(); + let clone = _this.get(); - // Strip properties - if (clone.templates) delete clone.templates; + // Strip properties + if (clone.templates) delete clone.templates; - // Write file - return SUtils.writeFile(path.join(_this._config.fullPath, 's-function.json'), - JSON.stringify(clone, null, 2)); - }) - .then(function() { - return _this; - }) + // Write file + return SUtils.writeFile(path.join(_this._config.fullPath, 's-function.json'), + JSON.stringify(clone, null, 2)); + }) + .then(function() { + return _this; + }) } /** @@ -308,16 +289,16 @@ class ServerlessFunction { // Runtime: nodejs writeDeferred.push( - fs.mkdirSync(_this._config.fullPath), - SUtils.writeFile(path.join(_this._config.fullPath, 'event.json'), '{}') + fs.mkdirSync(_this._config.fullPath), + SUtils.writeFile(path.join(_this._config.fullPath, 'event.json'), '{}') ); if (_this.getRuntime() === 'nodejs') { writeDeferred.push( - SUtils.writeFile(path.join(_this._config.fullPath, 'handler.js'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'handler.js'))) + SUtils.writeFile(path.join(_this._config.fullPath, 'handler.js'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'handler.js'))) ) } else if (_this.getRuntime() === 'python2.7') { writeDeferred.push( - SUtils.writeFile(path.join(_this._config.fullPath, 'handler.py'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', 'handler.py'))) + SUtils.writeFile(path.join(_this._config.fullPath, 'handler.py'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', 'handler.py'))) ) } return BbPromise.all(writeDeferred); diff --git a/tests/test-prj/nodejscomponent/function0/s-function.json b/tests/test-prj/nodejscomponent/function0/s-function.json index dce1e2274..45d738187 100644 --- a/tests/test-prj/nodejscomponent/function0/s-function.json +++ b/tests/test-prj/nodejscomponent/function0/s-function.json @@ -4,13 +4,13 @@ "envVars": [], "excludePatterns": [] }, - "handler": "module1/function0/handler.handler", + "handler": "function0/handler.handler", "timeout": 6, "memorySize": 1024, "events": [], "endpoints": [ { - "path": "module1/function0", + "path": "function0", "method": "GET", "authorizationType": "${endpointVariable}", "apiKeyRequired": false, diff --git a/tests/test-prj/nodejscomponent/group1/function1/s-function.json b/tests/test-prj/nodejscomponent/group1/function1/s-function.json index f2d997d3f..16f5cd128 100644 --- a/tests/test-prj/nodejscomponent/group1/function1/s-function.json +++ b/tests/test-prj/nodejscomponent/group1/function1/s-function.json @@ -4,13 +4,13 @@ "envVars": [], "excludePatterns": [] }, - "handler": "module1/function1/handler.handler", + "handler": "group1/function1/handler.handler", "timeout": 6, "memorySize": 1024, "events": [], "endpoints": [ { - "path": "module1/function1", + "path": "group1/function1", "method": "GET", "authorizationType": "${endpointVariable}", "apiKeyRequired": false, diff --git a/tests/test-prj/nodejscomponent/group1/function2/s-function.json b/tests/test-prj/nodejscomponent/group1/function2/s-function.json index 8760a9e97..513debc84 100644 --- a/tests/test-prj/nodejscomponent/group1/function2/s-function.json +++ b/tests/test-prj/nodejscomponent/group1/function2/s-function.json @@ -4,13 +4,13 @@ "envVars": [], "excludePatterns": [] }, - "handler": "module1/function2/handler.handler", + "handler": "group1/function2/handler.handler", "timeout": 6, "memorySize": 1024, "events": [], "endpoints": [ { - "path": "module1/function2", + "path": "group1/function2", "method": "GET", "authorizationType": "none", "apiKeyRequired": false, diff --git a/tests/test-prj/nodejscomponent/group1/function3/s-function.json b/tests/test-prj/nodejscomponent/group1/function3/s-function.json index d955a3e39..24fa4a506 100644 --- a/tests/test-prj/nodejscomponent/group1/function3/s-function.json +++ b/tests/test-prj/nodejscomponent/group1/function3/s-function.json @@ -4,13 +4,13 @@ "envVars": [], "excludePatterns": [] }, - "handler": "module1/function3/handler.handler", + "handler": "group1/function3/handler.handler", "timeout": 6, "memorySize": 1024, "events": [], "endpoints": [ { - "path": "module1/function3", + "path": "group1/function3", "method": "GET", "authorizationType": "none", "apiKeyRequired": false, diff --git a/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json b/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json index 226a62d6d..2c306553e 100644 --- a/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json +++ b/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json @@ -4,13 +4,13 @@ "envVars": [], "excludePatterns": [] }, - "handler": "module1/function4/handler.handler", + "handler": "group1/function4/handler.handler", "timeout": 6, "memorySize": 1024, "events": [], "endpoints": [ { - "path": "module1/function4", + "path": "group1/function4", "method": "GET", "authorizationType": "none", "apiKeyRequired": false, @@ -33,7 +33,7 @@ } }, { - "path": "module1/function4", + "path": "group1/function4", "method": "POST", "authorizationType": "none", "apiKeyRequired": false, From 741bfd15c91e521052702f279c3ca9ffebbb7ea5 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Wed, 3 Feb 2016 15:16:53 -0800 Subject: [PATCH 07/29] ServerlessState: updates to remove modules --- lib/ServerlessComponent.js | 36 ++++++++++++------- lib/ServerlessEndpoint.js | 40 ++++++++-------------- lib/ServerlessFunction.js | 24 ++++--------- lib/ServerlessProject.js | 4 +-- lib/ServerlessState.js | 3 +- tests/tests/classes/ServerlessStateTest.js | 24 ++++++------- 6 files changed, 60 insertions(+), 71 deletions(-) diff --git a/lib/ServerlessComponent.js b/lib/ServerlessComponent.js index f7dd41cf4..1e8069530 100644 --- a/lib/ServerlessComponent.js +++ b/lib/ServerlessComponent.js @@ -20,7 +20,7 @@ class ServerlessComponent { constructor(Serverless, config) { // Validate required attributes - if (!config.component || !config.sPath) throw new SError('config.sPath is required'); + if (!config.component && !config.sPath) throw new SError('config.sPath is required'); let _this = this; _this._S = Serverless; @@ -74,11 +74,9 @@ class ServerlessComponent { // Helper to instantiate functions let loadFn = function(sPath) { - console.log("Loading Function...", sPath) let func = new _this._S.classes.Function(_this._S, { sPath: sPath }); - return func.load() .then(function(instance) { componentJson.functions[sPath] = instance; @@ -101,22 +99,22 @@ class ServerlessComponent { componentContents = fs.readdirSync(_this._config.fullPath); return componentContents; }) - .each(function(sf, i) { + .each(function(sf) { let fPath1 = path.join(_this._config.fullPath, sf); // Skip reserved names and files if (reserved.indexOf(sf.trim()) !== -1 || !fs.lstatSync(fPath1).isDirectory()) return; - // If s-function.json doesn't exist, look in 2 subfolders + // If s-function.json doesn't exist, look in subfolders if (SUtils.fileExistsSync(path.join(fPath1, 's-function.json'))) { let fnJson = SUtils.readAndParseJsonSync(path.join(fPath1, 's-function.json')); - return loadFn(_this.sPath + '/' + fnJson.name); + return loadFn(_this._config.sPath + '/' + fnJson.name); } else { // Loop through 1st level subfolders looking for more functions - return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, componentContents[i]))) - .each(function(sf2, i) { + return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, sf))) + .each(function(sf2) { let fPath2 = path.join(fPath1, sf2); @@ -126,7 +124,23 @@ class ServerlessComponent { // If s-function.json doesn't exist, look in 2 subfolders if (SUtils.fileExistsSync(path.join(fPath2, 's-function.json'))) { let fnJson = SUtils.readAndParseJsonSync(path.join(fPath2, 's-function.json')); - return loadFn(_this.sPath + '/' + sf + '/' + fnJson.name); + return loadFn(_this._config.sPath + '/' + sf + '/' + fnJson.name); + } else { + + // Loop through 2nd level subfolders looking for more functions + return BbPromise.resolve(fs.readdirSync(path.join(_this._config.fullPath, sf, sf2))) + .each(function(sf3) { + + let fPath3 = path.join(fPath2, sf3); + // Skip reserved names and files + if (reserved.indexOf(sf3.trim()) !== -1 || !fs.lstatSync(fPath3).isDirectory()) return; + + // If s-function.json doesn't exist, look in 2 subfolders + if (SUtils.fileExistsSync(path.join(fPath3, 's-function.json'))) { + let fnJson = SUtils.readAndParseJsonSync(path.join(fPath3, 's-function.json')); + return loadFn(_this._config.sPath + '/' + sf + '/' + sf2 + '/' + fnJson.name); + } + }); } }); } @@ -163,9 +177,7 @@ class ServerlessComponent { } let instance = new _this._S.classes.Function(_this._S, { - component: _this._config.component, - function: data.functions[prop].name, - cPath: prop.indexOf('/') !== -1 ? prop : null + sPath: prop }); data.functions[prop] = instance.set(data.functions[prop]); } diff --git a/lib/ServerlessEndpoint.js b/lib/ServerlessEndpoint.js index 571e16510..d64407823 100644 --- a/lib/ServerlessEndpoint.js +++ b/lib/ServerlessEndpoint.js @@ -21,7 +21,7 @@ class ServerlessEndpoint { constructor(Serverless, config) { // Validate required attributes - if (!config.component || !config.function || !config.endpointPath || !config.endpointMethod) throw new SError('Missing required config.component, config.function, config.endpointMethod, config.endpointPath'); + if ((!config.component || !config.module || !config.function || !config.endpointPath || !config.endpointMethod) && !config.sPath) throw new SError('Missing required config.sPath'); // Private properties let _this = this; @@ -30,9 +30,9 @@ class ServerlessEndpoint { _this.updateConfig(config); // Default properties - _this.path = config.endpointPath; - _this.method = config.endpointMethod; - _this.type = _this._config.type; + _this.path = _this._config.sPath.split('@')[1].split('~')[0]; + _this.method = _this._config.sPath.split('~')[1].toUpperCase(); + _this.type = _this._config.type || 'AWS'; _this.authorizationType = 'none'; _this.apiKeyRequired = false; _this.requestParameters = {}; @@ -61,30 +61,18 @@ class ServerlessEndpoint { if (!config) return; // Set sPath - if (config.component || config.function || config.endpointPath || config.endpointMethod) { - this._config.component = config.component; - this._config.cPath = config.cPath ? config.cPath : null; - this._config.function = config.function; - this._config.endpointPath = config.endpointPath; - this._config.endpointMethod = config.endpointMethod; - this._config.type = config.type ? config.type : 'AWS'; - this._config.sPath = SUtils.buildSPath({ - component: config.component, - cPath: config.cPath, - function: config.function, - endpointPath: config.endpointPath, - endpointMethod: config.endpointMethod - }); + if (config.component && config.module && config.function && config.endpointPath && config.endpointMethod) { + this._config.sPath = config.component + '/' + config.module + '/' + config.function + '@' + config.endpointPath + '~' + config.endpointMethod; + } + if (config.sPath) { + this._config.sPath = config.sPath; } // Make full path if (this._S.config.projectPath && this._config.sPath) { - let parse = SUtils.parseSPath(this._config.sPath); this._config.fullPath = path.join( this._S.config.projectPath, - parse.component, - parse.cPath ? parse.cPath.split('/').join(path.sep) : '', - parse.function + this._config.sPath.split('@')[0].split('/').join(path.sep) ); } } @@ -112,8 +100,8 @@ class ServerlessEndpoint { let functionJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-function.json')); let endpoint = null; for (let i = 0; i < functionJson.endpoints.length; i++) { - if (functionJson.endpoints[i].path === _this._config.endpointPath && - functionJson.endpoints[i].method === _this._config.endpointMethod) { + if (functionJson.endpoints[i].path === _this.path && + functionJson.endpoints[i].method === _this.method) { endpoint = functionJson.endpoints[i]; } } @@ -211,8 +199,8 @@ class ServerlessEndpoint { let functionJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-function.json')); let endpoint = null; for (let i = 0; i < functionJson.endpoints.length; i++) { - if (functionJson.endpoints[i].path === _this._config.endpointPath && - functionJson.endpoints[i].method === _this._config.endpointMethod) { + if (functionJson.endpoints[i].path === _this.path && + functionJson.endpoints[i].method === _this.method) { endpoint = functionJson.endpoints[i]; functionJson.endpoints[i] = _this.get(); } diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index 4be15588d..2e35bbf2b 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -24,7 +24,7 @@ class ServerlessFunction { // Validate required attributes if ((!config.component || !config.module || !config.function) && !config.sPath) throw new SError('Missing required config.sPath'); - console.log("ServerlessFunction Constructor", config); + let _this = this; _this._S = Serverless; _this._config = {}; @@ -33,8 +33,7 @@ class ServerlessFunction { // Default properties _this.name = _this._config.function || 'function' + SUtils.generateShortId(6); _this.handler = path.posix.join( - _this._config.cPath ? _this._config.cPath.split('/').join(path.sep) : '', - _this._config.function, + _this._config.sPath.split('/').splice(1, _this._config.sPath.split('/').length).join('/'), 'handler.handler'); _this.timeout = 6; _this.memorySize = 1024; @@ -65,12 +64,8 @@ class ServerlessFunction { throw new SError('You cannot pass subclasses into the set method, only object literals'); } - let instance = new _this._S.classes.Endpoint(_this._S, { - component: _this._config.component, - cPath: _this._config.cPath, - function: _this.name, - endpointPath: _this._config.cPath ? _this._config.cPath + '/' + _this._config.function : _this._config.function, - endpointMethod: data.endpoints[i].method + let instance = new _this._S.classes.Endpoint(_this._S, { + sPath: _this._config.sPath + '@' + data.endpoints[i].path + '~' + data.endpoints[i].method }); data.endpoints[i] = instance.set(data.endpoints[i]); } @@ -82,7 +77,7 @@ class ServerlessFunction { /** * Update Config - * - Takes config.component, config.cPath, config.function + * - Takes config.sPath */ updateConfig(config) { @@ -99,7 +94,7 @@ class ServerlessFunction { // Make full path if (this._S.config.projectPath && this._config.sPath) { - this._config.fullPath = path.join(this._S.config.projectPath, this._config.sPath); + this._config.fullPath = path.join(this._S.config.projectPath, this._config.sPath.split('/').join(path.sep)); } } @@ -125,18 +120,13 @@ class ServerlessFunction { } functionJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-function.json')); - return functionJson.endpoints; }) .each(function(e, i) { // Add Endpoint Class Instances functionJson.endpoints[i] = new _this._S.classes.Endpoint(_this._S, { - component: _this._config.component, - cPath: _this._config.cPath, - function: functionJson.name, - endpointPath: e.path, - endpointMethod: e.method + sPath: _this._config.sPath + '@' + e.path + '~' + e.method }); return functionJson.endpoints[i].load() diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index cc9dc189c..0137be2b6 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -141,7 +141,7 @@ class ServerlessProject { if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, c, 's-component.json'))) { let component = new _this._S.classes.Component(_this._S, { - component: c + sPath: c }); return component.load() @@ -176,7 +176,7 @@ class ServerlessProject { } let instance = new _this._S.classes.Component(_this._S, { - component: data.components[prop].name + sPath: data.components[prop].name }); data.components[prop] = instance.set(data.components[prop]); } diff --git a/lib/ServerlessState.js b/lib/ServerlessState.js index ddc4e225f..d8f6dd2a6 100644 --- a/lib/ServerlessState.js +++ b/lib/ServerlessState.js @@ -203,7 +203,7 @@ class ServerlessState { let component = allComponents[i]; if (options.component) { - if (component._config.component == options.component) { + if (component._config.sPath.split('/')[0] == options.component) { foundComponents.push(options.returnPaths ? component._config.sPath : component); } continue; @@ -233,7 +233,6 @@ class ServerlessState { // Get all for (let i = 0; i < Object.keys(_this.project.components).length; i++) { let component = _this.project.components[Object.keys(_this.project.components)[i]]; - console.log("here", component.functions) for (let j = 0; j < Object.keys(component.functions).length; j++) { allFunctions.push(component.functions[Object.keys(component.functions)[j]]); } diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index a6944ddfc..de1a1ace1 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -62,12 +62,12 @@ describe('Test Serverless State Class', function() { done(); }); - it('Get populated instance data', function(done) { - let data = instance.getPopulated({ stage: config.stage, region: config.region }); - assert.equal(true, JSON.stringify(data).indexOf('$${') == -1); - assert.equal(true, JSON.stringify(data).indexOf('${') == -1); - done(); - }); + //it('Get populated instance data', function(done) { + // let data = instance.getPopulated({ stage: config.stage, region: config.region }); + // assert.equal(true, JSON.stringify(data).indexOf('$${') == -1); + // assert.equal(true, JSON.stringify(data).indexOf('${') == -1); + // done(); + //}); it('Set instance data', function(done) { let clone = instance.get(); @@ -105,12 +105,12 @@ describe('Test Serverless State Class', function() { done(); }); - it('Get resources (populated)', function(done) { - let resources = instance.getResources({ populate: true, stage: config.stage, region: config.region }); - assert.equal(true, JSON.stringify(resources).indexOf('$${') == -1); - assert.equal(true, JSON.stringify(resources).indexOf('${') == -1); - done(); - }); + //it('Get resources (populated)', function(done) { + // let resources = instance.getResources({ populate: true, stage: config.stage, region: config.region }); + // assert.equal(true, JSON.stringify(resources).indexOf('$${') == -1); + // assert.equal(true, JSON.stringify(resources).indexOf('${') == -1); + // done(); + //}); it('Get stages', function(done) { let stages = instance.getStages(); From da479d231bc1598f7e1d17d29958d40dddeecb1f Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Wed, 3 Feb 2016 19:00:49 -0800 Subject: [PATCH 08/29] Module remove: fix serverlessstate tests --- lib/ServerlessState.js | 162 +++++++----------- .../group1/function3/s-function.json | 2 +- tests/tests/classes/ServerlessStateTest.js | 26 ++- 3 files changed, 86 insertions(+), 104 deletions(-) diff --git a/lib/ServerlessState.js b/lib/ServerlessState.js index d8f6dd2a6..446d143cd 100644 --- a/lib/ServerlessState.js +++ b/lib/ServerlessState.js @@ -5,10 +5,10 @@ */ const SError = require('./ServerlessError'), - SUtils = require('./utils/index'), - _ = require('lodash'), - path = require('path'), - fs = require('fs'); + SUtils = require('./utils/index'), + _ = require('lodash'), + path = require('path'), + fs = require('fs'); class ServerlessState { @@ -35,9 +35,9 @@ class ServerlessState { let _this = this; return _this.project.load() - .then(function() { - return _this.meta.load(); - }); + .then(function() { + return _this.meta.load(); + }); } /** @@ -50,9 +50,9 @@ class ServerlessState { let _this = this; return _this.project.save({ deep: true }) - .then(function() { - return _this.meta.save({ deep: true }); - }); + .then(function() { + return _this.meta.save({ deep: true }); + }); } /** @@ -186,8 +186,8 @@ class ServerlessState { getComponents(options) { let _this = this, - allComponents = [], - foundComponents = []; + allComponents = [], + foundComponents = []; // Get all for (let i = 0; i < Object.keys(_this.project.components).length; i++) { @@ -220,15 +220,15 @@ class ServerlessState { /** * Get Functions * - Returns an array of this state's function instances - * - Options: paths, component, function + * - Options: paths * - options.paths is an array of Serverless paths like this: ['component/functionOne', 'component/subfolder1/subfolder2/functionTwo'] */ getFunctions(options) { let _this = this, - allFunctions = [], - foundFunctions = []; + allFunctions = [], + foundFunctions = []; // Get all for (let i = 0; i < Object.keys(_this.project.components).length; i++) { @@ -242,31 +242,23 @@ class ServerlessState { if (!options) return allFunctions; if (options && Object.keys(options).length === 1 && options.returnPaths === true) return allFunctions.map(function(d) { return d._config.sPath }); + // If component, module or functions convert to sPath + // TODO: Eventually remove and support sPath only + if (options.component) { + options.paths = [options.component]; + if (options.module) options.paths[0] = options.paths[0] + '/' + options.module; + if (options.function) options.paths[0] = options.paths[0] + '/' + options.function; + } + // If options specified, loop through and find the ones specified for (let i = 0; i < allFunctions.length; i++) { - let func = allFunctions[i]; - if (options.component && options.cPath && options.function) { - if (func._config.component == options.component && func._config.module == options.module && func.name == options.function) { - foundFunctions.push(options.returnPaths ? func._config.sPath : func); + let func = allFunctions[i]; + for (let j = 0; j < options.paths.length; j++) { + if (func._config.sPath.indexOf(options.paths[j]) !== -1) { + foundFunctions.push(func); + break; } - continue; - } - if (options.component && options.module) { - if (func._config.component == options.component && func._config.module == options.module) { - foundFunctions.push(options.returnPaths ? func._config.sPath : func); - } - continue; - } - if (options.component) { - if (func._config.component == options.component) { - foundFunctions.push(options.returnPaths ? func._config.sPath : func); - } - continue; - } - if (options.paths && options.paths.indexOf(func._config.sPath) !== -1) { - foundFunctions.push(func); - continue; } } @@ -276,28 +268,24 @@ class ServerlessState { /** * Get Endpoints * - Returns an array of this state's function instances - * - Options: paths, component, module, function, endpointPath, endpointMethod - * - options.paths is an array of Serverless paths like this: ['component/moduleOne/functionOne@moduleOne/functionOne~GET'] + * - Options: paths, endpointPath, endpointMethod + * - options.paths is an array of Serverless paths like this: ['component/groupOne/functionOne@moduleOne/functionOne~GET'] */ getEndpoints(options) { - let _this = this, - allEndpoints = [], - foundEndpoints = []; + let _this = this, + allEndpoints = [], + foundEndpoints = []; // Get all functions for (let i = 0; i < Object.keys(_this.project.components).length; i++) { let component = _this.project.components[Object.keys(_this.project.components)[i]]; - if (!component.modules) continue; - for (let j = 0; j < Object.keys(component.modules).length; j++) { - let module = component.modules[Object.keys(component.modules)[j]]; - if (!module.functions) continue; - for (let k = 0; k < Object.keys(module.functions).length; k++) { - let func = module.functions[Object.keys(module.functions)[k]]; - for (let l = 0; l < func.endpoints.length; l++) { - allEndpoints.push(func.endpoints[l]); - } + if (!component.functions) continue; + for (let k = 0; k < Object.keys(component.functions).length; k++) { + let func = component.functions[Object.keys(component.functions)[k]]; + for (let l = 0; l < func.endpoints.length; l++) { + allEndpoints.push(func.endpoints[l]); } } } @@ -306,61 +294,39 @@ class ServerlessState { if (!options) return allEndpoints; if (options && Object.keys(options).length === 1 && options.returnPaths === true) return allEndpoints.map(function(d) { return d._config.sPath }); + // If component, module or functions convert to sPath + // TODO: Eventually remove and support sPath only + if (options.component) { + options.paths = [options.component]; + if (options.module) options.paths[0] = options.paths[0] + '/' + options.module; + if (options.function) options.paths[0] = options.paths[0] + '/' + options.function; + if (options.endpointPath) options.paths[0] = options.paths[0] + '@' + options.endpointPath; + if (options.endpointMethod) options.paths[0] = options.paths[0] + '~' + options.endpointMethod; + console.log("HERE") + } + // If options specified, loop through functions and find the ones specified for (let i = 0; i < allEndpoints.length; i++) { let endpoint = allEndpoints[i]; - if (options.component && options.module && options.function && options.endpointPath && options.endpointMethod) { - if (endpoint._config.component == options.component && endpoint._config.module == options.module && endpoint._config.function == options.function && endpoint.path == options.endpointPath && endpoint.method == options.endpointMethod) { - foundEndpoints.push(options.returnPaths ? endpoint._config.sPath : endpoint); + for (let j = 0; j < options.paths.length; j++) { + console.log(endpoint._config.sPath, options.paths[j], endpoint._config.sPath.indexOf(options.paths[j])); + if (endpoint._config.sPath.indexOf(options.paths[j]) !== -1) { + foundEndpoints.push(endpoint); + break; } - continue; } - if (options.component && options.module && options.function && options.endpointPath && !options.endpointMethod) { - if (endpoint._config.component == options.component && endpoint._config.module == options.module && endpoint._config.function == options.function && endpoint.path == options.endpointPath) { - foundEndpoints.push(options.returnPaths ? endpoint._config.sPath : endpoint); - } - continue; + } + + // Filter if endpointPath or endpointMethod is specified + if (options.endpointPath) { + for (let i = 0; i < foundEndpoints.length; i++) { + if (foundEndpoints[i].path !== options.endpointPath) foundEndpoints.splice(i, 1); } - if (options.component && options.module && options.function && options.endpointMethod && !options.endpointPath) { - if (endpoint._config.component == options.component && endpoint._config.module == options.module && endpoint._config.function == options.function && endpoint.method == options.endpointMethod) { - foundEndpoints.push(options.returnPaths ? endpoint._config.sPath : endpoint); - } - continue; - } - if (options.component && options.module && options.function && !options.endpointPath && !options.endpointMethod) { - if (endpoint._config.component == options.component && endpoint._config.module == options.module && endpoint._config.function == options.function) { - foundEndpoints.push(options.returnPaths ? endpoint._config.sPath : endpoint); - } - continue; - } - if (options.component && options.module && options.endpointMethod && !options.function && !options.endpointPath) { - if (endpoint._config.component == options.component && endpoint._config.module == options.module && endpoint.method == options.endpointMethod) { - foundEndpoints.push(options.returnPaths ? endpoint._config.sPath : endpoint); - } - continue; - } - if (options.component && options.module && !options.function && !options.endpointPath && !options.endpointMethod) { - if (endpoint._config.component == options.component && endpoint._config.module == options.module) { - foundEndpoints.push(options.returnPaths ? endpoint._config.sPath : endpoint); - } - continue; - } - if (options.component && options.endpointMethod && !options.module && !options.function && !options.endpointPath) { - if (endpoint._config.component == options.component && endpoint.method == options.endpointMethod) { - foundEndpoints.push(options.returnPaths ? endpoint._config.sPath : endpoint); - } - continue; - } - if (options.component && !options.module && !options.function && !options.endpointPath && !options.endpointMethod) { - if (endpoint._config.component == options.component) { - foundEndpoints.push(options.returnPaths ? endpoint._config.sPath : endpoint); - } - continue; - } - if (options.paths && options.paths.indexOf(endpoint._config.sPath) !== -1) { - foundEndpoints.push(endpoint); - continue; + } + if (options.endpointMethod) { + for (let i = 0; i < foundEndpoints.length; i++) { + if (foundEndpoints[i].method !== options.endpointMethods) foundEndpoints.splice(i, 1); } } diff --git a/tests/test-prj/nodejscomponent/group1/function3/s-function.json b/tests/test-prj/nodejscomponent/group1/function3/s-function.json index 24fa4a506..5ef353f86 100644 --- a/tests/test-prj/nodejscomponent/group1/function3/s-function.json +++ b/tests/test-prj/nodejscomponent/group1/function3/s-function.json @@ -33,7 +33,7 @@ } }, { - "path": "module1/function3", + "path": "group1/function3", "method": "POST", "authorizationType": "none", "apiKeyRequired": false, diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index de1a1ace1..74f347d08 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -144,7 +144,7 @@ describe('Test Serverless State Class', function() { it('Get functions w/o paths', function(done) { let functions = instance.getFunctions(); - assert.equal(true, functions.length === 3); + assert.equal(true, functions.length === 5); done(); }); @@ -154,15 +154,24 @@ describe('Test Serverless State Class', function() { done(); }); + it('Get functions w/ partial paths', function(done) { + let functions = instance.getFunctions({ paths: ['nodejscomponent/group1/group2'] }); + assert.equal(true, functions.length === 1); + done(); + }); + it('Get functions by component, module and function', function(done) { - let functions = instance.getFunctions({ component: 'nodejscomponent', module: 'group1', function: 'function1' }); + let functions = instance.getFunctions({ + component: 'nodejscomponent', + module: 'group1', + function: 'function1' }); assert.equal(true, functions.length === 1); done(); }); it('Get endpoints w/o paths', function(done) { let endpoints = instance.getEndpoints(); - assert.equal(true, endpoints.length === 4); + assert.equal(true, endpoints.length === 7); done(); }); @@ -172,8 +181,15 @@ describe('Test Serverless State Class', function() { done(); }); + it('Get endpoints w/ partial paths', function(done) { + let endpoints = instance.getEndpoints({ paths: ['nodejscomponent/group1/group2'] }); + assert.equal(true, endpoints.length === 2); + done(); + }); + it('Get endpoints by component, module, function, path and method', function(done) { let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'group1', function: 'function3', endpointPath: 'group1/function3', endpointMethod: 'POST' }); + console.log(endpoints) assert.equal(true, endpoints.length === 1); done(); }); @@ -184,8 +200,8 @@ describe('Test Serverless State Class', function() { done(); }); - it('Get endpoints by component and method', function(done) { - let endpoints = instance.getEndpoints({ component: 'nodejscomponent', endpointMethod: 'GET' }); + it('Get endpoints by method', function(done) { + let endpoints = instance.getEndpoints({ paths: ['nodejscomponent/group1'], endpointMethod: 'GET' }); assert.equal(true, endpoints.length === 3); done(); }); From 953c1d313ef13558d57c425dee081a80e251cba9 Mon Sep 17 00:00:00 2001 From: ac360 Date: Wed, 3 Feb 2016 19:38:16 -0800 Subject: [PATCH 09/29] ServerlessState: fix tests to work w/o modules --- lib/ServerlessFunction.js | 2 +- lib/ServerlessState.js | 8 +++----- lib/utils/index.js | 3 +-- tests/tests/classes/ServerlessStateTest.js | 18 ++++++------------ 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index 2e35bbf2b..c65d16e53 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -320,7 +320,7 @@ class ServerlessFunction { getComponent() { let components = this._S.state.getComponents({ - component: this._config.component + paths: this._config.sPath.split('/')[0] }); if (components.length === 1) { diff --git a/lib/ServerlessState.js b/lib/ServerlessState.js index 446d143cd..01ce1b646 100644 --- a/lib/ServerlessState.js +++ b/lib/ServerlessState.js @@ -78,9 +78,9 @@ class ServerlessState { } else if (data instanceof this._S.classes.Component) { this.project.components[data.name] = data; } else if (data instanceof this._S.classes.Function) { - this.project.components[data._config.component].functions[data.name] = data; + this.project.components[data._config.sPath.split('/')[0]].functions[data._config.sPath] = data; } else if (data instanceof this._S.classes.Endpoint) { - let func = this.project.components[data._config.component].functions[data._config.function]; + let func = this.project.components[data._config.sPath.split('/')[0]].functions[data._config.sPath.split('@')[0]]; let added = false; for (let i = 0; i < func.endpoints.length; i++) { if (func.endpoints[i].path === data.path && func.endpoints[i].method === data.method) { @@ -302,7 +302,6 @@ class ServerlessState { if (options.function) options.paths[0] = options.paths[0] + '/' + options.function; if (options.endpointPath) options.paths[0] = options.paths[0] + '@' + options.endpointPath; if (options.endpointMethod) options.paths[0] = options.paths[0] + '~' + options.endpointMethod; - console.log("HERE") } // If options specified, loop through functions and find the ones specified @@ -310,7 +309,6 @@ class ServerlessState { let endpoint = allEndpoints[i]; for (let j = 0; j < options.paths.length; j++) { - console.log(endpoint._config.sPath, options.paths[j], endpoint._config.sPath.indexOf(options.paths[j])); if (endpoint._config.sPath.indexOf(options.paths[j]) !== -1) { foundEndpoints.push(endpoint); break; @@ -326,7 +324,7 @@ class ServerlessState { } if (options.endpointMethod) { for (let i = 0; i < foundEndpoints.length; i++) { - if (foundEndpoints[i].method !== options.endpointMethods) foundEndpoints.splice(i, 1); + if (foundEndpoints[i].method !== options.endpointMethod) foundEndpoints.splice(i, 1); } } diff --git a/lib/utils/index.js b/lib/utils/index.js index 9cd816ac9..3999646b8 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -71,7 +71,6 @@ exports.buildSPath = function(data) { */ exports.parseSPath = function(sPath) { - console.log("parseSPath", sPath) let pArray = sPath.split('/'); if (pArray.length < 1) { return { component: pArray[0] } @@ -245,7 +244,7 @@ exports.getEndpoints = function(baseDir, endpointPaths) { return BbPromise.try(function () { // Sanitize baseDir - if ((baseDir).indexOf('/back') == -1) baseDir = path.join(baseDir, 'back'); + if ((baseDir).indexOf('/back') == -1) baseDir = path.join(baseDir, 'back'); if ((baseDir).indexOf('/modules') == -1) baseDir = path.join(baseDir, 'modules'); // If endpointPaths, validate and return them diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index 74f347d08..8b06b58eb 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -189,7 +189,6 @@ describe('Test Serverless State Class', function() { it('Get endpoints by component, module, function, path and method', function(done) { let endpoints = instance.getEndpoints({ component: 'nodejscomponent', module: 'group1', function: 'function3', endpointPath: 'group1/function3', endpointMethod: 'POST' }); - console.log(endpoints) assert.equal(true, endpoints.length === 1); done(); }); @@ -202,7 +201,7 @@ describe('Test Serverless State Class', function() { it('Get endpoints by method', function(done) { let endpoints = instance.getEndpoints({ paths: ['nodejscomponent/group1'], endpointMethod: 'GET' }); - assert.equal(true, endpoints.length === 3); + assert.equal(true, endpoints.length === 4); done(); }); @@ -224,27 +223,22 @@ describe('Test Serverless State Class', function() { project.name = 'testProject'; instance.setAsset(project); - let component = new instance._S.classes.Component(instance._S, { component: 'testComponent' }); + let component = new instance._S.classes.Component(instance._S, { sPath: 'testComponent' }); component.name = 'testComponent'; instance.setAsset(component); - let module = new instance._S.classes.Module(instance._S, { component: component.name, module: 'testModule' }); - module.name = 'testModule'; - instance.setAsset(module); - - let func = new instance._S.classes.Function(instance._S, { component: component.name, module: module.name, function: 'testFunction' }); + let func = new instance._S.classes.Function(instance._S, { sPath: 'testComponent/group1/testFunction' }); func.name = 'testFunction'; instance.setAsset(func); - let endpoint = new instance._S.classes.Endpoint(instance._S, { component: component.name, module: module.name, function: func.name, endpointPath: 'test/function', endpointMethod: 'GET' }); + let endpoint = new instance._S.classes.Endpoint(instance._S, { sPath: 'testComponent/group1/testFunction@group1/testFunction~GET' }); endpoint.path = 'test/endpoint'; instance.setAsset(endpoint); assert.equal(true, instance.project.name === 'testProject'); assert.equal(true, typeof instance.project.components[component.name] !== 'undefined'); - assert.equal(true, typeof instance.project.components[component.name].modules[module.name] !== 'undefined'); - assert.equal(true, typeof instance.project.components[component.name].modules[module.name].functions[func.name] !== 'undefined'); - assert.equal(true, instance.project.components[component.name].modules[module.name].functions[func.name].endpoints.length > 0); + assert.equal(true, typeof instance.project.components[component.name].functions[func._config.sPath] !== 'undefined'); + assert.equal(true, instance.project.components[component.name].functions[func._config.sPath].endpoints.length > 0); done(); }); From ad5e929941081b0e6d4eff5942f069cc2c319422 Mon Sep 17 00:00:00 2001 From: ac360 Date: Wed, 3 Feb 2016 20:31:47 -0800 Subject: [PATCH 10/29] Classes: fix all tests --- lib/ServerlessEndpoint.js | 9 +- lib/ServerlessFunction.js | 179 ++++++++++-------- lib/utils/index.js | 2 - tests/all.js | 8 +- .../nodejscomponent/function0/s-function.json | 4 +- .../tests/classes/ServerlessComponentTest.js | 4 +- tests/tests/classes/ServerlessEndpointTest.js | 8 +- tests/tests/classes/ServerlessFunctionTest.js | 14 +- tests/tests/classes/ServerlessProjectTest.js | 12 +- tests/tests/classes/ServerlessStateTest.js | 14 +- 10 files changed, 136 insertions(+), 118 deletions(-) diff --git a/lib/ServerlessEndpoint.js b/lib/ServerlessEndpoint.js index d64407823..1efea82d8 100644 --- a/lib/ServerlessEndpoint.js +++ b/lib/ServerlessEndpoint.js @@ -170,8 +170,7 @@ class ServerlessEndpoint { return _.merge( this.getProject().getTemplates(), this.getComponent().getTemplates(), - this.getFunction().getTemplates(), - _.cloneDeep(this.templates) + this.getFunction().getTemplates() ); } @@ -231,7 +230,7 @@ class ServerlessEndpoint { getComponent() { let components = this._S.state.getComponents({ - component: this._config.component + paths: [this._config.sPath.split('/')[0]] }); if (components.length === 1) { @@ -249,9 +248,7 @@ class ServerlessEndpoint { getFunction() { let functions = this._S.state.getFunctions({ - component: this._config.component, - cPath: this._config.cPath, - function: this._config.function + paths: [this._config.sPath.split('@')[0]] }); if (functions.length === 1) { diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index c65d16e53..52677fe2b 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -7,12 +7,12 @@ */ const SError = require('./ServerlessError'), - SUtils = require('./utils/index'), - BbPromise = require('bluebird'), - async = require('async'), - path = require('path'), - fs = require('fs'), - _ = require('lodash'); + SUtils = require('./utils/index'), + BbPromise = require('bluebird'), + async = require('async'), + path = require('path'), + fs = require('fs'), + _ = require('lodash'); class ServerlessFunction { @@ -33,8 +33,8 @@ class ServerlessFunction { // Default properties _this.name = _this._config.function || 'function' + SUtils.generateShortId(6); _this.handler = path.posix.join( - _this._config.sPath.split('/').splice(1, _this._config.sPath.split('/').length).join('/'), - 'handler.handler'); + _this._config.sPath.split('/').splice(1, _this._config.sPath.split('/').length).join('/'), + 'handler.handler'); _this.timeout = 6; _this.memorySize = 1024; _this.custom = { @@ -107,47 +107,72 @@ class ServerlessFunction { load() { let _this = this, - functionJson; + functionJson; return BbPromise.try(function() { - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Function could not be loaded because no project path has been set on Serverless instance'); + // Validate: Check project path is set + if (!_this._S.config.projectPath) throw new SError('Function could not be loaded because no project path has been set on Serverless instance'); - // Validate: Check function exists - if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-function.json'))) { - throw new SError('Function could not be loaded because it does not exist in your project: ' + _this._config.sPath); - } + // Validate: Check function exists + if (!SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-function.json'))) { + throw new SError('Function could not be loaded because it does not exist in your project: ' + _this._config.sPath); + } - functionJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-function.json')); - return functionJson.endpoints; - }) - .each(function(e, i) { + functionJson = SUtils.readAndParseJsonSync(path.join(_this._config.fullPath, 's-function.json')); + return functionJson.endpoints; + }) + .each(function(e, i) { - // Add Endpoint Class Instances - functionJson.endpoints[i] = new _this._S.classes.Endpoint(_this._S, { - sPath: _this._config.sPath + '@' + e.path + '~' + e.method - }); - - return functionJson.endpoints[i].load() - .then(function(instance) { - functionJson.endpoints[i] = instance; - return functionJson.endpoints[i]; - }); - }) - .then(function() { - - // Get templates - if (_this._config.fullPath && SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-templates.json'))) { - functionJson.templates = require(path.join(_this._config.fullPath, 's-templates.json')); - } - }) - .then(function() { - - // Merge - _.assign(_this, functionJson); - return _this; + // Add Endpoint Class Instances + functionJson.endpoints[i] = new _this._S.classes.Endpoint(_this._S, { + sPath: _this._config.sPath + '@' + e.path + '~' + e.method }); + + return functionJson.endpoints[i].load() + .then(function(instance) { + functionJson.endpoints[i] = instance; + return functionJson.endpoints[i]; + }); + }) + .then(function() { + + // Get templates + if (_this._config.fullPath && SUtils.fileExistsSync(path.join(_this._config.fullPath, 's-templates.json'))) { + functionJson.templates = require(path.join(_this._config.fullPath, 's-templates.json')); + } + + // Get templates in parent folders and merge into this.templates + let parentOne = path.join(_this._config.fullPath, '..'), + parentTwo = path.join(_this._config.fullPath, '..', '..'), + parentTemplateOne = {}, + parentTemplateTwo = {}; + + if (!SUtils.fileExistsSync(path.join(parentOne, 's-component.json'))) { + if (SUtils.fileExistsSync(path.join(parentOne, 's-templates.json'))) { + parentTemplateOne = SUtils.readAndParseJsonSync(path.join(parentOne, 's-templates.json')); + } + + if (!SUtils.fileExistsSync(path.join(parentTwo, 's-component.json'))) { + if (SUtils.fileExistsSync(path.join(parentTwo, 's-templates.json'))) { + parentTemplateTwo = SUtils.readAndParseJsonSync(path.join(parentTwo, 's-templates.json')); + } + } + } + + // Merge + functionJson.templates = _.merge( + parentTemplateTwo, + parentTemplateOne, + functionJson.templates + ); + }) + .then(function() { + + // Merge + _.assign(_this, functionJson); + return _this; + }); } /** @@ -203,9 +228,9 @@ class ServerlessFunction { getTemplates() { return _.merge( - this.getProject().getTemplates(), - this.getComponent().getTemplates(), - _.cloneDeep(this.templates) + this.getProject().getTemplates(), + this.getComponent().getTemplates(), + _.cloneDeep(this.templates) ); } @@ -229,39 +254,39 @@ class ServerlessFunction { return _this._create(); } }) - .then(function() { + .then(function() { - // Save all nested endpoints - if (options && options.deep) { - return BbPromise.try(function () { - return _this.endpoints; - }) - .each(function(endpoint) { - return endpoint.save(); - }) - } - }) - .then(function() { + // Save all nested endpoints + if (options && options.deep) { + return BbPromise.try(function () { + return _this.endpoints; + }) + .each(function(endpoint) { + return endpoint.save(); + }) + } + }) + .then(function() { - // If templates, save templates - if (_this.templates && Object.keys(_this.templates).length) { - return SUtils.writeFile(path.join(_this._config.fullPath, 's-templates.json'), _this.templates); - } - }) - .then(function() { + // If templates, save templates + if (_this.templates && Object.keys(_this.templates).length) { + return SUtils.writeFile(path.join(_this._config.fullPath, 's-templates.json'), _this.templates); + } + }) + .then(function() { - let clone = _this.get(); + let clone = _this.get(); - // Strip properties - if (clone.templates) delete clone.templates; + // Strip properties + if (clone.templates) delete clone.templates; - // Write file - return SUtils.writeFile(path.join(_this._config.fullPath, 's-function.json'), - JSON.stringify(clone, null, 2)); - }) - .then(function() { - return _this; - }) + // Write file + return SUtils.writeFile(path.join(_this._config.fullPath, 's-function.json'), + JSON.stringify(clone, null, 2)); + }) + .then(function() { + return _this; + }) } /** @@ -279,16 +304,16 @@ class ServerlessFunction { // Runtime: nodejs writeDeferred.push( - fs.mkdirSync(_this._config.fullPath), - SUtils.writeFile(path.join(_this._config.fullPath, 'event.json'), '{}') + fs.mkdirSync(_this._config.fullPath), + SUtils.writeFile(path.join(_this._config.fullPath, 'event.json'), '{}') ); if (_this.getRuntime() === 'nodejs') { writeDeferred.push( - SUtils.writeFile(path.join(_this._config.fullPath, 'handler.js'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'handler.js'))) + SUtils.writeFile(path.join(_this._config.fullPath, 'handler.js'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'handler.js'))) ) } else if (_this.getRuntime() === 'python2.7') { writeDeferred.push( - SUtils.writeFile(path.join(_this._config.fullPath, 'handler.py'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', 'handler.py'))) + SUtils.writeFile(path.join(_this._config.fullPath, 'handler.py'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'python2.7', 'handler.py'))) ) } return BbPromise.all(writeDeferred); diff --git a/lib/utils/index.js b/lib/utils/index.js index 3999646b8..3b507f3a1 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -616,11 +616,9 @@ exports.populate = function(meta, templates, data, stage, region) { // Sanitize: Remove nested properties. DO NOT populate these. Rely on calling those classes getPopulated methods instead. if (data.components) delete data.components; - if (data.modules) delete data.modules; if (data.functions) delete data.functions; if (data.endpoints) delete data.endpoints; - // Populate templates traverse(data).forEach(function (val) { diff --git a/tests/all.js b/tests/all.js index bed6fffd9..b5774d0db 100644 --- a/tests/all.js +++ b/tests/all.js @@ -11,10 +11,10 @@ describe('All Tests', function() { after(function() {}); require('./tests/classes/ServerlessStateTest'); - //require('./tests/classes/ServerlessProjectTest'); - //require('./tests/classes/ServerlessComponentTest'); - //require('./tests/classes/ServerlessFunctionTest'); - //require('./tests/classes/ServerlessEndpointTest'); + require('./tests/classes/ServerlessProjectTest'); + require('./tests/classes/ServerlessComponentTest'); + require('./tests/classes/ServerlessFunctionTest'); + require('./tests/classes/ServerlessEndpointTest'); //require('./tests/actions/TestPluginCustom'); //require('./tests/actions/TestDefaultActionHook'); //require('./tests/actions/StageCreate'); diff --git a/tests/test-prj/nodejscomponent/function0/s-function.json b/tests/test-prj/nodejscomponent/function0/s-function.json index 45d738187..4ac0e5b7b 100644 --- a/tests/test-prj/nodejscomponent/function0/s-function.json +++ b/tests/test-prj/nodejscomponent/function0/s-function.json @@ -14,8 +14,8 @@ "method": "GET", "authorizationType": "${endpointVariable}", "apiKeyRequired": false, - "requestParameters": "$${endpointTemplate}", - "requestTemplates": "$${apiRequestTemplate}", + "requestParameters": {}, + "requestTemplates": {}, "responses": { "default": { "statusCode": "200", diff --git a/tests/tests/classes/ServerlessComponentTest.js b/tests/tests/classes/ServerlessComponentTest.js index 8083bc1e4..310dfba31 100644 --- a/tests/tests/classes/ServerlessComponentTest.js +++ b/tests/tests/classes/ServerlessComponentTest.js @@ -35,7 +35,7 @@ describe('Test Serverless Component Class', function() { // Instantiate Class instance = new serverless.classes.Component(serverless, { - component: 'nodejscomponent' + sPath: 'nodejscomponent' }); done(); @@ -92,7 +92,7 @@ describe('Test Serverless Component Class', function() { it('Create new and save', function(done) { let component = new serverless.classes.Component(serverless, { - component: 'nodejscomponent' + sPath: 'nodejscomponent' }); component.save() diff --git a/tests/tests/classes/ServerlessEndpointTest.js b/tests/tests/classes/ServerlessEndpointTest.js index 79739cd08..a1f3e0052 100644 --- a/tests/tests/classes/ServerlessEndpointTest.js +++ b/tests/tests/classes/ServerlessEndpointTest.js @@ -116,7 +116,13 @@ describe('Test Serverless Endpoint Class', function() { it('Get function', function() { let func = instance.getFunction(); assert.instanceOf(func, serverless.classes.Function); - assert.equal(func.name, instance._config.function); + assert.equal(true, instance._config.sPath.indexOf(func._config.sPath) !== -1) + }); + + it('Get component', function() { + let comp = instance.getComponent(); + assert.instanceOf(comp, serverless.classes.Component); + assert.equal(true, instance._config.sPath.indexOf(comp._config.sPath) !== -1) }); }); }); diff --git a/tests/tests/classes/ServerlessFunctionTest.js b/tests/tests/classes/ServerlessFunctionTest.js index f0ce3862e..e13a2863e 100644 --- a/tests/tests/classes/ServerlessFunctionTest.js +++ b/tests/tests/classes/ServerlessFunctionTest.js @@ -34,9 +34,7 @@ describe('Test Serverless Function Class', function() { // Instantiate Class instance = new serverless.classes.Function(serverless, { - component: 'nodejscomponent', - module: 'group1', - function: 'function1' + sPath: 'nodejscomponent/group1/function1' }); done(); @@ -93,9 +91,7 @@ describe('Test Serverless Function Class', function() { it('Create new and save', function(done) { let func = new serverless.classes.Function(serverless, { - component: 'nodejscomponent', - module: 'group1', - function: 'function4' + sPath: 'nodejscomponent/group1/function1' }); func.save() @@ -109,11 +105,5 @@ describe('Test Serverless Function Class', function() { done(e); }); }); - - it('Get module', function() { - let module = instance.getModule(); - assert.instanceOf(module, serverless.classes.Module); - assert.equal(module.name, instance._config.module); - }); }); }); diff --git a/tests/tests/classes/ServerlessProjectTest.js b/tests/tests/classes/ServerlessProjectTest.js index 0f8bf31d6..09ea8785e 100644 --- a/tests/tests/classes/ServerlessProjectTest.js +++ b/tests/tests/classes/ServerlessProjectTest.js @@ -70,21 +70,21 @@ describe('Test Serverless Project Class', function() { // We've set a template in the project that gets extended at the module level and function level, check it: // Project template - assert.equal(true, typeof data.components.nodejscomponent.modules.module1.functions.function1.endpoints[0].requestTemplates['application/json'].httpMethod !== 'undefined'); + assert.equal(true, typeof data.components.nodejscomponent.functions['nodejscomponent/group1/function1'].endpoints[0].requestTemplates['application/json'].httpMethod !== 'undefined'); // Component template - assert.equal(true, typeof data.components.nodejscomponent.modules.module1.functions.function1.endpoints[0].requestTemplates['application/json'].headerParams !== 'undefined'); + assert.equal(true, typeof data.components.nodejscomponent.functions['nodejscomponent/group1/function1'].endpoints[0].requestTemplates['application/json'].headerParams !== 'undefined'); // Module template - assert.equal(true, typeof data.components.nodejscomponent.modules.module1.functions.function1.endpoints[0].requestTemplates['application/json'].queryParams !== 'undefined'); + assert.equal(true, typeof data.components.nodejscomponent.functions['nodejscomponent/group1/function1'].endpoints[0].requestTemplates['application/json'].queryParams !== 'undefined'); // Test subjective template inheritance // These functions have their own s-templates.json files which give them the same template, with one different property // Function1 template - assert.equal(true, data.components.nodejscomponent.modules.module1.functions.function1.endpoints[0].requestTemplates['application/json'].pathParams === "$input.path('$.id1')"); + assert.equal(true, data.components.nodejscomponent.functions['nodejscomponent/group1/function1'].endpoints[0].requestTemplates['application/json'].pathParams === "$input.path('$.id1')"); // Function2 template - assert.equal(true, data.components.nodejscomponent.modules.module1.functions.function2.endpoints[0].requestTemplates['application/json'].pathParams === "$input.path('$.id2')"); + assert.equal(true, data.components.nodejscomponent.functions['nodejscomponent/group1/function2'].endpoints[0].requestTemplates['application/json'].pathParams === "$input.path('$.id2')"); // Function3 template - s-templates.json left undefined - assert.equal(true, typeof data.components.nodejscomponent.modules.module1.functions.function3.endpoints[0].requestTemplates['application/json'].pathParams === 'undefined'); + assert.equal(true, typeof data.components.nodejscomponent.functions['nodejscomponent/group1/function3'].endpoints[0].requestTemplates['application/json'].pathParams === 'undefined'); done(); }); diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index 8b06b58eb..e59d94143 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -105,12 +105,14 @@ describe('Test Serverless State Class', function() { done(); }); - //it('Get resources (populated)', function(done) { - // let resources = instance.getResources({ populate: true, stage: config.stage, region: config.region }); - // assert.equal(true, JSON.stringify(resources).indexOf('$${') == -1); - // assert.equal(true, JSON.stringify(resources).indexOf('${') == -1); - // done(); - //}); + it('Get resources (populated)', function(done) { + let resources = instance.getResources({ + populate: true, stage: config.stage, region: config.region + }); + assert.equal(true, JSON.stringify(resources).indexOf('$${') == -1); + assert.equal(true, JSON.stringify(resources).indexOf('${') == -1); + done(); + }); it('Get stages', function(done) { let stages = instance.getStages(); From 0cc83d9c4a5d474e9399510fbd1ca02540611de4 Mon Sep 17 00:00:00 2001 From: ac360 Date: Wed, 3 Feb 2016 21:08:04 -0800 Subject: [PATCH 11/29] Adding backward support for s-module.json cloudformation --- lib/ServerlessProject.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index 0137be2b6..a8401a16d 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -147,7 +147,19 @@ class ServerlessProject { return component.load() .then(function (instance) { projectJson.components[c] = instance; - return projectJson.components[c]; + + return fs.readdirSync(_this._S.config.path.join(_this._S.config.projectPath, c)); + }) + .each(function(sf) { + + // Backwards Compatibility Support for s-modules.json + // TODO: Remove this eventually + if (path.join(_this._S.config.projectPath, c, sf)) + + if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, c, sf, 's-module.json'))) { + let moduleJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, c, 's-module.json')); + + } }); } }) From 78b90cca85b123c6c0a06b39ac81f91b7ede4ed3 Mon Sep 17 00:00:00 2001 From: ac360 Date: Wed, 3 Feb 2016 22:42:20 -0800 Subject: [PATCH 12/29] Resources: continue work on getResources method --- lib/ServerlessFunction.js | 1 - lib/ServerlessProject.js | 72 +++++++++++++++---- lib/ServerlessState.js | 9 +-- lib/utils/index.js | 67 ----------------- .../nodejscomponent/group1/s-module.json | 8 ++- tests/tests/classes/ServerlessStateTest.js | 1 + 6 files changed, 68 insertions(+), 90 deletions(-) diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index 52677fe2b..5ff8c387b 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -217,7 +217,6 @@ class ServerlessFunction { clone.endpoints[i] = _this.endpoints[i].getPopulated(options); } return clone; - } /** diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index a8401a16d..9f25d1f76 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -6,6 +6,7 @@ const SError = require('./ServerlessError'), SUtils = require('./utils/index'), + SCli = require('./utils/cli'), BbPromise = require('bluebird'), path = require('path'), _ = require('lodash'), @@ -147,19 +148,6 @@ class ServerlessProject { return component.load() .then(function (instance) { projectJson.components[c] = instance; - - return fs.readdirSync(_this._S.config.path.join(_this._S.config.projectPath, c)); - }) - .each(function(sf) { - - // Backwards Compatibility Support for s-modules.json - // TODO: Remove this eventually - if (path.join(_this._S.config.projectPath, c, sf)) - - if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, c, sf, 's-module.json'))) { - let moduleJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, c, 's-module.json')); - - } }); } }) @@ -249,6 +237,64 @@ class ServerlessProject { return _.cloneDeep(this.templates ? this.templates : {}); } + /** + * Get Resources + * - Returns clone of resources + * - Always populated + */ + + getResources() { + + let _this = this; + + let componentFolders = fs.readdirSync(path.join(_this._S.config.projectPath, c)); + + // Backwards Compatibility Support for s-modules.json + // TODO: Remove this eventually + if (path.join(_this._S.config.projectPath, c, sf)) + + if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, c, sf, 's-module.json'))) { + let moduleJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, c, sf, 's-module.json')); + + // If no cloudFormation in module, skip... + if (!moduleJson.cloudFormation) return; + + // Merge Lambda Policy Statements + if (moduleJson.cloudFormation.lambdaIamPolicyDocumentStatements && + moduleJson.cloudFormation.lambdaIamPolicyDocumentStatements.length > 0) { + + moduleJson.cloudFormation.lambdaIamPolicyDocumentStatements.forEach(function (policyStmt) { + _this.cloudFormation.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); + }); + } + + // Merge Resources + if (moduleJson.cloudFormation.resources) { + + let cfResourceKeys = Object.keys(moduleJson.cloudFormation.resources); + + if (cfResourceKeys.length > 0) { + _this.sDebug('Merging in CF Resources from module: ' + moduleJson.name); + } + + cfResourceKeys.forEach(function (resourceKey) { + + if (_this.cloudFormation.Resources[resourceKey]) { + SCli.log(`WARN: Resource key ${resourceKey} already defined in CF template. Overwriting...`) + } + + _this.cloudFormation.Resources[resourceKey] = moduleJson.cloudFormation.resources[resourceKey]; + }); + } + } + + if (options.populate) { + return this.getPopulated(options).cloudFormation; + } else { + return this.get().cloudFormation; + } + } + /** * save * - Saves data to file system diff --git a/lib/ServerlessState.js b/lib/ServerlessState.js index 01ce1b646..119eaaaae 100644 --- a/lib/ServerlessState.js +++ b/lib/ServerlessState.js @@ -148,14 +148,7 @@ class ServerlessState { */ getResources(options) { - - options = options || {}; - - if (options.populate) { - return SUtils.getResources(this.project.getPopulated(options)); - } else { - return SUtils.getResources(this.project.get()); - } + return this.project.getResources(options); } /** diff --git a/lib/utils/index.js b/lib/utils/index.js index 3b507f3a1..1534acc80 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -128,73 +128,6 @@ exports.getProjectPath = function(startDir) { return projRootPath; }; -/** - * GetResources - * - Dynamically Create CloudFormation resources from s-project.json and s-module.json - * - Returns Aggregated CloudFormation Template - */ - -exports.getResources = function(projectData) { - - let _this = this; - let cfTemplate = JSON.parse(JSON.stringify(projectData.cloudFormation)); - - // Helper function to aggregate resources - function aggregate(cfData) { - - // If no cloudFormation in module, skip... - if (!cfData.cloudFormation) return; - - // Merge Lambda Policy Statements - if (cfData.cloudFormation.lambdaIamPolicyDocumentStatements && - cfData.cloudFormation.lambdaIamPolicyDocumentStatements.length > 0) { - _this.sDebug('Merging in Lambda IAM Policy statements from: ' + cfData.name); - - cfData.cloudFormation.lambdaIamPolicyDocumentStatements.forEach(function (policyStmt) { - cfTemplate.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); - }); - } - - // Merge Resources - if (cfData.cloudFormation.resources) { - - let cfResourceKeys = Object.keys(cfData.cloudFormation.resources); - - if (cfResourceKeys.length > 0) { - _this.sDebug('Merging in CF Resources from module: ' + cfData.name); - } - - cfResourceKeys.forEach(function (resourceKey) { - - if (cfTemplate.Resources[resourceKey]) { - _this.log( - chalk.bgYellow.white(' WARN ') + - chalk.magenta(` Resource key ${resourceKey} already defined in CF template. Overwriting...`) - ); - } - - cfTemplate.Resources[resourceKey] = cfData.cloudFormation.resources[resourceKey]; - }); - } - } - - // Aggregate Components CF - for (let i = 0; i < Object.keys(projectData.components).length; i++) { - let component = projectData.components[Object.keys(projectData.components)[i]]; - aggregate(component); - - - // Aggregate Modules CF - if (component.modules) { - for (let j = 0; j < Object.keys(component.modules).length; j++) { - aggregate(component.modules[Object.keys(component.modules)[j]]); - } - } - } - - return cfTemplate; -}; - /** * Read Recursively */ diff --git a/tests/test-prj/nodejscomponent/group1/s-module.json b/tests/test-prj/nodejscomponent/group1/s-module.json index 71f160d2e..5ca8639d0 100644 --- a/tests/test-prj/nodejscomponent/group1/s-module.json +++ b/tests/test-prj/nodejscomponent/group1/s-module.json @@ -7,7 +7,13 @@ "runtime": "nodejs", "description": "", "cloudFormation": { - "lambdaIamPolicyDocumentStatements": [], + "lambdaIamPolicyDocumentStatements": [ + { + "Effect": "Allow", + "Action": "s3:ListBucket", + "Resource": "arn:aws:s3:::fake_bucket" + } + ], "resources": {} } } \ No newline at end of file diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index e59d94143..2a1a8470a 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -101,6 +101,7 @@ describe('Test Serverless State Class', function() { it('Get resources (unpopulated)', function(done) { let resources = instance.getResources(); + console.log(resources) assert.equal(true, JSON.stringify(resources).indexOf('${') !== -1); done(); }); From 2bf2c30536d0ce70b64fa342bf66a4015581c1df Mon Sep 17 00:00:00 2001 From: Kamil Burzynski Date: Thu, 4 Feb 2016 11:35:24 +0100 Subject: [PATCH 13/29] getResources() doesnt crash due to undefined variables anymore --- lib/ServerlessProject.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index 9f25d1f76..90d142658 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -243,10 +243,10 @@ class ServerlessProject { * - Always populated */ - getResources() { + getResources(options) { let _this = this; - +/* let componentFolders = fs.readdirSync(path.join(_this._S.config.projectPath, c)); // Backwards Compatibility Support for s-modules.json @@ -287,7 +287,7 @@ class ServerlessProject { }); } } - +*/ if (options.populate) { return this.getPopulated(options).cloudFormation; } else { From 2281d9439a84de79f188455a83a02f4d607452fd Mon Sep 17 00:00:00 2001 From: Kamil Burzynski Date: Thu, 4 Feb 2016 13:38:31 +0100 Subject: [PATCH 14/29] WIP - module removal --- lib/actions/ComponentCreate.js | 48 ++++++++---------------- lib/actions/FunctionCreate.js | 49 ++++++------------------- lib/actions/FunctionRunLambdaNodeJs.js | 2 +- lib/actions/FunctionRunLambdaPython2.js | 2 +- lib/utils/index.js | 23 ++++++------ 5 files changed, 41 insertions(+), 83 deletions(-) diff --git a/lib/actions/ComponentCreate.js b/lib/actions/ComponentCreate.js index 31f4f8a15..51ff7bec6 100644 --- a/lib/actions/ComponentCreate.js +++ b/lib/actions/ComponentCreate.js @@ -2,13 +2,12 @@ /** * Action: ComponentCreate - * - takes component, module and function names + * - takes component and function names * - validates that component doesn't already exist - * - generates module and function sturcture based on runtime + * - generates function structure based on runtime * * Options: * - component: (String) Name of your new component - * - module: (String) Name of your first module in your new component * - function: (String) Name of your first function in your new component */ @@ -50,11 +49,6 @@ usage: serverless component create`, shortcut: 'c', description: 'The name of your new component' }, - { - option: 'module', - shortcut: 'm', - description: 'The name of your first module in your new component' - }, { option: 'function', shortcut: 'f', @@ -63,7 +57,7 @@ usage: serverless component create`, { option: 'runtime', shortcut: 'r', - description: 'Runtime of your new module. Default: nodejs' + description: 'Runtime of your new component. Default: nodejs' } ] }); @@ -84,7 +78,7 @@ usage: serverless component create`, .bind(_this) .then(_this._validateAndPrepare) .then(_this._createComponentSkeleton) - .then(_this._createModuleSkeleton) + .then(_this._createFunctionSkeleton) .then(_this._installComponentDependencies) .then(function() { @@ -101,7 +95,7 @@ usage: serverless component create`, }; /** - * Prompt component, module & function, if missing + * Prompt component & function, if missing */ _prompt() { @@ -111,7 +105,7 @@ usage: serverless component create`, if (!_this.S.config.interactive) return BbPromise.resolve(); - ['component', 'module', 'function'].forEach(memberVarKey => { + ['component', 'function'].forEach(memberVarKey => { overrides[memberVarKey] = _this.evt.options[memberVarKey]; }); @@ -123,21 +117,12 @@ usage: serverless component create`, message: 'Component name must contain only letters, numbers, hyphens, or underscores.', required: true, conform: function(componentName) { - return SUtils.isModuleNameValid(componentName); - } - }, - module: { - default: 'resource', - description: 'Enter a name for your component\'s first module: '.yellow, - message: 'Module name must contain only letters, numbers, hyphens, or underscores.', - required: true, - conform: function(moduleName) { - return SUtils.isModuleNameValid(moduleName); + return SUtils.isComponentNameValid(componentName); } }, function: { default: 'show', - description: 'Enter a name for your module\'s first function: '.yellow, + description: 'Enter a name for your component\'s first function: '.yellow, message: 'Function name must contain only letters, numbers, hyphens, or underscores.', required: true, conform: function(functionName) { @@ -150,21 +135,20 @@ usage: serverless component create`, return _this.cliPromptInput(prompts, overrides) .then(function(answers) { _this.evt.options.component = answers.component; - _this.evt.options.module = answers.module; _this.evt.options.function = answers.function; }); }; /** - * Validate and prepare data before creating module + * Validate and prepare data before creating component */ _validateAndPrepare() { // non interactive validation if (!this.S.config.interactive) { - if (!this.evt.options.component || !this.evt.options.module || !this.evt.options.function) { - return BbPromise.reject(new SError('Component, Module and Function names are all required.')); + if (!this.evt.options.component || !this.evt.options.function) { + return BbPromise.reject(new SError('Component and Function names are both required.')); } } @@ -200,30 +184,30 @@ usage: serverless component create`, _createComponentSkeleton() { let component = new this.S.classes.Component(this.S, { component: this.evt.options.component, - module: this.evt.options.module, runtime: this.evt.options.runtime }); this.S.state.setAsset(component); + return component.save(); }; /** - * Create Module Skeleton + * Create Function Skeleton */ - _createModuleSkeleton() { + _createFunctionSkeleton(component) { let _this = this, newEvt = { options: { + //sPath: component._config.sPath + "/" + _this.evt.options.function component: _this.evt.options.component, - module: _this.evt.options.module, function: _this.evt.options.function } }; - return _this.S.actions.moduleCreate(newEvt); + return _this.S.actions.functionCreate(newEvt); }; /** diff --git a/lib/actions/FunctionCreate.js b/lib/actions/FunctionCreate.js index eba99a4b0..8c4b03b12 100644 --- a/lib/actions/FunctionCreate.js +++ b/lib/actions/FunctionCreate.js @@ -2,14 +2,14 @@ /** * Action: FunctionCreate - * - takes existing module name and new function name - * - validates that module exists - * - validates that function does NOT already exists in module + * - takes existing component name and new function name + * - validates that component exists + * - validates that function does NOT already exists in component * - generates function structure based on runtime * * Event Options: - * - module: (String) Name of the existing module you want to create a function for - * - function: (String) Name of the new function for your existing module + * - component: (String) Name of the existing component you want to create a function for + * - function: (String) Name of the new function for your existing component * - template: (String) Name of the template to use to create the function JSON */ @@ -51,11 +51,6 @@ usage: serverless function create `, shortcut: 'c', description: 'The name of the component you want to create a module in' }, - { - option: 'module', - shortcut: 'm', - description: 'The name of the module you want to create a function in' - }, { option: 'function', shortcut: 'f', @@ -65,11 +60,6 @@ usage: serverless function create `, option: 'template', shortcut: 't', description: 'A template for a specific type of Function' - }, - { - option: 'runtime', - shortcut: 'r', - description: 'Optional - Runtime of your new module. Default: nodejs' } ] }); @@ -139,13 +129,6 @@ usage: serverless function create `, _this.evt.options.component = component; BbPromise.resolve(); }); - }) - .then(function() { - return _this.cliPromptSelectModule('Select a component module to create your function in: ', _this.evt.options.module, _this.evt.options.component) - .then(module => { - _this.evt.options.module = module; - BbPromise.resolve(); - }); }); }; @@ -156,8 +139,8 @@ usage: serverless function create `, _validateAndPrepare() { - if (!this.evt.options.component || !this.evt.options.module || !this.evt.options.function) { - return BbPromise.reject(new SError('Component, Module and Function names are all required.')); + if (!this.evt.options.component || !this.evt.options.function) { + return BbPromise.reject(new SError('Component and Function names are both required.')); } // If module does not exist in project, throw error @@ -168,22 +151,14 @@ usage: serverless function create `, )); } - // If module does not exist in project, throw error - if (!SUtils.doesModuleExist(this.evt.options.module, this.evt.options.component, this.S.config.projectPath)) { - return BbPromise.reject(new SError( - 'Module ' + this.evt.options.module + ' does NOT exist', - SError.errorCodes.INVALID_PROJECT_SERVERLESS - )); - } - if (['templates'].indexOf(this.evt.options.function) != -1) { return BbPromise.reject(new SError('This function name is reserved: ' + this.evt.options.function, SError.errorCodes.UNKNOWN)); } - // If function already exists in module, throw error - if (SUtils.doesFunctionExist(this.evt.options.function, this.evt.options.module, this.evt.options.component, this.S.config.projectPath)) { + // If function already exists in component, throw error + if (SUtils.doesFunctionExist(this.evt.options.function, this.evt.options.component, this.S.config.projectPath)) { return BbPromise.reject(new SError( - 'Function ' + this.evt.options.function + ' already exists in module ' + this.evt.options.module, + 'Function ' + this.evt.options.function + ' already exists in component ' + this.evt.options.component, SError.errorCodes.INVALID_PROJECT_SERVERLESS )); } @@ -199,9 +174,9 @@ usage: serverless function create `, // Instantiate Function let func = new this.S.classes.Function(this.S, { + //sPath: this.evt.options.sPath component: this.evt.options.component, - module: this.evt.options.module, - function: this.evt.options.function + function: this.evt.options.function, }); this.S.state.setAsset(func); diff --git a/lib/actions/FunctionRunLambdaNodeJs.js b/lib/actions/FunctionRunLambdaNodeJs.js index 86fe50bab..cf8ca7b82 100644 --- a/lib/actions/FunctionRunLambdaNodeJs.js +++ b/lib/actions/FunctionRunLambdaNodeJs.js @@ -53,7 +53,7 @@ module.exports = function(SPlugin, serverlessPath) { _this.function = _this.evt.options.path.split('/')[2]; - if (!SUtils.doesFunctionExist(_this.function, _this.module, _this.component, _this.S.config.projectPath)) { + if (!SUtils.doesFunctionExist(_this.function, _this.component, _this.S.config.projectPath)) { return BbPromise.reject(new SError( 'This function path does not exist', SError.errorCodes.INVALID_PROJECT_SERVERLESS diff --git a/lib/actions/FunctionRunLambdaPython2.js b/lib/actions/FunctionRunLambdaPython2.js index b0190ef03..7fbfa5695 100644 --- a/lib/actions/FunctionRunLambdaPython2.js +++ b/lib/actions/FunctionRunLambdaPython2.js @@ -52,7 +52,7 @@ module.exports = function(SPlugin, serverlessPath) { _this.module = _this.evt.options.path.split('/')[1]; _this.function = _this.evt.options.path.split('/')[2]; - if (!SUtils.doesFunctionExist(_this.function, _this.module, _this.component, _this.S.config.projectPath)) { + if (!SUtils.doesFunctionExist(_this.function, _this.component, _this.S.config.projectPath)) { return BbPromise.reject(new SError( 'This function path does not exist', SError.errorCodes.INVALID_PROJECT_SERVERLESS diff --git a/lib/utils/index.js b/lib/utils/index.js index 1534acc80..fcd78b649 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -490,8 +490,8 @@ exports.isStageNameValid = function(stageName) { return /^[a-zA-Z\d]+$/.test(stageName); }; -exports.isModuleNameValid = function(moduleName) { - return /^[\w-]{1,20}$/.test(moduleName); +exports.isComponentNameValid = function(componentName) { + return /^[\w-]{1,20}$/.test(componentName); }; exports.isFunctionNameValid = function(functionName) { @@ -508,26 +508,25 @@ exports.getModule = function(moduleName, componentName, projectRootPath) { ); }; -exports.getFunctionPath = function(functionName, moduleName, componentName, projectRootPath) { - return path.join(projectRootPath, componentName, moduleName, functionName); +exports.getFunctionPath = function(functionName, componentName, projectRootPath) { + return path.join(projectRootPath, componentName, functionName); }; -exports.getFunction = function(functionName, moduleName, projectRootPath) { +/* + TODO: this code is obviously wrong (calls getFunctionPath incorrectly) and not used anywhere. To be removed? +exports.getFunction = function(functionName, projectRootPath) { return this.readAndParseJsonSync( - path.join(this.getFunctionPath(functionName, moduleName, projectRootPath), 's-function.json') + path.join(this.getFunctionPath(functionName, projectRootPath), 's-function.json') ); }; +*/ exports.doesComponentExist = function(componentName, projectRootPath) { return this.dirExistsSync(path.join(projectRootPath, componentName)); }; -exports.doesModuleExist = function(moduleName, componentName, projectRootPath) { - return this.dirExistsSync(this.getModulePath(moduleName, componentName, projectRootPath)); -}; - -exports.doesFunctionExist = function(functionName, moduleName, componentName, projectRootPath) { - return this.dirExistsSync(this.getFunctionPath(functionName, moduleName, componentName, projectRootPath)); +exports.doesFunctionExist = function(functionName, componentName, projectRootPath) { + return this.dirExistsSync(this.getFunctionPath(functionName, componentName, projectRootPath)); }; /** From b68fb1e4aa63c81d22d599a66f39c71deddcaf25 Mon Sep 17 00:00:00 2001 From: ac360 Date: Thu, 4 Feb 2016 21:42:59 -0800 Subject: [PATCH 15/29] ServerlessProject: getResources returns promise and supports an s-cloudFormation.json file in root --- lib/ServerlessProject.js | 98 +++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index 90d142658..ea08d6cba 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -239,60 +239,84 @@ class ServerlessProject { /** * Get Resources - * - Returns clone of resources - * - Always populated + * - Returns Promise & clone of resources */ getResources(options) { - let _this = this; -/* - let componentFolders = fs.readdirSync(path.join(_this._S.config.projectPath, c)); + let _this = this, + cfTemplate; - // Backwards Compatibility Support for s-modules.json - // TODO: Remove this eventually - if (path.join(_this._S.config.projectPath, c, sf)) + return BbPromise.try(function() { - if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, c, sf, 's-module.json'))) { - let moduleJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, c, sf, 's-module.json')); + // Check for s-cloudFormation.json + if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, 's-cloudFormation.json'))) { + cfTemplate = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-cloudFormation.json')); + } - // If no cloudFormation in module, skip... - if (!moduleJson.cloudFormation) return; + // Validate: Can't have s-cloudFormation.json & project.cloudFormation + if (cfTemplate && _this.cloudFormation) { + throw new SError('You can\'t have a s-cloudFormation.json and cloudFormation syntax in s-project.json'); + } - // Merge Lambda Policy Statements - if (moduleJson.cloudFormation.lambdaIamPolicyDocumentStatements && - moduleJson.cloudFormation.lambdaIamPolicyDocumentStatements.length > 0) { + // Backward compat support for this.cloudFormation and s-module.json + if (!_this.cloudFormation) return; - moduleJson.cloudFormation.lambdaIamPolicyDocumentStatements.forEach(function (policyStmt) { - _this.cloudFormation.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); - }); - } + cfTemplate = _.clone(_this.cloudFormation); - // Merge Resources - if (moduleJson.cloudFormation.resources) { + let cPaths = [], + cfSnippets = []; - let cfResourceKeys = Object.keys(moduleJson.cloudFormation.resources); + for (c of _this.components) { + cPaths.push(c._config.fullPath); + } - if (cfResourceKeys.length > 0) { - _this.sDebug('Merging in CF Resources from module: ' + moduleJson.name); - } + return BbPromise.resolve(cPaths) + .each(function (cPath) { - cfResourceKeys.forEach(function (resourceKey) { + let cContents = fs.readdirSync(cPath); + return BbPromise.resolve(cContents) + .each(function (sf) { + if (SUtils.fileExistsSync(path.join(cPath, sf, 's-module.json'))) { + let moduleJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, c, sf, 's-module.json')); + if (moduleJson.cloudFormation) cfSnippets.push(moduleJson.cloudFormation); + } + }); + }) + .then(function () { - if (_this.cloudFormation.Resources[resourceKey]) { - SCli.log(`WARN: Resource key ${resourceKey} already defined in CF template. Overwriting...`) + // Merge s-module.json CF syntax + for (let i = 0; i < cfSnippets.length; i++) { + + let cf = cfSnippets[i]; + + // Merge Lambda Policy Statements + if (cf.lambdaIamPolicyDocumentStatements && cf.lambdaIamPolicyDocumentStatements.length > 0) { + cf.lambdaIamPolicyDocumentStatements.forEach(function (policyStmt) { + cfTemplate.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); + }); } - _this.cloudFormation.Resources[resourceKey] = moduleJson.cloudFormation.resources[resourceKey]; - }); - } + // Merge Resources + if (cf.resources) { + let cfResourceKeys = Object.keys(cf.resources); + cfResourceKeys.forEach(function (resourceKey) { + if (cfTemplate.Resources[resourceKey]) { + SCli.log(`WARN: Resource key ${resourceKey} already defined in CF template. Overwriting...`); + } + cfTemplate.Resources[resourceKey] = cf.resources[resourceKey]; + }); + } + } + }); + }) + .then(function() { + if (options.populate) { + return SUtils.populate(_this._S.state.getMeta(), _this.getTemplates(), cfTemplate, options.stage, options.region); + } else { + return cfTemplate; } -*/ - if (options.populate) { - return this.getPopulated(options).cloudFormation; - } else { - return this.get().cloudFormation; - } + }); } /** From c6bd8f41f0f75346c06aef5e31712f91aa84848c Mon Sep 17 00:00:00 2001 From: ac360 Date: Fri, 5 Feb 2016 00:23:50 -0800 Subject: [PATCH 16/29] FunctionCreate: refactor to remove modules concept --- lib/Actions.json | 1 - lib/ServerlessFunction.js | 2 +- lib/ServerlessProject.js | 123 +++++----- lib/ServerlessState.js | 11 +- lib/actions/ComponentCreate.js | 49 +--- lib/actions/FunctionCreate.js | 134 ++++++----- lib/actions/ModuleCreate.js | 238 ------------------- lib/actions/ResourcesDeploy.js | 185 +++++++------- lib/actions/StageCreate.js | 2 +- lib/actions/StageRemove.js | 2 +- tests/all.js | 13 +- tests/test-prj/s-project.json | 65 ++++- tests/test-prj/s-templates.json | 64 ----- tests/tests/actions/ComponentCreate.js | 12 +- tests/tests/actions/FunctionCreate.js | 10 +- tests/tests/actions/TestDefaultActionHook.js | 15 +- tests/tests/classes/ServerlessProjectTest.js | 1 - tests/tests/classes/ServerlessStateTest.js | 22 +- 18 files changed, 352 insertions(+), 597 deletions(-) delete mode 100644 lib/actions/ModuleCreate.js diff --git a/lib/Actions.json b/lib/Actions.json index ac148116b..244894662 100644 --- a/lib/Actions.json +++ b/lib/Actions.json @@ -4,7 +4,6 @@ "./actions/ProjectInstall.js", "./actions/ProjectInit.js", "./actions/ComponentCreate.js", - "./actions/ModuleCreate.js", "./actions/FunctionRun.js", "./actions/FunctionCreate.js", "./actions/FunctionRunLambdaNodeJs.js", diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index 5ff8c387b..c8e8f9447 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -322,7 +322,7 @@ class ServerlessFunction { getRuntime() { let _this = this; - let component = _this._S.state.getComponents({ paths: [_this._config.component] })[0]; + let component = _this._S.state.getComponents({ paths: [_this._config.sPath] })[0]; if (!component) throw new SError('The component containing runtime information for this function could not be found'); return component.runtime; } diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index ea08d6cba..09b14f26f 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -247,76 +247,83 @@ class ServerlessProject { let _this = this, cfTemplate; + options = options || {}; + return BbPromise.try(function() { - // Check for s-cloudFormation.json - if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, 's-cloudFormation.json'))) { - cfTemplate = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-cloudFormation.json')); - } + // Check for s-cloudFormation.json + if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, 's-cloudFormation.json'))) { + cfTemplate = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-cloudFormation.json')); + } - // Validate: Can't have s-cloudFormation.json & project.cloudFormation - if (cfTemplate && _this.cloudFormation) { - throw new SError('You can\'t have a s-cloudFormation.json and cloudFormation syntax in s-project.json'); - } + // Validate: Can't have s-cloudFormation.json & project.cloudFormation + if (cfTemplate && _this.cloudFormation) { + throw new SError('You can\'t have a s-cloudFormation.json and cloudFormation syntax in s-project.json'); + } - // Backward compat support for this.cloudFormation and s-module.json - if (!_this.cloudFormation) return; + // Backward compat support for this.cloudFormation and s-module.json + if (!_this.cloudFormation) return; - cfTemplate = _.clone(_this.cloudFormation); + cfTemplate = _.clone(_this.cloudFormation); - let cPaths = [], - cfSnippets = []; + let cPaths = [], + cfSnippets = []; - for (c of _this.components) { - cPaths.push(c._config.fullPath); - } + for (let c in _this.components) { + cPaths.push(_this.components[c]._config.fullPath); + } - return BbPromise.resolve(cPaths) - .each(function (cPath) { + return BbPromise.resolve(cPaths) + .each(function (cPath) { - let cContents = fs.readdirSync(cPath); - return BbPromise.resolve(cContents) - .each(function (sf) { - if (SUtils.fileExistsSync(path.join(cPath, sf, 's-module.json'))) { - let moduleJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, c, sf, 's-module.json')); - if (moduleJson.cloudFormation) cfSnippets.push(moduleJson.cloudFormation); - } - }); - }) - .then(function () { - - // Merge s-module.json CF syntax - for (let i = 0; i < cfSnippets.length; i++) { - - let cf = cfSnippets[i]; - - // Merge Lambda Policy Statements - if (cf.lambdaIamPolicyDocumentStatements && cf.lambdaIamPolicyDocumentStatements.length > 0) { - cf.lambdaIamPolicyDocumentStatements.forEach(function (policyStmt) { - cfTemplate.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); - }); - } - - // Merge Resources - if (cf.resources) { - let cfResourceKeys = Object.keys(cf.resources); - cfResourceKeys.forEach(function (resourceKey) { - if (cfTemplate.Resources[resourceKey]) { - SCli.log(`WARN: Resource key ${resourceKey} already defined in CF template. Overwriting...`); + let cContents = fs.readdirSync(cPath); + return BbPromise.resolve(cContents) + .each(function (sf) { + if (SUtils.fileExistsSync(path.join(cPath, sf, 's-module.json'))) { + let moduleJson = SUtils.readAndParseJsonSync(path.join(cPath, sf, 's-module.json')); + if (moduleJson.cloudFormation) cfSnippets.push(moduleJson.cloudFormation); } - cfTemplate.Resources[resourceKey] = cf.resources[resourceKey]; }); + }) + .then(function () { + + // Merge s-module.json CF syntax + for (let i = 0; i < cfSnippets.length; i++) { + + let cf = cfSnippets[i]; + + // Merge Lambda Policy Statements + if (cf.lambdaIamPolicyDocumentStatements && cf.lambdaIamPolicyDocumentStatements.length > 0) { + cf.lambdaIamPolicyDocumentStatements.forEach(function(policyStmt) { + try { + cfTemplate.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); + } + catch(e){} + }); + } + + // Merge Resources + if (cf.resources) { + let cfResourceKeys = Object.keys(cf.resources); + cfResourceKeys.forEach(function (resourceKey) { + if (cfTemplate.Resources[resourceKey]) { + SCli.log(`WARN: Resource key ${resourceKey} already defined in CF template. Overwriting...`); + } + try { + cfTemplate.Resources[resourceKey] = cf.resources[resourceKey]; + } catch(e){} + }); + } } - } - }); - }) - .then(function() { - if (options.populate) { - return SUtils.populate(_this._S.state.getMeta(), _this.getTemplates(), cfTemplate, options.stage, options.region); - } else { - return cfTemplate; - } - }); + }); + }) + .then(function() { + if (options.populate) { + return SUtils.populate(_this._S.state.getMeta(), _this.getTemplates(), cfTemplate, options.stage, options.region); + } else { + return cfTemplate; + } + }); } /** diff --git a/lib/ServerlessState.js b/lib/ServerlessState.js index 119eaaaae..cedea3ae1 100644 --- a/lib/ServerlessState.js +++ b/lib/ServerlessState.js @@ -178,7 +178,7 @@ class ServerlessState { getComponents(options) { - let _this = this, + let _this = this, allComponents = [], foundComponents = []; @@ -201,9 +201,12 @@ class ServerlessState { } continue; } - if (options.paths && options.paths.indexOf(component._config.sPath) !== -1) { - foundComponents.push(component); - continue; + + for (let j = 0; j < options.paths.length; j++) { + if (options.paths[j].indexOf(component._config.sPath) !== -1) { + foundComponents.push(component); + break; + } } } diff --git a/lib/actions/ComponentCreate.js b/lib/actions/ComponentCreate.js index 51ff7bec6..b53d105a5 100644 --- a/lib/actions/ComponentCreate.js +++ b/lib/actions/ComponentCreate.js @@ -2,13 +2,12 @@ /** * Action: ComponentCreate - * - takes component and function names + * - takes component name * - validates that component doesn't already exist - * - generates function structure based on runtime * * Options: * - component: (String) Name of your new component - * - function: (String) Name of your first function in your new component + * - runtime: (String) Runtime of your new component. Default: nodejs */ module.exports = function(SPlugin, serverlessPath) { @@ -49,11 +48,6 @@ usage: serverless component create`, shortcut: 'c', description: 'The name of your new component' }, - { - option: 'function', - shortcut: 'f', - description: 'The name of your first function in your new component' - }, { option: 'runtime', shortcut: 'r', @@ -78,7 +72,6 @@ usage: serverless component create`, .bind(_this) .then(_this._validateAndPrepare) .then(_this._createComponentSkeleton) - .then(_this._createFunctionSkeleton) .then(_this._installComponentDependencies) .then(function() { @@ -100,12 +93,12 @@ usage: serverless component create`, _prompt() { - let _this = this, + let _this = this, overrides = {}; if (!_this.S.config.interactive) return BbPromise.resolve(); - ['component', 'function'].forEach(memberVarKey => { + ['component'].forEach(memberVarKey => { overrides[memberVarKey] = _this.evt.options[memberVarKey]; }); @@ -119,15 +112,6 @@ usage: serverless component create`, conform: function(componentName) { return SUtils.isComponentNameValid(componentName); } - }, - function: { - default: 'show', - description: 'Enter a name for your component\'s first function: '.yellow, - message: 'Function name must contain only letters, numbers, hyphens, or underscores.', - required: true, - conform: function(functionName) { - return SUtils.isFunctionNameValid(functionName); - } } } }; @@ -135,7 +119,6 @@ usage: serverless component create`, return _this.cliPromptInput(prompts, overrides) .then(function(answers) { _this.evt.options.component = answers.component; - _this.evt.options.function = answers.function; }); }; @@ -147,8 +130,8 @@ usage: serverless component create`, // non interactive validation if (!this.S.config.interactive) { - if (!this.evt.options.component || !this.evt.options.function) { - return BbPromise.reject(new SError('Component and Function names are both required.')); + if (!this.evt.options.component) { + return BbPromise.reject(new SError('Component name is required.')); } } @@ -162,7 +145,7 @@ usage: serverless component create`, } // Check is not reserved name - if (['meta', 'plugins'].indexOf(this.evt.options.component) != -1) { + if (['meta', '_meta', 'plugins'].indexOf(this.evt.options.component) != -1) { return BbPromise.reject(new SError('This component name is reserved: ' + this.evt.options.component, SError.errorCodes.UNKNOWN)); } @@ -192,24 +175,6 @@ usage: serverless component create`, return component.save(); }; - /** - * Create Function Skeleton - */ - - _createFunctionSkeleton(component) { - - let _this = this, - newEvt = { - options: { - //sPath: component._config.sPath + "/" + _this.evt.options.function - component: _this.evt.options.component, - function: _this.evt.options.function - } - }; - - return _this.S.actions.functionCreate(newEvt); - }; - /** * Install Component Dependencies */ diff --git a/lib/actions/FunctionCreate.js b/lib/actions/FunctionCreate.js index 8c4b03b12..df71ebf89 100644 --- a/lib/actions/FunctionCreate.js +++ b/lib/actions/FunctionCreate.js @@ -8,9 +8,8 @@ * - generates function structure based on runtime * * Event Options: - * - component: (String) Name of the existing component you want to create a function for - * - function: (String) Name of the new function for your existing component - * - template: (String) Name of the template to use to create the function JSON + * - sPath: (String) The relative path of the function from project root + * - name: (String) Name of the new function for your existing component */ module.exports = function(SPlugin, serverlessPath) { @@ -31,7 +30,6 @@ module.exports = function(SPlugin, serverlessPath) { constructor(S, config) { super(S, config); - this._templatesDir = path.join(__dirname, '..', 'templates'); } static getName() { @@ -47,19 +45,16 @@ usage: serverless function create `, contextAction: 'create', options: [ { - option: 'component', - shortcut: 'c', - description: 'The name of the component you want to create a module in' - }, - { - option: 'function', - shortcut: 'f', + option: 'name', + shortcut: 'n', description: 'The name of your new function' - }, + } + ], + parameters: [ { - option: 'template', - shortcut: 't', - description: 'A template for a specific type of Function' + parameter: 'sPath', + description: 'One path to your function relative to the project root', + position: '0' } ] }); @@ -76,39 +71,39 @@ usage: serverless function create `, _this.evt = evt; return _this._prompt() - .bind(_this) - .then(_this._validateAndPrepare) - .then(_this._createFunctionSkeleton) - .then(function() { - SCli.log('Successfully created function: "' + _this.evt.options.function + '"'); + .bind(_this) + .then(_this._validateAndPrepare) + .then(_this._createFunctionSkeleton) + .then(function() { + SCli.log('Successfully created function: "' + _this.evt.options.name + '"'); - /** - * Return Event - */ + /** + * Return Event + */ - return _this.evt; + return _this.evt; - }); + }); } /** - * Prompt component, module & function if they're missing + * Prompt sPath & function if they're missing */ _prompt() { let _this = this, - overrides = {}; + overrides = {}; if (!_this.S.config.interactive) return BbPromise.resolve(); - ['function'].forEach(memberVarKey => { + ['name'].forEach(memberVarKey => { overrides[memberVarKey] = _this.evt.options[memberVarKey]; }); let prompts = { properties: { - function: { + name: { description: 'Enter a new function name: '.yellow, message: 'Function name must contain only letters, numbers, hyphens, or underscores.', required: true, @@ -120,16 +115,9 @@ usage: serverless function create `, }; return _this.cliPromptInput(prompts, overrides) - .then(function(answers) { - _this.evt.options.function = answers.function; - }) - .then(function() { - return _this.cliPromptSelectComponent('Select a component to create your function in: ', _this.evt.options.component) - .then(component => { - _this.evt.options.component = component; - BbPromise.resolve(); - }); - }); + .then(function(answers) { + _this.evt.options.name = answers.name; + }); }; @@ -139,26 +127,68 @@ usage: serverless function create `, _validateAndPrepare() { - if (!this.evt.options.component || !this.evt.options.function) { - return BbPromise.reject(new SError('Component and Function names are both required.')); + let _this = this; + + // Validate: check name + if (!_this.evt.options.name) { + return BbPromise.reject(new SError('name is required.')); } - // If module does not exist in project, throw error - if (!SUtils.doesComponentExist(this.evt.options.component, this.S.config.projectPath)) { + // Validate: If interactive and no sPath, check they are in a component, and get sPath + if (_this.S.config.interactive && !_this.evt.options.sPath) { + + let cwdArray = process.cwd().split(path.sep); + + if (SUtils.fileExistsSync(path.join(process.cwd(), 's-component.json'))) { + _this.evt.options.sPath = cwdArray.splice(cwdArray.length - 1, 1).join('/') + } else if (SUtils.fileExistsSync(path.join(process.cwd(), '..', 's-component.json'))) { + _this.evt.options.sPath = cwdArray.splice(cwdArray.length - 2, 2).join('/') + } else if (SUtils.fileExistsSync(path.join(process.cwd(), '..', '..', 's-component.json'))) { + _this.evt.options.sPath = cwdArray.splice(cwdArray.length - 3, 3).join('/') + } else { + return BbPromise.reject(new SError('You must be in a component or two subfolders max in a component to create a function')); + } + } + + // Validate: check sPath + if (!_this.evt.options.sPath) { + return BbPromise.reject(new SError('sPath is required.')); + } + + // Validate: Don't allow function creation within a function + if (SUtils.fileExistsSync(path.join(_this.S.config.projectPath, _this.evt.options.sPath.split('/').join(path.sep), 's-function.json'))) { + return BbPromise.reject(new SError('You cannot create a function in another function')); + } + + // Validate: check sPath isn't too long + if (_this.evt.options.sPath.split('/').length > 3) { + return BbPromise.reject(new SError('You can only create functions 2 subfolders deep in a component: component/subfolder/subfolder')); + } + + // If component does not exist in project, throw error + if (!_this.S.state.getComponents({ paths: [_this.evt.options.sPath] }).length) { return BbPromise.reject(new SError( - 'Component ' + this.evt.options.component + ' does NOT exist', + 'Component does NOT exist in ' + _this.evt.options.sPath, SError.errorCodes.INVALID_PROJECT_SERVERLESS )); } - if (['templates'].indexOf(this.evt.options.function) != -1) { - return BbPromise.reject(new SError('This function name is reserved: ' + this.evt.options.function, SError.errorCodes.UNKNOWN)); + // If subfolders are missing, create them + if (_this.evt.options.sPath.split('/').length > 1) { + let dir = _this.evt.options.sPath.split('/'); + let c = dir.shift(); + if (!SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0]))) { + fs.mkdirSync(path.join(_this.S.config.projectPath, c, dir[0])); + } + if (!SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0], dir[1]))) { + fs.mkdirSync(path.join(_this.S.config.projectPath, c, dir[0], dir[1])); + } } // If function already exists in component, throw error - if (SUtils.doesFunctionExist(this.evt.options.function, this.evt.options.component, this.S.config.projectPath)) { + if (_this.S.state.getFunctions({ paths: [_this.evt.options.sPath + '/' + _this.evt.options.name] }).length) { return BbPromise.reject(new SError( - 'Function ' + this.evt.options.function + ' already exists in component ' + this.evt.options.component, + 'Function ' + _this.evt.options.name + ' already exists in ' + _this.evt.options.sPath, SError.errorCodes.INVALID_PROJECT_SERVERLESS )); } @@ -171,14 +201,10 @@ usage: serverless function create `, */ _createFunctionSkeleton() { - - // Instantiate Function let func = new this.S.classes.Function(this.S, { - //sPath: this.evt.options.sPath - component: this.evt.options.component, - function: this.evt.options.function, + sPath: this.evt.options.sPath + '/' + this.evt.options.name }); - + func.name = this.evt.options.name; this.S.state.setAsset(func); return func.save(); }; diff --git a/lib/actions/ModuleCreate.js b/lib/actions/ModuleCreate.js deleted file mode 100644 index 8d52b0b32..000000000 --- a/lib/actions/ModuleCreate.js +++ /dev/null @@ -1,238 +0,0 @@ -'use strict'; - -/** - * Action: ModuleCreate - * - takes module and function names - * - validates that module doesn't already exist - * - generates function sturcture based on runtime - * - generates module structure - * - * Options: - * - module: (String) Name of your new module - * - function: (String) Name of your first function in your new module - */ - -module.exports = function(SPlugin, serverlessPath) { - const path = require('path'), - SError = require(path.join(serverlessPath, 'ServerlessError')), - SCli = require(path.join(serverlessPath, 'utils/cli')), - SUtils = require(path.join(serverlessPath, 'utils')), - BbPromise = require('bluebird'), - fs = require('fs'); - - BbPromise.promisifyAll(fs); - - /** - * ModuleCreate Class - */ - - class ModuleCreate extends SPlugin { - - constructor(S, config) { - super(S, config); - } - - static getName() { - return 'serverless.core.' + ModuleCreate.name; - } - - registerActions() { - this.S.addAction(this.moduleCreate.bind(this), { - handler: 'moduleCreate', - description: `Creates scaffolding for a new serverless module. -usage: serverless module create`, - context: 'module', - contextAction: 'create', - options: [ - { - option: 'component', - shortcut: 'c', - description: 'The name of the component you want to create a module in' - }, - { - option: 'module', - shortcut: 'm', - description: 'The name of your new module' - }, - { - option: 'function', - shortcut: 'f', - description: 'The name of your first function for your new module' - }, - { - option: 'template', - shortcut: 't', - description: 'A template for a specific type of Function' - }, - { - option: 'runtime', - shortcut: 'r', - description: 'Optional - Runtime of your new module. Default: nodejs' - } - ] - }); - - return BbPromise.resolve(); - }; - - /** - * Action - */ - - moduleCreate(evt) { - - let _this = this; - _this.evt = evt; - - return _this._prompt() - .bind(_this) - .then(_this._validateAndPrepare) - .then(_this._createModuleSkeleton) - .then(_this._createFunctionSkeleton) - .then(function() { - - SCli.log('Successfully created new serverless module "' + _this.evt.options.module + '" inside the component "' + _this.evt.options.component + '"'); - - /** - * Return Action Data - * - WARNING: Adjusting these will break Plugins - */ - - return _this.evt; - - }); - }; - - /** - * Prompt component, module & function, if missing - */ - - _prompt() { - - let _this = this, - overrides = {}; - - if (!_this.S.config.interactive) return BbPromise.resolve(); - - ['module', 'function'].forEach(memberVarKey => { - overrides[memberVarKey] = _this.evt.options[memberVarKey]; - }); - - let prompts = { - properties: { - module: { - default: 'resource', - description: 'Enter a name for your new module: '.yellow, - message: 'Module name must contain only letters, numbers, hyphens, or underscores.', - required: true, - conform: function(moduleName) { - return SUtils.isModuleNameValid(moduleName); - } - }, - function: { - default: 'show', - description: 'Enter a function name for your new module: '.yellow, - message: 'Function name must contain only letters, numbers, hyphens, or underscores.', - required: true, - conform: function(functionName) { - return SUtils.isFunctionNameValid(functionName); - } - } - } - }; - - return _this.cliPromptInput(prompts, overrides) - .then(function(answers) { - _this.evt.options.module = answers.module; - _this.evt.options.function = answers.function; - }) - .then(function() { - return _this.cliPromptSelectComponent('Select a component to create your module in: ', _this.evt.options.component) - .then(component => { - _this.evt.options.component = component; - BbPromise.resolve(); - }); - }); - }; - - /** - * Validate and prepare data before creating module - */ - - _validateAndPrepare() { - - // Add default runtime - if (!this.evt.options.runtime) { - this.evt.options.runtime = 'nodejs'; - } - - if (!this.evt.options.component || !this.evt.options.module || !this.evt.options.function) { - return BbPromise.reject(new SError('Component, Module and Function names are all required.')); - } - - if (!SUtils.doesComponentExist(this.evt.options.component, this.S.config.projectPath)) { - return BbPromise.reject(new SError( - 'Component ' + this.evt.options.component + ' does not exist in your project', - SError.errorCodes.INVALID_PROJECT_SERVERLESS - )); - } - - if (!SUtils.supportedRuntimes[this.evt.options.runtime]) { - return BbPromise.reject(new SError('Unsupported runtime ' + this.evt.options.runtime, SError.errorCodes.UNKNOWN)); - } - - if (['lib', 'node_modules'].indexOf(this.evt.options.module) != -1) { - return BbPromise.reject(new SError('This module name is reserved: ' + this.evt.options.component, SError.errorCodes.UNKNOWN)); - } - - // If module exists in project, throw error - if (SUtils.doesModuleExist(this.evt.options.module, this.evt.options.component, this.S.config.projectPath)) { - return BbPromise.reject(new SError( - 'Module ' + this.evt.options.module + ' already exists', - SError.errorCodes.INVALID_PROJECT_SERVERLESS - )); - } - - return BbPromise.resolve(); - }; - - /** - * Create Module Skeleton - */ - - _createModuleSkeleton() { - - // Instantiate Module - let module = new this.S.classes.Module(this.S, { - component: this.evt.options.component, - module: this.evt.options.module, - runtime: this.evt.options.runtime - }); - - this.S.state.setAsset(module); - return module.save(); - - }; - - /** - * Create Module Skeleton - */ - - _createFunctionSkeleton() { - - // Create new event and call FunctionCreate - let newEvt = { - options: { - component: this.evt.options.component, - module: this.evt.options.module, - function: this.evt.options.function, - runtime: this.evt.options.runtime || 'nodejs' - } - }; - - return this.S.actions.functionCreate(newEvt); - } - } - - return( ModuleCreate ); -}; diff --git a/lib/actions/ResourcesDeploy.js b/lib/actions/ResourcesDeploy.js index a337cb95c..bfa693aba 100644 --- a/lib/actions/ResourcesDeploy.js +++ b/lib/actions/ResourcesDeploy.js @@ -156,115 +156,116 @@ usage: serverless resources deploy`, let _this = this; let regionVars = _this.S.state.getMeta().stages[_this.evt.options.stage].regions[_this.evt.options.region].variables; - _this.cfTemplate = _this.S.state.getResources({ - populate: true, - stage: _this.evt.options.stage, - region: _this.evt.options.region - }); + return _this.S.state.getResources({ + populate: true, + stage: _this.evt.options.stage, + region: _this.evt.options.region + }) + .then(function(resources) { - return BbPromise.try(function() { + _this.cfTemplate = resources; - // Create CloudFormation template in _meta folder - return SUtils.writeFile( - path.join(_this.S.config.projectPath, '_meta', 'resources', 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'), - JSON.stringify(_this.cfTemplate, null, 2)) + // Create CloudFormation template in _meta folder + return SUtils.writeFile( + path.join(_this.S.config.projectPath, '_meta', 'resources', 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'), + JSON.stringify(_this.cfTemplate, null, 2)) - }).then(function() { + }).then(function() { - // If no NoExeCF is set, skip - if (_this.evt.options.noExeCf) { + // If no NoExeCF is set, skip + if (_this.evt.options.noExeCf) { - // Status - SCli.log('Notice -- You have chosen not to deploy your resources to CloudFormation. ' + - 'A CloudFormation template has been saved here: _meta/resources/' + - 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'); + // Status + SCli.log('Notice -- You have chosen not to deploy your resources to CloudFormation. ' + + 'A CloudFormation template has been saved here: _meta/resources/' + + 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'); - // Return - return; - } + // Return + return; + } - // Config AWS Services - let awsConfig = { - region: _this.evt.options.region, - accessKeyId: _this.S.config.awsAdminKeyId, - secretAccessKey: _this.S.config.awsAdminSecretKey - }; - _this.CF = require('../utils/aws/CloudFormation')(awsConfig); - _this.S3 = require('../utils/aws/S3')(awsConfig); + // Config AWS Services + let awsConfig = { + region: _this.evt.options.region, + accessKeyId: _this.S.config.awsAdminKeyId, + secretAccessKey: _this.S.config.awsAdminSecretKey + }; + _this.CF = require('../utils/aws/CloudFormation')(awsConfig); + _this.S3 = require('../utils/aws/S3')(awsConfig); - // Otherwise, deploy to CloudFormation - SCli.log('Deploying resources to stage "' - + _this.evt.options.stage - + '" in region "' - + _this.evt.options.region - + '" via Cloudformation (~3 minutes)...'); + // Otherwise, deploy to CloudFormation + SCli.log('Deploying resources to stage "' + + _this.evt.options.stage + + '" in region "' + + _this.evt.options.region + + '" via Cloudformation (~3 minutes)...'); - // Start spinner - _this._spinner = SCli.spinner(); - _this._spinner.start(); + // Start spinner + _this._spinner = SCli.spinner(); + _this._spinner.start(); - // Upload to S3 Bucket - return _this.S3.sPutCfFile( - _this.S.state.getMeta().variables.projectBucket, - _this.S.config.projectPath, - _this.S.state.getProject().name, - _this.evt.options.stage, - _this.evt.options.region, - _this.cfTemplate - ) - .then(function(templateUrl) { + // Upload to S3 Bucket + return _this.S3.sPutCfFile( + _this.S.state.getMeta().variables.projectBucket, + _this.S.config.projectPath, + _this.S.state.getProject().name, + _this.evt.options.stage, + _this.evt.options.region, + _this.cfTemplate + ) + .then(function(templateUrl) { - // Trigger CF Stack Create/Update - return _this.CF.sCreateOrUpdateResourcesStack( - _this.S.state.getProject().name, - _this.evt.options.stage, - _this.evt.options.region, - regionVars.resourcesStackName ? regionVars.resourcesStackName : null, - templateUrl) - .then(cfData => { + // Trigger CF Stack Create/Update + return _this.CF.sCreateOrUpdateResourcesStack( + _this.S.state.getProject().name, + _this.evt.options.stage, + _this.evt.options.region, + regionVars.resourcesStackName ? regionVars.resourcesStackName : null, + templateUrl) + .then(cfData => { - // If string, log output - if (typeof cfData === 'string') { - _this._spinner.stop(true); - SCli.log(cfData); - return; - } - - // Monitor CF Status - return _this.CF.sMonitorCf(cfData) - .then(cfStackData => { - - // Save stack name - regionVars.resourcesStackName = cfStackData.StackName; - - // Save IAM Role ARN for Project Lambdas - for (let i = 0; i < cfStackData.Outputs.length; i++) { - if (cfStackData.Outputs[i].OutputKey === 'IamRoleArnLambda') { - regionVars.iamRoleArnLambda = cfStackData.Outputs[i].OutputValue; - } - } - }) - .then(() => { - - // Stop Spinner + // If string, log output + if (typeof cfData === 'string') { _this._spinner.stop(true); + SCli.log(cfData); + return; + } - // Save State - _this.S.state.save(); + // Monitor CF Status + return _this.CF.sMonitorCf(cfData) + .then(cfStackData => { - // Status - SCli.log('Successfully deployed "' + _this.evt.options.stage + '" resources to "' + _this.evt.options.region + '"'); - }); - }) - }) - .catch(function(e) { + // Save stack name + regionVars.resourcesStackName = cfStackData.StackName; - // Stop Spinner - _this._spinner.stop(true); + // Save IAM Role ARN for Project Lambdas + for (let i = 0; i < cfStackData.Outputs.length; i++) { + if (cfStackData.Outputs[i].OutputKey === 'IamRoleArnLambda') { + regionVars.iamRoleArnLambda = cfStackData.Outputs[i].OutputValue; + } + } + }) + .then(() => { - throw new SError(e); - }) - }); + // Stop Spinner + _this._spinner.stop(true); + + // Save State + _this.S.state.save(); + + // Status + SCli.log('Successfully deployed "' + _this.evt.options.stage + '" resources to "' + _this.evt.options.region + '"'); + }); + }) + }) + .catch(function(e) { + + // Stop Spinner + _this._spinner.stop(true); + + throw new SError(e); + }) + }); } } diff --git a/lib/actions/StageCreate.js b/lib/actions/StageCreate.js index 094798f68..bc2de41d6 100644 --- a/lib/actions/StageCreate.js +++ b/lib/actions/StageCreate.js @@ -13,7 +13,7 @@ module.exports = function(SPlugin, serverlessPath) { - const path = require('path'), + const path = require('path'), SError = require(path.join(serverlessPath, 'ServerlessError')), SCli = require(path.join(serverlessPath, 'utils/cli')), os = require('os'), diff --git a/lib/actions/StageRemove.js b/lib/actions/StageRemove.js index 3178b44a3..586fb469c 100644 --- a/lib/actions/StageRemove.js +++ b/lib/actions/StageRemove.js @@ -10,7 +10,7 @@ module.exports = function(SPlugin, serverlessPath) { - const path = require('path'), + const path = require('path'), SError = require(path.join(serverlessPath, 'ServerlessError')), SCli = require(path.join(serverlessPath, 'utils/cli')), fs = require('fs'), diff --git a/tests/all.js b/tests/all.js index b5774d0db..056e0f243 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,18 +10,17 @@ describe('All Tests', function() { }); after(function() {}); - require('./tests/classes/ServerlessStateTest'); - require('./tests/classes/ServerlessProjectTest'); - require('./tests/classes/ServerlessComponentTest'); - require('./tests/classes/ServerlessFunctionTest'); - require('./tests/classes/ServerlessEndpointTest'); + //require('./tests/classes/ServerlessStateTest'); + //require('./tests/classes/ServerlessProjectTest'); + //require('./tests/classes/ServerlessComponentTest'); + //require('./tests/classes/ServerlessFunctionTest'); + //require('./tests/classes/ServerlessEndpointTest'); //require('./tests/actions/TestPluginCustom'); //require('./tests/actions/TestDefaultActionHook'); //require('./tests/actions/StageCreate'); //require('./tests/actions/RegionCreate'); //require('./tests/actions/ComponentCreate'); - //require('./tests/actions/ModuleCreate'); - //require('./tests/actions/FunctionCreate'); + require('./tests/actions/FunctionCreate'); //require('./tests/actions/EnvList'); //require('./tests/actions/EnvGet'); //require('./tests/actions/EnvSetUnset'); diff --git a/tests/test-prj/s-project.json b/tests/test-prj/s-project.json index e42ae8c13..129063061 100644 --- a/tests/test-prj/s-project.json +++ b/tests/test-prj/s-project.json @@ -8,5 +8,68 @@ "custom": {}, "modules": {}, "plugins": [], - "cloudFormation": "$${projectCloudFormation}" + "cloudFormation": { + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway", + "Resources": { + "IamRoleLambda": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + }, + "Action": [ + "sts:AssumeRole" + ] + } + ] + }, + "Path": "/" + } + }, + "IamPolicyLambda": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyName": "${stage}-${project}-lambda", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": "arn:aws:logs:${region}:*:*" + } + ] + }, + "Roles": [ + { + "Ref": "IamRoleLambda" + } + ] + } + } + }, + "Outputs": { + "IamRoleArnLambda": { + "Description": "ARN of the lambda IAM role", + "Value": { + "Fn::GetAtt": [ + "IamRoleLambda", + "Arn" + ] + } + } + } + } } \ No newline at end of file diff --git a/tests/test-prj/s-templates.json b/tests/test-prj/s-templates.json index a4a627f62..f17f5d71e 100644 --- a/tests/test-prj/s-templates.json +++ b/tests/test-prj/s-templates.json @@ -3,69 +3,5 @@ "application/json": { "httpMethod": "$context.httpMethod" } - }, - "projectCloudFormation": { - "AWSTemplateFormatVersion": "2010-09-09", - "Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway", - "Resources": { - "IamRoleLambda": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - }, - "Action": [ - "sts:AssumeRole" - ] - } - ] - }, - "Path": "/" - } - }, - "IamPolicyLambda": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyName": "${stage}-${project}-lambda", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Resource": "arn:aws:logs:${region}:*:*" - } - ] - }, - "Roles": [ - { - "Ref": "IamRoleLambda" - } - ] - } - } - }, - "Outputs": { - "IamRoleArnLambda": { - "Description": "ARN of the lambda IAM role", - "Value": { - "Fn::GetAtt": [ - "IamRoleLambda", - "Arn" - ] - } - } - } } } \ No newline at end of file diff --git a/tests/tests/actions/ComponentCreate.js b/tests/tests/actions/ComponentCreate.js index 3a46d21f3..b8dbaf892 100644 --- a/tests/tests/actions/ComponentCreate.js +++ b/tests/tests/actions/ComponentCreate.js @@ -6,12 +6,12 @@ * - Creates a new Component inside test project */ -let Serverless = require('../../../lib/Serverless.js'), - path = require('path'), - utils = require('../../../lib/utils/index'), - assert = require('chai').assert, - testUtils = require('../../test_utils'), - config = require('../../config'); +let Serverless = require('../../../lib/Serverless.js'), + path = require('path'), + utils = require('../../../lib/utils/index'), + assert = require('chai').assert, + testUtils = require('../../test_utils'), + config = require('../../config'); let serverless; diff --git a/tests/tests/actions/FunctionCreate.js b/tests/tests/actions/FunctionCreate.js index 8b7e2764b..2a482b5c4 100644 --- a/tests/tests/actions/FunctionCreate.js +++ b/tests/tests/actions/FunctionCreate.js @@ -21,9 +21,8 @@ let serverless; */ let validateEvent = function(evt) { - assert.equal(true, typeof evt.options.component != 'undefined'); - assert.equal(true, typeof evt.options.module != 'undefined'); - assert.equal(true, typeof evt.options.function != 'undefined'); + assert.equal(true, typeof evt.options.sPath != 'undefined'); + assert.equal(true, typeof evt.options.name != 'undefined'); }; describe('Test action: Function Create', function() { @@ -56,9 +55,8 @@ describe('Test action: Function Create', function() { this.timeout(0); let evt = { options: { - component: 'nodejscomponent', - module: 'group1', - function: 'new' + sPath: 'nodejscomponent/temp', + name: 'new' } }; diff --git a/tests/tests/actions/TestDefaultActionHook.js b/tests/tests/actions/TestDefaultActionHook.js index e4226f76c..4e61a44c7 100644 --- a/tests/tests/actions/TestDefaultActionHook.js +++ b/tests/tests/actions/TestDefaultActionHook.js @@ -41,7 +41,7 @@ class CustomPlugin extends SPlugin { registerHooks() { this.S.addHook(this._defaultActionPreHook.bind(this), { - action: 'moduleCreate', + action: 'componentCreate', event: 'pre' }); @@ -69,8 +69,6 @@ class CustomPlugin extends SPlugin { let validateResult = function(result) { assert.equal(true, typeof result.options.component != 'undefined'); - assert.equal(true, typeof result.options.module != 'undefined'); - assert.equal(true, typeof result.options.function != 'undefined'); assert.equal(true, typeof result.options.runtime != 'undefined'); assert.equal(true, typeof result.data.hook != 'undefined'); }; @@ -102,23 +100,18 @@ describe('Test Default Action With Pre Hook', function() { describe('Test Default Action With Pre Hook', function() { - it('adds a pre hook to Module Create default Action', function(done) { + it('adds a pre hook to Component Create default Action', function(done) { this.timeout(0); let evt = { options: { - component: 'nodejscomponent', - module: 'newmodule', - function: 'newfunction' + component: 'testcomponent' } }; - serverless.actions.moduleCreate(evt) + serverless.actions.componentCreate(evt) .then(function(result) { validateResult(result); - let functionJson = utils.readAndParseJsonSync(path.join(serverless.config.projectPath, 'nodejscomponent', 'newmodule', 'newfunction', 's-function.json')); - assert.equal(true, typeof functionJson.name != 'undefined'); - assert.equal(true, functionJson.endpoints.length); done(); }) .catch(e => { diff --git a/tests/tests/classes/ServerlessProjectTest.js b/tests/tests/classes/ServerlessProjectTest.js index 09ea8785e..6e34c5d11 100644 --- a/tests/tests/classes/ServerlessProjectTest.js +++ b/tests/tests/classes/ServerlessProjectTest.js @@ -67,7 +67,6 @@ describe('Test Serverless Project Class', function() { let data = instance.getPopulated({ stage: config.stage, region: config.region }); assert.equal(true, JSON.stringify(data).indexOf('$${') == -1); assert.equal(true, JSON.stringify(data).indexOf('${') == -1); - // We've set a template in the project that gets extended at the module level and function level, check it: // Project template assert.equal(true, typeof data.components.nodejscomponent.functions['nodejscomponent/group1/function1'].endpoints[0].requestTemplates['application/json'].httpMethod !== 'undefined'); diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index 2a1a8470a..89d600099 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -100,19 +100,23 @@ describe('Test Serverless State Class', function() { }); it('Get resources (unpopulated)', function(done) { - let resources = instance.getResources(); - console.log(resources) - assert.equal(true, JSON.stringify(resources).indexOf('${') !== -1); - done(); + let resources = instance.getResources() + .then(function(resources) { + console.log(resources) + assert.equal(true, JSON.stringify(resources).indexOf('${') !== -1); + done(); + }); }); it('Get resources (populated)', function(done) { let resources = instance.getResources({ - populate: true, stage: config.stage, region: config.region - }); - assert.equal(true, JSON.stringify(resources).indexOf('$${') == -1); - assert.equal(true, JSON.stringify(resources).indexOf('${') == -1); - done(); + populate: true, stage: config.stage, region: config.region + }) + .then(function() { + assert.equal(true, JSON.stringify(resources).indexOf('$${') == -1); + assert.equal(true, JSON.stringify(resources).indexOf('${') == -1); + done(); + }); }); it('Get stages', function(done) { From eebb7521ff1f81d65d031d924b4259607667e7c1 Mon Sep 17 00:00:00 2001 From: ac360 Date: Fri, 5 Feb 2016 00:35:29 -0800 Subject: [PATCH 17/29] ComponentCreate, FunctionCreate: finish refactor and fix tests --- lib/actions/ComponentCreate.js | 35 ++++++++++++-------------- lib/actions/FunctionCreate.js | 5 ++-- lib/utils/index.js | 2 +- tests/all.js | 2 +- tests/tests/actions/ComponentCreate.js | 13 +++------- tests/tests/actions/FunctionCreate.js | 1 + 6 files changed, 25 insertions(+), 33 deletions(-) diff --git a/lib/actions/ComponentCreate.js b/lib/actions/ComponentCreate.js index b53d105a5..98656e3ff 100644 --- a/lib/actions/ComponentCreate.js +++ b/lib/actions/ComponentCreate.js @@ -6,7 +6,7 @@ * - validates that component doesn't already exist * * Options: - * - component: (String) Name of your new component + * - name: (String) Name of your new component * - runtime: (String) Runtime of your new component. Default: nodejs */ @@ -44,8 +44,8 @@ usage: serverless component create`, contextAction: 'create', options: [ { - option: 'component', - shortcut: 'c', + option: 'name', + shortcut: 'n', description: 'The name of your new component' }, { @@ -75,7 +75,7 @@ usage: serverless component create`, .then(_this._installComponentDependencies) .then(function() { - SCli.log('Successfully created new serverless component: ' + _this.evt.options.component); + SCli.log('Successfully created new serverless component: ' + _this.evt.options.name); /** * Return Action Data @@ -98,13 +98,13 @@ usage: serverless component create`, if (!_this.S.config.interactive) return BbPromise.resolve(); - ['component'].forEach(memberVarKey => { + ['name'].forEach(memberVarKey => { overrides[memberVarKey] = _this.evt.options[memberVarKey]; }); let prompts = { properties: { - component: { + name: { default: 'nodejscomponent', description: 'Enter a name for your new component: '.yellow, message: 'Component name must contain only letters, numbers, hyphens, or underscores.', @@ -118,7 +118,7 @@ usage: serverless component create`, return _this.cliPromptInput(prompts, overrides) .then(function(answers) { - _this.evt.options.component = answers.component; + _this.evt.options.name = answers.name; }); }; @@ -130,7 +130,7 @@ usage: serverless component create`, // non interactive validation if (!this.S.config.interactive) { - if (!this.evt.options.component) { + if (!this.evt.options.name) { return BbPromise.reject(new SError('Component name is required.')); } } @@ -145,14 +145,14 @@ usage: serverless component create`, } // Check is not reserved name - if (['meta', '_meta', 'plugins'].indexOf(this.evt.options.component) != -1) { - return BbPromise.reject(new SError('This component name is reserved: ' + this.evt.options.component, SError.errorCodes.UNKNOWN)); + if (['meta', '_meta', 'plugins'].indexOf(this.evt.options.name) != -1) { + return BbPromise.reject(new SError('This component name is reserved: ' + this.evt.options.name, SError.errorCodes.UNKNOWN)); } // If component exists in project, throw error - if (SUtils.doesComponentExist(this.evt.options.component, this.S.config.projectPath)) { + if (SUtils.doesComponentExist(this.evt.options.name, this.S.config.projectPath)) { return BbPromise.reject(new SError( - 'Component ' + this.evt.options.component + ' already exists', + 'Component ' + this.evt.options.name + ' already exists', SError.errorCodes.INVALID_PROJECT_SERVERLESS )); } @@ -166,12 +166,9 @@ usage: serverless component create`, _createComponentSkeleton() { let component = new this.S.classes.Component(this.S, { - component: this.evt.options.component, - runtime: this.evt.options.runtime + sPath: this.evt.options.name }); - this.S.state.setAsset(component); - return component.save(); }; @@ -184,14 +181,14 @@ usage: serverless component create`, if (_this.evt.options.runtime === 'nodejs') { SCli.log('Installing "serverless-helpers" for this component via NPM...'); SCli.log(`-----------------`); - SUtils.npmInstall(path.join(this.S.config.projectPath, this.evt.options.component)); + SUtils.npmInstall(path.join(this.S.config.projectPath, this.evt.options.name)); SCli.log(`-----------------`); } else if (_this.evt.options.runtime === 'python2.7') { SCli.log("Installing default python dependencies with pip..."); SCli.log(`-----------------`); SUtils.pipPrefixInstall( - path.join(this.S.config.projectPath, this.evt.options.component, 'requirements.txt'), - path.join(this.S.config.projectPath, this.evt.options.component, 'vendored') + path.join(this.S.config.projectPath, this.evt.options.name, 'requirements.txt'), + path.join(this.S.config.projectPath, this.evt.options.name, 'vendored') ); SCli.log(`-----------------`); } diff --git a/lib/actions/FunctionCreate.js b/lib/actions/FunctionCreate.js index df71ebf89..29aa3fa8b 100644 --- a/lib/actions/FunctionCreate.js +++ b/lib/actions/FunctionCreate.js @@ -177,10 +177,10 @@ usage: serverless function create `, if (_this.evt.options.sPath.split('/').length > 1) { let dir = _this.evt.options.sPath.split('/'); let c = dir.shift(); - if (!SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0]))) { + if (dir[0] && !SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0]))) { fs.mkdirSync(path.join(_this.S.config.projectPath, c, dir[0])); } - if (!SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0], dir[1]))) { + if (dir[1] && !SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0], dir[1]))) { fs.mkdirSync(path.join(_this.S.config.projectPath, c, dir[0], dir[1])); } } @@ -206,6 +206,7 @@ usage: serverless function create `, }); func.name = this.evt.options.name; this.S.state.setAsset(func); + this.evt.data.sPath = func._config.sPath; return func.save(); }; } diff --git a/lib/utils/index.js b/lib/utils/index.js index fcd78b649..c8ad17c5b 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -25,7 +25,7 @@ BbPromise.promisifyAll(fs); */ module.exports.supportedRuntimes = { - nodejs: { + "nodejs": { defaultPkgMgr: 'npm', validPkgMgrs: ['npm'] }, diff --git a/tests/all.js b/tests/all.js index 056e0f243..1e34ac850 100644 --- a/tests/all.js +++ b/tests/all.js @@ -19,7 +19,7 @@ describe('All Tests', function() { //require('./tests/actions/TestDefaultActionHook'); //require('./tests/actions/StageCreate'); //require('./tests/actions/RegionCreate'); - //require('./tests/actions/ComponentCreate'); + require('./tests/actions/ComponentCreate'); require('./tests/actions/FunctionCreate'); //require('./tests/actions/EnvList'); //require('./tests/actions/EnvGet'); diff --git a/tests/tests/actions/ComponentCreate.js b/tests/tests/actions/ComponentCreate.js index b8dbaf892..5dcf501c9 100644 --- a/tests/tests/actions/ComponentCreate.js +++ b/tests/tests/actions/ComponentCreate.js @@ -21,9 +21,7 @@ let serverless; */ let validateEvent = function(evt) { - assert.equal(true, typeof evt.options.component != 'undefined'); - assert.equal(true, typeof evt.options.module != 'undefined'); - assert.equal(true, typeof evt.options.function != 'undefined'); + assert.equal(true, typeof evt.options.name != 'undefined'); assert.equal(true, typeof evt.options.runtime != 'undefined'); }; @@ -58,15 +56,10 @@ describe('Test action: Component Create', function() { this.timeout(0); serverless.actions.componentCreate({ - component: 'newcomponent', - module: 'newmodule', - function: 'newfunction' + name: 'newcomponent', + runtime: 'nodejs' }) .then(function(evt) { - let functionJson = utils.readAndParseJsonSync(path.join(serverless.config.projectPath, 'newcomponent', 'newmodule', 'newfunction', 's-function.json')); - assert.equal(true, typeof functionJson.name != 'undefined'); - assert.equal(true, functionJson.endpoints.length); - validateEvent(evt); done(); }) diff --git a/tests/tests/actions/FunctionCreate.js b/tests/tests/actions/FunctionCreate.js index 2a482b5c4..6a4d57fb8 100644 --- a/tests/tests/actions/FunctionCreate.js +++ b/tests/tests/actions/FunctionCreate.js @@ -23,6 +23,7 @@ let serverless; let validateEvent = function(evt) { assert.equal(true, typeof evt.options.sPath != 'undefined'); assert.equal(true, typeof evt.options.name != 'undefined'); + assert.equal(true, typeof evt.data.sPath != 'undefined'); }; describe('Test action: Function Create', function() { From 901b56fff4a80d5a293ed36ac1ac997f97820b07 Mon Sep 17 00:00:00 2001 From: ac360 Date: Fri, 5 Feb 2016 00:36:15 -0800 Subject: [PATCH 18/29] ComponentCreate: add sPath to evt.data --- lib/actions/ComponentCreate.js | 1 + tests/tests/actions/ComponentCreate.js | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/actions/ComponentCreate.js b/lib/actions/ComponentCreate.js index 98656e3ff..5a7fc19c6 100644 --- a/lib/actions/ComponentCreate.js +++ b/lib/actions/ComponentCreate.js @@ -169,6 +169,7 @@ usage: serverless component create`, sPath: this.evt.options.name }); this.S.state.setAsset(component); + this.evt.data.sPath = component._config.sPath; return component.save(); }; diff --git a/tests/tests/actions/ComponentCreate.js b/tests/tests/actions/ComponentCreate.js index 5dcf501c9..f378bcd91 100644 --- a/tests/tests/actions/ComponentCreate.js +++ b/tests/tests/actions/ComponentCreate.js @@ -23,6 +23,7 @@ let serverless; let validateEvent = function(evt) { assert.equal(true, typeof evt.options.name != 'undefined'); assert.equal(true, typeof evt.options.runtime != 'undefined'); + assert.equal(true, typeof evt.data.sPath != 'undefined'); }; describe('Test action: Component Create', function() { From 645b6193978d3a9cb52a63b2d294c031a9312eec Mon Sep 17 00:00:00 2001 From: ac360 Date: Fri, 5 Feb 2016 00:40:25 -0800 Subject: [PATCH 19/29] Fix class tests --- lib/ServerlessFunction.js | 2 +- tests/all.js | 14 +++++++------- tests/tests/actions/TestDefaultActionHook.js | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index c8e8f9447..5f3b6705d 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -344,7 +344,7 @@ class ServerlessFunction { getComponent() { let components = this._S.state.getComponents({ - paths: this._config.sPath.split('/')[0] + paths: [this._config.sPath.split('/')[0]] }); if (components.length === 1) { diff --git a/tests/all.js b/tests/all.js index 1e34ac850..ad3f60311 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,13 +10,13 @@ describe('All Tests', function() { }); after(function() {}); - //require('./tests/classes/ServerlessStateTest'); - //require('./tests/classes/ServerlessProjectTest'); - //require('./tests/classes/ServerlessComponentTest'); - //require('./tests/classes/ServerlessFunctionTest'); - //require('./tests/classes/ServerlessEndpointTest'); - //require('./tests/actions/TestPluginCustom'); - //require('./tests/actions/TestDefaultActionHook'); + require('./tests/classes/ServerlessStateTest'); + require('./tests/classes/ServerlessProjectTest'); + require('./tests/classes/ServerlessComponentTest'); + require('./tests/classes/ServerlessFunctionTest'); + require('./tests/classes/ServerlessEndpointTest'); + require('./tests/actions/TestPluginCustom'); + require('./tests/actions/TestDefaultActionHook'); //require('./tests/actions/StageCreate'); //require('./tests/actions/RegionCreate'); require('./tests/actions/ComponentCreate'); diff --git a/tests/tests/actions/TestDefaultActionHook.js b/tests/tests/actions/TestDefaultActionHook.js index 4e61a44c7..53dd73f9e 100644 --- a/tests/tests/actions/TestDefaultActionHook.js +++ b/tests/tests/actions/TestDefaultActionHook.js @@ -68,7 +68,7 @@ class CustomPlugin extends SPlugin { */ let validateResult = function(result) { - assert.equal(true, typeof result.options.component != 'undefined'); + assert.equal(true, typeof result.options.name != 'undefined'); assert.equal(true, typeof result.options.runtime != 'undefined'); assert.equal(true, typeof result.data.hook != 'undefined'); }; @@ -105,7 +105,7 @@ describe('Test Default Action With Pre Hook', function() { this.timeout(0); let evt = { options: { - component: 'testcomponent' + name: 'testcomponent' } }; From 6078539f22fae2555e27ca3edb1faa542e607624 Mon Sep 17 00:00:00 2001 From: ac360 Date: Fri, 5 Feb 2016 01:01:15 -0800 Subject: [PATCH 20/29] ResourcesDeploy, ServerlessProject.getResources: fix and fix test --- lib/ServerlessProject.js | 147 ++++++++--------- lib/actions/ResourcesDeploy.js | 213 ++++++++++++------------- tests/all.js | 20 +-- tests/tests/actions/EnvSetUnset.js | 2 +- tests/tests/actions/ResourcesDeploy.js | 59 +++---- 5 files changed, 217 insertions(+), 224 deletions(-) diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index 09b14f26f..daa5901f3 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -151,6 +151,70 @@ class ServerlessProject { }); } }) + .then(function() { + + // Check for s-resources-cf.json + if (!_this.cloudFormation && SUtils.fileExistsSync(path.join(_this._S.config.projectPath, 's-resources-cf.json'))) { + _this.cloudFormation = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-resources-cf.json')); + } + + // Backward compat support for this.cloudFormation and s-module.json + if (!_this.cloudFormation) return; + + let cPaths = [], + cfSnippets = []; + + for (let c in _this.components) { + cPaths.push(_this.components[c]._config.fullPath); + } + + return BbPromise.resolve(cPaths) + .each(function (cPath) { + + let cContents = fs.readdirSync(cPath); + return BbPromise.resolve(cContents) + .each(function (sf) { + if (SUtils.fileExistsSync(path.join(cPath, sf, 's-module.json'))) { + let moduleJson = SUtils.readAndParseJsonSync(path.join(cPath, sf, 's-module.json')); + if (moduleJson.cloudFormation) cfSnippets.push(moduleJson.cloudFormation); + } + }); + }) + .then(function () { + + // Merge s-module.json CF syntax + for (let i = 0; i < cfSnippets.length; i++) { + + let cf = cfSnippets[i]; + + // Merge Lambda Policy Statements + if (cf.lambdaIamPolicyDocumentStatements && cf.lambdaIamPolicyDocumentStatements.length > 0) { + cf.lambdaIamPolicyDocumentStatements.forEach(function(policyStmt) { + try { + _this.cloudFormation.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); + } + catch(e){} + }); + } + + // Merge Resources + if (cf.resources) { + let cfResourceKeys = Object.keys(cf.resources); + cfResourceKeys.forEach(function (resourceKey) { + if (_this.cloudFormation.Resources[resourceKey]) { + SCli.log(`WARN: Resource key ${resourceKey} already defined in CF template. Overwriting...`); + } + try { + _this.cloudFormation.Resources[resourceKey] = cf.resources[resourceKey]; + } catch(e){} + }); + } + } + }) + .then(function() { + + }); + }) .then(function () { // Merge @@ -244,86 +308,15 @@ class ServerlessProject { getResources(options) { - let _this = this, - cfTemplate; + let _this = this; options = options || {}; - return BbPromise.try(function() { - - // Check for s-cloudFormation.json - if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, 's-cloudFormation.json'))) { - cfTemplate = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-cloudFormation.json')); - } - - // Validate: Can't have s-cloudFormation.json & project.cloudFormation - if (cfTemplate && _this.cloudFormation) { - throw new SError('You can\'t have a s-cloudFormation.json and cloudFormation syntax in s-project.json'); - } - - // Backward compat support for this.cloudFormation and s-module.json - if (!_this.cloudFormation) return; - - cfTemplate = _.clone(_this.cloudFormation); - - let cPaths = [], - cfSnippets = []; - - for (let c in _this.components) { - cPaths.push(_this.components[c]._config.fullPath); - } - - return BbPromise.resolve(cPaths) - .each(function (cPath) { - - let cContents = fs.readdirSync(cPath); - return BbPromise.resolve(cContents) - .each(function (sf) { - if (SUtils.fileExistsSync(path.join(cPath, sf, 's-module.json'))) { - let moduleJson = SUtils.readAndParseJsonSync(path.join(cPath, sf, 's-module.json')); - if (moduleJson.cloudFormation) cfSnippets.push(moduleJson.cloudFormation); - } - }); - }) - .then(function () { - - // Merge s-module.json CF syntax - for (let i = 0; i < cfSnippets.length; i++) { - - let cf = cfSnippets[i]; - - // Merge Lambda Policy Statements - if (cf.lambdaIamPolicyDocumentStatements && cf.lambdaIamPolicyDocumentStatements.length > 0) { - cf.lambdaIamPolicyDocumentStatements.forEach(function(policyStmt) { - try { - cfTemplate.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); - } - catch(e){} - }); - } - - // Merge Resources - if (cf.resources) { - let cfResourceKeys = Object.keys(cf.resources); - cfResourceKeys.forEach(function (resourceKey) { - if (cfTemplate.Resources[resourceKey]) { - SCli.log(`WARN: Resource key ${resourceKey} already defined in CF template. Overwriting...`); - } - try { - cfTemplate.Resources[resourceKey] = cf.resources[resourceKey]; - } catch(e){} - }); - } - } - }); - }) - .then(function() { - if (options.populate) { - return SUtils.populate(_this._S.state.getMeta(), _this.getTemplates(), cfTemplate, options.stage, options.region); - } else { - return cfTemplate; - } - }); + if (options.populate) { + return SUtils.populate(_this._S.state.getMeta(), _this.getTemplates(), _this.cloudFormation, options.stage, options.region); + } else { + return _this.cloudFormation; + } } /** diff --git a/lib/actions/ResourcesDeploy.js b/lib/actions/ResourcesDeploy.js index bfa693aba..4535ad441 100644 --- a/lib/actions/ResourcesDeploy.js +++ b/lib/actions/ResourcesDeploy.js @@ -153,119 +153,118 @@ usage: serverless resources deploy`, _deployResources() { - let _this = this; + let _this = this; let regionVars = _this.S.state.getMeta().stages[_this.evt.options.stage].regions[_this.evt.options.region].variables; - return _this.S.state.getResources({ + return BbPromise.try(function() { + + _this.cfTemplate = _this.S.state.getResources({ populate: true, stage: _this.evt.options.stage, region: _this.evt.options.region - }) - .then(function(resources) { - - _this.cfTemplate = resources; - - // Create CloudFormation template in _meta folder - return SUtils.writeFile( - path.join(_this.S.config.projectPath, '_meta', 'resources', 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'), - JSON.stringify(_this.cfTemplate, null, 2)) - - }).then(function() { - - // If no NoExeCF is set, skip - if (_this.evt.options.noExeCf) { - - // Status - SCli.log('Notice -- You have chosen not to deploy your resources to CloudFormation. ' + - 'A CloudFormation template has been saved here: _meta/resources/' + - 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'); - - // Return - return; - } - - // Config AWS Services - let awsConfig = { - region: _this.evt.options.region, - accessKeyId: _this.S.config.awsAdminKeyId, - secretAccessKey: _this.S.config.awsAdminSecretKey - }; - _this.CF = require('../utils/aws/CloudFormation')(awsConfig); - _this.S3 = require('../utils/aws/S3')(awsConfig); - - // Otherwise, deploy to CloudFormation - SCli.log('Deploying resources to stage "' - + _this.evt.options.stage - + '" in region "' - + _this.evt.options.region - + '" via Cloudformation (~3 minutes)...'); - - // Start spinner - _this._spinner = SCli.spinner(); - _this._spinner.start(); - - // Upload to S3 Bucket - return _this.S3.sPutCfFile( - _this.S.state.getMeta().variables.projectBucket, - _this.S.config.projectPath, - _this.S.state.getProject().name, - _this.evt.options.stage, - _this.evt.options.region, - _this.cfTemplate - ) - .then(function(templateUrl) { - - // Trigger CF Stack Create/Update - return _this.CF.sCreateOrUpdateResourcesStack( - _this.S.state.getProject().name, - _this.evt.options.stage, - _this.evt.options.region, - regionVars.resourcesStackName ? regionVars.resourcesStackName : null, - templateUrl) - .then(cfData => { - - // If string, log output - if (typeof cfData === 'string') { - _this._spinner.stop(true); - SCli.log(cfData); - return; - } - - // Monitor CF Status - return _this.CF.sMonitorCf(cfData) - .then(cfStackData => { - - // Save stack name - regionVars.resourcesStackName = cfStackData.StackName; - - // Save IAM Role ARN for Project Lambdas - for (let i = 0; i < cfStackData.Outputs.length; i++) { - if (cfStackData.Outputs[i].OutputKey === 'IamRoleArnLambda') { - regionVars.iamRoleArnLambda = cfStackData.Outputs[i].OutputValue; - } - } - }) - .then(() => { - - // Stop Spinner - _this._spinner.stop(true); - - // Save State - _this.S.state.save(); - - // Status - SCli.log('Successfully deployed "' + _this.evt.options.stage + '" resources to "' + _this.evt.options.region + '"'); - }); - }) - }) - .catch(function(e) { - - // Stop Spinner - _this._spinner.stop(true); - - throw new SError(e); - }) }); + + // Create CloudFormation template in _meta folder + return SUtils.writeFile( + path.join(_this.S.config.projectPath, '_meta', 'resources', 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'), + JSON.stringify(_this.cfTemplate, null, 2)) + + }).then(function() { + + // If no NoExeCF is set, skip + if (_this.evt.options.noExeCf) { + + // Status + SCli.log('Notice -- You have chosen not to deploy your resources to CloudFormation. ' + + 'A CloudFormation template has been saved here: _meta/resources/' + + 's-resources-cf-' + _this.evt.options.stage + '-' + replaceall('-', '', _this.evt.options.region) + '.json'); + + // Return + return; + } + + // Config AWS Services + let awsConfig = { + region: _this.evt.options.region, + accessKeyId: _this.S.config.awsAdminKeyId, + secretAccessKey: _this.S.config.awsAdminSecretKey + }; + _this.CF = require('../utils/aws/CloudFormation')(awsConfig); + _this.S3 = require('../utils/aws/S3')(awsConfig); + + // Otherwise, deploy to CloudFormation + SCli.log('Deploying resources to stage "' + + _this.evt.options.stage + + '" in region "' + + _this.evt.options.region + + '" via Cloudformation (~3 minutes)...'); + + // Start spinner + _this._spinner = SCli.spinner(); + _this._spinner.start(); + + // Upload to S3 Bucket + return _this.S3.sPutCfFile( + _this.S.state.getMeta().variables.projectBucket, + _this.S.config.projectPath, + _this.S.state.getProject().name, + _this.evt.options.stage, + _this.evt.options.region, + _this.cfTemplate + ) + .then(function(templateUrl) { + + // Trigger CF Stack Create/Update + return _this.CF.sCreateOrUpdateResourcesStack( + _this.S.state.getProject().name, + _this.evt.options.stage, + _this.evt.options.region, + regionVars.resourcesStackName ? regionVars.resourcesStackName : null, + templateUrl) + .then(cfData => { + + // If string, log output + if (typeof cfData === 'string') { + _this._spinner.stop(true); + SCli.log(cfData); + return; + } + + // Monitor CF Status + return _this.CF.sMonitorCf(cfData) + .then(cfStackData => { + + // Save stack name + regionVars.resourcesStackName = cfStackData.StackName; + + // Save IAM Role ARN for Project Lambdas + for (let i = 0; i < cfStackData.Outputs.length; i++) { + if (cfStackData.Outputs[i].OutputKey === 'IamRoleArnLambda') { + regionVars.iamRoleArnLambda = cfStackData.Outputs[i].OutputValue; + } + } + }) + .then(() => { + + // Stop Spinner + _this._spinner.stop(true); + + // Save State + _this.S.state.save(); + + // Status + SCli.log('Successfully deployed "' + _this.evt.options.stage + '" resources to "' + _this.evt.options.region + '"'); + }); + }) + }) + .catch(function(e) { + + // Stop Spinner + _this._spinner.stop(true); + + throw new SError(e); + }) + }); } } diff --git a/tests/all.js b/tests/all.js index ad3f60311..28760484a 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,21 +10,21 @@ describe('All Tests', function() { }); after(function() {}); - require('./tests/classes/ServerlessStateTest'); - require('./tests/classes/ServerlessProjectTest'); - require('./tests/classes/ServerlessComponentTest'); - require('./tests/classes/ServerlessFunctionTest'); - require('./tests/classes/ServerlessEndpointTest'); - require('./tests/actions/TestPluginCustom'); - require('./tests/actions/TestDefaultActionHook'); + //require('./tests/classes/ServerlessStateTest'); + //require('./tests/classes/ServerlessProjectTest'); + //require('./tests/classes/ServerlessComponentTest'); + //require('./tests/classes/ServerlessFunctionTest'); + //require('./tests/classes/ServerlessEndpointTest'); + //require('./tests/actions/TestPluginCustom'); + //require('./tests/actions/TestDefaultActionHook'); //require('./tests/actions/StageCreate'); //require('./tests/actions/RegionCreate'); - require('./tests/actions/ComponentCreate'); - require('./tests/actions/FunctionCreate'); + //require('./tests/actions/ComponentCreate'); + //require('./tests/actions/FunctionCreate'); //require('./tests/actions/EnvList'); //require('./tests/actions/EnvGet'); //require('./tests/actions/EnvSetUnset'); - //require('./tests/actions/ResourcesDeploy'); + require('./tests/actions/ResourcesDeploy'); //require('./tests/actions/FunctionRun'); //require('./tests/actions/FunctionDeploy'); //require('./tests/actions/EndpointDeploy'); diff --git a/tests/tests/actions/EnvSetUnset.js b/tests/tests/actions/EnvSetUnset.js index 946204803..0cf55eb7b 100644 --- a/tests/tests/actions/EnvSetUnset.js +++ b/tests/tests/actions/EnvSetUnset.js @@ -78,7 +78,7 @@ describe('Test Env Set & Env Unset actions', function() { let unsetEvt = { stage: setEvt.options.stage, region: setEvt.options.region, - key: setEvt.options.key, + key: setEvt.options.key }; serverless.actions.envUnset(unsetEvt) diff --git a/tests/tests/actions/ResourcesDeploy.js b/tests/tests/actions/ResourcesDeploy.js index 0fb4ba096..9c771ffe8 100644 --- a/tests/tests/actions/ResourcesDeploy.js +++ b/tests/tests/actions/ResourcesDeploy.js @@ -9,13 +9,13 @@ */ let Serverless = require('../../../lib/Serverless.js'), - path = require('path'), - utils = require('../../../lib/utils/index'), - assert = require('chai').assert, - testUtils = require('../../test_utils'), - SUtils = require('../../../lib/utils/index'), - fs = require('fs'), - config = require('../../config'); + path = require('path'), + utils = require('../../../lib/utils/index'), + assert = require('chai').assert, + testUtils = require('../../test_utils'), + SUtils = require('../../../lib/utils/index'), + fs = require('fs'), + config = require('../../config'); let serverless; @@ -35,33 +35,34 @@ describe('Test action: Resources Deploy', function() { before(function(done) { this.timeout(0); testUtils.createTestProject(config) - .then(projPath => { + .then(projPath => { - process.chdir(projPath); + process.chdir(projPath); - serverless = new Serverless({ - interactive: false, - awsAdminKeyId: config.awsAdminKeyId, - awsAdminSecretKey: config.awsAdminSecretKey, - projectPath: projPath - }); + serverless = new Serverless({ + interactive: false, + awsAdminKeyId: config.awsAdminKeyId, + awsAdminSecretKey: config.awsAdminSecretKey, + projectPath: projPath + }); - return serverless.init() - .then(function() { + return serverless.init() + .then(function() { SUtils.sDebug('Adding test bucket resource'); let newProject = serverless.state.project.get(); - // adding new Module resource - newProject.components.nodejscomponent.modules.module1.cloudFormation.resources['testBucket' + (new Date).getTime().toString()] = { "Type" : "AWS::S3::Bucket" }; - serverless.state.set({project: newProject}); + + // Adding new Module resource + newProject.cloudFormation.Resources['testBucket' + (new Date).getTime().toString()] = { "Type" : "AWS::S3::Bucket" }; + serverless.state.setAsset(newProject); return serverless.state.save() .then(function() { done(); }); }); - }); + }); }); after(function(done) { @@ -80,16 +81,16 @@ describe('Test action: Resources Deploy', function() { }; serverless.actions.resourcesDeploy(evt) - .then(function(evt) { + .then(function(evt) { - // Validate Evt - validateEvent(evt); - done(); + // Validate Evt + validateEvent(evt); + done(); - }) - .catch(e => { - done(e); - }); + }) + .catch(e => { + done(e); + }); }); }); }); From e5b877ff401cabf236449ee9596f3c3159a6c096 Mon Sep 17 00:00:00 2001 From: ac360 Date: Fri, 5 Feb 2016 16:31:09 -0800 Subject: [PATCH 21/29] FunctionCreate: start refactoring to only use sPath --- lib/Serverless.js | 1 - lib/ServerlessPlugin.js | 59 ++++++++++++------------- lib/actions/FunctionCreate.js | 49 ++++++-------------- lib/actions/FunctionDeploy.js | 3 -- lib/actions/FunctionRun.js | 12 +++-- lib/actions/FunctionRunLambdaNodeJs.js | 28 +++--------- lib/actions/FunctionRunLambdaPython2.js | 28 +++--------- lib/utils/index.js | 2 +- tests/all.js | 4 +- 9 files changed, 68 insertions(+), 118 deletions(-) diff --git a/lib/Serverless.js b/lib/Serverless.js index 004b3f4a9..c4dd2d00c 100644 --- a/lib/Serverless.js +++ b/lib/Serverless.js @@ -58,7 +58,6 @@ class Serverless { this.cli = null; this.state = new this.classes.State(this); - // If project if (this.config.projectPath) { diff --git a/lib/ServerlessPlugin.js b/lib/ServerlessPlugin.js index b7d0fa490..689145340 100644 --- a/lib/ServerlessPlugin.js +++ b/lib/ServerlessPlugin.js @@ -3,6 +3,7 @@ const SError = require('./ServerlessError'), SUtils = require('./utils/index'), SCli = require('./utils/cli'), + path = require('path'), awsMisc = require('./utils/aws/Misc'), BbPromise = require('bluebird'); @@ -44,6 +45,34 @@ class ServerlessPlugin { return BbPromise.resolve(); } + /** + * Get sPath From CWD + * - Gets the sPath of the cwd, if any. + * - Returns false if project root or outside project + */ + + getSPathFromCwd(projectPath) { + + let cwd = process.cwd(); + + // Check if in project + if (cwd.indexOf(projectPath) == -1) return false; + + // Strip project path from cwd + cwd = cwd.replace(projectPath, '').split(path.sep); + + // In component + if (cwd.length === 2) return cwd[1]; + // In component subfolder 1 + if (cwd.length === 3) return cwd[1] + '/' + cwd[2]; + // In component subfolder 2 + if (cwd.length === 4) return cwd[1] + '/' + cwd[2] + '/' + cwd[3]; + // In component subfolder 3 + if (cwd.length === 5) return cwd[1] + '/' + cwd[2] + '/' + cwd[3] + '/' + cwd[4]; + + return false; + } + /** * CLI: Prompt Input * - Handy CLI Prompt Input function for Plugins @@ -202,36 +231,6 @@ class ServerlessPlugin { return results[0].value; }); } - - cliPromptSelectComponent(message, component) { - - let _this = this; - - if (component || !_this.S.config.interactive) return BbPromise.resolve(component); - - let componentChoices = Object.keys(_this.S.state.project.components), - choices = []; - - if(componentChoices.length === 0) { - return BbPromise.reject(new SError('Your project has no components.', SError.errorCodes.UNKNOWN)); - } - - if(componentChoices.length === 1) return BbPromise.resolve(componentChoices[0]); - - componentChoices.forEach(function(r) { - choices.push({ - key: '', - value: r, - label: r - }); - }); - - return _this.cliPromptSelect(message, choices, false) - .then(results => { - return results[0].value; - }); - - } } module.exports = ServerlessPlugin; diff --git a/lib/actions/FunctionCreate.js b/lib/actions/FunctionCreate.js index 29aa3fa8b..72a359ae8 100644 --- a/lib/actions/FunctionCreate.js +++ b/lib/actions/FunctionCreate.js @@ -9,7 +9,6 @@ * * Event Options: * - sPath: (String) The relative path of the function from project root - * - name: (String) Name of the new function for your existing component */ module.exports = function(SPlugin, serverlessPath) { @@ -43,13 +42,7 @@ module.exports = function(SPlugin, serverlessPath) { usage: serverless function create `, context: 'function', contextAction: 'create', - options: [ - { - option: 'name', - shortcut: 'n', - description: 'The name of your new function' - } - ], + options: [], parameters: [ { parameter: 'sPath', @@ -70,11 +63,14 @@ usage: serverless function create `, let _this = this; _this.evt = evt; + + return _this._prompt() .bind(_this) - .then(_this._validateAndPrepare) + .then(_this._validateAndPrepare()) .then(_this._createFunctionSkeleton) .then(function() { + SCli.log('Successfully created function: "' + _this.evt.options.name + '"'); /** @@ -87,7 +83,7 @@ usage: serverless function create `, } /** - * Prompt sPath & function if they're missing + * Prompt component, module & function if they're missing */ _prompt() { @@ -95,15 +91,12 @@ usage: serverless function create `, let _this = this, overrides = {}; - if (!_this.S.config.interactive) return BbPromise.resolve(); - - ['name'].forEach(memberVarKey => { - overrides[memberVarKey] = _this.evt.options[memberVarKey]; - }); + // If non-interactive or sPath exists, skip + if (!_this.S.config.interactive || _this.evt.options.sPath) return BbPromise.resolve(); let prompts = { properties: { - name: { + function: { description: 'Enter a new function name: '.yellow, message: 'Function name must contain only letters, numbers, hyphens, or underscores.', required: true, @@ -116,11 +109,10 @@ usage: serverless function create `, return _this.cliPromptInput(prompts, overrides) .then(function(answers) { - _this.evt.options.name = answers.name; + _this.evt.options.sPath = answers.function; }); }; - /** * Validate and prepare data before creating module */ @@ -129,23 +121,10 @@ usage: serverless function create `, let _this = this; - // Validate: check name - if (!_this.evt.options.name) { - return BbPromise.reject(new SError('name is required.')); - } - // Validate: If interactive and no sPath, check they are in a component, and get sPath if (_this.S.config.interactive && !_this.evt.options.sPath) { - - let cwdArray = process.cwd().split(path.sep); - - if (SUtils.fileExistsSync(path.join(process.cwd(), 's-component.json'))) { - _this.evt.options.sPath = cwdArray.splice(cwdArray.length - 1, 1).join('/') - } else if (SUtils.fileExistsSync(path.join(process.cwd(), '..', 's-component.json'))) { - _this.evt.options.sPath = cwdArray.splice(cwdArray.length - 2, 2).join('/') - } else if (SUtils.fileExistsSync(path.join(process.cwd(), '..', '..', 's-component.json'))) { - _this.evt.options.sPath = cwdArray.splice(cwdArray.length - 3, 3).join('/') - } else { + _this.evt.options.sPath = _this.getSPathFromCwd(_this.S.config.projectPath); + if (!_this.evt.options.sPath) { return BbPromise.reject(new SError('You must be in a component or two subfolders max in a component to create a function')); } } @@ -156,7 +135,7 @@ usage: serverless function create `, } // Validate: Don't allow function creation within a function - if (SUtils.fileExistsSync(path.join(_this.S.config.projectPath, _this.evt.options.sPath.split('/').join(path.sep), 's-function.json'))) { + if (_this.S.state.getFunctions({ paths: [ _this.evt.options.sPath ] }).length) { return BbPromise.reject(new SError('You cannot create a function in another function')); } @@ -168,7 +147,7 @@ usage: serverless function create `, // If component does not exist in project, throw error if (!_this.S.state.getComponents({ paths: [_this.evt.options.sPath] }).length) { return BbPromise.reject(new SError( - 'Component does NOT exist in ' + _this.evt.options.sPath, + 'Component (' + _this.evt.options.sPath.split('/')[0] + ') does not exist in project', SError.errorCodes.INVALID_PROJECT_SERVERLESS )); } diff --git a/lib/actions/FunctionDeploy.js b/lib/actions/FunctionDeploy.js index d5fd0e2e2..59a45b080 100644 --- a/lib/actions/FunctionDeploy.js +++ b/lib/actions/FunctionDeploy.js @@ -183,9 +183,6 @@ module.exports = function(SPlugin, serverlessPath) { // If CLI and no functions targeted, deploy from CWD if Function, otherwise error if (_this.S.cli && !_this.evt.options.paths.length && - !_this.evt.options.component && - !_this.evt.options.module && - !_this.evt.options.function && !_this.evt.options.all) { if (SUtils.fileExistsSync(path.join(process.cwd(), 's-function.json'))) { diff --git a/lib/actions/FunctionRun.js b/lib/actions/FunctionRun.js index 95d79dd8f..f44c7aede 100644 --- a/lib/actions/FunctionRun.js +++ b/lib/actions/FunctionRun.js @@ -72,6 +72,11 @@ module.exports = function(SPlugin, serverlessPath) { let _this = this; _this.evt = evt; + // Ensure in component + if (!_this.getSPathFromCwd(_this.S.config.projectPath)) { + return BbPromise.reject('You must be in a function folder to run a function'); + } + // Instantiate Classes _this.project = _this.S.state.project.get(); @@ -83,8 +88,7 @@ module.exports = function(SPlugin, serverlessPath) { let moduleName = path.basename(path.join(process.cwd(), '..')); let functionName = path.basename(process.cwd()); _this.evt.options.path = componentName + "/" + moduleName + "/" + functionName; - } - else { + } else { return BbPromise.reject(new SError('Missing required function path param. Run from within a function directory, or add a function path in this format: componentName/moduleName/functionName')); } } @@ -100,7 +104,7 @@ module.exports = function(SPlugin, serverlessPath) { .then(_this._runByRuntime) .then(function(evt) { - // delete temp stage/region env var + // Delete temp stage/region env var if (this.evt.options.stage && this.evt.options.region) { fs.unlinkSync(path.join(this.S.config.projectPath, this.evt.options.path.split('/')[0], '.env')); } @@ -154,7 +158,7 @@ module.exports = function(SPlugin, serverlessPath) { _runByRuntime() { let _this = this, - runtime = _this.S.state.getComponents({"paths": [_this.function._config.component]})[0].runtime + runtime = _this.S.state.getComponents({ "paths": [_this.function._config.sPath] })[0].runtime let newOptions = { options: { diff --git a/lib/actions/FunctionRunLambdaNodeJs.js b/lib/actions/FunctionRunLambdaNodeJs.js index cf8ca7b82..a0e637fe7 100644 --- a/lib/actions/FunctionRunLambdaNodeJs.js +++ b/lib/actions/FunctionRunLambdaNodeJs.js @@ -14,7 +14,6 @@ module.exports = function(SPlugin, serverlessPath) { chalk = require('chalk'), context = require(path.join(serverlessPath, 'utils/context')); - class FunctionRunLambdaNodeJs extends SPlugin { constructor(S, config) { @@ -48,20 +47,8 @@ module.exports = function(SPlugin, serverlessPath) { return BbPromise.reject(new SError('Invalid function path. Function path should be in this format: component/module/function .')); } - _this.component = _this.evt.options.path.split('/')[0]; - _this.module = _this.evt.options.path.split('/')[1]; - _this.function = _this.evt.options.path.split('/')[2]; - - - if (!SUtils.doesFunctionExist(_this.function, _this.component, _this.S.config.projectPath)) { - return BbPromise.reject(new SError( - 'This function path does not exist', - SError.errorCodes.INVALID_PROJECT_SERVERLESS - )); - } - // Instantiate Classes - _this.functionData = _this.S.state.getFunctions({ paths: [_this.evt.options.path] })[0]; + _this.function = _this.S.state.getFunctions({ paths: [_this.evt.options.path] })[0]; // Prepare result object _this.evt.data.result = { status: false }; @@ -69,20 +56,19 @@ module.exports = function(SPlugin, serverlessPath) { // Run Function return new BbPromise(function(resolve) { - SCli.log(`Running ${_this.functionData._config.sPath}...`); + SCli.log(`Running ${_this.function._config.sPath}...`); try { // Load function file & handler - let functionFile = _this.functionData.handler.split('/').pop().split('.')[0]; - let functionHandler = _this.functionData.handler.split('/').pop().split('.')[1]; - let functionPath = path.join(_this.S.config.projectPath, _this.component, _this.module, _this.functionData.name); - functionFile = path.join(functionPath, (functionFile + '.js')); + let functionFile = _this.function.handler.split('/').pop().split('.')[0]; + let functionHandler = _this.function.handler.split('/').pop().split('.')[1]; + functionFile = path.join(_this.function._config.fullPath, (functionFile + '.js')); functionHandler = require(functionFile)[functionHandler]; // Fire function - let functionEvent = SUtils.readAndParseJsonSync(path.join(functionPath, 'event.json')); - functionHandler(functionEvent, context(_this.functionData.name, function (err, result) { + let functionEvent = SUtils.readAndParseJsonSync(path.join(_this.function._config.fullPath, 'event.json')); + functionHandler(functionEvent, context(_this.function.name, function (err, result) { SCli.log(`-----------------`); diff --git a/lib/actions/FunctionRunLambdaPython2.js b/lib/actions/FunctionRunLambdaPython2.js index 7fbfa5695..4f9013018 100644 --- a/lib/actions/FunctionRunLambdaPython2.js +++ b/lib/actions/FunctionRunLambdaPython2.js @@ -14,7 +14,6 @@ module.exports = function(SPlugin, serverlessPath) { chalk = require('chalk'), context = require(path.join(serverlessPath, 'utils/context')); - class FunctionRunLambdaPython2 extends SPlugin { constructor(S, config) { @@ -48,19 +47,8 @@ module.exports = function(SPlugin, serverlessPath) { return BbPromise.reject(new SError('Invalid function path. Function path should be in this format: component/module/function .')); } - _this.component = _this.evt.options.path.split('/')[0]; - _this.module = _this.evt.options.path.split('/')[1]; - _this.function = _this.evt.options.path.split('/')[2]; - - if (!SUtils.doesFunctionExist(_this.function, _this.component, _this.S.config.projectPath)) { - return BbPromise.reject(new SError( - 'This function path does not exist', - SError.errorCodes.INVALID_PROJECT_SERVERLESS - )); - } - // Instantiate Classes - _this.functionData = _this.S.state.getFunctions({ paths: [_this.evt.options.path] })[0]; + _this.function = _this.S.state.getFunctions({ paths: [_this.evt.options.path] })[0]; // Prepare result object _this.evt.data.result = { status: false }; @@ -68,25 +56,23 @@ module.exports = function(SPlugin, serverlessPath) { // Run Function return new BbPromise(function(resolve) { - SCli.log(`Running ${_this.functionData._config.sPath}...`); + SCli.log(`Running ${_this.function._config.sPath}...`); try { // Load function file & handler - let functionFile = _this.functionData.handler.split('/').pop().split('.')[0]; - let functionHandler = _this.functionData.handler.split('/').pop().split('.')[1]; - let functionPath = path.join(_this.S.config.projectPath, _this.component, _this.module, _this.functionData.name); - let functionEvent = SUtils.readAndParseJsonSync(path.join(functionPath, 'event.json')); + let functionFile = _this.function.handler.split('/').pop().split('.')[0]; + let functionHandler = _this.function.handler.split('/').pop().split('.')[1]; + let functionEvent = SUtils.readAndParseJsonSync(path.join(_this.function._config.fullPath, 'event.json')); - functionFile = path.join(functionPath, (functionFile + '.py')); - //functionHandler = require(functionFile)[functionHandler]; + functionFile = path.join(_this.function._config.fullPath, (functionFile + '.py')); var child = spawnSync( "serverless-run-python-handler", [ '--event', JSON.stringify(functionEvent), '--handler-path', functionFile, - '--handler-function', functionHandler, + '--handler-function', functionHandler ], {} ); diff --git a/lib/utils/index.js b/lib/utils/index.js index c8ad17c5b..f6da13a73 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -109,7 +109,7 @@ exports.getProjectPath = function(startDir) { } // Check up to 10 parent levels - let previous = './', + let previous = './', projRootPath = false; for (let i = 0; i < 10; i++) { diff --git a/tests/all.js b/tests/all.js index 28760484a..45900f5f9 100644 --- a/tests/all.js +++ b/tests/all.js @@ -24,8 +24,8 @@ describe('All Tests', function() { //require('./tests/actions/EnvList'); //require('./tests/actions/EnvGet'); //require('./tests/actions/EnvSetUnset'); - require('./tests/actions/ResourcesDeploy'); - //require('./tests/actions/FunctionRun'); + //require('./tests/actions/ResourcesDeploy'); + require('./tests/actions/FunctionRun'); //require('./tests/actions/FunctionDeploy'); //require('./tests/actions/EndpointDeploy'); //require('./tests/actions/ProjectInit'); From cea1a30e8d36f5bea81af80a2b5002c6c01a1858 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Fri, 5 Feb 2016 20:14:29 -0800 Subject: [PATCH 22/29] FunctionCreate: fix functioncreate --- lib/actions/FunctionCreate.js | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/actions/FunctionCreate.js b/lib/actions/FunctionCreate.js index 72a359ae8..f2b89c354 100644 --- a/lib/actions/FunctionCreate.js +++ b/lib/actions/FunctionCreate.js @@ -63,15 +63,13 @@ usage: serverless function create `, let _this = this; _this.evt = evt; - - return _this._prompt() .bind(_this) - .then(_this._validateAndPrepare()) + .then(_this._validateAndPrepare) .then(_this._createFunctionSkeleton) .then(function() { - SCli.log('Successfully created function: "' + _this.evt.options.name + '"'); + SCli.log('Successfully created function: "' + _this.evt.options.sPath + '"'); /** * Return Event @@ -88,15 +86,23 @@ usage: serverless function create `, _prompt() { - let _this = this, + let _this = this, overrides = {}; // If non-interactive or sPath exists, skip if (!_this.S.config.interactive || _this.evt.options.sPath) return BbPromise.resolve(); + // Get sPath + _this.evt.options.sPath = _this.getSPathFromCwd(_this.S.config.projectPath); + + // Validate + if (!_this.evt.options.sPath) { + return BbPromise.reject(new SError('You must be in a component to create a function')); + } + let prompts = { properties: { - function: { + name: { description: 'Enter a new function name: '.yellow, message: 'Function name must contain only letters, numbers, hyphens, or underscores.', required: true, @@ -109,7 +115,7 @@ usage: serverless function create `, return _this.cliPromptInput(prompts, overrides) .then(function(answers) { - _this.evt.options.sPath = answers.function; + _this.evt.options.sPath = _this.evt.options.sPath + '/' + answers.name; }); }; @@ -155,6 +161,7 @@ usage: serverless function create `, // If subfolders are missing, create them if (_this.evt.options.sPath.split('/').length > 1) { let dir = _this.evt.options.sPath.split('/'); + dir.pop(); let c = dir.shift(); if (dir[0] && !SUtils.dirExistsSync(path.join(_this.S.config.projectPath, c, dir[0]))) { fs.mkdirSync(path.join(_this.S.config.projectPath, c, dir[0])); @@ -165,9 +172,9 @@ usage: serverless function create `, } // If function already exists in component, throw error - if (_this.S.state.getFunctions({ paths: [_this.evt.options.sPath + '/' + _this.evt.options.name] }).length) { + if (_this.S.state.getFunctions({ paths: [_this.evt.options.sPath] }).length) { return BbPromise.reject(new SError( - 'Function ' + _this.evt.options.name + ' already exists in ' + _this.evt.options.sPath, + 'Function ' + _this.evt.options.sPath + ' already exists in this component', SError.errorCodes.INVALID_PROJECT_SERVERLESS )); } @@ -181,9 +188,9 @@ usage: serverless function create `, _createFunctionSkeleton() { let func = new this.S.classes.Function(this.S, { - sPath: this.evt.options.sPath + '/' + this.evt.options.name + sPath: this.evt.options.sPath }); - func.name = this.evt.options.name; + func.name = this.evt.options.sPath.split('/').pop(); this.S.state.setAsset(func); this.evt.data.sPath = func._config.sPath; return func.save(); From b2ba5dd1a5952e029093ff5227ede008b6b7cf0f Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Fri, 5 Feb 2016 20:18:08 -0800 Subject: [PATCH 23/29] ServerlessState.getResources(): remove promises --- tests/all.js | 30 ++++---- tests/tests/classes/ServerlessStateTest.js | 87 ++++++++++------------ 2 files changed, 56 insertions(+), 61 deletions(-) diff --git a/tests/all.js b/tests/all.js index 45900f5f9..3ea6d3d41 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,21 +10,21 @@ describe('All Tests', function() { }); after(function() {}); - //require('./tests/classes/ServerlessStateTest'); - //require('./tests/classes/ServerlessProjectTest'); - //require('./tests/classes/ServerlessComponentTest'); - //require('./tests/classes/ServerlessFunctionTest'); - //require('./tests/classes/ServerlessEndpointTest'); - //require('./tests/actions/TestPluginCustom'); - //require('./tests/actions/TestDefaultActionHook'); - //require('./tests/actions/StageCreate'); - //require('./tests/actions/RegionCreate'); - //require('./tests/actions/ComponentCreate'); - //require('./tests/actions/FunctionCreate'); - //require('./tests/actions/EnvList'); - //require('./tests/actions/EnvGet'); - //require('./tests/actions/EnvSetUnset'); - //require('./tests/actions/ResourcesDeploy'); + require('./tests/classes/ServerlessStateTest'); + require('./tests/classes/ServerlessProjectTest'); + require('./tests/classes/ServerlessComponentTest'); + require('./tests/classes/ServerlessFunctionTest'); + require('./tests/classes/ServerlessEndpointTest'); + require('./tests/actions/TestPluginCustom'); + require('./tests/actions/TestDefaultActionHook'); + require('./tests/actions/StageCreate'); + require('./tests/actions/RegionCreate'); + require('./tests/actions/ComponentCreate'); + require('./tests/actions/FunctionCreate'); + require('./tests/actions/EnvList'); + require('./tests/actions/EnvGet'); + require('./tests/actions/EnvSetUnset'); + require('./tests/actions/ResourcesDeploy'); require('./tests/actions/FunctionRun'); //require('./tests/actions/FunctionDeploy'); //require('./tests/actions/EndpointDeploy'); diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index 89d600099..43e087a38 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -5,11 +5,11 @@ */ let Serverless = require('../../../lib/Serverless.js'), - path = require('path'), - utils = require('../../../lib/utils/index'), - assert = require('chai').assert, - testUtils = require('../../test_utils'), - config = require('../../config'); + path = require('path'), + utils = require('../../../lib/utils/index'), + assert = require('chai').assert, + testUtils = require('../../test_utils'), + config = require('../../config'); let serverless; let instance; @@ -19,25 +19,25 @@ describe('Test Serverless State Class', function() { before(function(done) { this.timeout(0); testUtils.createTestProject(config) - .then(projPath => { + .then(projPath => { - process.chdir(projPath); + process.chdir(projPath); - // Instantiate Serverless - serverless = new Serverless({ - interactive: false, - projectPath: projPath - }); - - return serverless.init() - .then(function() { - - // Instantiate Class - instance = new serverless.classes.State(serverless); - - done(); + // Instantiate Serverless + serverless = new Serverless({ + interactive: false, + projectPath: projPath }); - }); + + return serverless.init() + .then(function() { + + // Instantiate Class + instance = new serverless.classes.State(serverless); + + done(); + }); + }); }); after(function(done) { @@ -48,12 +48,12 @@ describe('Test Serverless State Class', function() { it('Load instance from file system', function(done) { instance.load() - .then(function(instance) { - done(); - }) - .catch(e => { - done(e); - }); + .then(function(instance) { + done(); + }) + .catch(e => { + done(e); + }); }); it('Get instance data, without private properties', function(done) { @@ -79,12 +79,12 @@ describe('Test Serverless State Class', function() { it('Save instance to the file system', function(done) { instance.save() - .then(function(instance) { - done(); - }) - .catch(e => { - done(e); - }); + .then(function(instance) { + done(); + }) + .catch(e => { + done(e); + }); }); it('Get project', function(done) { @@ -100,23 +100,18 @@ describe('Test Serverless State Class', function() { }); it('Get resources (unpopulated)', function(done) { - let resources = instance.getResources() - .then(function(resources) { - console.log(resources) - assert.equal(true, JSON.stringify(resources).indexOf('${') !== -1); - done(); - }); + let resources = instance.getResources(); + assert.equal(true, JSON.stringify(resources).indexOf('${') !== -1); + done(); }); it('Get resources (populated)', function(done) { let resources = instance.getResources({ - populate: true, stage: config.stage, region: config.region - }) - .then(function() { - assert.equal(true, JSON.stringify(resources).indexOf('$${') == -1); - assert.equal(true, JSON.stringify(resources).indexOf('${') == -1); - done(); - }); + populate: true, stage: config.stage, region: config.region + }) + assert.equal(true, JSON.stringify(resources).indexOf('$${') == -1); + assert.equal(true, JSON.stringify(resources).indexOf('${') == -1); + done(); }); it('Get stages', function(done) { From 20a90c405c9f127042e031947b04a2101de76f9c Mon Sep 17 00:00:00 2001 From: ac360 Date: Sun, 7 Feb 2016 09:02:01 -0800 Subject: [PATCH 24/29] FunctionDeploy: refactor for module removal --- lib/actions/FunctionDeploy.js | 39 ++++++++++++++++++----------------- lib/actions/FunctionRun.js | 4 ++-- tests/all.js | 34 +++++++++++++++--------------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/lib/actions/FunctionDeploy.js b/lib/actions/FunctionDeploy.js index 59a45b080..1c40d4c07 100644 --- a/lib/actions/FunctionDeploy.js +++ b/lib/actions/FunctionDeploy.js @@ -167,7 +167,6 @@ module.exports = function(SPlugin, serverlessPath) { // Set Defaults _this.evt.options.stage = _this.evt.options.stage ? _this.evt.options.stage : null; - _this.evt.options.paths = _this.evt.options.paths ? _this.evt.options.paths : []; _this.evt.options.aliasFunction = _this.evt.options.aliasFunction ? _this.evt.options.aliasFunction : null; // Instantiate Classes @@ -180,24 +179,28 @@ module.exports = function(SPlugin, serverlessPath) { // Validate Stage if (!_this.evt.options.stage) throw new SError(`Stage is required`); - // If CLI and no functions targeted, deploy from CWD if Function, otherwise error + // If CLI and no paths targeted, deploy from CWD if Function if (_this.S.cli && !_this.evt.options.paths.length && !_this.evt.options.all) { - if (SUtils.fileExistsSync(path.join(process.cwd(), 's-function.json'))) { - let componentJson = SUtils.readAndParseJsonSync(path.join(process.cwd(), '..', '..', 's-component.json')); - let moduleJson = SUtils.readAndParseJsonSync(path.join(process.cwd(), '..', 's-module.json')); - let functionJson = SUtils.readAndParseJsonSync(path.join(process.cwd(), 's-function.json')); - _this.evt.options.paths = _this.S.state.getFunctions({ - component: componentJson.name, - module: moduleJson.name, - function: functionJson.name, - returnPaths: true - }); - } else { - throw new SError(`You must be in a function folder to deploy. Otherwise, use the command "serverless dash deploy"`); + // Get all functions in CWD + let sPath = _this.getSPathFromCwd(_this.S.config.projectPath); + + if (!sPath) { + throw new SError(`You must be in a component or function folder to deploy. Otherwise, use the command "serverless dash deploy"`); } + + _this.evt.options.paths = _this.S.state.getFunctions({ + paths: [sPath], + returnPaths: true + }); + + if (!_this.evt.options.paths.length) { + throw new SError(`No functions found in this location`); + } + + SCli.log('Deploying all functions in: ' + sPath + '...'); } // If --all is selected, load all paths @@ -301,9 +304,8 @@ module.exports = function(SPlugin, serverlessPath) { if (!_this.deployed) _this.deployed = {}; if (!_this.deployed[region]) _this.deployed[region] = []; _this.deployed[region].push({ - component: func._config.component, - module: func._config.module, function: func.name, + sPath: func._config.sPath, Arn: result.data.lambdaAliasArn }); @@ -316,9 +318,8 @@ module.exports = function(SPlugin, serverlessPath) { if (!_this.failed) _this.failed = {}; if (!_this.failed[region]) _this.failed[region] = []; _this.failed[region].push({ - component: func ? func._config.component : 'unknown', - module: func ? func._config.module : 'unknown', - function: func ? func.name : 'unknown', + function: func.name, + sPath: func._config.sPath, message: e.message, stack: e.stack }); diff --git a/lib/actions/FunctionRun.js b/lib/actions/FunctionRun.js index f44c7aede..8cc48693e 100644 --- a/lib/actions/FunctionRun.js +++ b/lib/actions/FunctionRun.js @@ -105,8 +105,8 @@ module.exports = function(SPlugin, serverlessPath) { .then(function(evt) { // Delete temp stage/region env var - if (this.evt.options.stage && this.evt.options.region) { - fs.unlinkSync(path.join(this.S.config.projectPath, this.evt.options.path.split('/')[0], '.env')); + if (_this.evt.options.stage && _this.evt.options.region) { + fs.unlinkSync(path.join(_this.S.config.projectPath, _this.evt.options.path.split('/')[0], '.env')); } /** diff --git a/tests/all.js b/tests/all.js index 3ea6d3d41..04dd67be8 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,23 +10,23 @@ describe('All Tests', function() { }); after(function() {}); - require('./tests/classes/ServerlessStateTest'); - require('./tests/classes/ServerlessProjectTest'); - require('./tests/classes/ServerlessComponentTest'); - require('./tests/classes/ServerlessFunctionTest'); - require('./tests/classes/ServerlessEndpointTest'); - require('./tests/actions/TestPluginCustom'); - require('./tests/actions/TestDefaultActionHook'); - require('./tests/actions/StageCreate'); - require('./tests/actions/RegionCreate'); - require('./tests/actions/ComponentCreate'); - require('./tests/actions/FunctionCreate'); - require('./tests/actions/EnvList'); - require('./tests/actions/EnvGet'); - require('./tests/actions/EnvSetUnset'); - require('./tests/actions/ResourcesDeploy'); - require('./tests/actions/FunctionRun'); - //require('./tests/actions/FunctionDeploy'); + //require('./tests/classes/ServerlessStateTest'); + //require('./tests/classes/ServerlessProjectTest'); + //require('./tests/classes/ServerlessComponentTest'); + //require('./tests/classes/ServerlessFunctionTest'); + //require('./tests/classes/ServerlessEndpointTest'); + //require('./tests/actions/TestPluginCustom'); + //require('./tests/actions/TestDefaultActionHook'); + //require('./tests/actions/StageCreate'); + //require('./tests/actions/RegionCreate'); + //require('./tests/actions/ComponentCreate'); + //require('./tests/actions/FunctionCreate'); + //require('./tests/actions/EnvList'); + //require('./tests/actions/EnvGet'); + //require('./tests/actions/EnvSetUnset'); + //require('./tests/actions/ResourcesDeploy'); + //require('./tests/actions/FunctionRun'); + require('./tests/actions/FunctionDeploy'); //require('./tests/actions/EndpointDeploy'); //require('./tests/actions/ProjectInit'); //require('./tests/actions/ProjectInstall'); From 8172c7664a36ca707de1ce1f7b0a0194791f55b0 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Sun, 7 Feb 2016 09:25:35 -0800 Subject: [PATCH 25/29] CodeDeploy: add support for nametempltae --- lib/actions/CodeDeployLambda.js | 38 ++++++++--- lib/utils/aws/Lambda.js | 115 ++++++++++++++++---------------- 2 files changed, 87 insertions(+), 66 deletions(-) diff --git a/lib/actions/CodeDeployLambda.js b/lib/actions/CodeDeployLambda.js index f4f0b9bb2..7b69171e2 100644 --- a/lib/actions/CodeDeployLambda.js +++ b/lib/actions/CodeDeployLambda.js @@ -118,6 +118,24 @@ module.exports = function(SPlugin, serverlessPath) { _this.project = _this.S.state.getProject(); _this.function = _this.S.state.getFunctions({ paths: [_this.evt.options.path] })[0]; + // Set default function name + _this.functionName = _this.Lambda.sGetLambdaName(_this.project.name, _this.function._config.component, null, _this.function.name); + // Backwards Compatibility Support TODO: Remove in the future but will result in breaking change. + if (_this.function._config.sPath.split('/').length == 3) { + + // Check if s-module.json exists in subfolder + if (_this.fileExistsSync(path.join( + _this.S.config.projectPath, + _this.function._config.sPath.split('/').splice(0, 2).join(path.sep), + 's-module.json'))) { + _this.functionName = _this.Lambda.sGetLambdaName(_this.project.name, _this.function._config.component, _this.function._config.sPath.split('/')[1], _this.function.name); + } + } + // If nametemplate, use that + if (_this.function.nameTemplate) { + _this.functionName = _this.function.getPopulated(_this.evt.options.stage, _this.evt.options.region).nameTemplate; + } + return BbPromise.resolve(); } @@ -151,7 +169,7 @@ module.exports = function(SPlugin, serverlessPath) { // Create compressed package fs.writeFileSync(this.pathCompressed, this.zipBuffer); - SUtils.sDebug(`"${this.evt.options.stage} - ${this.evt.options.region} - ${this.function.name}": Compressed file created - ${this.pathCompressed}`); + SUtils.sDebug(`"${this.evt.options.stage} - ${this.evt.options.region} - ${this.functionName}": Compressed file created - ${this.pathCompressed}`); return BbPromise.resolve(); } @@ -165,13 +183,13 @@ module.exports = function(SPlugin, serverlessPath) { let _this = this; - SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.function.name}": Uploading to project bucket...`); + SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Uploading to project bucket...`); return _this.S3.sPutLambdaZip( _this.meta.variables.projectBucket, _this.project.name, _this.evt.options.stage, - _this.function.name, + _this.functionName, fs.createReadStream(_this.pathCompressed)) .then(function (s3Key) { @@ -192,7 +210,7 @@ module.exports = function(SPlugin, serverlessPath) { let _this = this; var params = { - FunctionName: _this.Lambda.sGetLambdaName(_this.project.name, _this.function._config.component, _this.function._config.module, _this.function.name), + FunctionName: _this.functionName, Qualifier: '$LATEST' }; @@ -208,14 +226,14 @@ module.exports = function(SPlugin, serverlessPath) { // Create or Update Lambda if (!_this.lambda) { - SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.function.name}": Creating Lambda function...`); + SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.function.functionName}": Creating Lambda function...`); // Create Lambda let params = { Code: { ZipFile: _this.zipBuffer }, - FunctionName: _this.Lambda.sGetLambdaName(_this.project.name, _this.function._config.component, _this.function._config.module, _this.function.name), /* required */ + FunctionName: _this.functionName, /* required */ Handler: _this.function.handler, /* required */ Role: _this.meta.stages[_this.evt.options.stage].regions[_this.evt.options.region].variables.iamRoleArnLambda, /* required */ Runtime: _this.function.getRuntime(), /* required */ @@ -235,7 +253,7 @@ module.exports = function(SPlugin, serverlessPath) { } else { - SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.function.name}": Updating Lambda configuration...`); + SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Updating Lambda configuration...`); let params = { FunctionName: _this.lambda.Configuration.FunctionName, /* required */ @@ -248,7 +266,7 @@ module.exports = function(SPlugin, serverlessPath) { return _this.Lambda.updateFunctionConfigurationPromised(params) .then(function () { - SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.function.name}": Updating Lambda function...`); + SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Updating Lambda function...`); // Update Lambda Code let params = { @@ -296,7 +314,7 @@ module.exports = function(SPlugin, serverlessPath) { // Update Existing Alias - SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.function.name}": Updating Lambda Alias for version - ${_this.lambdaVersion}`); + SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Updating Lambda Alias for version - ${_this.lambdaVersion}`); let params = { FunctionName: _this.lambda.FunctionName, /* required */ @@ -314,7 +332,7 @@ module.exports = function(SPlugin, serverlessPath) { // Create New Alias - SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.function.name}": Creating New Lambda Alias for version - ${_this.lambdaVersion}`); + SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Creating New Lambda Alias for version - ${_this.lambdaVersion}`); let params = { FunctionName: _this.lambda.FunctionName, /* required */ diff --git a/lib/utils/aws/Lambda.js b/lib/utils/aws/Lambda.js index fb4189aa2..69f840c96 100644 --- a/lib/utils/aws/Lambda.js +++ b/lib/utils/aws/Lambda.js @@ -19,76 +19,79 @@ BbPromise.promisifyAll(fs); module.exports = function(config) { - // Promisify and configure instance - const Lambda = BbPromise.promisifyAll(new AWS.Lambda(config), { suffix: "Promised" }); + // Promisify and configure instance + const Lambda = BbPromise.promisifyAll(new AWS.Lambda(config), { suffix: "Promised" }); - /** - * Get Lambda Name - */ + /** + * Get Lambda Name + */ - Lambda.sGetLambdaName = function(projectName, componentName, moduleName, functionName) { - return projectName + '-' + componentName + '-' + moduleName + '-' + functionName; - }; + Lambda.sGetLambdaName = function(projectName, componentName, moduleName, functionName) { + let lambdaName = projectName + '-' + componentName; + if (moduleName) lambdaName = lambdaName + '-' + moduleName; + lambdaName = lambdaName + '-' + functionName; + return lambdaName; + }; - /** - * Retrns [{FunctionName: "", Version: "", FunctionArn: ""},...] - * @param awsProfile - * @param awsRegion - * @param functionNames - * @returns {BbPromise.} - */ - Lambda.sPublishVersions = function(functionNames) { + /** + * Retrns [{FunctionName: "", Version: "", FunctionArn: ""},...] + * @param awsProfile + * @param awsRegion + * @param functionNames + * @returns {BbPromise.} + */ + Lambda.sPublishVersions = function(functionNames) { - let d = new Date(), - ds = `versioned at ${d}`, - deferreds = []; + let d = new Date(), + ds = `versioned at ${d}`, + deferreds = []; - functionNames.forEach(fn => { - let params = { - FunctionName: fn, - Description: ds, - }; + functionNames.forEach(fn => { + let params = { + FunctionName: fn, + Description: ds, + }; - SUtils.sDebug('Pushing version to publish: ', params); + SUtils.sDebug('Pushing version to publish: ', params); - deferreds.push(Lambda.publishVersionPromised(params)); - }); - - return BbPromise.all(deferreds) - .then(data => { - return data.map(d => { - return {FunctionName: d.FunctionName, Version: d.Version, FunctionArn: d.FunctionArn}; + deferreds.push(Lambda.publishVersionPromised(params)); }); - }) - .catch(e => { - if (e.code == 'ServiceUnavailableException') { - console.error('ServiceUnavailableException when trying to version lambda. This could mean you have not deployed the lambda since last time you published a version.'); - } - throw e; - }); - }; + return BbPromise.all(deferreds) + .then(data => { + return data.map(d => { + return {FunctionName: d.FunctionName, Version: d.Version, FunctionArn: d.FunctionArn}; + }); + }) + .catch(e => { + if (e.code == 'ServiceUnavailableException') { + console.error('ServiceUnavailableException when trying to version lambda. This could mean you have not deployed the lambda since last time you published a version.'); + } - Lambda.sCreateAlias = function(functionName, functionVersion, aliasName) { + throw e; + }); + }; + + Lambda.sCreateAlias = function(functionName, functionVersion, aliasName) { - let d = new Date(), - params = { - FunctionName: functionName, - FunctionVersion: functionVersion + '', - Name: aliasName, - Description: `aliased at ${d}`, - }; + let d = new Date(), + params = { + FunctionName: functionName, + FunctionVersion: functionVersion + '', + Name: aliasName, + Description: `aliased at ${d}`, + }; - SUtils.sDebug('Creating alias', params); + SUtils.sDebug('Creating alias', params); - return Lambda.createAliasPromised(params) - .then(d => { - return {AliasArn: d.AliasArn, FunctionVersion: d.FunctionVersion}; - }); - }; + return Lambda.createAliasPromised(params) + .then(d => { + return {AliasArn: d.AliasArn, FunctionVersion: d.FunctionVersion}; + }); + }; - // Return configured, customized instance - return Lambda; + // Return configured, customized instance + return Lambda; }; From eb4b673490f5fdf6ac35963c9200f6e9338ced8b Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Sun, 7 Feb 2016 11:15:32 -0800 Subject: [PATCH 26/29] FunctionDeploy: finish, add templateName, update tests --- lib/actions/CodeDeployLambda.js | 15 ++- lib/actions/CodePackageLambda.js | 4 +- lib/actions/FunctionDeploy.js | 6 +- tests/all.js | 4 +- .../group1/group2/function4/s-function.json | 1 + tests/tests/actions/FunctionDeploy.js | 108 ++++-------------- 6 files changed, 39 insertions(+), 99 deletions(-) diff --git a/lib/actions/CodeDeployLambda.js b/lib/actions/CodeDeployLambda.js index 7b69171e2..f1d3b8237 100644 --- a/lib/actions/CodeDeployLambda.js +++ b/lib/actions/CodeDeployLambda.js @@ -82,6 +82,7 @@ module.exports = function(SPlugin, serverlessPath) { * Return EVT */ + _this.evt.data.functioName = _this.functionName; _this.evt.data.pathCompressed = _this.pathCompressed; _this.evt.data.s3Bucket = _this.s3Bucket; _this.evt.data.s3Key = _this.s3Key; @@ -119,23 +120,25 @@ module.exports = function(SPlugin, serverlessPath) { _this.function = _this.S.state.getFunctions({ paths: [_this.evt.options.path] })[0]; // Set default function name - _this.functionName = _this.Lambda.sGetLambdaName(_this.project.name, _this.function._config.component, null, _this.function.name); + _this.functionName = _this.Lambda.sGetLambdaName(_this.project.name, _this.function.getComponent().name, null, _this.function.name); // Backwards Compatibility Support TODO: Remove in the future but will result in breaking change. if (_this.function._config.sPath.split('/').length == 3) { // Check if s-module.json exists in subfolder - if (_this.fileExistsSync(path.join( + if (SUtils.fileExistsSync(path.join( _this.S.config.projectPath, _this.function._config.sPath.split('/').splice(0, 2).join(path.sep), 's-module.json'))) { - _this.functionName = _this.Lambda.sGetLambdaName(_this.project.name, _this.function._config.component, _this.function._config.sPath.split('/')[1], _this.function.name); + _this.functionName = _this.Lambda.sGetLambdaName(_this.project.name, _this.function.getComponent().name, _this.function._config.sPath.split('/')[1], _this.function.name); } } - // If nametemplate, use that + // If nameTemplate, use that if (_this.function.nameTemplate) { - _this.functionName = _this.function.getPopulated(_this.evt.options.stage, _this.evt.options.region).nameTemplate; + _this.functionName = _this.function.getPopulated({ + stage: _this.evt.options.stage, + region: _this.evt.options.region }).nameTemplate; } - + console.log("FunctionName: ", _this.functionName); return BbPromise.resolve(); } diff --git a/lib/actions/CodePackageLambda.js b/lib/actions/CodePackageLambda.js index 9e0ff1df8..792a6353d 100644 --- a/lib/actions/CodePackageLambda.js +++ b/lib/actions/CodePackageLambda.js @@ -149,8 +149,8 @@ module.exports = function(SPlugin, serverlessPath) { let excludePatterns = this.function.custom.excludePatterns || []; wrench.copyDirSyncRecursive( - path.join(_this.S.config.projectPath, _this.function._config.component), - _this.pathDist, + _this.function.getComponent()._config.fullPath, + _this.pathDist, { exclude: function(name, prefix) { diff --git a/lib/actions/FunctionDeploy.js b/lib/actions/FunctionDeploy.js index 1c40d4c07..004d3c86a 100644 --- a/lib/actions/FunctionDeploy.js +++ b/lib/actions/FunctionDeploy.js @@ -121,7 +121,7 @@ module.exports = function(SPlugin, serverlessPath) { let region = _this.failed[Object.keys(_this.failed)[i]]; SCli.log(Object.keys(_this.failed)[i] + ' ------------------------'); for (let j = 0; j < region.length; j++) { - SCli.log(' ' + region[j].component + '/' + region[j].module + '/' + region[j].function + ': ' + region[j].message ); + SCli.log(' ' + region[j].sPath + ': ' + region[j].message ); SUtils.sDebug(region[j].stack); } } @@ -140,7 +140,7 @@ module.exports = function(SPlugin, serverlessPath) { let region = _this.deployed[Object.keys(_this.deployed)[i]]; SCli.log(Object.keys(_this.deployed)[i] + ' ------------------------'); for (let j = 0; j < region.length; j++) { - SCli.log(' ' + region[j].component + '/' + region[j].module + '/' + region[j].function + ': ' + region[j].Arn ); + SCli.log(' ' + region[j].sPath + ' (' + region[j].functionName + '): ' + region[j].Arn ); } } } @@ -304,7 +304,7 @@ module.exports = function(SPlugin, serverlessPath) { if (!_this.deployed) _this.deployed = {}; if (!_this.deployed[region]) _this.deployed[region] = []; _this.deployed[region].push({ - function: func.name, + function: result.data.functionName, sPath: func._config.sPath, Arn: result.data.lambdaAliasArn }); diff --git a/tests/all.js b/tests/all.js index 04dd67be8..b525a93a8 100644 --- a/tests/all.js +++ b/tests/all.js @@ -26,8 +26,8 @@ describe('All Tests', function() { //require('./tests/actions/EnvSetUnset'); //require('./tests/actions/ResourcesDeploy'); //require('./tests/actions/FunctionRun'); - require('./tests/actions/FunctionDeploy'); - //require('./tests/actions/EndpointDeploy'); + //require('./tests/actions/FunctionDeploy'); + require('./tests/actions/EndpointDeploy'); //require('./tests/actions/ProjectInit'); //require('./tests/actions/ProjectInstall'); //require('./tests/actions/ProjectLifeCycle.js'); diff --git a/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json b/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json index 2c306553e..64afdefd2 100644 --- a/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json +++ b/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json @@ -1,5 +1,6 @@ { "name": "function4", + "nameTemplate": "${project}-${stage}-function4", "custom": { "envVars": [], "excludePatterns": [] diff --git a/tests/tests/actions/FunctionDeploy.js b/tests/tests/actions/FunctionDeploy.js index 472c3336b..b7b5f381e 100644 --- a/tests/tests/actions/FunctionDeploy.js +++ b/tests/tests/actions/FunctionDeploy.js @@ -123,92 +123,28 @@ describe('Test Action: Function Deploy', function() { }); }); - //describe('Function Deploy: Specify One Path with S3 event source', function() { - // it('should deploy function and S3 event source', function(done) { - // - // this.timeout(0); - // - // let event = { - // stage: config.stage, - // region: config.region, - // paths: [ - // 'nodejscomponent/group1/function1' - // ] - // }; - // - // serverless.actions.functionDeploy(event) - // .then(function(evt) { - // validateEvent(evt); - // let awsConfig = { - // region: config.region, - // accessKeyId: config.awsAdminKeyId, - // secretAccessKey: config.awsAdminSecretKey, - // }; - // let eventSource = { - // bucket: 's3-event-source-test', - // bucketEvents: ['s3:ObjectCreated:*'] - // }; - // Eventsutils.s3(awsConfig, null, eventSource, done); - // - // //done(); - // }) - // .catch(e => { - // done(e); - // }); - // }); - //}); + describe('Function Deploy: Nested W/ Name Template', function() { + it('should deploy functions', function(done) { - //describe('Function Deploy: Specify One with event source', function() { - // it('should deploy functions and event source', function(done) { - // - // this.timeout(0); - // - // let event = { - // stage: config.stage, - // region: config.region, - // paths: [ - // 'nodejscomponent/group1/function1' - // ] - // }; - // - // serverless.actions.functionDeploy(event) - // .then(function(evt) { - // validateEvent(evt); - // - // // validate event source was created and returned UUID - // assert.equal(true, typeof evt.functions[0].events[0].UUID != 'undefined'); - // - // - // cleanup(evt.functions[0].events[0].UUID, done); - // - // }) - // .catch(e => { - // done(e); - // }); - // }); - //}); + this.timeout(0); + + let options = { + stage: config.stage, + region: config.region, + paths: [ + 'nodejscomponent/group1/group2/function4' + ] + }; + + serverless.actions.functionDeploy(options) + .then(function(evt) { + validateEvent(evt); + done(); + }) + .catch(e => { + done(e); + }); + }); + }); - //describe('Function Deploy: Specify All Paths', function() { - // it('should deploy code', function(done) { - // - // this.timeout(0); - // - // let event = { - // stage: config.stage, - // region: config.region, - // all: true - // }; - // - // serverless.actions.functionDeploy(event) - // .then(function(evt) { - // validateEvent(evt); - // - // cleanup(evt.functions[1].events[0].UUID, done); - // - // }) - // .catch(e => { - // done(e); - // }); - // }); - //}); }); From 3a9c52159f793ea3d74761a8a27b47d36f7e7a17 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Sun, 7 Feb 2016 11:53:18 -0800 Subject: [PATCH 27/29] EndpointDeploy: update tests --- lib/actions/CodeDeployLambda.js | 2 +- lib/actions/EndpointBuildApiGateway.js | 22 ++- lib/actions/EndpointDeploy.js | 47 +++-- lib/actions/FunctionDeploy.js | 2 +- .../group1/group2/function4/s-function.json | 4 +- tests/tests/actions/EndpointDeploy.js | 182 ++++++++++-------- 6 files changed, 152 insertions(+), 107 deletions(-) diff --git a/lib/actions/CodeDeployLambda.js b/lib/actions/CodeDeployLambda.js index f1d3b8237..da1ec3b5c 100644 --- a/lib/actions/CodeDeployLambda.js +++ b/lib/actions/CodeDeployLambda.js @@ -138,7 +138,7 @@ module.exports = function(SPlugin, serverlessPath) { stage: _this.evt.options.stage, region: _this.evt.options.region }).nameTemplate; } - console.log("FunctionName: ", _this.functionName); + return BbPromise.resolve(); } diff --git a/lib/actions/EndpointBuildApiGateway.js b/lib/actions/EndpointBuildApiGateway.js index dedf1f062..8a43faddb 100644 --- a/lib/actions/EndpointBuildApiGateway.js +++ b/lib/actions/EndpointBuildApiGateway.js @@ -160,6 +160,26 @@ module.exports = function(SPlugin, serverlessPath) { if (!_this.endpoint) BbPromise.reject(new SError(`Endpoint could not be found: ${_this.evt.options.path}`)); + // Set default function name + _this.functionName = _this.Lambda.sGetLambdaName(_this.project.name, _this.endpoint.getComponent().name, null, _this.endpoint.getFunction().name); + // Backwards Compatibility Support TODO: Remove in the future but will result in breaking change. + if (_this.endpoint.getFunction()._config.sPath.split('/').length == 3) { + // Check if s-module.json exists in subfolder + if (SUtils.fileExistsSync(path.join( + _this.S.config.projectPath, + _this.endpoint._config.sPath.split('/').splice(0, 2).join(path.sep), + 's-module.json'))) { + _this.functionName = _this.Lambda.sGetLambdaName(_this.project.name, _this.endpoint.getComponent().name, _this.endpoint._config.sPath.split('/')[1], _this.endpoint.getFunction().name); + } + } + // If nameTemplate, use that + if (_this.endpoint.getFunction().nameTemplate) { + _this.functionName = _this.endpoint.getFunction().getPopulated({ + stage: _this.evt.options.stage, + region: _this.evt.options.region }).nameTemplate; + } + + // Populate endpoint _this.endpoint = _this.endpoint.getPopulated({ stage: _this.evt.options.stage, region: _this.evt.options.region }); // Validate and sanitize endpoint attributes @@ -228,7 +248,7 @@ module.exports = function(SPlugin, serverlessPath) { })[0]; let params = { - FunctionName: _this.Lambda.sGetLambdaName(_this.project.name, endpointInstance._config.component, endpointInstance._config.module, endpointInstance._config.function), /* required */ + FunctionName: _this.functionName, Qualifier: _this.evt.options.stage }; diff --git a/lib/actions/EndpointDeploy.js b/lib/actions/EndpointDeploy.js index 347c631f2..d1943a438 100644 --- a/lib/actions/EndpointDeploy.js +++ b/lib/actions/EndpointDeploy.js @@ -168,27 +168,28 @@ module.exports = function(SPlugin, serverlessPath) { _this.evt.options.paths = _this.evt.options.paths ? _this.evt.options.paths : []; _this.regions = _this.evt.options.region ? [_this.evt.options.region] : Object.keys(_this.meta.stages[_this.evt.options.stage].regions); - // If CLI and no endpoints targeted, deploy from CWD if Function, otherwise error + // If CLI and no paths targeted, deploy from CWD if Function if (_this.S.cli && - !_this.evt.options.paths.length && - !_this.evt.options.component && - !_this.evt.options.module && - !_this.evt.options.function && - !_this.evt.options.all) { + !_this.evt.options.paths.length && + !_this.evt.options.all) { - if (SUtils.fileExistsSync(path.join(process.cwd(), 's-function.json'))) { - let componentJson = SUtils.readAndParseJsonSync(path.join(process.cwd(), '..', '..', 's-component.json')); - let moduleJson = SUtils.readAndParseJsonSync(path.join(process.cwd(), '..', 's-module.json')); - let functionJson = SUtils.readAndParseJsonSync(path.join(process.cwd(), 's-function.json')); - _this.evt.options.paths = _this.S.state.getEndpoints({ - component: componentJson.name, - module: moduleJson.name, - function: functionJson.name, - returnPaths: true - }); - } else { - throw new SError(`You must be in a function folder to deploy its endpoints. Otherwise, use the command "serverless dash deploy"`); + // Get all functions in CWD + let sPath = _this.getSPathFromCwd(_this.S.config.projectPath); + + if (!sPath) { + throw new SError(`You must be in a component to deploy. Otherwise, use the command "serverless dash deploy"`); } + + _this.evt.options.paths = _this.S.state.getEndpoints({ + paths: [sPath], + returnPaths: true + }); + + if (!_this.evt.options.paths.length) { + throw new SError(`No endpoints found in this location: ` + sPath); + } + + SCli.log('Deploying all endpoints in: ' + sPath + '...'); } // If --all is selected, load all paths @@ -283,6 +284,7 @@ module.exports = function(SPlugin, serverlessPath) { return BbPromise.try(function() { endpoint = _this.S.state.getEndpoints({ paths: [path] })[0]; + if (!endpoint) throw new SError(`Endpoint could not be found: ${path}`); // Create new event object @@ -304,9 +306,7 @@ module.exports = function(SPlugin, serverlessPath) { if (!_this.deployed) _this.deployed = {}; if (!_this.deployed[region]) _this.deployed[region] = []; _this.deployed[region].push({ - module: endpoint._config.module, - function: endpoint._config.function, - endpointSPath: path, + sPath: endpoint._config.sPath, endpointPath: endpoint.path, endpointMethod: endpoint.method, endpointUrl: result.data.url @@ -320,9 +320,7 @@ module.exports = function(SPlugin, serverlessPath) { if (!_this.failed) _this.failed = {}; if (!_this.failed[region]) _this.failed[region] = []; _this.failed[region].push({ - module: endpoint ? endpoint._config.module : 'unknown', - function: endpoint ? endpoint._config.function : 'unknown', - endpointSPath: path, + sPath: endpoint ? endpoint._config.sPath : path, endpointPath: endpoint ? endpoint.path : 'unknown', endpointMethod: endpoint ? endpoint.method : 'unknown', message: e.message, @@ -348,7 +346,6 @@ module.exports = function(SPlugin, serverlessPath) { region: region, restApiId: _this.restApiData.id, description: _this.evt.options.description - } }; diff --git a/lib/actions/FunctionDeploy.js b/lib/actions/FunctionDeploy.js index 004d3c86a..7eea45a1d 100644 --- a/lib/actions/FunctionDeploy.js +++ b/lib/actions/FunctionDeploy.js @@ -197,7 +197,7 @@ module.exports = function(SPlugin, serverlessPath) { }); if (!_this.evt.options.paths.length) { - throw new SError(`No functions found in this location`); + throw new SError(`No functions found in this location ${sPath}`); } SCli.log('Deploying all functions in: ' + sPath + '...'); diff --git a/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json b/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json index 64afdefd2..443b37c0d 100644 --- a/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json +++ b/tests/test-prj/nodejscomponent/group1/group2/function4/s-function.json @@ -11,7 +11,7 @@ "events": [], "endpoints": [ { - "path": "group1/function4", + "path": "group2/function4", "method": "GET", "authorizationType": "none", "apiKeyRequired": false, @@ -34,7 +34,7 @@ } }, { - "path": "group1/function4", + "path": "group2/function4", "method": "POST", "authorizationType": "none", "apiKeyRequired": false, diff --git a/tests/tests/actions/EndpointDeploy.js b/tests/tests/actions/EndpointDeploy.js index 437e47a50..db2a10712 100644 --- a/tests/tests/actions/EndpointDeploy.js +++ b/tests/tests/actions/EndpointDeploy.js @@ -19,19 +19,19 @@ let serverless; */ let validateEvent = function(evt) { - assert.equal(true, typeof evt.options.stage != 'undefined'); - assert.equal(true, typeof evt.options.region != 'undefined'); - assert.equal(true, typeof evt.options.paths != 'undefined'); - assert.equal(true, typeof evt.data.deployed != 'undefined'); + assert.equal(true, typeof evt.options.stage != 'undefined'); + assert.equal(true, typeof evt.options.region != 'undefined'); + assert.equal(true, typeof evt.options.paths != 'undefined'); + assert.equal(true, typeof evt.data.deployed != 'undefined'); - if (evt.data.failed) { - for (let i = 0; i < Object.keys(evt.data.failed).length; i++) { - console.log(Object.keys(evt.data.failed)[i]); - console.log(evt.data.failed[Object.keys(evt.data.failed)[i]]); + if (evt.data.failed) { + for (let i = 0; i < Object.keys(evt.data.failed).length; i++) { + console.log(Object.keys(evt.data.failed)[i]); + console.log(evt.data.failed[Object.keys(evt.data.failed)[i]]); + } } - } - assert.equal(true, typeof evt.data.failed === 'undefined'); + assert.equal(true, typeof evt.data.failed === 'undefined'); }; /** @@ -40,77 +40,105 @@ let validateEvent = function(evt) { describe('Test Action: Endpoint Deploy', function() { - before(function(done) { - this.timeout(0); + before(function(done) { + this.timeout(0); - testUtils.createTestProject(config) - .then(projPath => { + testUtils.createTestProject(config) + .then(projPath => { - process.chdir(projPath); + process.chdir(projPath); - serverless = new Serverless({ - interactive: false, - awsAdminKeyId: config.awsAdminKeyId, - awsAdminSecretKey: config.awsAdminSecretKey, - projectPath: projPath - }); + serverless = new Serverless({ + interactive: false, + awsAdminKeyId: config.awsAdminKeyId, + awsAdminSecretKey: config.awsAdminSecretKey, + projectPath: projPath + }); - return serverless.state.load().then(function() { - done(); - }); - }); - }); - - after(function(done) { - done(); - }); - - /** - * Tests - */ - - describe('Endpoint Deploy: Specify One Path', function() { - it('should deploy endpoints', function(done) { - - this.timeout(0); - - let event = { - stage: config.stage, - region: config.region, - paths: [ - 'nodejscomponent/group1/function1@group1/function1~GET' - ] - }; - - serverless.actions.endpointDeploy(event) - .then(function(evt) { - validateEvent(evt); - done(); - }) - .catch(e => { - done(e); - }); + return serverless.state.load().then(function() { + done(); + }); + }); }); - }); - //describe('Endpoint Deploy: Specify All Paths', function() { - // it('should deploy endpoints', function(done) { - // this.timeout(0); - // - // let event = { - // stage: config.stage, - // region: config.region, - // all: true - // }; - // - // serverless.actions.endpointDeploy(event) - // .then(function(evt) { - // validateEvent(evt); - // done(); - // }) - // .catch(e => { - // done(e); - // }); - // }); - //}); + after(function(done) { + done(); + }); + + /** + * Tests + */ + + describe('Endpoint Deploy: Specify One Path', function() { + it('should deploy endpoints', function(done) { + + this.timeout(0); + + let event = { + stage: config.stage, + region: config.region, + paths: [ + 'nodejscomponent/group1/function1@group1/function1~GET' + ] + }; + + serverless.actions.endpointDeploy(event) + .then(function(evt) { + validateEvent(evt); + done(); + }) + .catch(e => { + done(e); + }); + }); + }); + + /** + * Tests + */ + + describe('Endpoint Deploy: Nested Function W/ TemplateName', function() { + it('should deploy endpoints', function(done) { + + this.timeout(0); + + let event = { + stage: config.stage, + region: config.region, + paths: [ + 'nodejscomponent/group1/group2/function4@group2/function4~GET' + ] + }; + + serverless.actions.endpointDeploy(event) + .then(function(evt) { + validateEvent(evt); + done(); + }) + .catch(e => { + done(e); + }); + }); + }); + + //describe('Endpoint Deploy: Specify All Paths', function() { + // it('should deploy endpoints', function(done) { + // this.timeout(0); + // + // let event = { + // stage: config.stage, + // region: config.region, + // all: true + // }; + // + // serverless.actions.endpointDeploy(event) + // .then(function(evt) { + // validateEvent(evt); + // done(); + // }) + // .catch(e => { + // done(e); + // }); + // }); + //}); }); \ No newline at end of file From 1e35cbd2207ebaa357393a71944bee4a5ae6d8e3 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Sun, 7 Feb 2016 12:01:08 -0800 Subject: [PATCH 28/29] Tests: update --- tests/all.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/all.js b/tests/all.js index b525a93a8..9797769c7 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,25 +10,25 @@ describe('All Tests', function() { }); after(function() {}); - //require('./tests/classes/ServerlessStateTest'); - //require('./tests/classes/ServerlessProjectTest'); - //require('./tests/classes/ServerlessComponentTest'); - //require('./tests/classes/ServerlessFunctionTest'); - //require('./tests/classes/ServerlessEndpointTest'); - //require('./tests/actions/TestPluginCustom'); - //require('./tests/actions/TestDefaultActionHook'); - //require('./tests/actions/StageCreate'); - //require('./tests/actions/RegionCreate'); - //require('./tests/actions/ComponentCreate'); - //require('./tests/actions/FunctionCreate'); - //require('./tests/actions/EnvList'); - //require('./tests/actions/EnvGet'); - //require('./tests/actions/EnvSetUnset'); - //require('./tests/actions/ResourcesDeploy'); - //require('./tests/actions/FunctionRun'); - //require('./tests/actions/FunctionDeploy'); + require('./tests/classes/ServerlessStateTest'); + require('./tests/classes/ServerlessProjectTest'); + require('./tests/classes/ServerlessComponentTest'); + require('./tests/classes/ServerlessFunctionTest'); + require('./tests/classes/ServerlessEndpointTest'); + require('./tests/actions/TestPluginCustom'); + require('./tests/actions/TestDefaultActionHook'); + require('./tests/actions/StageCreate'); + require('./tests/actions/RegionCreate'); + require('./tests/actions/ComponentCreate'); + require('./tests/actions/FunctionCreate'); + require('./tests/actions/EnvList'); + require('./tests/actions/EnvGet'); + require('./tests/actions/EnvSetUnset'); + require('./tests/actions/ResourcesDeploy'); + require('./tests/actions/FunctionRun'); + require('./tests/actions/FunctionDeploy'); require('./tests/actions/EndpointDeploy'); - //require('./tests/actions/ProjectInit'); - //require('./tests/actions/ProjectInstall'); - //require('./tests/actions/ProjectLifeCycle.js'); + require('./tests/actions/ProjectInit'); + require('./tests/actions/ProjectInstall'); + require('./tests/actions/ProjectLifeCycle.js'); }); \ No newline at end of file From e0a0f02ca86003dccccdc22fe1d9c8c751518b44 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Sun, 7 Feb 2016 12:19:38 -0800 Subject: [PATCH 29/29] FunctionRun: Improve --- lib/actions/FunctionRun.js | 81 +++++++++++++++++++------------------- tests/all.js | 40 +++++++++---------- 2 files changed, 60 insertions(+), 61 deletions(-) diff --git a/lib/actions/FunctionRun.js b/lib/actions/FunctionRun.js index 8cc48693e..745150cb6 100644 --- a/lib/actions/FunctionRun.js +++ b/lib/actions/FunctionRun.js @@ -5,13 +5,13 @@ * - Runs the function in the CWD for local testing */ -module.exports = function(SPlugin, serverlessPath) { +module.exports = function(SPlugin, serverlessPath) { const path = require('path'), - SUtils = require( path.join( serverlessPath, 'utils' ) ), - SError = require(path.join(serverlessPath, 'ServerlessError')), - BbPromise = require('bluebird'), - awsMisc = require(path.join(serverlessPath, 'utils/aws/Misc')), - fs = require('fs'); + SUtils = require( path.join( serverlessPath, 'utils' ) ), + SError = require(path.join(serverlessPath, 'ServerlessError')), + BbPromise = require('bluebird'), + awsMisc = require(path.join(serverlessPath, 'utils/aws/Misc')), + fs = require('fs'); BbPromise.promisifyAll(fs); @@ -72,50 +72,49 @@ module.exports = function(SPlugin, serverlessPath) { let _this = this; _this.evt = evt; - // Ensure in component - if (!_this.getSPathFromCwd(_this.S.config.projectPath)) { - return BbPromise.reject('You must be in a function folder to run a function'); - } - // Instantiate Classes _this.project = _this.S.state.project.get(); - // If a function path was not specified. - if(!_this.evt.options.path) { - // If s-function.json exists in the current path, then use the current function path. - if(SUtils.fileExistsSync(path.join(process.cwd(), 's-function.json'))) { - let componentName = path.basename(path.join(process.cwd(), '..', '..')); - let moduleName = path.basename(path.join(process.cwd(), '..')); - let functionName = path.basename(process.cwd()); - _this.evt.options.path = componentName + "/" + moduleName + "/" + functionName; - } else { - return BbPromise.reject(new SError('Missing required function path param. Run from within a function directory, or add a function path in this format: componentName/moduleName/functionName')); + // If CLI and no paths targeted, deploy from CWD if Function + if (_this.S.cli && !_this.evt.options.path) { + + // Get all functions in CWD + let sPath = _this.getSPathFromCwd(_this.S.config.projectPath); + + if (!sPath) { + throw new SError(`You must be in a function folder to run it`); } + + _this.evt.options.path = [sPath]; } - _this.function = _this.S.state.getFunctions({ paths: [_this.evt.options.path] })[0]; + let funcs = _this.S.state.getFunctions({ paths: [_this.evt.options.path] }); + if (funcs.length > 1) { + throw new SError(`You must be in a function folder to run it`); + } + _this.function = funcs[0]; // Missing function if (!_this.function) return BbPromise.reject(new SError('Function could not be found at the path specified.')); // Flow return BbPromise.resolve(_this._fetchEnvFile()) - .bind(_this) - .then(_this._runByRuntime) - .then(function(evt) { + .bind(_this) + .then(_this._runByRuntime) + .then(function(evt) { - // Delete temp stage/region env var - if (_this.evt.options.stage && _this.evt.options.region) { - fs.unlinkSync(path.join(_this.S.config.projectPath, _this.evt.options.path.split('/')[0], '.env')); - } + // Delete temp stage/region env var + if (_this.evt.options.stage && _this.evt.options.region) { + fs.unlinkSync(path.join(_this.S.config.projectPath, _this.evt.options.path.split('/')[0], '.env')); + } - /** - * Return EVT - */ + /** + * Return EVT + */ - return evt; + return evt; - }); + }); } @@ -141,14 +140,14 @@ module.exports = function(SPlugin, serverlessPath) { } return awsMisc.getEnvFiles(_this.S, _this.evt.options.region, _this.evt.options.stage) - .then(function(envMapsByRegion) { - let contents = ''; - Object.keys(envMapsByRegion[0].vars).forEach(newKey => { - contents += [newKey, envMapsByRegion[0].vars[newKey]].join('=') + '\n'; + .then(function(envMapsByRegion) { + let contents = ''; + Object.keys(envMapsByRegion[0].vars).forEach(newKey => { + contents += [newKey, envMapsByRegion[0].vars[newKey]].join('=') + '\n'; + }); + fs.writeFileAsync(path.join(_this.S.config.projectPath, _this.evt.options.path.split('/')[0], '.env'), contents); + return BbPromise.resolve(); }); - fs.writeFileAsync(path.join(_this.S.config.projectPath, _this.evt.options.path.split('/')[0], '.env'), contents); - return BbPromise.resolve(); - }); } /** diff --git a/tests/all.js b/tests/all.js index 9797769c7..45900f5f9 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,25 +10,25 @@ describe('All Tests', function() { }); after(function() {}); - require('./tests/classes/ServerlessStateTest'); - require('./tests/classes/ServerlessProjectTest'); - require('./tests/classes/ServerlessComponentTest'); - require('./tests/classes/ServerlessFunctionTest'); - require('./tests/classes/ServerlessEndpointTest'); - require('./tests/actions/TestPluginCustom'); - require('./tests/actions/TestDefaultActionHook'); - require('./tests/actions/StageCreate'); - require('./tests/actions/RegionCreate'); - require('./tests/actions/ComponentCreate'); - require('./tests/actions/FunctionCreate'); - require('./tests/actions/EnvList'); - require('./tests/actions/EnvGet'); - require('./tests/actions/EnvSetUnset'); - require('./tests/actions/ResourcesDeploy'); + //require('./tests/classes/ServerlessStateTest'); + //require('./tests/classes/ServerlessProjectTest'); + //require('./tests/classes/ServerlessComponentTest'); + //require('./tests/classes/ServerlessFunctionTest'); + //require('./tests/classes/ServerlessEndpointTest'); + //require('./tests/actions/TestPluginCustom'); + //require('./tests/actions/TestDefaultActionHook'); + //require('./tests/actions/StageCreate'); + //require('./tests/actions/RegionCreate'); + //require('./tests/actions/ComponentCreate'); + //require('./tests/actions/FunctionCreate'); + //require('./tests/actions/EnvList'); + //require('./tests/actions/EnvGet'); + //require('./tests/actions/EnvSetUnset'); + //require('./tests/actions/ResourcesDeploy'); require('./tests/actions/FunctionRun'); - require('./tests/actions/FunctionDeploy'); - require('./tests/actions/EndpointDeploy'); - require('./tests/actions/ProjectInit'); - require('./tests/actions/ProjectInstall'); - require('./tests/actions/ProjectLifeCycle.js'); + //require('./tests/actions/FunctionDeploy'); + //require('./tests/actions/EndpointDeploy'); + //require('./tests/actions/ProjectInit'); + //require('./tests/actions/ProjectInstall'); + //require('./tests/actions/ProjectLifeCycle.js'); }); \ No newline at end of file