Allow for Self-Reference

1. Newly allow '${self:}' as a valid reference to the current serverless.yml's root
  a. This allows for a more clean manner of passing names and identifiers around between projects.  Particularly, this facilitates exporting values from projects so they can be used in other projects via Fn::ImportValue.
2. Write a test ensuring the above
3. Document the new capability

Example usage: https://github.com/Nordstrom/hello-retail/pull/26/files
This commit is contained in:
Erik Erikson 2017-02-07 19:19:30 -08:00
parent 78e1fbfa80
commit 230f37f4a4
3 changed files with 32 additions and 6 deletions

View File

@ -24,13 +24,17 @@ The Serverless framework provides a powerful variable system which allows you to
**Note:** You can only use variables in `serverless.yml` property **values**, not property keys. So you can't use variables to generate dynamic logical IDs in the custom resources section for example.
## Reference Properties In serverless.yml
To self-reference properties in `serverless.yml`, use the `${self:someProperty}` syntax in your `serverless.yml`. This functionality is recursive, so you can go as deep in the object tree as you want.
To self-reference properties in `serverless.yml`, use the `${self:someProperty}` syntax in your `serverless.yml`. `someProperty` can contain the empty string for a top-level self-reference or a dotted attribute reference to any depth of attribute, so you can go as shallow or deep in the object tree as you want.
```yml
service: new-service
provider: aws
custom:
globalSchedule: rate(10 minutes)
newService: ${self:}
# the following will resolve identically in other serverless.yml files so long as they define
# `custom.newService: ${file(<relative-path-to-this-file>/serverless.yml)}`
exportName: ${self:custom.newService.service}-export
functions:
hello:
@ -40,7 +44,13 @@ functions:
world:
handler: handler.world
events:
- schedule: ${self:custom.globalSchedule}
- schedule: ${self:custom.newService.custom.globalSchedule}
resources:
Outputs:
NewServiceExport:
Value: 'A Value To Export'
Export:
Name: ${self:custom.exportName}
```
In the above example you're setting a global schedule for all functions by referencing the `globalSchedule` property in the same `serverless.yml` file. This way, you can easily change the schedule for all functions whenever you like.

View File

@ -15,7 +15,7 @@ class Variables {
this.fileRefSyntax = RegExp(/^file\(([a-zA-Z0-9._\-/]+?)\)/g);
this.envRefSyntax = RegExp(/^env:./g);
this.optRefSyntax = RegExp(/^opt:./g);
this.selfRefSyntax = RegExp(/^self:./g);
this.selfRefSyntax = RegExp(/^self:/g);
}
loadVariableSyntax() {
@ -70,7 +70,10 @@ class Variables {
property = this.populateVariable(property, matchedString, valueToPopulate);
});
return this.populateProperty(property);
if (property !== this.service) {
property = this.populateProperty(property);
}
return property;
}
return property;
}
@ -145,7 +148,7 @@ class Variables {
}
getValueFromSelf(variableString) {
let valueToPopulate = _.cloneDeep(this.service);
let valueToPopulate = this.service;
const deepProperties = variableString.split(':')[1].split('.');
valueToPopulate = this.getDeepValue(deepProperties, valueToPopulate);
return valueToPopulate;
@ -216,7 +219,7 @@ class Variables {
deepProperties.forEach(subProperty => {
if (typeof valueToPopulate === 'undefined') {
valueToPopulate = {};
} else {
} else if (subProperty !== '') {
valueToPopulate = valueToPopulate[subProperty];
}
if (typeof valueToPopulate === 'string' && valueToPopulate.match(this.variableSyntax)) {

View File

@ -361,6 +361,19 @@ describe('Variables', () => {
const valueToPopulate = serverless.variables.getValueFromSelf('self:provider');
expect(valueToPopulate).to.be.equal('testProvider');
});
it('should handle self-references to the root of the serverless.yml file', () => {
const serverless = new Serverless();
serverless.variables.service = {
service: 'testService',
provider: 'testProvider',
defaults: serverless.service.defaults,
};
serverless.variables.loadVariableSyntax();
const valueToPopulate = serverless.variables.getValueFromSelf('self:').provider;
expect(valueToPopulate).to.be.equal('testProvider');
});
});
describe('#getValueFromFile()', () => {