From f95d92f720cd0007838446a42e0919ed60baa4cb Mon Sep 17 00:00:00 2001 From: ac360 Date: Fri, 5 Feb 2016 16:31:09 -0800 Subject: [PATCH] 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 e2f7e4c7b..b5af299f7 100644 --- a/lib/actions/FunctionDeploy.js +++ b/lib/actions/FunctionDeploy.js @@ -185,9 +185,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');