From df68c4770ac3eed0ad80abfd80df0bacdd10ff6c Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Wed, 23 Sep 2020 16:31:50 +0200 Subject: [PATCH] Fix #3184 Compatibility with native AWS CloudFormation variable syntax --- lib/classes/Service.js | 9 ++++++- lib/classes/Variables.test.js | 43 +++++++++++++++++++++++++++++++++ lib/plugins/print/print.js | 9 ++++++- lib/plugins/print/print.test.js | 32 ------------------------ 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/lib/classes/Service.js b/lib/classes/Service.js index eb48cd964ca7..08c4bf7c6deb 100644 --- a/lib/classes/Service.js +++ b/lib/classes/Service.js @@ -20,7 +20,14 @@ class Service { this.serviceObject = null; this.provider = { stage: 'dev', - variableSyntax: '\\${([^{}]+?)}', + variableSyntax: + '\\${(' + + // either 'foo:bar' + '[^{}:]+?:[^:{}][^{}]*?' + + '|' + + // or 'file(...)' + 'file\\([^\\)]*\\)' + + ')}', }; this.custom = {}; this.plugins = []; diff --git a/lib/classes/Variables.test.js b/lib/classes/Variables.test.js index c2a380c776b9..eb30294e6133 100644 --- a/lib/classes/Variables.test.js +++ b/lib/classes/Variables.test.js @@ -1359,6 +1359,49 @@ module.exports = { .populateProperty(property) .should.eventually.eql('my stage is dev'); }); + + it('should support ${file(...)} syntax', () => { + // eslint-disable-next-line no-template-curly-in-string + const property = '${file(~/somedir/../config.yml)}'; + + const expectedFileName = `${os.homedir()}/somedir/../config.yml`; + const configYml = { + foo: 'bar', + }; + const fileExistsStub = sinon.stub(serverless.utils, 'fileExistsSync').returns(true); + const realpathSync = sinon.stub(fse, 'realpathSync').returns(expectedFileName); + const readFileSyncStub = sinon.stub(serverless.utils, 'readFileSync').returns(configYml); + + return serverless.variables + .populateProperty(property) + .then(valueToPopulate => { + expect(realpathSync).to.not.have.been.called; + expect(fileExistsStub).to.have.been.calledWithMatch(expectedFileName); + expect(readFileSyncStub).to.have.been.calledWithMatch(expectedFileName); + expect(valueToPopulate).to.deep.equal(configYml); + }) + .finally(() => { + realpathSync.restore(); + readFileSyncStub.restore(); + fileExistsStub.restore(); + }); + }); + + it('should ignore native CloudFormation variables', () => { + // eslint-disable-next-line no-template-curly-in-string + const property = 'the region is ${AWS::Region}'; + return serverless.variables + .populateProperty(property) + .should.eventually.eql('the region is ${AWS::Region}'); + }); + + it('should ignore CloudFormation references', () => { + // eslint-disable-next-line no-template-curly-in-string + const property = 'the region is ${AnotherResource}'; + return serverless.variables + .populateProperty(property) + .should.eventually.eql('the region is ${AnotherResource}'); + }); }); describe('#populateVariable()', () => { diff --git a/lib/plugins/print/print.js b/lib/plugins/print/print.js index 2420f01c94bf..9bf859f5a9af 100644 --- a/lib/plugins/print/print.js +++ b/lib/plugins/print/print.js @@ -54,7 +54,14 @@ class Print { { stage: 'dev', region: 'us-east-1', - variableSyntax: '\\${([^{}]+?)}', + variableSyntax: + '\\${(' + + // either 'foo:bar' + '[^{}:]+?:[^:{}][^{}]*?' + + '|' + + // or 'file(...)' + 'file\\([^\\)]*\\)' + + ')}', }, service.provider ); diff --git a/lib/plugins/print/print.test.js b/lib/plugins/print/print.test.js index 917760b6ae01..9819ba371fb3 100644 --- a/lib/plugins/print/print.test.js +++ b/lib/plugins/print/print.test.js @@ -326,38 +326,6 @@ describe('Print', () => { }); }); - it('should resolve self references', () => { - const conf = { - custom: { - me: '${self:}', - }, - provider: {}, - }; - getServerlessConfigFileStub.resolves(conf); - - serverless.processedInput = { - commands: ['print'], - options: {}, - }; - - const expected = { - custom: { - me: { - $ref: '$', - }, - }, - provider: {}, - }; - - return print.print().then(() => { - const message = print.serverless.cli.consoleLog.args.join(); - - expect(getServerlessConfigFileStub.calledOnce).to.equal(true); - expect(print.serverless.cli.consoleLog.called).to.be.equal(true); - expect(YAML.load(message)).to.eql(expected); - }); - }); - describe('should resolve fallback', () => { [ { value: 'hello_123@~:/+', description: 'ascii chars' },