diff --git a/lib/Serverless.js b/lib/Serverless.js index f1e48368a..95433c112 100644 --- a/lib/Serverless.js +++ b/lib/Serverless.js @@ -59,6 +59,7 @@ class Serverless { this.cli = null; this.state = new this.classes.State(this); + // If project if (this.config.projectPath) { @@ -108,7 +109,7 @@ class Serverless { * - Returns a Promise */ - _init() { + init() { return this.state.load(); } @@ -134,7 +135,7 @@ class Serverless { options: extend(_this.cli.options, _this.cli.params) }; - if (_this.config.projectPath) return _this._init(); + if (_this.config.projectPath) return _this.init(); } else { diff --git a/lib/ServerlessFunction.js b/lib/ServerlessFunction.js index 8b81133bb..0eec263f7 100644 --- a/lib/ServerlessFunction.js +++ b/lib/ServerlessFunction.js @@ -288,9 +288,7 @@ class ServerlessFunction { module: this._config.module }); - if (modules.length === 1) { - return modules[0]; - } + if (modules.length === 1) return modules[0]; throw new SError('Could not find module for function'); } diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index 4234559c5..a4c250ebf 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -21,19 +21,20 @@ class ServerlessProject { constructor(Serverless) { let _this = this; - this._S = Serverless; + this._S = Serverless; // Default properties - _this.name = 'serverless' + SUtils.generateShortId(6); - _this.version = '0.0.1'; - _this.profile = 'serverless-v' + require('../package.json').version; - _this.location = 'https://github.com/...'; - _this.author = ''; - _this.description = 'A Slick New Serverless Project'; - _this.custom = {}; - _this.components = {}; - _this.plugins = []; - _this.cloudFormation = { + _this.name = 'serverless' + SUtils.generateShortId(6); + _this.version = '0.0.1'; + _this.profile = 'serverless-v' + require('../package.json').version; + _this.location = 'https://github.com/...'; + _this.author = ''; + _this.description = 'A Slick New Serverless Project'; + _this.custom = {}; + _this.components = {}; + _this.templates = {}; + _this.plugins = []; + _this.cloudFormation = { "AWSTemplateFormatVersion": "2010-09-09", "Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway", "Resources": { @@ -121,27 +122,34 @@ class ServerlessProject { throw new SError('Project could not be loaded because it does not exist'); } - projectJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-project.json')); - projectJson.components = {}; - projectContents = fs.readdirSync(_this._S.config.projectPath); + projectJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, 's-project.json')); + projectJson.components = {}; + projectJson.templates = {}; + projectContents = fs.readdirSync(_this._S.config.projectPath); return projectContents; }) .each(function (c, i) { - if (!SUtils.fileExistsSync(path.join( - _this._S.config.projectPath, projectContents[i], - 's-component.json'))) return; + // If template, load template + if (c.indexOf('s-template') !== -1) { + projectJson.templates = _.assign(projectJson.templates, SUtils.readAndParseJsonSync(path.join(_this._S.config.projectPath, c))); + return; + } - let component = new _this._S.classes.Component(_this._S, { - component: c - }); + // If component, load component + if (SUtils.fileExistsSync(path.join(_this._S.config.projectPath, c, 's-component.json'))) { - return component.load() - .then(function (instance) { - projectJson.components[c] = instance; - return projectJson.components[c]; + let component = new _this._S.classes.Component(_this._S, { + component: c }); + + return component.load() + .then(function (instance) { + projectJson.components[c] = instance; + return projectJson.components[c]; + }); + } }) .then(function () { @@ -230,7 +238,7 @@ class ServerlessProject { getTemplates() { let templates = { - _project: {}, + _project: this.templates || {}, _components: {} }; diff --git a/lib/ServerlessState.js b/lib/ServerlessState.js index 0848f758b..73641caec 100644 --- a/lib/ServerlessState.js +++ b/lib/ServerlessState.js @@ -22,8 +22,6 @@ class ServerlessState { this.meta = new this._S.classes.Meta(this._S); this.project = new this._S.classes.Project(this._S); - // If project path, load state - if (Serverless.config.projectPath) this.load(); } /** diff --git a/lib/utils/index.js b/lib/utils/index.js index da37b5285..809a75c13 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -158,6 +158,7 @@ exports.getProjectPath = function(startDir) { exports.getResources = function(projectData) { + let _this = this; let cfTemplate = JSON.parse(JSON.stringify(projectData.cloudFormation)); // Helper function to aggregate resources @@ -169,7 +170,7 @@ exports.getResources = function(projectData) { // Merge Lambda Policy Statements if (cfData.cloudFormation.lambdaIamPolicyDocumentStatements && cfData.cloudFormation.lambdaIamPolicyDocumentStatements.length > 0) { - SCli.log('Merging in Lambda IAM Policy statements from: ' + cfData.name); + _this.sDebug('Merging in Lambda IAM Policy statements from: ' + cfData.name); cfData.cloudFormation.lambdaIamPolicyDocumentStatements.forEach(function (policyStmt) { cfTemplate.Resources.IamPolicyLambda.Properties.PolicyDocument.Statement.push(policyStmt); @@ -182,13 +183,13 @@ exports.getResources = function(projectData) { let cfResourceKeys = Object.keys(cfData.cloudFormation.resources); if (cfResourceKeys.length > 0) { - SCli.log('Merging in CF Resources from module: ' + cfData.name); + _this.sDebug('Merging in CF Resources from module: ' + cfData.name); } cfResourceKeys.forEach(function (resourceKey) { if (cfTemplate.Resources[resourceKey]) { - SCli.log( + _this.log( chalk.bgYellow.white(' WARN ') + chalk.magenta(` Resource key ${resourceKey} already defined in CF template. Overwriting...`) ); @@ -623,19 +624,28 @@ exports.populate = function(S, data, stage, region) { let templates = project.getTemplates(); // Helper function to find template - let _findTemplate = function(template) { - - let tempArray = template.split('.'); - let module = tempArray[0]; - let templateKey = tempArray[1]; + let _findTemplate = function(templateVariable) { let val = null; - for (let prop in templates._components) { - if (templates._components[prop][module] && - templates._components[prop][module][templateKey]) { - val = templates._components[prop][module][templateKey]; + if (templateVariable.indexOf('.') !== -1) { + + // Template is a Module Template + let tempArray = templateVariable.split('.'); + let module = tempArray[0]; + let templateKey = tempArray[1]; + + // Check module-level templates + for (let prop in templates._components) { + if (templates._components[prop][module] && + templates._components[prop][module][templateKey]) { + val = templates._components[prop][module][templateKey]; + } } + } else { + + // Template is a Project Template + if (templates._project[templateVariable]) val = templates._project[templateVariable]; } return val; diff --git a/tests/all.js b/tests/all.js index 0282c3447..a7310501d 100644 --- a/tests/all.js +++ b/tests/all.js @@ -10,23 +10,23 @@ describe('All Tests', function() { }); after(function() {}); - require('./tests/classes/ServerlessEndpointTest'); - require('./tests/classes/ServerlessFunctionTest'); - require('./tests/classes/ServerlessModuleTest'); - require('./tests/classes/ServerlessComponentTest'); - require('./tests/classes/ServerlessProjectTest'); - require('./tests/classes/ServerlessStateTest'); + //require('./tests/classes/ServerlessEndpointTest'); + //require('./tests/classes/ServerlessFunctionTest'); + //require('./tests/classes/ServerlessModuleTest'); + //require('./tests/classes/ServerlessComponentTest'); + //require('./tests/classes/ServerlessProjectTest'); + //require('./tests/classes/ServerlessStateTest'); //require('./tests/actions/TestPluginCustom'); //require('./tests/actions/TestDefaultActionHook'); //require('./tests/actions/StageCreate'); //require('./tests/actions/RegionCreate'); - require('./tests/actions/ComponentCreate'); - require('./tests/actions/ModuleCreate'); - require('./tests/actions/FunctionCreate'); + //require('./tests/actions/ComponentCreate'); + //require('./tests/actions/ModuleCreate'); + //require('./tests/actions/FunctionCreate'); //require('./tests/actions/EnvList'); //require('./tests/actions/EnvGet'); //require('./tests/actions/EnvSetUnset'); - //require('./tests/actions/ResourcesDeploy'); + require('./tests/actions/ResourcesDeploy'); //require('./tests/actions/FunctionRun'); //require('./tests/actions/FunctionDeploy'); //require('./tests/actions/EndpointDeploy'); diff --git a/tests/test-prj/s-project.json b/tests/test-prj/s-project.json index 129063061..e42ae8c13 100644 --- a/tests/test-prj/s-project.json +++ b/tests/test-prj/s-project.json @@ -8,68 +8,5 @@ "custom": {}, "modules": {}, "plugins": [], - "cloudFormation": { - "AWSTemplateFormatVersion": "2010-09-09", - "Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway", - "Resources": { - "IamRoleLambda": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - }, - "Action": [ - "sts:AssumeRole" - ] - } - ] - }, - "Path": "/" - } - }, - "IamPolicyLambda": { - "Type": "AWS::IAM::Policy", - "Properties": { - "PolicyName": "${stage}-${project}-lambda", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Resource": "arn:aws:logs:${region}:*:*" - } - ] - }, - "Roles": [ - { - "Ref": "IamRoleLambda" - } - ] - } - } - }, - "Outputs": { - "IamRoleArnLambda": { - "Description": "ARN of the lambda IAM role", - "Value": { - "Fn::GetAtt": [ - "IamRoleLambda", - "Arn" - ] - } - } - } - } + "cloudFormation": "$${projectCloudFormation}" } \ No newline at end of file diff --git a/tests/test-prj/s-templates.json b/tests/test-prj/s-templates.json new file mode 100644 index 000000000..a9bbb9a1c --- /dev/null +++ b/tests/test-prj/s-templates.json @@ -0,0 +1,66 @@ +{ + "projectCloudFormation": { + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway", + "Resources": { + "IamRoleLambda": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + }, + "Action": [ + "sts:AssumeRole" + ] + } + ] + }, + "Path": "/" + } + }, + "IamPolicyLambda": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyName": "${stage}-${project}-lambda", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": "arn:aws:logs:${region}:*:*" + } + ] + }, + "Roles": [ + { + "Ref": "IamRoleLambda" + } + ] + } + } + }, + "Outputs": { + "IamRoleArnLambda": { + "Description": "ARN of the lambda IAM role", + "Value": { + "Fn::GetAtt": [ + "IamRoleLambda", + "Arn" + ] + } + } + } + } +} \ No newline at end of file diff --git a/tests/tests/actions/ResourcesDeploy.js b/tests/tests/actions/ResourcesDeploy.js index 579485c8d..98289654d 100644 --- a/tests/tests/actions/ResourcesDeploy.js +++ b/tests/tests/actions/ResourcesDeploy.js @@ -46,7 +46,8 @@ describe('Test action: Resources Deploy', function() { projectPath: projPath }); - return serverless.state.load().then(function() { + return serverless.init() + .then(function() { SUtils.sDebug('Adding test bucket resource'); diff --git a/tests/tests/classes/ServerlessStateTest.js b/tests/tests/classes/ServerlessStateTest.js index 9bab0ada4..9ae15243c 100644 --- a/tests/tests/classes/ServerlessStateTest.js +++ b/tests/tests/classes/ServerlessStateTest.js @@ -59,7 +59,7 @@ describe('Test Serverless State Class', function() { }); it('Get populated instance data', function(done) { - let data = instance.getPopulated({ stage: config.stage, region: config.region }) + let data = instance.getPopulated({ stage: config.stage, region: config.region }); assert.equal(true, JSON.stringify(data).indexOf('$${') == -1); assert.equal(true, JSON.stringify(data).indexOf('${') == -1); done();