serverless/lib/actions/FunctionRun.js

205 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
/**
* Action: FunctionRun
* - Runs the function in the CWD for local testing
*/
module.exports = function(SPlugin, serverlessPath) {
const path = require('path'),
SError = require(path.join(serverlessPath, 'Error')),
BbPromise = require('bluebird'),
chalk = require('chalk'),
SCli = require( path.join( serverlessPath, 'utils', 'cli'));
let SUtils;
/**
* FunctionRun Class
*/
class FunctionRun extends SPlugin {
constructor(S, config) {
super(S, config);
SUtils = S.utils;
}
static getName() {
return 'serverless.core.' + FunctionRun.name;
}
registerActions() {
this.S.addAction(this.functionRun.bind(this), {
handler: 'functionRun',
description: `Runs the service locally. Reads the services runtime and passes it off to a runtime-specific runner`,
context: 'function',
contextAction: 'run',
options: [
{
option: 'region',
shortcut: 'r',
description: 'region you want to run your function in'
},
{
option: 'stage',
shortcut: 's',
description: 'stage you want to run your function in'
},
{
option: 'invocationType',
shortcut: 'i',
description: 'Valid Values: Event | RequestResponse | DryRun . Default is RequestResponse'
},
{
option: 'log',
shortcut: 'l',
description: 'Show the log output'
}
],
parameters: [
{
parameter: 'name',
description: 'The name of the function you want to run',
position: '0'
}
]
});
return BbPromise.resolve();
}
/**
* Action
*/
functionRun(evt) {
let _this = this;
_this.evt = evt;
// Flow
return _this._validateAndPrepare()
.bind(_this)
.then(function() {
// Run local or deployed
if (_this.evt.options.stage) {
return _this._runDeployed();
} else {
return _this._runLocal();
}
})
.then(() => this.evt);
}
/**
* Validate And Prepare
*/
_validateAndPrepare() {
let _this = this;
// If CLI and path is not specified, deploy from CWD if Function
if (_this.S.cli && !_this.evt.options.name) {
// Get all functions in CWD
if (!SUtils.fileExistsSync(path.join(process.cwd(), 's-function.json'))) {
return BbPromise.reject(new SError('You must be in a function folder to run it'));
}
_this.evt.options.name = process.cwd().split(path.sep)[process.cwd().split(path.sep).length - 1];
}
_this.function = _this.S.getProject().getFunction(_this.evt.options.name);
// Missing function
if (!_this.function) return BbPromise.reject(new SError(`Function ${_this.evt.options.name} does not exist in your project.`));
return BbPromise.resolve();
}
/**
* Run Local
*/
_runLocal() {
if (!this.evt.options.name) {
return BbPromise.reject(new SError('Please provide a function name to run'));
}
SCli.log(`Running ${this.evt.options.name}...`);
return this.function.run()
.then(result => this.evt.data.result = result);
}
/**
* Run Deployed
*/
_runDeployed() {
let _this = this;
_this.evt.options.invocationType = _this.evt.options.invocationType || 'RequestResponse';
_this.evt.options.region = _this.evt.options.region || _this.S.getProject().getAllRegions(_this.evt.options.stage)[0].name;
if (_this.evt.options.invocationType !== 'RequestResponse') {
_this.evt.options.logType = 'None';
} else {
_this.evt.options.logType = _this.evt.options.log ? 'Tail' : 'None'
}
// validate stage: make sure stage exists
if (!_this.S.getProject().validateStageExists(_this.evt.options.stage)) {
return BbPromise.reject(new SError('Stage ' + _this.evt.options.stage + ' does not exist in your project', SError.errorCodes.UNKNOWN));
}
// validate region: make sure region exists in stage
if (!_this.S.getProject().validateRegionExists(_this.evt.options.stage, _this.evt.options.region)) {
return BbPromise.reject(new SError('Region "' + _this.evt.options.region + '" does not exist in stage "' + _this.evt.options.stage + '"'));
}
// Invoke Lambda
let params = {
FunctionName: _this.function.getDeployedName({ stage: _this.evt.options.stage, region: _this.evt.options.region }),
// ClientContext: new Buffer(JSON.stringify({x: 1, y: [3,4]})).toString('base64'),
InvocationType: _this.evt.options.invocationType,
LogType: _this.evt.options.logType,
Payload: new Buffer(JSON.stringify(SUtils.readFileSync(_this.function.getRootPath('event.json')))),
Qualifier: _this.evt.options.stage
};
return this.S.getProvider('aws')
.request('Lambda', 'invoke', params, _this.evt.options.stage, _this.evt.options.region)
.then( reply => {
let color = !reply.FunctionError ? 'white' : 'red';
if (reply.Payload) {
let payload = JSON.parse(reply.Payload)
_this.S.config.interactive && console.log(chalk[color](JSON.stringify(payload, null, ' ')));
_this.evt.data.result = {
status: 'success',
response: payload
};
}
if (reply.LogResult) {
console.log(chalk.gray('--------------------------------------------------------------------'));
let logResult = new Buffer(reply.LogResult, 'base64').toString();
logResult.split('\n').forEach( line => {
console.log(SCli.formatLambdaLogEvent(line));
});
}
})
.catch(function(e) {
_this.evt.data.result = {
status: 'error',
message: e.message,
stack: e.stack
};
BbPromise.reject(e);
});
}
}
return( FunctionRun );
};