Skip to content

Commit b997b0b

Browse files
authored
Merge pull request #29 from gitops-toolbox/feature/group-by
Add group-by option and rename limit to filter-by
2 parents fe36bdd + e750f7e commit b997b0b

File tree

9 files changed

+287
-55
lines changed

9 files changed

+287
-55
lines changed

README.md

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ locations:
251251
## Render one file in human readable
252252

253253
```
254-
> ./bin/cli.js -b examples generate nested/example.njk dev -h --limit-to '{"type": "database"}' # renderHumanReadableLimitTo
254+
> ./bin/cli.js -b examples generate nested/example.njk dev -h --filter-by '{"type": "database"}' # renderHumanReadableFilterBy
255255
---
256256
{"destination":{"type":"echo","params":{"repo":"myorg/development","filepath":"database.json"}},"tags":{"type":"database"}}
257257
---
@@ -265,7 +265,7 @@ locations:
265265
## Render human readable limit to one file and hide header
266266

267267
```
268-
> ./bin/cli.js -b examples generate nested/example.njk dev -h --limit-to '{"type": "database"}' --hide-headers # renderFileContent
268+
> ./bin/cli.js -b examples generate nested/example.njk dev -h --filter-by '{"type": "database"}' --hide-headers # renderFileContent
269269
# Template file templates/context.js
270270
# Mapping file mappings/nested/example.njk
271271
@@ -333,6 +333,74 @@ locations:
333333
}
334334
```
335335

336+
## Persist the result grouping by a tag
337+
338+
```
339+
./bin/cli.js -b examples generate nested/example.njk dev --persist --group-by type # groupBy
340+
{
341+
"echo": {
342+
"application": {
343+
"templates": [
344+
{
345+
"template": "context.njk",
346+
"contextSelector": "components.application",
347+
"destination": {
348+
"type": "echo",
349+
"params": {
350+
"repo": "myorg/development",
351+
"filepath": "application.json"
352+
}
353+
},
354+
"tags": {
355+
"type": "application"
356+
},
357+
"templateData": {
358+
"context": {
359+
"name": "templator"
360+
},
361+
"meta": {
362+
"__mapping": "mappings/nested/example.njk",
363+
"__template": "templates/context.njk"
364+
}
365+
},
366+
"renderedTemplate": "# Template file templates/context.njk\n# Mapping file mappings/nested/example.njk\n\n{\"name\":\"templator\"}\n",
367+
"group": "application"
368+
}
369+
]
370+
},
371+
"database": {
372+
"templates": [
373+
{
374+
"template": "context.js",
375+
"contextSelector": "components.database",
376+
"destination": {
377+
"type": "echo",
378+
"params": {
379+
"repo": "myorg/development",
380+
"filepath": "database.json"
381+
}
382+
},
383+
"tags": {
384+
"type": "database"
385+
},
386+
"templateData": {
387+
"context": {
388+
"name": "Database"
389+
},
390+
"meta": {
391+
"__mapping": "mappings/nested/example.njk",
392+
"__template": "templates/context.js"
393+
}
394+
},
395+
"renderedTemplate": "# Template file templates/context.js\n# Mapping file mappings/nested/example.njk\n\n{\"name\":\"Database\"}\n",
396+
"group": "database"
397+
}
398+
]
399+
}
400+
}
401+
}
402+
```
403+
336404
## Create a new mapping
337405

338406
```

cmds/generate.js

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
const lodash = require('lodash');
22
const Templator = require('../lib/templator');
33
const persist = require('../lib/persist');
4-
const { tryJSONParse, output, commonYargsOptions } = require('../lib/utils');
4+
const {
5+
tryJSONParse,
6+
output,
7+
commonYargsOptions,
8+
parseTags,
9+
} = require('../lib/utils');
510

611
exports.command = 'generate <mapping> [context-selector]';
712

@@ -28,13 +33,17 @@ exports.builder = (yargs) => {
2833
describe: 'Hide the header from the human readable output',
2934
type: 'boolean',
3035
default: false,
31-
implies: 'limit-to',
36+
implies: 'filter-by',
3237
})
33-
.options('limit-to', {
38+
.options('filter-by', {
3439
describe: 'Only show file with a give set of tags',
3540
type: 'string',
3641
implies: 'human-readable',
37-
coerce: (param) => tryJSONParse(param),
42+
coerce: (param) => parseTags(param),
43+
})
44+
.options('group-by', {
45+
describe: 'group templates by a set of given tags names',
46+
type: 'array',
3847
})
3948
.options('just-mapping', {
4049
describe: 'Just render mapping, not templates, useful to debug issues',
@@ -49,13 +58,6 @@ exports.builder = (yargs) => {
4958

5059
function _human_reabable(render, args) {
5160
for (const item of render.locations) {
52-
if (
53-
!lodash.isUndefined(args.limitTo) &&
54-
!lodash.isMatch(item.tags, args.limitTo)
55-
) {
56-
continue;
57-
}
58-
5961
if (!args.hideHeaders) {
6062
console.log('---');
6163
console.log(
@@ -83,15 +85,29 @@ exports.handler = async (args) => {
8385
return;
8486
}
8587

86-
const render = await templator.render(args.mapping, args.contextSelector);
88+
let render = await templator.render(args.mapping, args.contextSelector);
89+
90+
if (!lodash.isUndefined(args.filterBy)) {
91+
render.locations = lodash.filter(render.locations, (render) =>
92+
lodash.isMatch(render.tags, args.filterBy)
93+
);
94+
}
8795

8896
if (args.h) {
8997
_human_reabable(render, args);
9098
}
9199

92-
output(render, args.output);
100+
if (!lodash.isUndefined(args.groupBy)) {
101+
for (const location of render.locations) {
102+
location.group = Object.values(
103+
lodash.pick(location.tags, args.groupBy)
104+
).join('_');
105+
}
106+
}
93107

94108
if (args.persist) {
95109
output(await persist(render, args), args.output || 'json');
110+
} else {
111+
output(render, args.output);
96112
}
97113
};

examples/mappings/example.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
1-
exports.render = function (data) {
1+
exports.render = function (context, meta) {
22
const locations = [];
33

4-
for (const [key, value] of Object.entries(data.context.components)) {
5-
locations.push({
6-
template: '<path_component_to_template>',
7-
contextSelector: `components.${key}`,
8-
destination: {},
9-
});
4+
for (const [environment, environment_data] of Object.entries(context)) {
5+
for (const [component, component_data] of Object.entries(
6+
environment_data.components
7+
)) {
8+
locations.push({
9+
template: `context.js`,
10+
contextSelector: `${environment}.components.${component}`,
11+
destination: {
12+
type: 'echo',
13+
params: {
14+
repo: environment_data.environment,
15+
filepath: `${component}.json`,
16+
},
17+
},
18+
tags: {
19+
env: environment_data.environment,
20+
component: component,
21+
},
22+
});
23+
}
1024
}
1125

1226
return { locations };

lib/destinations/echo.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const _ = require('lodash');
2+
13
class EchoDestination {
24
constructor(templates, logs, args) {
35
this.templates = templates;
@@ -6,8 +8,10 @@ class EchoDestination {
68
}
79

810
async persist() {
11+
let templates = this.templates;
12+
913
return {
10-
templates: this.templates,
14+
templates,
1115
};
1216
}
1317
}

lib/persist.js

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,28 @@ const _ = require('lodash');
22
const destinations = require('./destinations');
33
const [log, error] = require('./utils').getLogger(__filename);
44

5+
async function _persist(dest_type, templates, args) {
6+
let results;
7+
try {
8+
const destination = new destinations[dest_type](templates, log, args);
9+
results = await destination.persist();
10+
} catch (e) {
11+
results = e.message;
12+
}
13+
14+
return results;
15+
}
16+
17+
async function _persistByGroup(dest_type, templates, args) {
18+
const results = {};
19+
const templates_by_group = _.groupBy(templates, 'group');
20+
for (const [group, template] of Object.entries(templates_by_group)) {
21+
results[group] = await _persist(dest_type, template, args);
22+
}
23+
24+
return results;
25+
}
26+
527
module.exports = async function (mappings, args) {
628
const templates_by_destinations = _.groupBy(
729
mappings.locations,
@@ -19,11 +41,10 @@ module.exports = async function (mappings, args) {
1941
continue;
2042
}
2143

22-
try {
23-
const destination = new destinations[dest_type](templates, log, args);
24-
results[dest_type] = await destination.persist();
25-
} catch (e) {
26-
results[dest_type] = e.message;
44+
if (!_.isUndefined(args.groupBy)) {
45+
results[dest_type] = await _persistByGroup(dest_type, templates, args);
46+
} else {
47+
results[dest_type] = await _persist(dest_type, templates, args);
2748
}
2849
}
2950

lib/utils.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,27 @@ function getContextParser(base, args) {
7777
});
7878
}
7979

80+
function parseTags(str) {
81+
const tags = {};
82+
83+
if (str === undefined || str === '') {
84+
return undefined;
85+
}
86+
87+
try {
88+
return JSON.parse(str);
89+
} catch (e) {
90+
error('Could not json parse %s tags', str);
91+
}
92+
93+
for (const tag of str.split(',')) {
94+
const [key, value] = tag.split('=');
95+
tags[key] = value;
96+
}
97+
98+
return tags;
99+
}
100+
80101
function output(obj, type) {
81102
if (type === 'json') {
82103
console.log(JSON.stringify(obj, null, 2));
@@ -108,4 +129,5 @@ module.exports = {
108129
output,
109130
commonYargsOptions,
110131
loadJsonOrYaml,
132+
parseTags,
111133
};

tests/cli.test.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const tap = require('tap');
22
const { readFileSync } = require('fs');
3-
const { execSync, spawnSync } = require('child_process');
3+
const { execSync } = require('child_process');
44

55
const README = readFileSync('./README.md').toString('utf-8').split('\n');
66

@@ -18,7 +18,7 @@ function getCommand(comment) {
1818
}
1919

2020
tap.test('Give the readme examples', (t) => {
21-
t.plan(14);
21+
t.plan(15);
2222

2323
t.test('Sohuld list mappings', (t) => {
2424
t.plan(1);
@@ -102,7 +102,7 @@ tap.test('Give the readme examples', (t) => {
102102

103103
t.test('Should render the example template in human readable format', (t) => {
104104
t.plan(1);
105-
const [command, output] = getCommand('# renderHumanReadableLimitTo');
105+
const [command, output] = getCommand('# renderHumanReadableFilterBy');
106106
const result = execSync(command, { encoding: 'utf-8' });
107107

108108
t.strictSame(result, output);
@@ -124,6 +124,17 @@ tap.test('Give the readme examples', (t) => {
124124
t.strictSame(result, output);
125125
});
126126

127+
t.test(
128+
'Should render the example template and persist groupping by the type tag',
129+
(t) => {
130+
t.plan(1);
131+
const [command, output] = getCommand('# groupBy');
132+
const result = execSync(command, { encoding: 'utf-8' });
133+
134+
t.strictSame(result, output);
135+
}
136+
);
137+
127138
t.test('Should render the example to generate a njk mapping', (t) => {
128139
t.plan(1);
129140
const [command, output] = getCommand('# createNjkMapping');

0 commit comments

Comments
 (0)