serverless/lib/classes/Service.js
Philipp Muens 39d30b3ca0 Add automatic servicePath detection functionality
The service directory will be automatically detected if the users CWD is a valid
Serverless service directory.
2016-05-26 14:34:56 +02:00

183 lines
5.5 KiB
JavaScript

'use strict';
const SError = require('./Error');
const path = require('path');
const _ = require('lodash');
const traverse = require('traverse');
const replaceall = require('replaceall');
const BbPromise = require('bluebird');
class Service {
constructor(serverless, data) {
this.serverless = serverless;
// Default properties
this.service = null;
this.variableSyntax = null;
this.custom = {};
this.plugins = [];
this.functions = {};
this.environment = {};
this.resources = {};
if (data) this.update(data);
}
load(opts) {
const that = this;
const options = opts || {};
const servicePath = that.serverless.config.servicePath;
// skip if the service path is not found
// because the user might want to create a new service
if (!servicePath) {
return BbPromise.resolve();
}
return that.serverless.yamlParser
.parse(path.join(servicePath, 'serverless.yaml'))
.then((serverlessYaml) => {
that.service = serverlessYaml.service;
that.variableSyntax = serverlessYaml.variableSyntax;
that.custom = serverlessYaml.custom;
that.plugins = serverlessYaml.plugins;
that.resources = serverlessYaml.resources;
that.functions = serverlessYaml.functions;
})
.then(() => that.serverless.yamlParser
.parse(path.join(servicePath, 'serverless.env.yaml')))
.then((serverlessEnvYaml) => {
that.environment = serverlessEnvYaml;
BbPromise.resolve(that);
})
.then(() => {
// Validate: Check stage exists
if (options.stage) this.getStage(options.stage);
// Validate: Check region exists in stage
if (options.region) this.getRegionInStage(options.stage, options.region);
let varTemplateSyntax = /\${([\s\S]+?)}/g;
if (this.variableSyntax) {
varTemplateSyntax = RegExp(this.variableSyntax, 'g');
// temporally remove variable syntax from service otherwise it'll match
this.variableSyntax = true;
}
const variablesObject = this.getVariables(options.stage, options.region);
// temporally remove environment obj. Doesn't make sense to
// populate environment (stages, regions, vars)
const environment = _.cloneDeep(this.environment);
this.environment = null;
/*
* we can't use an arrow function in this case cause that would
* change the lexical scoping required by the traverse module
*/
traverse(this).forEach(function (valParam) {
const t = this;
let val = valParam;
// check if the current string is a variable
if (typeof(val) === 'string' && val.match(varTemplateSyntax)) {
// get all ${variable} in the string
val.match(varTemplateSyntax).forEach((variableSyntax) => {
const variableName = variableSyntax
.replace(varTemplateSyntax, (match, varName) => varName.trim());
let value;
if (variableName in variablesObject) {
value = variablesObject[variableName];
}
// Populate
if (!value && !value !== '') {
// SCLI.log('WARNING: This variable is not defined: ' + variableName);
} else if (typeof value === 'string') {
// for string variables, we use replaceall in case the user
// includes the variable as a substring (ie. "hello ${name}")
val = replaceall(variableSyntax, value, val);
} else {
val = value;
}
});
// Replace
t.update(val);
}
});
// put back environment that we temporally removed earlier
this.environment = environment;
// put back variable syntax if we removed it for processing
if (this.variableSyntax) this.variableSyntax = varTemplateSyntax;
return this;
});
}
update(data) {
return _.merge(this, data);
}
getAllFunctions() {
return Object.keys(this.functions);
}
getFunction(functionName) {
if (functionName in this.functions) {
return this.functions[functionName];
}
throw new SError(`function ${functionName} doesn't exist in this Service`);
}
getEventInFunction(eventName, functionName) {
if (eventName in this.getFunction(functionName).events) {
return this.getFunction(functionName).events[eventName];
}
throw new SError(`event ${eventName} doesn't exist in function ${functionName}`);
}
getAllEventsInFunction(functionName) {
return Object.keys(this.getFunction(functionName).events);
}
getStage(stageName) {
if (stageName in this.environment.stages) {
return this.environment.stages[stageName];
}
throw new SError(`stage ${stageName} doesn't exist in this Service`);
}
getAllStages() {
return Object.keys(this.environment.stages);
}
getRegionInStage(stageName, regionName) {
if (regionName in this.getStage(stageName).regions) {
return this.getStage(stageName).regions[regionName];
}
throw new SError(`region ${regionName} doesn't exist in stage ${stageName}`);
}
getAllRegionsInStage(stageName) {
return Object.keys(this.getStage(stageName).regions);
}
getVariables(stageName, regionName) {
if (stageName && regionName) {
return this.getRegionInStage(stageName, regionName).vars;
} else if (stageName) {
return this.getStage(stageName).vars;
}
return this.environment.vars;
}
}
module.exports = Service;