diff --git a/lib/classes/Variables.js b/lib/classes/Variables.js index 724981983..7349b77e2 100644 --- a/lib/classes/Variables.js +++ b/lib/classes/Variables.js @@ -37,24 +37,29 @@ class Variables { this.service.provider.variableSyntax = true; this.serverless.service.serverless = null; + + return this.populateObject(this.service).then(() => { + this.service.provider.variableSyntax = variableSyntaxProperty; + this.serverless.service.serverless = this.serverless; + return BbPromise.resolve(this.service); + }); + } + + populateObject(objectToPopulate) { const populateAll = []; - _.deepMapValues(this.service, (property, propertyPath) => { + _.deepMapValues(objectToPopulate, (property, propertyPath) => { if (typeof property === 'string') { const populateSingleProperty = new Promise((resolve) => this .populateProperty(property, true).then(newProperty => { - _.set(this.service, propertyPath, newProperty); + _.set(objectToPopulate, propertyPath, newProperty); return resolve(); })); populateAll.push(populateSingleProperty); } }); - return BbPromise.all(populateAll).then(() => { - this.service.provider.variableSyntax = variableSyntaxProperty; - this.serverless.service.serverless = this.serverless; - return BbPromise.resolve(this.service); - }); + return BbPromise.all(populateAll).then(() => objectToPopulate); } populateProperty(propertyParam, populateInPlace) { @@ -78,7 +83,12 @@ class Variables { .then(valueToPopulate => resolve(valueToPopulate)); } return this.getValueFromSource(variableString) - .then(valueToPopulate => resolve(valueToPopulate)); + .then(valueToPopulate => { + if (typeof valueToPopulate === 'object') { + return resolve(this.populateObject(valueToPopulate)); + } + return resolve(valueToPopulate); + }); }); singleValueToPopulate.then(valueToPopulate => { @@ -252,6 +262,7 @@ class Variables { return this.getDeepValue(deepProperties, valueToPopulate); } } + return BbPromise.resolve(valueToPopulate); } diff --git a/lib/classes/Variables.test.js b/lib/classes/Variables.test.js index f96a33b2d..d1719a046 100644 --- a/lib/classes/Variables.test.js +++ b/lib/classes/Variables.test.js @@ -43,11 +43,11 @@ describe('Variables', () => { const serverless = new Serverless(); const populatePropertyStub = sinon - .stub(serverless.variables, 'populateProperty').resolves(); + .stub(serverless.variables, 'populateObject').resolves(); return serverless.variables.populateService().then(() => { expect(populatePropertyStub.called).to.equal(true); - serverless.variables.populateProperty.restore(); + serverless.variables.populateObject.restore(); }); }); @@ -77,6 +77,40 @@ describe('Variables', () => { }); }); + describe('#populateObject()', () => { + it('should call populateProperty method', () => { + const serverless = new Serverless(); + const object = { + stage: '${opt:stage}', + }; + + const populatePropertyStub = sinon + .stub(serverless.variables, 'populateProperty').resolves('prod'); + + return serverless.variables.populateObject(object).then(() => { + expect(populatePropertyStub.called).to.equal(true); + serverless.variables.populateProperty.restore(); + }); + }); + + it('should populate object and return it', () => { + const serverless = new Serverless(); + const object = { + stage: '${opt:stage}', + }; + const expectedPopulatedObject = { + stage: 'prod', + }; + + sinon.stub(serverless.variables, 'populateProperty').resolves('prod'); + + return serverless.variables.populateObject(object).then(populatedObject => { + expect(populatedObject).to.deep.equal(expectedPopulatedObject); + serverless.variables.populateProperty.restore(); + }); + }); + }); + describe('#populateProperty()', () => { it('should call overwrite if overwrite syntax provided', () => { const serverless = new Serverless(); @@ -123,6 +157,44 @@ describe('Variables', () => { }); }); + it('should call populateObject if variable value is an object', () => { + const serverless = new Serverless(); + serverless.variables.options = { + stage: 'prod', + }; + const property = '${opt:stage}'; + const variableValue = { + stage: '${opt:stage}', + }; + const variableValuePopulated = { + stage: 'prod', + }; + + serverless.variables.loadVariableSyntax(); + + const populateObjectStub = sinon + .stub(serverless.variables, 'populateObject') + .resolves(variableValuePopulated); + const getValueFromSourceStub = sinon + .stub(serverless.variables, 'getValueFromSource') + .resolves(variableValue); + const populateVariableStub = sinon + .stub(serverless.variables, 'populateVariable') + .resolves(variableValuePopulated); + + return serverless.variables.populateProperty(property).then(newProperty => { + expect(populateObjectStub.called).to.equal(true); + expect(getValueFromSourceStub.called).to.equal(true); + expect(populateVariableStub.called).to.equal(true); + expect(newProperty).to.deep.equal(variableValuePopulated); + + serverless.variables.populateObject.restore(); + serverless.variables.getValueFromSource.restore(); + serverless.variables.populateVariable.restore(); + return Promise.resolve(); + }); + }); + it('should run recursively if nested variables provided', () => { const serverless = new Serverless(); const property = 'my stage is ${env:${opt.name}}';