Skip to content

Commit

Permalink
Fn::Flatten
Browse files Browse the repository at this point in the history
  • Loading branch information
monken committed Feb 20, 2016
1 parent 817ed11 commit edcd0c5
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 26 deletions.
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,58 @@ By default the string `_` is used as the variable in the map function.
}]
```

## Fn::Flatten

This function flattens an array a single level. This is useful for flattening out nested `Fn::Map` calls.

```json
{
"Fn::Flatten": {
"Fn::Map": [
[80, 443], "$", {
"Fn::Map": [
["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"], {
"CirdIp": "_",
"FromPort": "$",
"ToPort": "$"
}
]
}
]
}
},
```

Results in:

```json
[{
"CirdIp": "10.0.0.0/8",
"FromPort": "80",
"ToPort": "80"
}, {
"CirdIp": "172.16.0.0/12",
"FromPort": "80",
"ToPort": "80"
}, {
"CirdIp": "192.168.0.0/16",
"FromPort": "80",
"ToPort": "80"
}, {
"CirdIp": "10.0.0.0/8",
"FromPort": "443",
"ToPort": "443"
}, {
"CirdIp": "172.16.0.0/12",
"FromPort": "443",
"ToPort": "443"
}, {
"CirdIp": "192.168.0.0/16",
"FromPort": "443",
"ToPort": "443"
}]
```

## Examples

See [/examples](https://github.com/monken/cfn-include/tree/master/examples) for templates that call an API Gateway endpoint to collect AMI IDs for all regions.
Expand Down
48 changes: 24 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,31 @@ function recurse(base, scope, object) {
scope = _.clone(scope);
if (_.isArray(object)) return Promise.all(object.map(_.bind(recurse, this, base, scope)));
else if (_.isPlainObject(object)) {
return Promise.try(function() {
if (object["Fn::Map"]) {
var args = object["Fn::Map"],
list = args[0],
placeholder = args[1],
body = args[args.length - 1];
if (args.length === 2) placeholder = '_';
return Promise.resolve(list.map(function(replace) {
scope = _.clone(scope);
scope[placeholder] = replace;
var replaced = findAndReplace(scope, _.cloneDeep(body));
return recurse(base, scope, replaced);
}));
} else if (object["Fn::Include"]) {
return include(base, object["Fn::Include"]).then(function(json) {
delete object["Fn::Include"];
_.extend(object, json);
return object;
}).then(_.bind(findAndReplace, this, scope)).then(_.bind(recurse, this, base, scope));
} else {
if (object["Fn::Map"]) {
var args = object["Fn::Map"],
list = args[0],
placeholder = args[1],
body = args[args.length - 1];
if (args.length === 2) placeholder = '_';
return Promise.all(list.map(function(replace) {
scope = _.clone(scope);
scope[placeholder] = replace;
var replaced = findAndReplace(scope, _.cloneDeep(body));
return recurse(base, scope, replaced);
}));
} else if (object["Fn::Include"]) {
return include(base, object["Fn::Include"]).then(function(json) {
delete object["Fn::Include"];
_.extend(object, json);
return object;
}
}).then(function(object) {
return Promise.props(_.mapValues(object, _.bind(recurse, this, base, scope)));
});
}).then(_.bind(findAndReplace, this, scope)).then(_.bind(recurse, this, base, scope));
} else if (object["Fn::Flatten"]) {
return recurse(base, scope, object["Fn::Flatten"]).then(function(json) {
return _.flatten(json);
});
} else {
return Promise.props(_.mapValues(object, _.bind(recurse, this, base, scope)))
}
} else {
return object;
}
Expand Down
4 changes: 2 additions & 2 deletions t/include.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ var include = require('../index'),
assert = require('assert'),
fs = require('fs');

var tests = ['map', 'literal', 'location'];
var tests = ['location', 'literal', 'map', 'flatten'];
if(process.env['TEST_S3']) tests.push('s3');

//var tests = ['map'];
//var tests = ['flatten'];

tests.forEach(function(file) {
var tests = require('./tests/' + file + '.json');
Expand Down
178 changes: 178 additions & 0 deletions t/tests/flatten.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
{
"basics": [{
"name": "flatten flat list",
"template": {
"Fn::Flatten": [1, 2, 3]
},
"output": [1, 2, 3]
}],
"flatten nested map": [{
"name": "nesting with underscore",
"template": {
"Fn::Flatten": {
"Fn::Map": [
[1, 2], {
"Fn::Map": [
[3, 4], {
"foo": "_"
}
]
}
]
}
},
"output": [{
"foo": "3"
}, {
"foo": "4"
}, {
"foo": "3"
}, {
"foo": "4"
}]

}, {
"name": "flatten custom placeholder",
"template": {
"Fn::Flatten": {
"Fn::Map": [
[1, 2], "$", {
"Fn::Map": [
[3, 4], {
"foo": "_",
"bar": "$"
}
]
}
]
}
},
"output": [{
"foo": "3",
"bar": "1"
}, {
"foo": "4",
"bar": "1"
}, {
"foo": "3",
"bar": "2"
}, {
"foo": "4",
"bar": "2"
}]

}, {
"name": "nesting with underscore and includes",
"template": {
"Fn::Flatten": {
"Fn::Map": [
["includes/foobar.json", "includes/subfolder/include2.json"], {
"Fn::Map": [
["includes/foobar.json", "includes/subfolder/include2.json"], {
"foo": "bar"
}
]
}
]
}
},
"output": [{
"foo": "bar"
}, {
"foo": "bar"
}, {
"foo": "bar"
}, {
"foo": "bar"
}]

}],
"include": [{
"name": "include template with variables",
"template": {
"Fn::Flatten": {
"Fn::Map": [
[1, 2], "$", {
"Fn::Map": [
[3, 4], {
"Fn::Include": "includes/mapvariable.json"
}
]
}
]
}
},
"output": [{
"foo": "3",
"bar": "1",
"baz": [{
"foo": "3",
"bar": "1"
}]
}, {
"foo": "4",
"bar": "1",
"baz": [{
"foo": "4",
"bar": "1"
}]
}, {
"foo": "3",
"bar": "2",
"baz": [{
"foo": "3",
"bar": "2"
}]
}, {
"foo": "4",
"bar": "2",
"baz": [{
"foo": "4",
"bar": "2"
}]
}]
}],
"synopsis": [{
"name": "flatten nested map",
"template": {
"Fn::Flatten": {
"Fn::Map": [
[80, 443], "$", {
"Fn::Map": [
["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"], {
"CirdIp": "_",
"FromPort": "$",
"ToPort": "$"
}
]
}
]
}
},
"output": [{
"CirdIp": "10.0.0.0/8",
"FromPort": "80",
"ToPort": "80"
}, {
"CirdIp": "172.16.0.0/12",
"FromPort": "80",
"ToPort": "80"
}, {
"CirdIp": "192.168.0.0/16",
"FromPort": "80",
"ToPort": "80"
}, {
"CirdIp": "10.0.0.0/8",
"FromPort": "443",
"ToPort": "443"
}, {
"CirdIp": "172.16.0.0/12",
"FromPort": "443",
"ToPort": "443"
}, {
"CirdIp": "192.168.0.0/16",
"FromPort": "443",
"ToPort": "443"
}]
}]
}

0 comments on commit edcd0c5

Please sign in to comment.