FunctionCreate: start refactoring to only use sPath

This commit is contained in:
ac360 2016-02-05 16:31:09 -08:00 committed by Austen Collins
parent 055d7b9501
commit f95d92f720
9 changed files with 68 additions and 118 deletions

View File

@ -58,7 +58,6 @@ class Serverless {
this.cli = null;
this.state = new this.classes.State(this);
// If project
if (this.config.projectPath) {

View File

@ -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;

View File

@ -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 <function>`,
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 <function>`,
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 <function>`,
}
/**
* Prompt sPath & function if they're missing
* Prompt component, module & function if they're missing
*/
_prompt() {
@ -95,15 +91,12 @@ usage: serverless function create <function>`,
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 <function>`,
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 <function>`,
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 <function>`,
}
// 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 <function>`,
// 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
));
}

View File

@ -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'))) {

View File

@ -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: {

View File

@ -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(`-----------------`);

View File

@ -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
],
{}
);

View File

@ -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++) {

View File

@ -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');