serverless/lib/utils/index.js
2015-12-28 08:56:23 -08:00

902 lines
29 KiB
JavaScript

'use strict';
/**
* Serverless CLI: Utilities
*/
require('shelljs/global');
let BbPromise = require('bluebird'),
rawDebug = require('debug'),
path = require('path'),
async = require('async'),
traverse = require('traverse'),
readdirp = require('readdirp'),
SError = require('../ServerlessError'),
fs = require('fs'),
mkdirpAsync = require('mkdirp-then'),
shortid = require('shortid');
BbPromise.promisifyAll(fs);
/**
* Supported Runtimes
*/
module.exports.supportedRuntimes = {
nodejs: {
defaultPkgMgr: 'npm',
validPkgMgrs: ['npm'],
},
};
/**
* Get Root Path
*/
exports.getProjectPath = function(startDir) {
let _this = this;
// Check if startDir is root
if (_this.fileExistsSync(path.join(startDir, 's-project.json'))) {
let jawsJsonInDir = require(path.join(startDir, 's-project.json'));
if (typeof jawsJsonInDir.name !== 'undefined') return path.resolve(startDir);
}
// Check up to 10 parent levels
let previous = './',
projRootPath = false;
for (let i = 0; i < 10; i++) {
previous = path.join(previous, '../');
let fullPath = path.resolve(startDir, previous);
if (_this.fileExistsSync(path.join(fullPath, 's-project.json'))) {
let jawsJson = require(path.join(fullPath, 's-project.json'));
if (typeof jawsJson.name !== 'undefined') {
projRootPath = fullPath;
break;
}
}
}
return projRootPath;
};
/**
* GetProject
* - Create a project javascript object from al JSON files
*/
exports.getProject = function(projectRootPath) {
let _this = this,
project = {};
// Get Project JSON
project = _this.readAndParseJsonSync(path.join(projectRootPath, 's-project.json'));
// Add Modules & Functions
project.modules = {};
let moduleList = fs.readdirSync(path.join(projectRootPath, 'back', 'modules'));
for (let i = 0; i < moduleList.length; i++) {
try {
let module = _this.readAndParseJsonSync(path.join(projectRootPath, 'back', 'modules', moduleList[i], 's-module.json'));
project.modules[module.name] = module;
project.modules[module.name].pathModule = path.join('back', 'modules', moduleList[i], 's-module.json');
project.modules[module.name].functions = {};
// Get Functions
let moduleFolders = fs.readdirSync(path.join(projectRootPath, 'back', 'modules', moduleList[i]));
for (let j = 0; j < moduleFolders.length; j++) {
// Check functionPath exists
if (_this.fileExistsSync(path.join(projectRootPath, 'back', 'modules', moduleList[i], moduleFolders[j], 's-function.json'))) {
let funcs = _this.readAndParseJsonSync(path.join(projectRootPath, 'back', 'modules', moduleList[i], moduleFolders[j], 's-function.json'));
for (let k = 0; k < Object.keys(funcs.functions).length; k++) {
project.modules[module.name].functions[Object.keys(funcs.functions)[k]] = funcs.functions[Object.keys(funcs.functions)[k]];
project.modules[module.name].functions[Object.keys(funcs.functions)[k]].name = Object.keys(funcs.functions)[k];
project.modules[module.name].functions[Object.keys(funcs.functions)[k]].pathFunction = path.join('back', 'modules', moduleList[i], moduleFolders[j], 's-function.json');
}
}
}
} catch (e) {
console.log(e);
}
}
return project;
};
/**
* Get Meta
* - Get Project Meta Information
*/
exports.getMeta = function(projectRootPath) {
let _this = this,
projectMeta = {
private: {
stages: {},
variables: {}
},
public: {
variables: {}
}
};
// Re-usable function to traverse public or private variable folders
let _getVariables = function(type) {
let variableFiles = fs.readdirSync(path.join(projectRootPath, 'meta', type, 'variables'));
for (let i = 0; i < variableFiles.length; i++) {
let variableFile = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', type, 'variables', variableFiles[i]));
// Parse file name to get stage/region
let file = variableFiles[i].replace('s-variables-', '').replace('.json', '');
if (file === 'common') {
// Set Common variables
projectMeta[type].variables = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', type, 'variables', variableFiles[i]));
} else if (type === 'private') {
// Set Stage/Region variables
file = file.split('-');
if (!projectMeta[type].stages) projectMeta[type].stages = {};
if (!projectMeta[type].stages[file[0]]) projectMeta[type].stages[file[0]] = {
regions: {},
variables: {}
};
if (file.length === 1) {
// Set Stage Variables
projectMeta[type].stages[file[0]].variables = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', type, 'variables', variableFiles[i]));
} else if (file.length === 2) {
// Set Stage-Region Variables
let region;
if (file[1] === 'useast1') region = 'us-east-1';
if (file[1] === 'uswest2') region = 'us-west-2';
if (file[1] === 'euwest1') region = 'eu-west-1';
if (file[1] === 'apnortheast1') region = 'ap-northeast-1';
if (!projectMeta[type].stages[file[0]].regions[region]) projectMeta[type].stages[file[0]].regions[region] = {
variables: _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', type, 'variables', variableFiles[i]))
};
}
}
}
};
if (_this.dirExistsSync(path.join(projectRootPath, 'meta', 'public'))) _getVariables('public');
if (_this.dirExistsSync(path.join(projectRootPath, 'meta', 'private'))) _getVariables('private');
return projectMeta;
};
/**
* Save Meta
*/
exports.saveMeta = function(projectRootPath, projectMeta) {
// Re-usable function to save public or private variables
let _saveVariables = function(type) {
// Save Common Variables
fs.writeFileSync(path.join(projectRootPath, 'meta', type, 'variables', 's-variables-common.json'),
JSON.stringify(projectMeta[type].variables, null, 2));
if (type === 'private') {
for (let i = 0; i < Object.keys(projectMeta[type].stages).length; i++) {
let stage = projectMeta[type].stages[Object.keys(projectMeta[type].stages)[i]];
// Save Stage Variables
fs.writeFileSync(path.join(projectRootPath, 'meta', type, 'variables', 's-variables-' + Object.keys(projectMeta[type].stages)[i] + '.json'),
JSON.stringify(stage.variables, null, 2));
// Save Stage Region Variables
for (let j = 0; j < Object.keys(stage.regions).length; j++) {
fs.writeFileSync(path.join(projectRootPath, 'meta', type, 'variables', 's-variables-' + Object.keys(projectMeta[type].stages)[i] + '-' + Object.keys(stage.regions)[j].replace(/-/g, '') + '.json'),
JSON.stringify(stage.regions[Object.keys(stage.regions)[j]].variables, null, 2));
}
}
}
};
if (this.dirExistsSync(path.join(projectRootPath, 'meta', 'public'))) _saveVariables('public');
if (this.dirExistsSync(path.join(projectRootPath, 'meta', 'private'))) _saveVariables('private');
};
/**
* Populate Variables
* - variable names are provided with the ${someVar} syntax. Ex. {projectName: '${someVar}'}
* - variables can be of any type, but since they're referenced in JSON, variable names should always be in string quotes:
* - Ex. {projectName: '${someVar}'} - someVar can be an obj, or array...etc
* - you can provide more than one variable in a string. Ex. {projectName: '${firstName}-${lastName}-project'}
* - we get the value of the variable based on stage/region provided. If both are provided, we'll get the region specific value,
* - if only stage is provided, we'll get the stage specific value, if none is provided, we'll get the private or public specific value (depending on the type property of the option obj)
* - example options: {type: 'private', stage: 'development', region: 'us-east-1'}
**/
exports.populateVariables = function(project, meta, options) {
options.type = options.type || 'private'; // private is the default type
// Validate providing region without stage is invalid!
if (!options.stage) options.region = null;
// Validate Stage exists
if (typeof options.stage != 'undefined' && !meta.private.stages[options.stage]) {
throw Error('Stage doesnt exist!');
}
// Validate Region exists
if (typeof options.region != 'undefined' && !meta.private.stages[options.stage].regions[options.region]) {
throw Error('Region doesnt exist in the provided stage!');
}
// Traverse the whole project and replace variables
traverse(project).forEach(function(projectVal) {
// check if the current string is a variable
if (typeof(projectVal) === 'string' && projectVal.match(/\${([^{}]*)}/g) != null) {
let newVal = projectVal;
// get all ${variable}
projectVal.match(/\${([^{}]*)}/g).forEach(function(variableSyntax) {
let variableName = variableSyntax.replace('${', '').replace('}', '');
let value;
if (options.stage && options.region) {
value = meta[options.type].stages[options.stage].regions[options.region].variables[variableName];
} else if (options.stage) {
value = meta[options.type].stages[options.stage].variables[variableName];
} else if (!options.stage && !options.region) {
value = meta[options.type].variables[variableName];
}
if (typeof value === 'undefined') {
throw Error('Variable Doesnt exist!');
} else if (typeof value === 'string') {
newVal = newVal.replace(variableSyntax, value);
} else {
newVal = value;
}
});
this.update(newVal);
}
});
return project;
};
/**
* Execute (Command)
*/
exports.execute = function(promise) {
promise
.catch(SError, function(e) {
throw e;
process.exit(e.messageId);
})
.error(function(e) {
console.error(e);
process.exit(1);
})
.done();
};
/**
* Read Recursively
*/
exports.readRecursively = function(path, filter) {
return new BbPromise(function(resolve, reject) {
let files = [];
readdirp({
root: path,
fileFilter: filter,
})
.on('data', function(entry) {
files.push(entry.path);
})
.on('error', function(error) {
reject(error);
})
.on('end', function() {
resolve(files);
});
});
};
/**
* Get Modules
* - Find all s-module json paths underneath given dir
*/
exports.getModules = function(baseDir) {
return this.readRecursively(baseDir, '*s-module.json')
.then(function(jsonPaths) {
return jsonPaths.map(function(jsonPath) {
return path.resolve(path.join(baseDir, jsonPath));
});
});
};
/**
* Return Partial
*/
exports.returnPartial = function(string, symbol, number, defaultResponse) {
if (string.indexOf(symbol) > -1) return string.split(symbol)[number];
else return defaultResponse;
};
/**
* Get Functions
* - Resolve paths and return function JSONs
* - If no paths specified, finds all functions in baseDir
* - Each function must contain # in path: module/function#get
*/
exports.getFunctions = function(baseDir, functionPaths) {
let _this = this,
allFunctionJsons = [];
return BbPromise.try(function () {
// Sanitize baseDir
if ((baseDir).indexOf('back') == -1) baseDir = path.join(baseDir, 'back');
if ((baseDir).indexOf('modules') == -1) baseDir = path.join(baseDir, 'modules');
// If functionPaths, validate and return them
if (functionPaths) {
// Validate - ensure functionPath contains #
for (let i = 0; i < functionPaths.length; i++) {
let path = functionPaths[i];
if (path.indexOf('#') === -1) {
throw new SError(`Function path is missing '#' : ${path}`,
SError.errorCodes.INVALID_RESOURCE_NAME);
}
// If absolute path, trim to be relative
if (path.indexOf('modules/') > -1) path = path.split('modules/')[1];
if (path.indexOf('modules\\') > -1) path = path.split('modules\\')[1];
}
return functionPaths;
}
// If no functionPaths, get all functions in project and create paths
functionPaths = [];
return _this.readRecursively(baseDir, '*s-function.json')
.then(function(functionFilePaths) {
// If inside modules, grab path prefix
let baseArray = baseDir.split(path.sep),
pathPrefix;
if (baseArray[baseArray.indexOf('modules') + 1]) {
pathPrefix = (baseArray.splice(baseArray.indexOf('modules') + 1)).join(path.sep);
}
// We've used the basDir to locate functions. Now, normalize baseDir
if (baseDir.indexOf('modules/') > -1) baseDir = path.join(baseDir.split('modules/')[0], 'modules/');
if (baseDir.indexOf('modules\\') > -1) baseDir = path.join(baseDir.split('modules\\')[0], 'modules\\'); // Windows
for (let i = 0; i < functionFilePaths.length; i++) {
// Read JSON
let filePath = pathPrefix ? path.join(pathPrefix, functionFilePaths[i]) : functionFilePaths[i];
let functionsObject = _this.readAndParseJsonSync(path.join(baseDir, filePath));
functionsObject = functionsObject.functions;
// Create paths for each function in s-function.json
for (let j = 0; j < Object.keys(functionsObject).length; j++ ) {
functionPaths.push(filePath + '#' + Object.keys(functionsObject)[j]);
}
}
return functionPaths;
});
})
.then(function(paths) {
// Sanitize Paths
for (let i = 0; i < paths.length; i++) {
paths[i] = paths[i].replace('s-function.json', '');
paths[i] = paths[i].replace('/#', '#');
paths[i] = paths[i].replace('\\#', '#');
// Remove slashes after functionPath
if (['/', '\\'].indexOf(paths[i].charAt(0)) !== -1) paths[i] = paths[i].substring(1, paths[i].length);
}
return paths;
})
.then(function(functionPaths) {
return new BbPromise(function(resolve, reject){
// Loop through function paths and process
async.eachLimit(functionPaths, 10, function (functionPath, cb) {
// Strip functionPath, functionKey & endpointKeys
let functionKey = _this.returnPartial(functionPath, '#', 1);
let pathFunctionRelative = _this.returnPartial(functionPath, '#', 0);
// Check functionPath exists
if (!_this.fileExistsSync(path.join(baseDir, pathFunctionRelative, 's-function.json'))) {
throw new SError(`Invalid function path ${functionPath}`, SError.errorCodes.INVALID_RESOURCE_NAME);
}
// Get FunctionJSON
let functionsObject;
let functionJson;
try {
functionsObject = _this.readAndParseJsonSync(path.join(baseDir, pathFunctionRelative, 's-function.json'));
functionJson = functionsObject.functions[functionKey];
} catch(e) {
throw new SError(`Invalid JSON in ${functionPath}`, SError.errorCodes.INVALID_RESOURCE_NAME);
}
// Get ModuleJSON
let moduleJson;
let modulePath = path.join(baseDir, functionPath.split(path.sep)[0], 's-module.json');
try {
moduleJson = _this.readAndParseJsonSync(modulePath);
moduleJson.pathModule = path.join('back', 'modules', pathFunctionRelative.split(path.sep)[0]);
} catch(e) {
throw new SError(
`This function has missing or invalid parent module JSON (${modulePath}) ${functionPath}`,
SError.errorCodes.INVALID_RESOURCE_NAME);
}
// Add attributes
functionJson.name = functionKey;
functionJson.module = moduleJson;
functionJson.pathFunction = path.join('back', 'modules', pathFunctionRelative);
// Add to main array
allFunctionJsons.push(functionJson);
// Callback
return cb();
}, function (error) {
if (error) return reject(new SError(error.message));
return resolve(allFunctionJsons);
});
});
});
};
/**
* Get Endpoints
* - Return endpoint JSONs
* - If no paths specified, finds all endpoints in baseDir
* - Each endpoint must contain #, @ and ~ in path: module/function#get@get-data~GET
*/
exports.getEndpoints = function(baseDir, endpointPaths) {
let _this = this,
allEndpointJsons = [];
return BbPromise.try(function () {
// Sanitize baseDir
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
if (endpointPaths) {
// Validate - ensure endpoint path contains #
for (let i = 0; i < endpointPaths.length; i++) {
let path = endpointPaths[i];
// Ensure pointers are included
if (path.indexOf('#') === -1 || path.indexOf('@') === -1 || path.indexOf('~') === -1) {
throw new SError(`Endpoint path is missing '#', '@' or '~' : ${path}`,
SError.errorCodes.INVALID_RESOURCE_NAME);
}
// If absolute path, trim to be relative
if (path.indexOf('/back/modules') === -1) {
path = path.split('back/modules/')[1];
}
}
return endpointPaths;
}
// If no endpointPaths, get all functions in project and create their endpoint paths
endpointPaths = [];
return _this.readRecursively(baseDir, '*s-function.json')
.then(function(functionFilePaths) {
// If inside modules, grab path prefix
let baseArray = baseDir.split(path.sep),
pathPrefix;
if (baseArray[baseArray.indexOf('modules') + 1]) {
pathPrefix = (baseArray.splice(baseArray.indexOf('modules') + 1)).join(path.sep);
}
// We've used the basDir to locate functions. Now, normalize baseDir
if (baseDir.indexOf('modules/') > -1) baseDir = path.join(baseDir.split('modules/')[0], 'modules/');
if (baseDir.indexOf('modules\\') > -1) baseDir = path.join(baseDir.split('modules\\')[0], 'modules\\'); // Windows
for (let i = 0; i < functionFilePaths.length; i++) {
// Read JSON
let filePath = pathPrefix ? path.join(pathPrefix, functionFilePaths[i]) : functionFilePaths[i];
let functionsObject = _this.readAndParseJsonSync(path.join(baseDir, filePath));
functionsObject = functionsObject.functions;
// Create paths for each function in s-function.json
for (let j = 0; j < Object.keys(functionsObject).length; j++ ) {
let functionPath = filePath + '#' + Object.keys(functionsObject)[j];
let funcObject = functionsObject[Object.keys(functionsObject)[j]];
for (let k = 0; k < funcObject.endpoints.length; k++) {
let endpointPath = functionPath + '@' + funcObject.endpoints[k].path + '~' + funcObject.endpoints[k].method;
endpointPaths.push(endpointPath);
}
}
}
return endpointPaths;
});
})
.then(function(paths) {
// Sanitize Paths
for (let i = 0; i < paths.length; i++) {
paths[i] = paths[i].replace('s-function.json', '');
paths[i] = paths[i].replace('/#', '#');
paths[i] = paths[i].replace('\\#', '#');
// Remove slashes after functionPath
if (['/', '\\'].indexOf(paths[i].charAt(0)) !== -1) paths[i] = paths[i].substring(1, paths[i].length);
}
return paths;
})
.then(function(endpointPaths) {
return new BbPromise(function(resolve, reject){
// Loop through function paths and process
async.eachLimit(endpointPaths, 10, function (pathEndpoint, cb) {
let pathFunctionRelative = null,
nameFunction = null,
endpointUrlPath = null,
endpointMethod = null;
// Get Function Properties
pathFunctionRelative = _this.returnPartial(pathEndpoint, '@', 0, null);
nameFunction = _this.returnPartial(pathFunctionRelative, '#', 1, null);
pathFunctionRelative = _this.returnPartial(pathFunctionRelative, '#', 0, null);
// Get Endpoint Properties
endpointUrlPath = _this.returnPartial(pathEndpoint, '@', 1, null);
endpointMethod = _this.returnPartial(pathEndpoint, '~', 1, null);
endpointUrlPath = _this.returnPartial(endpointUrlPath, '~', 0, null);
// If endpointPath has s-function.json missing, add it
pathEndpoint = pathEndpoint.replace('s-function.json', '');
// Check function exists
if (!_this.fileExistsSync(path.join(baseDir, pathFunctionRelative, 's-function.json'))) {
throw new SError(
`Invalid endpoint path ${pathEndpoint}`,
SError.errorCodes.INVALID_RESOURCE_NAME);
}
// Get FunctionJSON
let functionsObject;
let endpointJson = {};
try {
functionsObject = _this.readAndParseJsonSync(path.join(baseDir, pathFunctionRelative, 's-function.json'));
let func = functionsObject.functions[nameFunction];
for (let i = 0; i < func.endpoints.length; i++) {
let endpoint = func.endpoints[i];
if (endpoint.path === endpointUrlPath && endpoint.method === endpointMethod) {
endpointJson = endpoint;
endpointJson.function = func;
endpointJson.function.name = nameFunction;
endpointJson.function.pathFunction = path.join('back', 'modules', pathFunctionRelative);
}
}
} catch(e) {
console.log(e);
throw new SError(`Invalid JSON in ${endpointUrlPath}`, SError.errorCodes.INVALID_RESOURCE_NAME);
}
// Get ModuleJSON
let pathModule = path.join(baseDir, pathFunctionRelative.split(path.sep)[0], 's-module.json');
let moduleJson;
try {
moduleJson = _this.readAndParseJsonSync(pathModule);
moduleJson.pathModule = path.join('back', 'modules', pathFunctionRelative.split(path.sep)[0]);
} catch(e) {
throw new SError(`This endpoint has missing or invalid parent module JSON (s-module.json) ${endpointUrlPath} - ${pathModule}`,
SError.errorCodes.INVALID_RESOURCE_NAME);
}
// Add attributes
endpointJson.module = moduleJson;
// Add to main array
allEndpointJsons.push(endpointJson);
// Callback
return cb();
}, function () {
return resolve(allEndpointJsons);
});
});
});
};
/**
* getFunctionEnvVars
* - Finds all Env Vars that are used by all Functions in a given Module
*/
exports.getFunctionEnvVars = function(projectRootPath, modName) {
return this.getFunctions(path.join(projectRootPath, 'back', 'modules', modName), null)
.then(function(functionJsons) {
let envVars = [];
functionJsons.forEach(function(functionJson) {
if (functionJson.lambda && functionJson.lambda.envVar) {
functionJson.lambda.envVar.forEach(function(envVar) {
if (envVars.indexOf(envVar) == -1) {
envVars.push(envVar);
}
});
}
});
return envVars;
});
};
/**
* Write File
* - Writes file and makes any parent dirs if necessary
* @param filePath
* @param contents node Buffer
* @returns {Promise}
*/
exports.writeFile = function(filePath, contents) {
this.sDebug('Writing file:', filePath);
if (contents === undefined) {
contents = '';
}
return mkdirpAsync(path.dirname(filePath))
.then(function() {
return fs.writeFileAsync(filePath, contents);
});
};
exports.generateShortId = function(maxLen) {
return shortid.generate().replace(/\W+/g, '').substring(0, maxLen).replace(/[_-]/g, '');
};
/**
* Generate JawsBucket Name
*/
exports.generateProjectBucketName = function(region, projectDomain) {
// Sanitize
region = region.trim().replace(/-/g, '').toLowerCase();
projectDomain = projectDomain.trim().toLowerCase();
return `serverless.${region}.${projectDomain}`;
};
/**
* Given list of project stage objects, extract given region
*/
exports.getRegionConfig = function(projectMeta, stage, regionName) {
if (!projectMeta.project.stages[stage].regions[region]) {
throw new SError(`Could not find region ${regionName}`, SError.errorCodes.UNKNOWN);
}
return projectMeta.project.stages[stage].regions[region];
};
exports.dirExistsSync = function(path) {
try {
let stats = fs.statSync(path);
return stats.isDirectory();
}
catch (e) {
return false;
}
};
exports.fileExistsSync = function(path) {
try {
let stats = fs.lstatSync(path);
return stats.isFile();
}
catch (e) {
return false;
}
};
exports.readAndParseJsonSync = function(path) {
return JSON.parse(fs.readFileSync(path));
};
exports.endsWith = function(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
};
/**
* NPM Install
* - Programatically install NPM dependencies
*/
exports.npmInstall = function(dir) {
process.chdir(dir);
if (exec('npm install ', { silent: false }).code !== 0) {
throw new SError(`Error executing NPM install on ${dir}`, SError.errorCodes.UNKNOWN);
}
process.chdir(process.cwd());
};
/**
* Write to console.log if process.env.DEBUG is true
* - If we ever want to get more complicated with log levels we should use winston
*/
let debuggerCache = {};
exports.sDebugWithContext = function(context) {
if (process.env.DEBUG) {
context = `serverless:${context}`;
if (!debuggerCache[context]) {
debuggerCache[context] = rawDebug(context);
}
debuggerCache[context].apply(null, Array.prototype.slice.call(arguments, 1));
}
};
exports.sDebug = function() {
if (process.env.DEBUG) {
let caller = getCaller();
let context = pathToContext(caller);
let args = Array.prototype.slice.call(arguments);
args.unshift(context);
this.sDebugWithContext.apply(this, args);
}
};
exports.isStageNameValid = function(stageName) {
return /^[a-zA-Z\d]+$/.test(stageName);
};
/**
* Find Regional API
* - Finds a project REST API ID that already exists
*/
exports.findRegionalApi = function(projectJawsJson, regionName) {
for (let stages of Object.keys(projectJawsJson.stage)) {
for (let i = 0; i < stages.length; i++) {
if (stages[i].region === regionName && stages[i].restApiId) {
return stages[i].restApiId;
}
}
}
};
/**
* Save Regional API
* - Saves regional API to all stage that have this region
*/
exports.saveRegionalApi = function(projectJawsJson, regionName, restApiId, rootPath) {
for (let stages of Object.keys(projectJawsJson.stages)) {
for (let i = 0; i < projectJawsJson.stages[stages].length; i++) {
if (projectJawsJson.stages[stages][i].region === regionName) {
projectJawsJson.stages[stages][i].restApiId = restApiId;
}
}
}
fs.writeFileSync(path.join(rootPath, 's-project.json'), JSON.stringify(projectJawsJson, null, 2));
};
function pathToContext(path) {
// Match files under lib, tests, or bin so we only report the
// relevant part of the file name as the context
let pathRegex = /\/((lib|tests|bin)\/.*?)\.js$/i;
let match = pathRegex.exec(path);
if (match.length >= 2) {
return match[1].replace(/[\/\\]/g, '.');
} else {
return path;
}
}
function getCaller() {
let stack = getStack();
// Remove unwanted function calls on stack -- ourselves and our caller
stack.shift();
stack.shift();
// Now the top of the stack is the CallSite we want
// See this for available methods:
// https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi
let path = stack[0].getFileName();
return path;
}
function getStack() {
// Save original Error.prepareStackTrace
let origPrepareStackTrace = Error.prepareStackTrace;
// Override with function that just returns `stack`
Error.prepareStackTrace = function(_, stack) {
return stack;
};
let err = new Error();
// Get `err.stack`, which calls our new `Error.prepareStackTrace`
let stack = err.stack;
// Restore original `Error.prepareStackTrace`
Error.prepareStackTrace = origPrepareStackTrace;
// Remove ourselves from the stack
stack.shift();
return stack;
}