Skip to content

Commit

Permalink
feat: Allow iterator values to be objects
Browse files Browse the repository at this point in the history
* User can provide iterator with objects as values
* The nested keys can be targeted with dot notation in the template
  • Loading branch information
1-900-MIXALOT committed Dec 22, 2022
1 parent eebf3cb commit 5114155
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 4 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ An array or object to replace `$forEach` block with. Template can contain `$forE
- `$forEach.key` - environment variable name
- `$forEach.value` - environment variable value

When using an object type iterator, nested values can be accessed with dot notation, (e.g. `$forEach.value.nestedKey`).

## Examples

### Populate environment variables based on the list
### Populate environment variables based on the object

#### Config
```yaml
Expand Down
51 changes: 48 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@ class ForEachPlugin {
interpolate(template, key, value) {
const stringified = JSON.stringify(template);

const interpolated = stringified
.replace(/\$forEach.key/g, key)
.replace(/\$forEach.value/g, value);
const keyRegex = /\$forEach.key/g;
const valueRegex = /\$forEach\.value/g;

const template_with_keys_updated = stringified.replace(keyRegex, key);

const interpolated = this.replaceValues(template_with_keys_updated, valueRegex, value);

try {
return JSON.parse(interpolated);
Expand All @@ -82,6 +85,47 @@ class ForEachPlugin {
}
}

/**
*
* This function handles values that may be objects instead of strings.
* If the value variable is an object, check to see if the template indicates
* nested keys should be used.
*/
replaceValues(stringified, valueRegex, value) {
if (typeof value === 'string') {
return stringified.replace(valueRegex, value);
} else if (typeof value === 'object') {
const nestedKeyCaptureRegex = new RegExp(valueRegex.source + '\\.(?<nestedKey>\\w*)', 'g');

const nestedKeyMatches = [...stringified.matchAll(nestedKeyCaptureRegex)];

const ValueObjectErrorMsg = (
'ForEach value is an object, but the template did not use a valid key from the object.\n' +
`Value: ${JSON.stringify(value)}\nTemplate: ${stringified}`
)

if (!nestedKeyMatches.length) {
throw new Error(ValueObjectErrorMsg);
}

for (const nestedKeyMatch of nestedKeyMatches) {
const nestedKey = nestedKeyMatch.groups.nestedKey;

if (!(nestedKey in value)) {
throw new Error(ValueObjectErrorMsg);
}

const nestedValue = value[nestedKey]

const nestedValueRegex = new RegExp(valueRegex.source + '\\.' + nestedKey);

stringified = this.replaceValues(stringified, nestedValueRegex, nestedValue);
}

return stringified
}
}

findAndReplace(obj, path) {
let count = 0;

Expand All @@ -103,6 +147,7 @@ class ForEachPlugin {
const { iterator: rawIterator, template } = obj[key];

const iterator = {};

if (rawIterator.$env) {
Object.entries(process.env).forEach(([name, value]) => {
if (name.match(rawIterator.$env)) {
Expand Down
40 changes: 40 additions & 0 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,46 @@ describe('ForEachPlugin', function() {
]);
});

it('should support using iterators with objects as values', function() {
const { plugin, serverless } = createTestInstance({
custom: {
$forEach: {
iterator: {
bar: {
'name': 'bar',
'nicknames': {
'main': 'barberator',
'secondary': 'barbarella',
},
},
baz: {
'name': 'baz',
'nicknames': {
'main': 'bazerator',
'secondary': 'big baz',
},
}
},
template: {
'$forEach.key': '$forEach.value.name',
'$forEach.key_nickname': '$forEach.value.nicknames.main'
}
}
}
});

expect(
() => plugin.replace()
).to.not.throw();

expect(serverless.service.custom).to.deep.equal({
'bar': 'bar',
'bar_nickname': 'barberator',
'baz': 'baz',
'baz_nickname': 'bazerator',
});
});

it('should flatten one level when replacing array item and template is an array', function() {
const { plugin, serverless } = createTestInstance({
custom: {
Expand Down

0 comments on commit 5114155

Please sign in to comment.