serverless/lib/classes/Service.js
2016-11-01 08:24:31 +01:00

195 lines
6.9 KiB
JavaScript

'use strict';
const SError = require('./Error').SError;
const path = require('path');
const _ = require('lodash');
const BbPromise = require('bluebird');
const version = require('../../package.json').version;
class Service {
constructor(serverless, data) {
this.serverless = serverless;
// Default properties
this.service = null;
this.provider = {};
this.defaults = {
stage: 'dev',
region: 'us-east-1',
variableSyntax: '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}',
};
this.custom = {};
this.plugins = [];
this.functions = {};
this.resources = {};
this.package = {};
if (data) this.update(data);
}
load(rawOptions) {
const that = this;
const options = rawOptions || {};
options.stage = options.stage || options.s;
options.region = options.region || options.r;
const servicePath = that.serverless.config.servicePath;
// skip if the service path is not found
// because the user might be creating a new service
if (!servicePath) {
return BbPromise.resolve();
}
let serverlessYmlPath = path.join(servicePath, 'serverless.yml');
// change to serverless.yaml if the file could not be found
if (!this.serverless.utils.fileExistsSync(serverlessYmlPath)) {
serverlessYmlPath = path
.join(this.serverless.config.servicePath, 'serverless.yaml');
}
return that.serverless.yamlParser
.parse(serverlessYmlPath)
.then((serverlessFileParam) => {
const serverlessFile = serverlessFileParam;
// basic service level validation
const ymlVersion = serverlessFile.frameworkVersion;
if (ymlVersion && ymlVersion !== version) {
const msgPartOne = `The Serverless version (${version}) is not matching the`;
const msgPartTwo = `"frameworkVersion" (${ymlVersion}) in serverless.yml`;
throw new SError(`${msgPartOne} ${msgPartTwo}`);
}
if (!serverlessFile.service) {
throw new SError('"service" property is missing in serverless.yml');
}
if (!serverlessFile.provider) {
throw new SError('"provider" property is missing in serverless.yml');
}
if (typeof serverlessFile.provider !== 'object') {
const providerName = serverlessFile.provider;
serverlessFile.provider = {
name: providerName,
};
}
const providers = ['aws', 'azure', 'google', 'ibm'];
if (providers.indexOf(serverlessFile.provider.name) === -1) {
const errorMessage = [
`Provider "${serverlessFile.provider.name}" is not supported.`,
` Valid values for provider are: ${providers.join(', ')}.`,
' Please provide one of those values to the "provider" property in serverless.yml.',
].join('');
throw new SError(errorMessage);
}
that.service = serverlessFile.service;
that.provider = serverlessFile.provider;
that.custom = serverlessFile.custom;
that.plugins = serverlessFile.plugins;
that.resources = serverlessFile.resources;
that.functions = serverlessFile.functions || {};
if (serverlessFile.package) {
that.package.individually = serverlessFile.package.individually;
that.package.artifact = serverlessFile.package.artifact;
that.package.exclude = serverlessFile.package.exclude;
}
if (serverlessFile.defaults && serverlessFile.defaults.stage) {
this.defaults.stage = serverlessFile.defaults.stage;
}
if (serverlessFile.defaults && serverlessFile.defaults.region) {
this.defaults.region = serverlessFile.defaults.region;
}
if (serverlessFile.defaults && serverlessFile.defaults.variableSyntax) {
this.defaults.variableSyntax = serverlessFile.defaults.variableSyntax;
}
// load defaults property for backward compatibility
if (serverlessFile.defaults) {
const warningMessage = [
'Deprecation Notice: the "defaults" property in serverless.yml',
' is deprecated. The "stage", "region" & "variableSyntax" properties',
' has been moved to the "provider" property instead. Please update',
' your serverless.yml file asap. For more info, you can check our docs.',
].join('');
this.serverless.cli.log(warningMessage);
if (serverlessFile.defaults.stage) {
this.defaults.stage = serverlessFile.defaults.stage;
}
if (serverlessFile.defaults.region) {
this.defaults.region = serverlessFile.defaults.region;
}
if (serverlessFile.defaults.variableSyntax) {
this.defaults.variableSyntax = serverlessFile.defaults.variableSyntax;
}
}
// if exists, move provider to defaults for backward compatibility
if (serverlessFile.provider.stage) {
this.defaults.stage = serverlessFile.provider.stage;
}
if (serverlessFile.provider.region) {
this.defaults.region = serverlessFile.provider.region;
}
if (serverlessFile.provider.variableSyntax) {
this.defaults.variableSyntax = serverlessFile.provider.variableSyntax;
}
// make sure provider obj is in sync with default for backward compatibility
this.provider.stage = this.defaults.stage;
this.provider.region = this.defaults.region;
this.provider.variableSyntax = this.defaults.variableSyntax;
// setup function.name property
const stageNameForFunction = options.stage || this.provider.stage;
_.forEach(that.functions, (functionObj, functionName) => {
if (!functionObj.events) {
that.functions[functionName].events = [];
}
if (!_.isArray(functionObj.events)) {
throw new SError(`Events for "${functionName}" must be an array,` +
` not an ${typeof functionObj.events}`);
}
if (!functionObj.name) {
that.functions[functionName].name =
`${that.service}-${stageNameForFunction}-${functionName}`;
}
});
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);
}
}
module.exports = Service;