mcdev 3.0.2 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.eslintrc.json +1 -1
  2. package/.github/ISSUE_TEMPLATE/bug.yml +75 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +3 -2
  4. package/.issuetracker +11 -3
  5. package/.vscode/settings.json +3 -3
  6. package/CHANGELOG.md +88 -0
  7. package/README.md +245 -141
  8. package/boilerplate/config.json +3 -2
  9. package/docs/dist/documentation.md +818 -352
  10. package/lib/Deployer.js +4 -1
  11. package/lib/MetadataTypeDefinitions.js +1 -0
  12. package/lib/MetadataTypeInfo.js +1 -0
  13. package/lib/Retriever.js +30 -14
  14. package/lib/cli.js +295 -0
  15. package/lib/index.js +774 -1019
  16. package/lib/metadataTypes/AccountUser.js +389 -0
  17. package/lib/metadataTypes/Asset.js +8 -7
  18. package/lib/metadataTypes/Automation.js +121 -56
  19. package/lib/metadataTypes/DataExtension.js +167 -121
  20. package/lib/metadataTypes/DataExtensionField.js +134 -4
  21. package/lib/metadataTypes/DataExtract.js +9 -5
  22. package/lib/metadataTypes/EventDefinition.js +9 -5
  23. package/lib/metadataTypes/FileTransfer.js +9 -5
  24. package/lib/metadataTypes/Folder.js +66 -69
  25. package/lib/metadataTypes/ImportFile.js +13 -12
  26. package/lib/metadataTypes/MetadataType.js +138 -77
  27. package/lib/metadataTypes/Query.js +2 -3
  28. package/lib/metadataTypes/Role.js +13 -8
  29. package/lib/metadataTypes/Script.js +2 -2
  30. package/lib/metadataTypes/definitions/AccountUser.definition.js +227 -0
  31. package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
  32. package/lib/metadataTypes/definitions/DataExtension.definition.js +1 -1
  33. package/lib/metadataTypes/definitions/DataExtensionField.definition.js +1 -1
  34. package/lib/metadataTypes/definitions/Folder.definition.js +1 -1
  35. package/lib/metadataTypes/definitions/ImportFile.definition.js +2 -1
  36. package/lib/metadataTypes/definitions/Script.definition.js +5 -5
  37. package/lib/retrieveChangelog.js +96 -0
  38. package/lib/util/cli.js +4 -6
  39. package/lib/util/init.git.js +2 -1
  40. package/lib/util/util.js +20 -3
  41. package/package.json +19 -23
  42. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
  43. package/img/README.md/troubleshoot-nodejs-postinstall.jpg +0 -0
  44. package/postinstall.js +0 -41
package/lib/index.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  'use strict';
3
2
 
4
3
  const Util = require('./util/util');
@@ -13,1121 +12,877 @@ const Deployer = require('./Deployer');
13
12
  const MetadataTypeInfo = require('./MetadataTypeInfo');
14
13
  const MetadataTypeDefinitions = require('./MetadataTypeDefinitions');
15
14
  const Retriever = require('./Retriever');
16
- const yargs = require('yargs');
17
15
  const inquirer = require('inquirer');
18
16
  let properties;
19
17
 
20
- // CLI framework
21
- yargs
22
- .scriptName('mcdev')
23
- .usage('$0 <command> [options]')
24
- .command({
25
- command: 'retrieve [BU] [TYPE]',
26
- aliases: ['r'],
27
- desc: 'retrieves metadata of a business unit',
28
- // @ts-ignore
29
- builder: (yargs) => {
30
- yargs
31
- .positional('BU', {
32
- type: 'string',
33
- describe:
34
- 'the business unit to retrieve from (in format "credential name/BU name")',
35
- })
36
- .positional('TYPE', {
37
- type: 'string',
38
- describe: 'metadata type that shall be exclusively downloaded',
39
- });
40
- },
41
- handler: (argv) => {
42
- _setLoggingLevel(argv);
43
- retrieve(argv.BU, argv.TYPE);
44
- },
45
- })
46
- .command({
47
- command: 'deploy [BU] [TYPE]',
48
- aliases: ['d'],
49
- desc: 'deploys local metadata to a business unit',
50
- builder: (yargs) => {
51
- yargs
52
- .positional('BU', {
53
- type: 'string',
54
- describe:
55
- 'the business unit to deploy to (in format "credential name/BU name")',
56
- })
57
- .positional('TYPE', {
58
- type: 'string',
59
- describe: 'metadata type that shall be exclusively uploaded',
60
- });
61
- },
62
- handler: (argv) => {
63
- _setLoggingLevel(argv);
64
- deploy(argv.BU, argv.TYPE);
65
- },
66
- })
67
- .command({
68
- command: 'init [credentialsName]',
69
- desc: `creates '${Util.configFileName}' in your root or adds additional credentials to the existing one`,
70
- builder: (yargs) => {
71
- yargs.positional('credentialsName', {
72
- type: 'string',
73
- describe: 'name of your installed package',
74
- });
75
- },
76
- handler: (argv) => {
77
- _setLoggingLevel(argv);
78
- initProject(argv.credentialsName, argv.skipInteraction);
79
- },
80
- })
81
- .command({
82
- command: 'reloadBUs [credentialsName]',
83
- aliases: ['rb'],
84
- desc: 'loads the list of available BUs from the server and saves it to your config',
85
- builder: (yargs) => {
86
- yargs.positional('credentialsName', {
87
- type: 'string',
88
- describe: 'name of your installed package',
89
- });
90
- },
91
- handler: (argv) => {
92
- _setLoggingLevel(argv);
93
- findBUs(argv.credentialsName);
94
- },
95
- })
96
- .command({
97
- command: 'badKeys [BU]',
98
- desc: 'lists metadata with random API names in specified Business Unit directory',
99
- builder: (yargs) => {
100
- yargs.positional('BU', {
101
- type: 'string',
102
- describe: 'the business unit to deploy to',
103
- });
104
- },
105
- handler: (argv) => {
106
- _setLoggingLevel(argv);
107
- badKeys(argv.BU);
108
- },
109
- })
110
- .command({
111
- command: 'document <TYPE> [BU]',
112
- aliases: ['doc'],
113
- desc: 'Creates Markdown or HTML documentation for the selected type',
114
- builder: (yargs) => {
115
- yargs
116
- .positional('TYPE', {
117
- type: 'string',
118
- describe:
119
- 'metadata type to generate docs for; currently supported: dataExtension, role',
120
- })
121
- .positional('BU', {
122
- type: 'string',
123
- describe:
124
- 'the business unit to generate docs for (in format "credential name/BU name")',
125
- });
126
- },
127
- handler: (argv) => {
128
- _setLoggingLevel(argv);
129
- document(argv.BU, argv.TYPE);
130
- },
131
- })
132
- .command({
133
- command: 'delete <BU> <TYPE> <EXTERNALKEY>',
134
- aliases: ['del'],
135
- desc: 'deletes metadata of selected type and external key',
136
- builder: (yargs) => {
137
- yargs
138
- .positional('BU', {
139
- type: 'string',
140
- describe:
141
- 'the business unit to delete from (in format "credential name/BU name")',
142
- })
143
- .positional('TYPE', {
144
- type: 'string',
145
- describe: 'metadata type to delete from; currently supported: dataExtension',
146
- })
147
- .positional('EXTERNALKEY', {
148
- type: 'string',
149
- describe: 'the key to delete',
150
- });
151
- },
152
- handler: (argv) => {
153
- _setLoggingLevel(argv);
154
- deleteByKey(argv.BU, argv.TYPE, argv.EXTERNALKEY);
155
- },
156
- })
157
- .command({
158
- command: 'retrieveAsTemplate <BU> <TYPE> <NAME> <MARKET>',
159
- aliases: ['rt'],
160
- desc: 'Retrieves a specific metadata file by name for templating',
161
- builder: (yargs) => {
162
- yargs
163
- .positional('BU', {
164
- type: 'string',
165
- describe:
166
- 'the business unit to deploy to (in format "credential name/BU name")',
167
- })
168
- .positional('TYPE', {
169
- type: 'string',
170
- describe: 'metadata type',
171
- })
172
- .positional('NAME', {
173
- type: 'string',
174
- describe: 'name of the metadata component',
175
- })
176
- .positional('MARKET', {
177
- type: 'string',
178
- describe: 'market used for reverse building template',
179
- });
180
- },
181
- handler: (argv) => {
182
- _setLoggingLevel(argv);
183
- retrieveAsTemplate(argv.BU, argv.TYPE, argv.NAME, argv.MARKET);
184
- },
185
- })
186
- .command({
187
- command: 'buildDefinition <BU> <TYPE> <NAME> <MARKET>',
188
- aliases: ['bd'],
189
- desc: 'builds metadata definition based on template',
190
- builder: (yargs) => {
191
- yargs
192
- .positional('BU', {
193
- type: 'string',
194
- describe: 'the business unit to deploy to',
195
- })
196
- .positional('TYPE', {
197
- type: 'string',
198
- describe: 'metadata type',
199
- })
200
- .positional('NAME', {
201
- type: 'string',
202
- describe: 'name of the metadata component',
203
- })
204
- .positional('MARKET', {
205
- type: 'string',
206
- describe: 'the business unit to deploy to',
207
- });
208
- },
209
- handler: (argv) => {
210
- _setLoggingLevel(argv);
211
- buildDefinition(argv.BU, argv.TYPE, argv.NAME, argv.MARKET);
212
- },
213
- })
214
- .command({
215
- command: 'buildDefinitionBulk <LISTNAME> <TYPE> <NAME>',
216
- aliases: ['bdb'],
217
- desc: 'builds metadata definition based on template en bulk',
218
- builder: (yargs) => {
219
- yargs
220
- .positional('LISTNAME', {
221
- type: 'string',
222
- describe: 'name of list of BU-market combos',
223
- })
224
- .positional('TYPE', {
225
- type: 'string',
226
- describe: 'metadata type',
227
- })
228
- .positional('NAME', {
229
- type: 'string',
230
- describe: 'name of the metadata component',
231
- });
232
- },
233
- handler: (argv) => {
234
- _setLoggingLevel(argv);
235
- buildDefinitionBulk(argv.LISTNAME, argv.TYPE, argv.NAME);
236
- },
237
- })
238
- .command({
239
- command: 'selectTypes',
240
- aliases: ['st'],
241
- desc: 'lets you choose what metadata types to retrieve',
242
- handler: (argv) => {
243
- _setLoggingLevel(argv);
244
- selectTypes();
245
- },
246
- })
247
- .command({
248
- command: 'explainTypes',
249
- aliases: ['et'],
250
- desc: 'explains metadata types that can be retrieved',
251
- handler: (argv) => {
252
- _setLoggingLevel(argv);
253
- explainTypes();
254
- },
255
- })
256
- .command({
257
- command: 'createDeltaPkg [range] [filter]',
258
- aliases: ['cdp'],
259
- desc: 'Copies commit-based file delta into deploy folder',
260
- builder: (yargs) => {
261
- yargs
262
- .positional('range', {
263
- type: 'string',
264
- describe: 'Pull Request target branch or git commit range',
265
- })
266
- .positional('filter', {
267
- type: 'string',
268
- describe:
269
- 'Disable templating & instead filter by the specified file path (comma separated)',
270
- });
271
- },
272
- handler: createDeltaPkg,
273
- })
274
- .command({
275
- command: 'upgrade',
276
- aliases: ['up'],
277
- desc: 'Add NPM dependencies and IDE configuration files to your project',
278
- handler: (argv) => {
279
- _setLoggingLevel(argv);
280
- upgrade(argv.skipInteraction);
281
- },
282
- })
283
- .option('verbose', {
284
- type: 'boolean',
285
- description: 'Run with verbose CLI output',
286
- })
287
- .option('debug', {
288
- type: 'boolean',
289
- description: 'Enable developer & edge-case features',
290
- })
291
- .option('silent', {
292
- type: 'boolean',
293
- description: 'Only output errors to CLI',
294
- })
295
- .option('skipInteraction', {
296
- alias: ['yes', 'y'],
297
- description: 'Interactive questions where possible and go with defaults instead',
298
- })
299
- .demandCommand(1, 'Please enter a valid command')
300
- .strict()
301
- .recommendCommands()
302
- .wrap(yargs.terminalWidth())
303
- .epilog('Copyright 2021. Accenture.')
304
- .help().argv;
305
-
306
18
  /**
307
- * handler for 'mcdev createDeltaPkg
308
- * @param {Object} argv yargs parameters
309
- * @param {String} [argv.range] git commit range
310
- into deploy directory
311
- * @param {String} [argv.filter] filter file paths that start with any
312
- * @param {Boolean} [argv.skipInteraction] allows to skip interactive wizard
313
- * @returns {void}
19
+ * main class
314
20
  */
315
- async function createDeltaPkg(argv) {
316
- _setLoggingLevel(argv);
317
- properties = properties || File.loadConfigFile();
318
- if (!Util.checkProperties(properties)) {
319
- return null;
320
- }
321
- // get source market and source BU from config
322
- if (argv.filter) {
323
- return DevOps.createDeltaPkg(properties, argv.range, true, argv.filter);
324
- } else {
325
- // If no custom filter was provided, use deployment marketLists & templating
326
-
327
- // check if sourceTargetMapping is valid
328
- if (
329
- !properties.options.deployment.sourceTargetMapping ||
330
- !Object.keys(properties.options.deployment.sourceTargetMapping).length
331
- ) {
332
- Util.logger.error('Bad configuration of options.deployment.sourceTargetMapping');
333
- return;
21
+ class Mcdev {
22
+ /**
23
+ * handler for 'mcdev createDeltaPkg
24
+ * @param {Object} argv yargs parameters
25
+ * @param {String} [argv.range] git commit range
26
+ into deploy directory
27
+ * @param {String} [argv.filter] filter file paths that start with any
28
+ * @param {Boolean} [argv.skipInteraction] allows to skip interactive wizard
29
+ * @returns {void}
30
+ */
31
+ static async createDeltaPkg(argv) {
32
+ this._setLoggingLevel(argv);
33
+ properties = properties || File.loadConfigFile();
34
+ if (!Util.checkProperties(properties)) {
35
+ return null;
334
36
  }
335
- const sourceMarketListArr = Object.keys(properties.options.deployment.sourceTargetMapping);
37
+ // get source market and source BU from config
38
+ if (argv.filter) {
39
+ return DevOps.createDeltaPkg(properties, argv.range, true, argv.filter);
40
+ } else {
41
+ // If no custom filter was provided, use deployment marketLists & templating
336
42
 
337
- for (const sourceML of sourceMarketListArr) {
338
- // check if sourceTargetMapping has valid values
339
- // #1 check source marketlist
340
- try {
341
- Builder.verifyMarketList(sourceML, properties);
342
- // remove potentially existing "description"-entry
343
- delete properties.marketList[sourceML].description;
43
+ // check if sourceTargetMapping is valid
44
+ if (
45
+ !properties.options.deployment.sourceTargetMapping ||
46
+ !Object.keys(properties.options.deployment.sourceTargetMapping).length
47
+ ) {
48
+ Util.logger.error('Bad configuration of options.deployment.sourceTargetMapping');
49
+ return;
50
+ }
51
+ const sourceMarketListArr = Object.keys(
52
+ properties.options.deployment.sourceTargetMapping
53
+ );
344
54
 
345
- const sourceMarketBuArr = Object.keys(properties.marketList[sourceML]);
346
- if (sourceMarketBuArr.length !== 1) {
347
- throw new Error('Only 1 BU is allowed per source marketList');
55
+ for (const sourceML of sourceMarketListArr) {
56
+ // check if sourceTargetMapping has valid values
57
+ // #1 check source marketlist
58
+ try {
59
+ Builder.verifyMarketList(sourceML, properties);
60
+ // remove potentially existing "description"-entry
61
+ delete properties.marketList[sourceML].description;
62
+
63
+ const sourceMarketBuArr = Object.keys(properties.marketList[sourceML]);
64
+ if (sourceMarketBuArr.length !== 1) {
65
+ throw new Error('Only 1 BU is allowed per source marketList');
66
+ }
67
+ if ('string' !== typeof properties.marketList[sourceML][sourceMarketBuArr[0]]) {
68
+ throw new Error('Only 1 market per BU is allowed per source marketList');
69
+ }
70
+ } catch (ex) {
71
+ Util.logger.error('Deployment Source: ' + ex.message);
72
+ return;
348
73
  }
349
- if ('string' !== typeof properties.marketList[sourceML][sourceMarketBuArr[0]]) {
350
- throw new Error('Only 1 market per BU is allowed per source marketList');
74
+ // #2 check corresponding target marketList
75
+ let targetML;
76
+ try {
77
+ targetML = properties.options.deployment.sourceTargetMapping[sourceML];
78
+ if ('string' !== typeof targetML) {
79
+ throw new Error(
80
+ 'Please define one target marketList per source in deployment.sourceTargetMapping (No arrays allowed)'
81
+ );
82
+ }
83
+ Builder.verifyMarketList(targetML, properties);
84
+ // remove potentially existing "description"-entry
85
+ delete properties.marketList[targetML].description;
86
+ } catch (ex) {
87
+ Util.logger.error('Deployment Target: ' + ex.message);
351
88
  }
352
- } catch (ex) {
353
- Util.logger.error('Deployment Source: ' + ex.message);
354
- return;
355
89
  }
356
- // #2 check corresponding target marketList
357
- let targetML;
358
- try {
359
- targetML = properties.options.deployment.sourceTargetMapping[sourceML];
360
- if ('string' !== typeof targetML) {
361
- throw new Error(
362
- 'Please define one target marketList per source in deployment.sourceTargetMapping (No arrays allowed)'
363
- );
90
+ // all good let's loop a second time for actual execution
91
+ for (const sourceMlName of sourceMarketListArr) {
92
+ const targetMlName =
93
+ properties.options.deployment.sourceTargetMapping[sourceMlName];
94
+ const sourceBU = Object.keys(properties.marketList[sourceMlName])[0];
95
+ const sourceMarket = Object.values(properties.marketList[sourceMlName])[0];
96
+
97
+ const delta = await DevOps.createDeltaPkg(properties, argv.range, false, sourceBU);
98
+ // If only chaing templating and buildDefinition if required
99
+ if (!delta || delta.length === 0) {
100
+ // info/error messages was printed by DevOps.createDeltaPkg() already
101
+ return;
364
102
  }
365
- Builder.verifyMarketList(targetML, properties);
366
- // remove potentially existing "description"-entry
367
- delete properties.marketList[targetML].description;
368
- } catch (ex) {
369
- Util.logger.error('Deployment Target: ' + ex.message);
370
- }
371
- }
372
- // all good let's loop a second time for actual execution
373
- for (const sourceMlName of sourceMarketListArr) {
374
- const targetMlName = properties.options.deployment.sourceTargetMapping[sourceMlName];
375
- const sourceBU = Object.keys(properties.marketList[sourceMlName])[0];
376
- const sourceMarket = Object.values(properties.marketList[sourceMlName])[0];
377
-
378
- const delta = await DevOps.createDeltaPkg(properties, argv.range, false, sourceBU);
379
- // If only chaing templating and buildDefinition if required
380
- if (!delta || delta.length === 0) {
381
- // info/error messages was printed by DevOps.createDeltaPkg() already
382
- return;
383
- }
384
- Util.logger.info('=============');
103
+ Util.logger.info('=============');
104
+
105
+ // Put files into maps. One map with BU -> type -> file (for retrieveAsTemplate)
106
+ // Other map only with type -> file (for buildDefinitionBulk)
107
+ const buTypeDelta = {};
108
+ const typeDelta = {};
109
+ delta
110
+ // Only template/build files that were added/updated/moved. no deletions
111
+ // ! doesn't work for folder, because their name parsing doesnt work at the moment
112
+ .filter((file) => file.gitAction !== 'delete' && file.name)
113
+ .forEach((file) => {
114
+ const buPath = `${file._credential}/${file._businessUnit}`;
115
+ if (!buTypeDelta[buPath]) {
116
+ buTypeDelta[buPath] = {};
117
+ }
118
+ if (!buTypeDelta[buPath][file.type]) {
119
+ buTypeDelta[buPath][file.type] = [];
120
+ }
121
+ buTypeDelta[buPath][file.type].push(file.name);
122
+
123
+ if (!typeDelta[file.type]) {
124
+ typeDelta[file.type] = [];
125
+ }
126
+ typeDelta[file.type].push(file.name);
127
+ });
385
128
 
386
- // Put files into maps. One map with BU -> type -> file (for retrieveAsTemplate)
387
- // Other map only with type -> file (for buildDefinitionBulk)
388
- const buTypeDelta = {};
389
- const typeDelta = {};
390
- delta
391
- // Only template/build files that were added/updated/moved. no deletions
392
- // ! doesn't work for folder, because their name parsing doesnt work at the moment
393
- .filter((file) => file.gitAction !== 'delete' && file.name)
394
- .forEach((file) => {
395
- const buPath = `${file._credential}/${file._businessUnit}`;
396
- if (!buTypeDelta[buPath]) {
397
- buTypeDelta[buPath] = {};
129
+ // Run retrieve as template for each business unit for each type
130
+ Util.logger.info('Retrieve template from Git delta');
131
+ // ! needs to be for (.. in ..) loop so that it gets executed in series
132
+ for (const bu in buTypeDelta) {
133
+ for (const type in buTypeDelta[bu]) {
134
+ Util.logger.info(
135
+ `⚡ mcdev rt ${bu} ${type} "${buTypeDelta[bu][type].join(
136
+ ','
137
+ )}" ${sourceMarket}`
138
+ );
139
+ await this.retrieveAsTemplate(
140
+ bu,
141
+ type,
142
+ buTypeDelta[bu][type].join(','),
143
+ sourceMarket
144
+ );
398
145
  }
399
- if (!buTypeDelta[buPath][file.type]) {
400
- buTypeDelta[buPath][file.type] = [];
401
- }
402
- buTypeDelta[buPath][file.type].push(file.name);
146
+ }
403
147
 
404
- if (!typeDelta[file.type]) {
405
- typeDelta[file.type] = [];
148
+ // Run build definitions bulk for each type
149
+ Util.logger.info(`- ✔️ Templates created`);
150
+ Util.logger.info('=============');
151
+ Util.logger.info('Build definitions from delta templates');
152
+ if (
153
+ properties.directories.templateBuilds == properties.directories.deploy ||
154
+ (Array.isArray(properties.directories.templateBuilds) &&
155
+ properties.directories.templateBuilds.includes(
156
+ properties.directories.deploy
157
+ ))
158
+ ) {
159
+ let responses;
160
+ if (!argv.skipInteraction) {
161
+ // deploy folder is in targets for definition creation
162
+ // recommend to purge their content first
163
+ const questions = [
164
+ {
165
+ type: 'confirm',
166
+ name: 'isPurgeDeployFolder',
167
+ message:
168
+ 'Do you want to empty the deploy folder (ensures no files from previous deployments remain)?',
169
+ default: true,
170
+ },
171
+ ];
172
+ responses = await new Promise((resolve) => {
173
+ inquirer.prompt(questions).then((answers) => {
174
+ resolve(answers);
175
+ });
176
+ });
406
177
  }
407
- typeDelta[file.type].push(file.name);
408
- });
409
-
410
- // Run retrieve as template for each business unit for each type
411
- Util.logger.info('Retrieve template from Git delta');
412
- // ! needs to be for (.. in ..) loop so that it gets executed in series
413
- for (const bu in buTypeDelta) {
414
- for (const type in buTypeDelta[bu]) {
178
+ if (argv.skipInteraction || responses.isPurgeDeployFolders) {
179
+ // Clear output folder structure for selected sub-type
180
+ File.removeSync(File.normalizePath([properties.directories.deploy]));
181
+ }
182
+ }
183
+ const bdPromises = [];
184
+ for (const type in typeDelta) {
415
185
  Util.logger.info(
416
- `⚡ mcdev rt ${bu} ${type} "${buTypeDelta[bu][type].join(
417
- ','
418
- )}" ${sourceMarket}`
186
+ `⚡ mcdev bdb ${targetMlName} ${type} "${typeDelta[type].join(',')}"`
419
187
  );
420
- await retrieveAsTemplate(
421
- bu,
422
- type,
423
- buTypeDelta[bu][type].join(','),
424
- sourceMarket
188
+ // omitting "await" to speed up creation
189
+ bdPromises.push(
190
+ this.buildDefinitionBulk(targetMlName, type, typeDelta[type].join(','))
425
191
  );
426
192
  }
427
- }
428
-
429
- // Run build definitions bulk for each type
430
- Util.logger.info(`- ✔️ Templates created`);
431
- Util.logger.info('=============');
432
- Util.logger.info('Build definitions from delta templates');
433
- if (
434
- properties.directories.templateBuilds == properties.directories.deploy ||
435
- (Array.isArray(properties.directories.templateBuilds) &&
436
- properties.directories.templateBuilds.includes(properties.directories.deploy))
437
- ) {
438
- let responses;
439
- if (!argv.skipInteraction) {
440
- // deploy folder is in targets for definition creation
441
- // recommend to purge their content first
442
- const questions = [
443
- {
444
- type: 'confirm',
445
- name: 'isPurgeDeployFolder',
446
- message:
447
- 'Do you want to empty the deploy folder (ensures no files from previous deployments remain)?',
448
- default: true,
449
- },
450
- ];
451
- responses = await new Promise((resolve) => {
452
- inquirer.prompt(questions).then((answers) => {
453
- resolve(answers);
454
- });
455
- });
456
- }
457
- if (argv.skipInteraction || responses.isPurgeDeployFolders) {
458
- // Clear output folder structure for selected sub-type
459
- File.removeSync(File.normalizePath([properties.directories.deploy]));
193
+ await Promise.all(bdPromises);
194
+ Util.logger.info(`- ✔️ Deploy defintions created`);
195
+ if (
196
+ properties.directories.templateBuilds == properties.directories.deploy ||
197
+ (Array.isArray(properties.directories.templateBuilds) &&
198
+ properties.directories.templateBuilds.includes(
199
+ properties.directories.deploy
200
+ ))
201
+ ) {
202
+ Util.logger.info(`You can now run deploy on the prepared BUs`);
203
+ } else {
204
+ Util.logger.info(
205
+ `Your templated defintions are now ready to be copied into the deploy folder. Hint: You can have this auto-copied if you adjust directories.templateBuilds in your config.`
206
+ );
460
207
  }
461
208
  }
462
- const bdPromises = [];
463
- for (const type in typeDelta) {
464
- Util.logger.info(
465
- `⚡ mcdev bdb ${targetMlName} ${type} "${typeDelta[type].join(',')}"`
466
- );
467
- // omitting "await" to speed up creation
468
- bdPromises.push(buildDefinitionBulk(targetMlName, type, typeDelta[type].join(',')));
469
- }
470
- await Promise.all(bdPromises);
471
- Util.logger.info(`- ✔️ Deploy defintions created`);
472
- if (
473
- properties.directories.templateBuilds == properties.directories.deploy ||
474
- (Array.isArray(properties.directories.templateBuilds) &&
475
- properties.directories.templateBuilds.includes(properties.directories.deploy))
476
- ) {
477
- Util.logger.info(`You can now run deploy on the prepared BUs`);
478
- } else {
479
- Util.logger.info(
480
- `Your templated defintions are now ready to be copied into the deploy folder. Hint: You can have this auto-copied if you adjust directories.templateBuilds in your config.`
481
- );
482
- }
483
209
  }
484
210
  }
485
- }
486
211
 
487
- /**
488
- * configures what is displayed in the console
489
- * @param {object} argv list of command line parameters given by user
490
- * @param {Boolean} [argv.silent] only errors printed to CLI
491
- * @param {Boolean} [argv.verbose] chatty user CLI output
492
- * @param {Boolean} [argv.debug] enables developer output & features
493
- * @returns {void}
494
- */
495
- function _setLoggingLevel(argv) {
496
- if (argv.silent) {
497
- // only errors printed to CLI
498
- Util.logger.level = 'error';
499
- Util.loggerTransports.console.level = 'error';
500
- } else if (argv.verbose) {
501
- // chatty user cli logs
502
- Util.logger.level = 'verbose';
503
- Util.loggerTransports.console.level = 'verbose';
504
- } else {
505
- // default user cli logs
506
- // TODO to be switched to "warn" when cli-process is integrated
507
- Util.logger.level = 'info';
508
- Util.loggerTransports.console.level = 'info';
509
- }
510
- if (argv.debug) {
511
- // enables developer output & features. no change to actual logs
512
- Util.logger.level = 'debug';
513
- }
514
- }
515
-
516
- /**
517
- * @returns {Promise} .
518
- */
519
- async function selectTypes() {
520
- properties = properties || File.loadConfigFile();
521
- if (!Util.checkProperties(properties)) {
522
- return null;
523
- }
524
- await Cli.selectTypes(properties);
525
- }
526
- /**
527
- * @returns {Promise} .
528
- */
529
- function explainTypes() {
530
- Cli.explainTypes();
531
- }
532
- /**
533
- * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard
534
- * @returns {Promise} .
535
- */
536
- async function upgrade(skipInteraction) {
537
- properties = properties || File.loadConfigFile();
538
- if (!properties) {
539
- Util.logger.error('No config found. Please run mcdev init');
540
- return;
541
- }
542
- if ((await InitGit.initGitRepo(skipInteraction)).status === 'error') {
543
- return;
544
- }
545
-
546
- Init.upgradeProject(properties, false);
547
- }
548
-
549
- /**
550
- * Retrieve all metadata from the specified business unit into the local file system.
551
- * @param {String} businessUnit references credentials from properties.json
552
- * @param {String} [selectedType] limit retrieval to given metadata type
553
- * @returns {Promise<void>} -
554
- */
555
- async function retrieve(businessUnit, selectedType) {
556
- Util.logger.info('mcdev:: Retrieve');
557
- properties = properties || File.loadConfigFile();
558
- if (!Util.checkProperties(properties)) {
559
- // return null here to avoid seeing 2 error messages for the same issue
560
- return null;
561
- }
562
- const [type, subType] = selectedType ? selectedType.split('-') : [];
563
- if (type && !MetadataTypeInfo[type]) {
564
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
565
- return;
566
- } else if (
567
- type &&
568
- subType &&
569
- (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
570
- ) {
571
- Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
572
- return;
212
+ /**
213
+ * configures what is displayed in the console
214
+ * @param {object} argv list of command line parameters given by user
215
+ * @param {Boolean} [argv.silent] only errors printed to CLI
216
+ * @param {Boolean} [argv.verbose] chatty user CLI output
217
+ * @param {Boolean} [argv.debug] enables developer output & features
218
+ * @returns {void}
219
+ */
220
+ static _setLoggingLevel(argv) {
221
+ if (argv.silent) {
222
+ // only errors printed to CLI
223
+ Util.logger.level = 'error';
224
+ Util.loggerTransports.console.level = 'error';
225
+ } else if (argv.verbose) {
226
+ // chatty user cli logs
227
+ Util.logger.level = 'verbose';
228
+ Util.loggerTransports.console.level = 'verbose';
229
+ } else {
230
+ // default user cli logs
231
+ // TODO to be switched to "warn" when cli-process is integrated
232
+ Util.logger.level = 'info';
233
+ Util.loggerTransports.console.level = 'info';
234
+ }
235
+ if (argv.debug) {
236
+ // enables developer output & features. no change to actual logs
237
+ Util.logger.level = 'debug';
238
+ }
573
239
  }
574
240
 
575
- if (businessUnit === '*') {
576
- Util.logger.info('\n:: Retrieving all BUs for all credentials');
577
- let counter_credTotal = 0;
578
- for (const cred in properties.credentials) {
579
- Util.logger.info(`\n:: Retrieving all BUs for ${cred}`);
580
- let counter_credBu = 0;
581
- for (const bu in properties.credentials[cred].businessUnits) {
582
- await _retrieveBU(cred, bu, selectedType);
583
- counter_credBu++;
584
- Util.restartLogger();
585
- }
586
- counter_credTotal += counter_credBu;
587
- Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
241
+ /**
242
+ * @returns {Promise} .
243
+ */
244
+ static async selectTypes() {
245
+ properties = properties || File.loadConfigFile();
246
+ if (!Util.checkProperties(properties)) {
247
+ return null;
588
248
  }
589
- Util.logger.info(`\n:: ${counter_credTotal} BUs in total\n`);
590
- } else {
591
- let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
592
- // to allow all-BU via user selection we need to run this here already
593
- if (
594
- properties.credentials &&
595
- (!properties.credentials[cred] ||
596
- (bu !== '*' && properties.credentials[cred].businessUnits[bu]))
597
- ) {
598
- const buObject = await Cli.getCredentialObject(
599
- properties,
600
- cred !== null ? cred + '/' + bu : null,
601
- null,
602
- true
603
- );
604
- if (buObject !== null) {
605
- cred = buObject.credential;
606
- bu = buObject.businessUnit;
607
- } else {
608
- return;
609
- }
249
+ await Cli.selectTypes(properties);
250
+ }
251
+ /**
252
+ * @returns {Promise} .
253
+ */
254
+ static explainTypes() {
255
+ Cli.explainTypes();
256
+ }
257
+ /**
258
+ * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard
259
+ * @returns {Promise} .
260
+ */
261
+ static async upgrade(skipInteraction) {
262
+ properties = properties || File.loadConfigFile();
263
+ if (!properties) {
264
+ Util.logger.error('No config found. Please run mcdev init');
265
+ return;
266
+ }
267
+ if ((await InitGit.initGitRepo(skipInteraction)).status === 'error') {
268
+ return;
610
269
  }
611
270
 
612
- if (bu === '*' && properties.credentials && properties.credentials[cred]) {
613
- Util.logger.info(`\n:: Retrieving all BUs for ${cred}`);
614
- let counter_credBu = 0;
615
- for (const bu in properties.credentials[cred].businessUnits) {
616
- await _retrieveBU(cred, bu, selectedType);
617
- counter_credBu++;
618
- Util.restartLogger();
619
- }
620
- Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
621
- } else {
622
- await _retrieveBU(cred, bu, selectedType);
623
- Util.logger.info(`:: Done\n`);
271
+ Init.upgradeProject(properties, false);
272
+ }
273
+
274
+ /**
275
+ * Retrieve all metadata from the specified business unit into the local file system.
276
+ * @param {String} businessUnit references credentials from properties.json
277
+ * @param {String} [selectedType] limit retrieval to given metadata type
278
+ * @param {boolean} [changelogOnly] skip saving, only create json in memory
279
+ * @returns {Promise<Object>} -
280
+ */
281
+ static async retrieve(businessUnit, selectedType, changelogOnly) {
282
+ Util.logger.info('mcdev:: Retrieve');
283
+ properties = properties || File.loadConfigFile();
284
+ if (!Util.checkProperties(properties)) {
285
+ // return null here to avoid seeing 2 error messages for the same issue
286
+ return null;
624
287
  }
625
- }
626
- }
627
- /**
628
- * helper for retrieve()
629
- * @param {String} cred name of Credential
630
- * @param {String} bu name of BU
631
- * @param {String} [selectedType] limit retrieval to given metadata type/subtype
632
- * @returns {Promise} ensure that BUs are worked on sequentially
633
- */
634
- async function _retrieveBU(cred, bu, selectedType) {
635
- properties = properties || File.loadConfigFile();
636
- const buObject = await Cli.getCredentialObject(
637
- properties,
638
- cred !== null ? cred + '/' + bu : null,
639
- null,
640
- true
641
- );
642
- if (buObject !== null) {
643
- cred = buObject.credential;
644
- bu = buObject.businessUnit;
645
- Util.logger.info(`\n:: Retrieving ${cred}/${bu}\n`);
646
- let retrieveTypesArr;
647
288
  const [type, subType] = selectedType ? selectedType.split('-') : [];
648
- if (
289
+ if (type && !MetadataTypeInfo[type]) {
290
+ Util.logger.error(`:: '${type}' is not a valid metadata type`);
291
+ return;
292
+ } else if (
649
293
  type &&
650
294
  subType &&
651
- MetadataTypeInfo[type] &&
652
- MetadataTypeDefinitions[type].subTypes.includes(subType)
295
+ (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
653
296
  ) {
654
- // Clear output folder structure for selected sub-type
655
- File.removeSync(
656
- File.normalizePath([properties.directories.retrieve, cred, bu, type, subType])
657
- );
658
- retrieveTypesArr = [selectedType];
659
- } else if (type && MetadataTypeInfo[type]) {
660
- // Clear output folder structure for selected type
661
- File.removeSync(File.normalizePath([properties.directories.retrieve, cred, bu, type]));
662
- retrieveTypesArr = [type];
663
- } else {
664
- // Clear output folder structure
665
- File.removeSync(File.normalizePath([properties.directories.retrieve, cred, bu]));
666
- // assume no type was given and config settings are used instead:
667
- // removes subtypes and removes duplicates
668
- retrieveTypesArr = [
669
- ...new Set(properties.metaDataTypes.retrieve.map((type) => type.split('-')[0])),
670
- ];
671
- }
672
- let client;
673
- try {
674
- client = await Util.getETClient(buObject);
675
- } catch (ex) {
676
- Util.logger.error(ex.message);
297
+ Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
677
298
  return;
678
299
  }
679
- const retriever = new Retriever(properties, buObject, client);
680
300
 
681
- try {
682
- // await is required or the calls end up conflicting
683
- await retriever.retrieve(retrieveTypesArr, null, null);
684
- if (properties.options.documentOnRetrieve) {
685
- // todo: find the underlying async issue that makes this wait necessary
686
- await new Promise((resolve) => {
687
- setTimeout(() => resolve('done!'), 1000);
688
- });
689
- await badKeys(`${cred}/${bu}`);
301
+ if (businessUnit === '*') {
302
+ Util.logger.info('\n:: Retrieving all BUs for all credentials');
303
+ let counter_credTotal = 0;
304
+ for (const cred in properties.credentials) {
305
+ Util.logger.info(`\n:: Retrieving all BUs for ${cred}`);
306
+ let counter_credBu = 0;
307
+ for (const bu in properties.credentials[cred].businessUnits) {
308
+ await this._retrieveBU(cred, bu, selectedType);
309
+ counter_credBu++;
310
+ Util.restartLogger();
311
+ }
312
+ counter_credTotal += counter_credBu;
313
+ Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
690
314
  }
691
- } catch (ex) {
692
- Util.logger.error('mcdev.retrieve failed: ' + ex.message);
693
- Util.logger.debug(ex.stack);
694
- if (Util.logger.level === 'debug') {
695
- console.log(ex.stack);
315
+ Util.logger.info(`\n:: ${counter_credTotal} BUs in total\n`);
316
+ } else {
317
+ let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
318
+ // to allow all-BU via user selection we need to run this here already
319
+ if (
320
+ properties.credentials &&
321
+ (!properties.credentials[cred] ||
322
+ (bu !== '*' && properties.credentials[cred].businessUnits[bu]))
323
+ ) {
324
+ const buObject = await Cli.getCredentialObject(
325
+ properties,
326
+ cred !== null ? cred + '/' + bu : null,
327
+ null,
328
+ true
329
+ );
330
+ if (buObject !== null) {
331
+ cred = buObject.credential;
332
+ bu = buObject.businessUnit;
333
+ } else {
334
+ return;
335
+ }
336
+ }
337
+
338
+ if (bu === '*' && properties.credentials && properties.credentials[cred]) {
339
+ Util.logger.info(`\n:: Retrieving all BUs for ${cred}`);
340
+ let counter_credBu = 0;
341
+ for (const bu in properties.credentials[cred].businessUnits) {
342
+ await this._retrieveBU(cred, bu, selectedType);
343
+ counter_credBu++;
344
+ Util.restartLogger();
345
+ }
346
+ Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
347
+ } else {
348
+ // retrieve a single BU; return
349
+ const retrieveChangelog = await this._retrieveBU(
350
+ cred,
351
+ bu,
352
+ selectedType,
353
+ changelogOnly
354
+ );
355
+ if (changelogOnly) {
356
+ return retrieveChangelog;
357
+ }
358
+ Util.logger.info(`:: Done\n`);
696
359
  }
697
360
  }
698
361
  }
699
- }
700
- /**
701
- * helper for deploy()
702
- * @param {String} cred name of Credential
703
- * @param {String} bu name of BU
704
- * @param {String} [type] limit deployment to given metadata type
705
- * @returns {Promise} ensure that BUs are worked on sequentially
706
- */
707
- async function _deployBU(cred, bu, type) {
708
- const buPath = `${cred}/${bu}`;
709
- Util.logger.info(`::Deploying ${buPath}`);
710
- properties = properties || File.loadConfigFile();
711
- const buObject = await Cli.getCredentialObject(properties, buPath, null, true);
712
- if (buObject !== null) {
713
- let client;
714
- try {
715
- client = await Util.getETClient(buObject);
716
- } catch (ex) {
717
- Util.logger.error(ex.message);
718
- return;
362
+ /**
363
+ * helper for retrieve()
364
+ * @param {String} cred name of Credential
365
+ * @param {String} bu name of BU
366
+ * @param {String} [selectedType] limit retrieval to given metadata type/subtype
367
+ * @param {boolean} [changelogOnly] skip saving, only create json in memory
368
+ * @returns {Promise<Object>} ensure that BUs are worked on sequentially
369
+ */
370
+ static async _retrieveBU(cred, bu, selectedType, changelogOnly) {
371
+ properties = properties || File.loadConfigFile();
372
+ const buObject = await Cli.getCredentialObject(
373
+ properties,
374
+ cred !== null ? cred + '/' + bu : null,
375
+ null,
376
+ true
377
+ );
378
+ if (buObject !== null) {
379
+ cred = buObject.credential;
380
+ bu = buObject.businessUnit;
381
+ Util.logger.info(`\n:: Retrieving ${cred}/${bu}\n`);
382
+ let retrieveTypesArr;
383
+ const [type, subType] = selectedType ? selectedType.split('-') : [];
384
+ if (
385
+ type &&
386
+ subType &&
387
+ MetadataTypeInfo[type] &&
388
+ MetadataTypeDefinitions[type].subTypes.includes(subType)
389
+ ) {
390
+ // Clear output folder structure for selected sub-type
391
+ File.removeSync(
392
+ File.normalizePath([properties.directories.retrieve, cred, bu, type, subType])
393
+ );
394
+ retrieveTypesArr = [selectedType];
395
+ } else if (type && MetadataTypeInfo[type]) {
396
+ // Clear output folder structure for selected type
397
+ File.removeSync(
398
+ File.normalizePath([properties.directories.retrieve, cred, bu, type])
399
+ );
400
+ retrieveTypesArr = [type];
401
+ } else {
402
+ // Clear output folder structure
403
+ File.removeSync(File.normalizePath([properties.directories.retrieve, cred, bu]));
404
+ // assume no type was given and config settings are used instead:
405
+ // removes subtypes and removes duplicates
406
+ retrieveTypesArr = [
407
+ ...new Set(properties.metaDataTypes.retrieve.map((type) => type.split('-')[0])),
408
+ ];
409
+ }
410
+ let client;
411
+ try {
412
+ client = await Util.getETClient(buObject);
413
+ } catch (ex) {
414
+ Util.logger.error(ex.message);
415
+ return;
416
+ }
417
+ const retriever = new Retriever(properties, buObject, client);
418
+
419
+ try {
420
+ // await is required or the calls end up conflicting
421
+ const retrieveChangelog = await retriever.retrieve(
422
+ retrieveTypesArr,
423
+ null,
424
+ null,
425
+ changelogOnly
426
+ );
427
+ if (changelogOnly) {
428
+ return retrieveChangelog;
429
+ }
430
+ if (properties.options.documentOnRetrieve) {
431
+ // todo: find the underlying async issue that makes this wait necessary
432
+ await new Promise((resolve) => {
433
+ setTimeout(() => resolve('done!'), 1000);
434
+ });
435
+ await this.badKeys(`${cred}/${bu}`);
436
+ }
437
+ } catch (ex) {
438
+ Util.logger.error('mcdev.retrieve failed: ' + ex.message);
439
+ Util.logger.debug(ex.stack);
440
+ if (Util.logger.level === 'debug') {
441
+ console.log(ex.stack);
442
+ }
443
+ }
719
444
  }
720
- const deployer = new Deployer(properties, buObject, client, type);
721
- try {
722
- // await is required or the calls end up conflicting
723
- await deployer.deploy();
724
- } catch (ex) {
725
- Util.logger.error('mcdev.deploy failed: ' + ex.message);
726
- Util.logger.debug(ex.stack);
727
- if (Util.logger.level === 'debug') {
728
- console.log(ex.stack);
445
+ }
446
+ /**
447
+ * helper for deploy()
448
+ * @param {String} cred name of Credential
449
+ * @param {String} bu name of BU
450
+ * @param {String} [type] limit deployment to given metadata type
451
+ * @returns {Promise} ensure that BUs are worked on sequentially
452
+ */
453
+ static async _deployBU(cred, bu, type) {
454
+ const buPath = `${cred}/${bu}`;
455
+ Util.logger.info(`::Deploying ${buPath}`);
456
+ properties = properties || File.loadConfigFile();
457
+ const buObject = await Cli.getCredentialObject(properties, buPath, null, true);
458
+ if (buObject !== null) {
459
+ let client;
460
+ try {
461
+ client = await Util.getETClient(buObject);
462
+ } catch (ex) {
463
+ Util.logger.error(ex.message);
464
+ return;
465
+ }
466
+ const deployer = new Deployer(properties, buObject, client, type);
467
+ try {
468
+ // await is required or the calls end up conflicting
469
+ await deployer.deploy();
470
+ } catch (ex) {
471
+ Util.logger.error('mcdev.deploy failed: ' + ex.message);
472
+ Util.logger.debug(ex.stack);
473
+ if (Util.logger.level === 'debug') {
474
+ console.log(ex.stack);
475
+ }
729
476
  }
730
477
  }
731
478
  }
732
- }
733
479
 
734
- /**
735
- * Deploys all metadata located in the 'deploy' directory to the specified business unit
736
- * @param {String} businessUnit references credentials from properties.json
737
- * @param {String} [selectedType] limit deployment to given metadata type
738
- * @returns {Promise<void>} -
739
- */
740
- async function deploy(businessUnit, selectedType) {
741
- Util.logger.info('mcdev:: Deploy');
742
- properties = properties || File.loadConfigFile();
743
-
744
- const [type, subType] = selectedType ? selectedType.split('-') : [];
745
- if (type && !MetadataTypeInfo[type]) {
746
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
747
- return;
748
- } else if (
749
- type &&
750
- subType &&
751
- (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
752
- ) {
753
- Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
754
- return;
755
- }
756
- let counter_credBu = 0;
757
- if (businessUnit === '*') {
758
- // all credentials and all BUs shall be deployed to
759
- const deployFolders = await File.readDirectories(properties.directories.deploy, 2, false);
760
- for (const buPath of deployFolders.filter((r) => r.includes('/'))) {
761
- const [cred, bu] = buPath.split('/');
762
- await _deployBU(cred, bu, type);
763
- counter_credBu++;
764
- Util.logger.info('');
765
- Util.restartLogger();
766
- }
767
- } else {
768
- // anything but "*" passed in
769
- let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
480
+ /**
481
+ * Deploys all metadata located in the 'deploy' directory to the specified business unit
482
+ * @param {String} businessUnit references credentials from properties.json
483
+ * @param {String} [selectedType] limit deployment to given metadata type
484
+ * @returns {Promise<void>} -
485
+ */
486
+ static async deploy(businessUnit, selectedType) {
487
+ Util.logger.info('mcdev:: Deploy');
488
+ properties = properties || File.loadConfigFile();
770
489
 
771
- // to allow all-BU via user selection we need to run this here already
772
- if (
773
- properties.credentials &&
774
- (!properties.credentials[cred] ||
775
- (bu !== '*' && properties.credentials[cred].businessUnits[bu]))
490
+ const [type, subType] = selectedType ? selectedType.split('-') : [];
491
+ if (type && !MetadataTypeInfo[type]) {
492
+ Util.logger.error(`:: '${type}' is not a valid metadata type`);
493
+ return;
494
+ } else if (
495
+ type &&
496
+ subType &&
497
+ (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
776
498
  ) {
777
- const buObject = await Cli.getCredentialObject(
778
- properties,
779
- cred !== null ? cred + '/' + bu : null,
780
- null,
781
- true
782
- );
783
- if (buObject !== null) {
784
- cred = buObject.credential;
785
- bu = buObject.businessUnit;
786
- } else {
787
- return;
788
- }
499
+ Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
500
+ return;
789
501
  }
790
-
791
- if (bu === '*' && properties.credentials && properties.credentials[cred]) {
792
- // valid credential given and -all- BUs targeted
793
- Util.logger.info(`\n:: Deploying all BUs for ${cred}`);
794
- let counter_credBu = 0;
795
- // for (const bu in properties.credentials[cred].businessUnits) {
502
+ let counter_credBu = 0;
503
+ if (businessUnit === '*') {
504
+ // all credentials and all BUs shall be deployed to
796
505
  const deployFolders = await File.readDirectories(
797
- File.normalizePath([properties.directories.deploy, cred]),
798
- 1,
506
+ properties.directories.deploy,
507
+ 2,
799
508
  false
800
509
  );
801
- for (const buPath of deployFolders) {
802
- await _deployBU(cred, buPath, type);
510
+ for (const buPath of deployFolders.filter((r) => r.includes('/'))) {
511
+ const [cred, bu] = buPath.split('/');
512
+ await this._deployBU(cred, bu, type);
803
513
  counter_credBu++;
804
514
  Util.logger.info('');
805
515
  Util.restartLogger();
806
516
  }
807
- Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
808
517
  } else {
809
- // either bad credential or specific BU or no BU given
810
- await _deployBU(cred, bu, type);
811
- counter_credBu++;
812
- }
813
- }
814
- if (counter_credBu !== 0) {
815
- Util.logger.info(`\n:: Deployed ${counter_credBu} BUs\n`);
816
- }
817
- }
518
+ // anything but "*" passed in
519
+ let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
818
520
 
819
- /**
820
- * Creates template file for properties.json
821
- * @param {string} [credentialsName] identifying name of the installed package / project
822
- * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard
823
- * @returns {Promise<void>} -
824
- */
825
- async function initProject(credentialsName, skipInteraction) {
826
- Util.logger.info('mcdev:: Setting up project');
827
- properties = properties || File.loadConfigFile(!!credentialsName);
828
- await Init.initProject(properties, credentialsName, skipInteraction);
829
- }
521
+ // to allow all-BU via user selection we need to run this here already
522
+ if (
523
+ properties.credentials &&
524
+ (!properties.credentials[cred] ||
525
+ (bu !== '*' && properties.credentials[cred].businessUnits[bu]))
526
+ ) {
527
+ const buObject = await Cli.getCredentialObject(
528
+ properties,
529
+ cred !== null ? cred + '/' + bu : null,
530
+ null,
531
+ true
532
+ );
533
+ if (buObject !== null) {
534
+ cred = buObject.credential;
535
+ bu = buObject.businessUnit;
536
+ } else {
537
+ return;
538
+ }
539
+ }
830
540
 
831
- /**
832
- * Refreshes BU names and ID's from MC instance
833
- * @param {string} credentialsName identifying name of the installed package / project
834
- * @returns {Promise<void>} -
835
- */
836
- async function findBUs(credentialsName) {
837
- Util.logger.info('mcdev:: Load BUs');
838
- properties = properties || File.loadConfigFile();
839
- const buObject = await Cli.getCredentialObject(properties, credentialsName, true);
840
- if (buObject !== null) {
841
- BuHelper.refreshBUProperties(properties, buObject.credential);
541
+ if (bu === '*' && properties.credentials && properties.credentials[cred]) {
542
+ // valid credential given and -all- BUs targeted
543
+ Util.logger.info(`\n:: Deploying all BUs for ${cred}`);
544
+ let counter_credBu = 0;
545
+ // for (const bu in properties.credentials[cred].businessUnits) {
546
+ const deployFolders = await File.readDirectories(
547
+ File.normalizePath([properties.directories.deploy, cred]),
548
+ 1,
549
+ false
550
+ );
551
+ for (const buPath of deployFolders) {
552
+ await this._deployBU(cred, buPath, type);
553
+ counter_credBu++;
554
+ Util.logger.info('');
555
+ Util.restartLogger();
556
+ }
557
+ Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`);
558
+ } else {
559
+ // either bad credential or specific BU or no BU given
560
+ await this._deployBU(cred, bu, type);
561
+ counter_credBu++;
562
+ }
563
+ }
564
+ if (counter_credBu !== 0) {
565
+ Util.logger.info(`\n:: Deployed ${counter_credBu} BUs\n`);
566
+ }
842
567
  }
843
- }
844
568
 
845
- /**
846
- * Creates docs for supported metadata types in Markdown and/or HTML format
847
- *
848
- * @param {String} businessUnit references credentials from properties.json
849
- * @param {String} type metadata type
850
- * @returns {Promise<void>} -
851
- */
852
- async function document(businessUnit, type) {
853
- Util.logger.info('mcdev:: Document');
854
- properties = properties || File.loadConfigFile();
855
- if (type && !MetadataTypeInfo[type]) {
856
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
857
- return;
858
- }
859
- try {
860
- const parentBUOnlyTypes = ['role'];
861
- const buObject = await Cli.getCredentialObject(
862
- properties,
863
- parentBUOnlyTypes.includes(type) ? businessUnit.split('/')[0] : businessUnit,
864
- parentBUOnlyTypes.includes(type) ? Util.parentBuName : null
865
- );
569
+ /**
570
+ * Creates template file for properties.json
571
+ * @param {string} [credentialsName] identifying name of the installed package / project
572
+ * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard
573
+ * @returns {Promise<void>} -
574
+ */
575
+ static async initProject(credentialsName, skipInteraction) {
576
+ Util.logger.info('mcdev:: Setting up project');
577
+ properties = properties || File.loadConfigFile(!!credentialsName);
578
+ await Init.initProject(properties, credentialsName, skipInteraction);
579
+ }
580
+
581
+ /**
582
+ * Refreshes BU names and ID's from MC instance
583
+ * @param {string} credentialsName identifying name of the installed package / project
584
+ * @returns {Promise<void>} -
585
+ */
586
+ static async findBUs(credentialsName) {
587
+ Util.logger.info('mcdev:: Load BUs');
588
+ properties = properties || File.loadConfigFile();
589
+ const buObject = await Cli.getCredentialObject(properties, credentialsName, true);
866
590
  if (buObject !== null) {
867
- MetadataTypeInfo[type].properties = properties;
868
- MetadataTypeInfo[type].document(buObject);
591
+ BuHelper.refreshBUProperties(properties, buObject.credential);
869
592
  }
870
- } catch (ex) {
871
- Util.logger.error('mcdev.document ' + ex.message);
872
- Util.logger.debug(ex.stack);
873
- Util.logger.info('If the directoy does not exist, you may need to retrieve this BU first.');
874
593
  }
875
- }
876
594
 
877
- /**
878
- * Creates docs for supported metadata types in Markdown and/or HTML format
879
- *
880
- * @param {String} businessUnit references credentials from properties.json
881
- * @param {String} type supported metadata type
882
- * @param {String} customerKey Identifier of data extension
883
- * @returns {Promise<void>} -
884
- */
885
- async function deleteByKey(businessUnit, type, customerKey) {
886
- Util.logger.info('mcdev:: delete');
887
- properties = properties || File.loadConfigFile();
888
- const buObject = await Cli.getCredentialObject(properties, businessUnit);
889
- if (buObject !== null) {
890
- if ('string' !== typeof type) {
891
- Util.logger.error('mcdev.delete failed: Bad metadata type passed in');
595
+ /**
596
+ * Creates docs for supported metadata types in Markdown and/or HTML format
597
+ *
598
+ * @param {String} businessUnit references credentials from properties.json
599
+ * @param {String} type metadata type
600
+ * @returns {Promise<void>} -
601
+ */
602
+ static async document(businessUnit, type) {
603
+ Util.logger.info('mcdev:: Document');
604
+ properties = properties || File.loadConfigFile();
605
+ if (type && !MetadataTypeInfo[type]) {
606
+ Util.logger.error(`:: '${type}' is not a valid metadata type`);
892
607
  return;
893
608
  }
894
609
  try {
895
- MetadataTypeInfo[type].properties = properties;
896
- MetadataTypeInfo[type].deleteByKey(buObject, customerKey);
610
+ const parentBUOnlyTypes = ['role'];
611
+ const buObject = await Cli.getCredentialObject(
612
+ properties,
613
+ parentBUOnlyTypes.includes(type) ? businessUnit.split('/')[0] : businessUnit,
614
+ parentBUOnlyTypes.includes(type) ? Util.parentBuName : null
615
+ );
616
+ if (buObject !== null) {
617
+ MetadataTypeInfo[type].properties = properties;
618
+ MetadataTypeInfo[type].document(buObject);
619
+ }
897
620
  } catch (ex) {
898
- Util.logger.error('mcdev.delete ' + ex.message);
621
+ Util.logger.error('mcdev.document ' + ex.message);
622
+ Util.logger.debug(ex.stack);
623
+ Util.logger.info(
624
+ 'If the directoy does not exist, you may need to retrieve this BU first.'
625
+ );
899
626
  }
900
627
  }
901
- }
902
628
 
903
- /**
904
- * Converts metadata to legacy format. Output is saved in 'converted' directory
905
- * @param {String} businessUnit references credentials from properties.json
906
- * @returns {Promise<void>} -
907
- */
908
- async function badKeys(businessUnit) {
909
- properties = properties || File.loadConfigFile();
910
- const buObject = await Cli.getCredentialObject(properties, businessUnit);
911
- if (buObject !== null) {
912
- Util.logger.info('Gathering list of Name<>External Key mismatches (bad keys)');
913
- const retrieveDir = File.filterIllegalPathChars(
914
- File.normalizePath([
915
- properties.directories.retrieve,
916
- buObject.credential,
917
- buObject.businessUnit,
918
- ])
919
- );
920
- const docPath = File.filterIllegalPathChars(
921
- properties.directories.badKeys + buObject.credential
922
- );
923
- const filename = File.normalizePath([
924
- docPath,
925
- File.filterIllegalFilenames(buObject.businessUnit) + '.badKeys.md',
926
- ]);
927
- if (!File.existsSync(docPath)) {
928
- File.mkdirpSync(docPath);
929
- } else if (File.existsSync(filename)) {
930
- File.removeSync(filename);
629
+ /**
630
+ * Creates docs for supported metadata types in Markdown and/or HTML format
631
+ *
632
+ * @param {String} businessUnit references credentials from properties.json
633
+ * @param {String} type supported metadata type
634
+ * @param {String} customerKey Identifier of data extension
635
+ * @returns {Promise<void>} -
636
+ */
637
+ static async deleteByKey(businessUnit, type, customerKey) {
638
+ Util.logger.info('mcdev:: delete');
639
+ properties = properties || File.loadConfigFile();
640
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
641
+ if (buObject !== null) {
642
+ if ('string' !== typeof type) {
643
+ Util.logger.error('mcdev.delete failed: Bad metadata type passed in');
644
+ return;
645
+ }
646
+ try {
647
+ MetadataTypeInfo[type].properties = properties;
648
+ MetadataTypeInfo[type].deleteByKey(buObject, customerKey);
649
+ } catch (ex) {
650
+ Util.logger.error('mcdev.delete ' + ex.message);
651
+ }
931
652
  }
653
+ }
932
654
 
933
- const regex = RegExp('(\\w+-){4}\\w+');
934
- let metadata;
935
- if (File.existsSync(retrieveDir)) {
936
- metadata = Deployer.readBUMetadata(retrieveDir, null, true);
937
- } else {
938
- Util.logger.warn(
939
- `Looks like ${retrieveDir} does not exist. If there was no metadata retrieved this is expected, in other cases re-run retrieve to attempt to fix this issue`
655
+ /**
656
+ * Converts metadata to legacy format. Output is saved in 'converted' directory
657
+ * @param {String} businessUnit references credentials from properties.json
658
+ * @returns {Promise<void>} -
659
+ */
660
+ static async badKeys(businessUnit) {
661
+ properties = properties || File.loadConfigFile();
662
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
663
+ if (buObject !== null) {
664
+ Util.logger.info('Gathering list of Name<>External Key mismatches (bad keys)');
665
+ const retrieveDir = File.filterIllegalPathChars(
666
+ File.normalizePath([
667
+ properties.directories.retrieve,
668
+ buObject.credential,
669
+ buObject.businessUnit,
670
+ ])
940
671
  );
941
- return;
942
- }
943
- let output = '# List of Metadata with Name-Key mismatches\n';
944
- for (const metadataType in metadata) {
945
- let listEntries = '';
946
- for (const entry in metadata[metadataType]) {
947
- const metadataEntry = metadata[metadataType][entry];
948
- if (regex.test(entry)) {
949
- if (metadataType === 'query' && metadataEntry.Status === 'Inactive') {
950
- continue;
672
+ const docPath = File.filterIllegalPathChars(
673
+ properties.directories.badKeys + buObject.credential
674
+ );
675
+ const filename = File.normalizePath([
676
+ docPath,
677
+ File.filterIllegalFilenames(buObject.businessUnit) + '.badKeys.md',
678
+ ]);
679
+ if (!File.existsSync(docPath)) {
680
+ File.mkdirpSync(docPath);
681
+ } else if (File.existsSync(filename)) {
682
+ File.removeSync(filename);
683
+ }
684
+
685
+ const regex = RegExp('(\\w+-){4}\\w+');
686
+ let metadata;
687
+ if (File.existsSync(retrieveDir)) {
688
+ metadata = Deployer.readBUMetadata(retrieveDir, null, true);
689
+ } else {
690
+ Util.logger.warn(
691
+ `Looks like ${retrieveDir} does not exist. If there was no metadata retrieved this is expected, in other cases re-run retrieve to attempt to fix this issue`
692
+ );
693
+ return;
694
+ }
695
+ let output = '# List of Metadata with Name-Key mismatches\n';
696
+ for (const metadataType in metadata) {
697
+ let listEntries = '';
698
+ for (const entry in metadata[metadataType]) {
699
+ const metadataEntry = metadata[metadataType][entry];
700
+ if (regex.test(entry)) {
701
+ if (metadataType === 'query' && metadataEntry.Status === 'Inactive') {
702
+ continue;
703
+ }
704
+ listEntries +=
705
+ '- ' +
706
+ entry +
707
+ (metadataEntry.name || metadataEntry.Name
708
+ ? ' => ' + (metadataEntry.name || metadataEntry.Name)
709
+ : '') +
710
+ '\n';
951
711
  }
952
- listEntries +=
953
- '- ' +
954
- entry +
955
- (metadataEntry.name || metadataEntry.Name
956
- ? ' => ' + (metadataEntry.name || metadataEntry.Name)
957
- : '') +
958
- '\n';
712
+ }
713
+ if (listEntries !== '') {
714
+ output += '\n## ' + metadataType + '\n\n' + listEntries;
959
715
  }
960
716
  }
961
- if (listEntries !== '') {
962
- output += '\n## ' + metadataType + '\n\n' + listEntries;
963
- }
717
+ await File.writeToFile(
718
+ docPath,
719
+ File.filterIllegalFilenames(buObject.businessUnit) + '.badKeys',
720
+ 'md',
721
+ output
722
+ );
723
+ Util.logger.info('Bad keys documented in ' + filename);
964
724
  }
965
- await File.writeToFile(
966
- docPath,
967
- File.filterIllegalFilenames(buObject.businessUnit) + '.badKeys',
968
- 'md',
969
- output
970
- );
971
- Util.logger.info('Bad keys documented in ' + filename);
972
725
  }
973
- }
974
726
 
975
- /**
976
- * Retrieve a specific metadata file and templatise.
977
- * @param {String} businessUnit references credentials from properties.json
978
- * @param {String} selectedType supported metadata type
979
- * @param {String} name name of the metadata
980
- * @param {String} market market which should be used to revert template
981
- * @returns {Promise<void>} -
982
- */
983
- async function retrieveAsTemplate(businessUnit, selectedType, name, market) {
984
- Util.logger.info('mcdev:: Retrieve as Template');
985
- properties = properties || File.loadConfigFile();
986
- const [type, subType] = selectedType ? selectedType.split('-') : [];
987
- if (type && !MetadataTypeInfo[type]) {
988
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
989
- return;
990
- } else if (
991
- type &&
992
- subType &&
993
- (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
994
- ) {
995
- Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
996
- return;
997
- }
998
-
999
- let retrieveTypesArr;
1000
- if (
1001
- type &&
1002
- subType &&
1003
- MetadataTypeInfo[type] &&
1004
- MetadataTypeDefinitions[type].subTypes.includes(subType)
1005
- ) {
1006
- retrieveTypesArr = [selectedType];
1007
- } else if (type && MetadataTypeInfo[type]) {
1008
- retrieveTypesArr = [type];
1009
- }
1010
- const buObject = await Cli.getCredentialObject(properties, businessUnit);
1011
- if (buObject !== null) {
1012
- let client;
1013
- try {
1014
- client = await Util.getETClient(buObject);
1015
- } catch (ex) {
1016
- Util.logger.error(ex.message);
727
+ /**
728
+ * Retrieve a specific metadata file and templatise.
729
+ * @param {String} businessUnit references credentials from properties.json
730
+ * @param {String} selectedType supported metadata type
731
+ * @param {String} name name of the metadata
732
+ * @param {String} market market which should be used to revert template
733
+ * @returns {Promise<void>} -
734
+ */
735
+ static async retrieveAsTemplate(businessUnit, selectedType, name, market) {
736
+ Util.logger.info('mcdev:: Retrieve as Template');
737
+ properties = properties || File.loadConfigFile();
738
+ const [type, subType] = selectedType ? selectedType.split('-') : [];
739
+ if (type && !MetadataTypeInfo[type]) {
740
+ Util.logger.error(`:: '${type}' is not a valid metadata type`);
741
+ return;
742
+ } else if (
743
+ type &&
744
+ subType &&
745
+ (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType))
746
+ ) {
747
+ Util.logger.error(`:: '${selectedType}' is not a valid metadata type`);
1017
748
  return;
1018
749
  }
1019
- const retriever = new Retriever(properties, buObject, client);
1020
- if (_checkMarket(market)) {
1021
- return retriever.retrieve(retrieveTypesArr, name, properties.markets[market]);
750
+
751
+ let retrieveTypesArr;
752
+ if (
753
+ type &&
754
+ subType &&
755
+ MetadataTypeInfo[type] &&
756
+ MetadataTypeDefinitions[type].subTypes.includes(subType)
757
+ ) {
758
+ retrieveTypesArr = [selectedType];
759
+ } else if (type && MetadataTypeInfo[type]) {
760
+ retrieveTypesArr = [type];
761
+ }
762
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
763
+ if (buObject !== null) {
764
+ let client;
765
+ try {
766
+ client = await Util.getETClient(buObject);
767
+ } catch (ex) {
768
+ Util.logger.error(ex.message);
769
+ return;
770
+ }
771
+ const retriever = new Retriever(properties, buObject, client);
772
+ if (this._checkMarket(market)) {
773
+ return retriever.retrieve(retrieveTypesArr, name, properties.markets[market]);
774
+ }
1022
775
  }
1023
776
  }
1024
- }
1025
777
 
1026
- /**
1027
- * Build a specific metadata file based on a template.
1028
- * @param {String} businessUnit references credentials from properties.json
1029
- * @param {String} type supported metadata type
1030
- * @param {String} name name of the metadata
1031
- * @param {String} market market localizations
1032
- * @returns {Promise<void>} -
1033
- */
1034
- async function buildDefinition(businessUnit, type, name, market) {
1035
- Util.logger.info('mcdev:: Build Definition from Template');
1036
- properties = properties || File.loadConfigFile();
1037
- if (type.includes('-')) {
1038
- Util.logger.error(
1039
- `:: '${type}' is not a valid metadata type. Please don't include subtypes.`
1040
- );
1041
- return;
1042
- }
1043
- if (type && !MetadataTypeInfo[type]) {
1044
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
1045
- return;
778
+ /**
779
+ * Build a specific metadata file based on a template.
780
+ * @param {String} businessUnit references credentials from properties.json
781
+ * @param {String} type supported metadata type
782
+ * @param {String} name name of the metadata
783
+ * @param {String} market market localizations
784
+ * @returns {Promise<void>} -
785
+ */
786
+ static async buildDefinition(businessUnit, type, name, market) {
787
+ Util.logger.info('mcdev:: Build Definition from Template');
788
+ properties = properties || File.loadConfigFile();
789
+ if (type.includes('-')) {
790
+ Util.logger.error(
791
+ `:: '${type}' is not a valid metadata type. Please don't include subtypes.`
792
+ );
793
+ return;
794
+ }
795
+ if (type && !MetadataTypeInfo[type]) {
796
+ Util.logger.error(`:: '${type}' is not a valid metadata type`);
797
+ return;
798
+ }
799
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
800
+ if (buObject !== null) {
801
+ const builder = new Builder(properties, buObject, null);
802
+ if (market === '*') {
803
+ for (const oneMarket in properties.markets) {
804
+ builder.buildDefinition(type, name, properties.markets[oneMarket]);
805
+ }
806
+ } else {
807
+ if (this._checkMarket(market)) {
808
+ builder.buildDefinition(type, name, properties.markets[market]);
809
+ }
810
+ }
811
+ }
1046
812
  }
1047
- const buObject = await Cli.getCredentialObject(properties, businessUnit);
1048
- if (buObject !== null) {
1049
- const builder = new Builder(properties, buObject, null);
1050
- if (market === '*') {
813
+ /**
814
+ * check if a market name exists in current mcdev config
815
+ * @param {String} market market localizations
816
+ * @returns {Boolean} found market or not
817
+ */
818
+ static _checkMarket(market) {
819
+ properties = properties || File.loadConfigFile();
820
+ if (properties.markets[market]) {
821
+ return true;
822
+ } else {
823
+ Util.logger.error(`Could not find the market '${market}' in your configuration file.`);
824
+ const marketArr = [];
1051
825
  for (const oneMarket in properties.markets) {
1052
- builder.buildDefinition(type, name, properties.markets[oneMarket]);
826
+ marketArr.push(oneMarket);
1053
827
  }
1054
- } else {
1055
- if (_checkMarket(market)) {
1056
- builder.buildDefinition(type, name, properties.markets[market]);
828
+ if (marketArr.length) {
829
+ Util.logger.info('Available markets are: ' + marketArr.join(', '));
1057
830
  }
831
+ return false;
1058
832
  }
1059
833
  }
1060
- }
1061
- /**
1062
- * check if a market name exists in current mcdev config
1063
- * @param {String} market market localizations
1064
- * @returns {Boolean} found market or not
1065
- */
1066
- function _checkMarket(market) {
1067
- properties = properties || File.loadConfigFile();
1068
- if (properties.markets[market]) {
1069
- return true;
1070
- } else {
1071
- Util.logger.error(`Could not find the market '${market}' in your configuration file.`);
1072
- const marketArr = [];
1073
- for (const oneMarket in properties.markets) {
1074
- marketArr.push(oneMarket);
1075
- }
1076
- if (marketArr.length) {
1077
- Util.logger.info('Available markets are: ' + marketArr.join(', '));
1078
- }
1079
- return false;
1080
- }
1081
- }
1082
834
 
1083
- /**
1084
- * Build a specific metadata file based on a template using a list of bu-market combos
1085
- * @param {String} listName name of list of BU-market combos
1086
- * @param {String} type supported metadata type
1087
- * @param {String} name name of the metadata
1088
- * @returns {Promise<void>} -
1089
- */
1090
- async function buildDefinitionBulk(listName, type, name) {
1091
- Util.logger.info('mcdev:: Build Definition from Template Bulk');
1092
- properties = properties || File.loadConfigFile();
1093
- if (!properties.marketList) {
1094
- Util.logger.error('Please define properties.marketList object in your config');
1095
- return;
1096
- }
1097
- if (!properties.marketList[listName]) {
1098
- Util.logger.error(`Please define properties.marketList.${listName} in your config`);
1099
- return;
1100
- }
1101
- if (type && !MetadataTypeInfo[type]) {
1102
- Util.logger.error(`:: '${type}' is not a valid metadata type`);
1103
- return;
1104
- }
1105
- let i = 0;
1106
- for (const businessUnit in properties.marketList[listName]) {
1107
- if (businessUnit === 'description') {
1108
- // skip, it's just a metadata on this list and not a BU
1109
- continue;
835
+ /**
836
+ * Build a specific metadata file based on a template using a list of bu-market combos
837
+ * @param {String} listName name of list of BU-market combos
838
+ * @param {String} type supported metadata type
839
+ * @param {String} name name of the metadata
840
+ * @returns {Promise<void>} -
841
+ */
842
+ static async buildDefinitionBulk(listName, type, name) {
843
+ Util.logger.info('mcdev:: Build Definition from Template Bulk');
844
+ properties = properties || File.loadConfigFile();
845
+ if (!properties.marketList) {
846
+ Util.logger.error('Please define properties.marketList object in your config');
847
+ return;
1110
848
  }
1111
- i++;
1112
- const market = properties.marketList[listName][businessUnit];
1113
- let marketList = [];
1114
- if ('string' === typeof market) {
1115
- marketList.push(market);
1116
- } else {
1117
- marketList = market;
849
+ if (!properties.marketList[listName]) {
850
+ Util.logger.error(`Please define properties.marketList.${listName} in your config`);
851
+ return;
852
+ }
853
+ if (type && !MetadataTypeInfo[type]) {
854
+ Util.logger.error(`:: '${type}' is not a valid metadata type`);
855
+ return;
1118
856
  }
1119
- marketList.forEach((market) => {
1120
- if (market && properties.markets[market]) {
1121
- Util.logger.info(`Executing for '${businessUnit}': '${market}'`);
1122
- buildDefinition(businessUnit, type, name, market);
857
+ let i = 0;
858
+ for (const businessUnit in properties.marketList[listName]) {
859
+ if (businessUnit === 'description') {
860
+ // skip, it's just a metadata on this list and not a BU
861
+ continue;
862
+ }
863
+ i++;
864
+ const market = properties.marketList[listName][businessUnit];
865
+ let marketList = [];
866
+ if ('string' === typeof market) {
867
+ marketList.push(market);
1123
868
  } else {
1124
- Util.logger.error(
1125
- `Could not find '${market}' in properties.markets. Please check your properties.marketList.${listName} confguration.`
1126
- );
869
+ marketList = market;
1127
870
  }
1128
- });
1129
- }
1130
- if (!i) {
1131
- Util.logger.error('Please define properties.marketList in your config');
871
+ marketList.forEach((market) => {
872
+ if (market && properties.markets[market]) {
873
+ Util.logger.info(`Executing for '${businessUnit}': '${market}'`);
874
+ this.buildDefinition(businessUnit, type, name, market);
875
+ } else {
876
+ Util.logger.error(
877
+ `Could not find '${market}' in properties.markets. Please check your properties.marketList.${listName} confguration.`
878
+ );
879
+ }
880
+ });
881
+ }
882
+ if (!i) {
883
+ Util.logger.error('Please define properties.marketList in your config');
884
+ }
1132
885
  }
1133
886
  }
887
+
888
+ module.exports = Mcdev;