mcdev 3.1.1 → 4.0.0

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 (135) hide show
  1. package/.eslintrc.json +67 -7
  2. package/.github/ISSUE_TEMPLATE/bug.yml +5 -1
  3. package/.github/ISSUE_TEMPLATE/task.md +1 -1
  4. package/.github/PULL_REQUEST_TEMPLATE.md +5 -3
  5. package/.github/dependabot.yml +14 -0
  6. package/.github/workflows/code-analysis.yml +57 -0
  7. package/.husky/commit-msg +10 -0
  8. package/.husky/post-checkout +5 -0
  9. package/.husky/pre-commit +2 -1
  10. package/.prettierrc +8 -0
  11. package/.vscode/settings.json +1 -1
  12. package/LICENSE +2 -2
  13. package/README.md +134 -45
  14. package/boilerplate/config.json +5 -11
  15. package/boilerplate/files/.prettierrc +8 -0
  16. package/boilerplate/files/.vscode/extensions.json +0 -1
  17. package/boilerplate/files/.vscode/settings.json +28 -2
  18. package/boilerplate/files/README.md +2 -2
  19. package/boilerplate/forcedUpdates.json +10 -0
  20. package/boilerplate/npm-dependencies.json +5 -5
  21. package/docs/dist/documentation.md +2795 -1724
  22. package/jsconfig.json +1 -1
  23. package/lib/Builder.js +166 -75
  24. package/lib/Deployer.js +244 -96
  25. package/lib/MetadataTypeDefinitions.js +2 -0
  26. package/lib/MetadataTypeInfo.js +2 -0
  27. package/lib/Retriever.js +61 -84
  28. package/lib/cli.js +133 -25
  29. package/lib/index.js +242 -563
  30. package/lib/metadataTypes/AccountUser.js +101 -95
  31. package/lib/metadataTypes/Asset.js +677 -248
  32. package/lib/metadataTypes/AttributeGroup.js +23 -12
  33. package/lib/metadataTypes/Automation.js +456 -357
  34. package/lib/metadataTypes/Campaign.js +33 -93
  35. package/lib/metadataTypes/ContentArea.js +31 -11
  36. package/lib/metadataTypes/DataExtension.js +391 -376
  37. package/lib/metadataTypes/DataExtensionField.js +131 -54
  38. package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
  39. package/lib/metadataTypes/DataExtract.js +67 -50
  40. package/lib/metadataTypes/DataExtractType.js +14 -8
  41. package/lib/metadataTypes/Discovery.js +21 -16
  42. package/lib/metadataTypes/Email.js +32 -12
  43. package/lib/metadataTypes/EmailSendDefinition.js +85 -80
  44. package/lib/metadataTypes/EventDefinition.js +69 -47
  45. package/lib/metadataTypes/FileTransfer.js +78 -54
  46. package/lib/metadataTypes/Filter.js +11 -4
  47. package/lib/metadataTypes/Folder.js +149 -117
  48. package/lib/metadataTypes/FtpLocation.js +14 -8
  49. package/lib/metadataTypes/ImportFile.js +69 -69
  50. package/lib/metadataTypes/Interaction.js +19 -4
  51. package/lib/metadataTypes/List.js +54 -13
  52. package/lib/metadataTypes/MetadataType.js +687 -479
  53. package/lib/metadataTypes/MobileCode.js +46 -0
  54. package/lib/metadataTypes/MobileKeyword.js +114 -0
  55. package/lib/metadataTypes/Query.js +204 -103
  56. package/lib/metadataTypes/Role.js +76 -61
  57. package/lib/metadataTypes/Script.js +146 -82
  58. package/lib/metadataTypes/SetDefinition.js +20 -8
  59. package/lib/metadataTypes/TriggeredSendDefinition.js +78 -58
  60. package/lib/metadataTypes/definitions/Asset.definition.js +21 -10
  61. package/lib/metadataTypes/definitions/AttributeGroup.definition.js +12 -0
  62. package/lib/metadataTypes/definitions/Automation.definition.js +10 -5
  63. package/lib/metadataTypes/definitions/Campaign.definition.js +44 -1
  64. package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -0
  65. package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +6 -0
  66. package/lib/metadataTypes/definitions/DataExtract.definition.js +18 -14
  67. package/lib/metadataTypes/definitions/Discovery.definition.js +12 -0
  68. package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +4 -0
  69. package/lib/metadataTypes/definitions/EventDefinition.definition.js +22 -0
  70. package/lib/metadataTypes/definitions/FileTransfer.definition.js +4 -0
  71. package/lib/metadataTypes/definitions/Filter.definition.js +4 -0
  72. package/lib/metadataTypes/definitions/Folder.definition.js +6 -0
  73. package/lib/metadataTypes/definitions/FtpLocation.definition.js +4 -0
  74. package/lib/metadataTypes/definitions/ImportFile.definition.js +10 -5
  75. package/lib/metadataTypes/definitions/Interaction.definition.js +4 -0
  76. package/lib/metadataTypes/definitions/MobileCode.definition.js +163 -0
  77. package/lib/metadataTypes/definitions/MobileKeyword.definition.js +253 -0
  78. package/lib/metadataTypes/definitions/Query.definition.js +4 -0
  79. package/lib/metadataTypes/definitions/Role.definition.js +5 -0
  80. package/lib/metadataTypes/definitions/Script.definition.js +4 -0
  81. package/lib/metadataTypes/definitions/SetDefinition.definition.js +28 -0
  82. package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +4 -0
  83. package/lib/retrieveChangelog.js +7 -6
  84. package/lib/util/auth.js +117 -0
  85. package/lib/util/businessUnit.js +55 -66
  86. package/lib/util/cache.js +194 -0
  87. package/lib/util/cli.js +90 -116
  88. package/lib/util/config.js +302 -0
  89. package/lib/util/devops.js +240 -50
  90. package/lib/util/file.js +120 -191
  91. package/lib/util/init.config.js +195 -69
  92. package/lib/util/init.git.js +45 -50
  93. package/lib/util/init.js +72 -59
  94. package/lib/util/init.npm.js +48 -39
  95. package/lib/util/util.js +280 -564
  96. package/package.json +44 -33
  97. package/test/dataExtension.test.js +152 -0
  98. package/test/mockRoot/.mcdev-auth.json +8 -0
  99. package/test/mockRoot/.mcdevrc.json +67 -0
  100. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +39 -0
  101. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testDataExtension.dataExtension-meta.json +23 -0
  102. package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +11 -0
  103. package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +4 -0
  104. package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +11 -0
  105. package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql +4 -0
  106. package/test/query.test.js +149 -0
  107. package/test/resourceFactory.js +142 -0
  108. package/test/resources/1111111/dataFolder/retrieve-response.xml +43 -0
  109. package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +18 -0
  110. package/test/resources/9999999/automation/v1/queries/get-response.json +24 -0
  111. package/test/resources/9999999/automation/v1/queries/post-response.json +18 -0
  112. package/test/resources/9999999/dataExtension/build-expected.json +51 -0
  113. package/test/resources/9999999/dataExtension/create-expected.json +23 -0
  114. package/test/resources/9999999/dataExtension/create-response.xml +54 -0
  115. package/test/resources/9999999/dataExtension/retrieve-expected.json +51 -0
  116. package/test/resources/9999999/dataExtension/retrieve-response.xml +47 -0
  117. package/test/resources/9999999/dataExtension/template-expected.json +51 -0
  118. package/test/resources/9999999/dataExtension/update-expected.json +55 -0
  119. package/test/resources/9999999/dataExtension/update-response.xml +52 -0
  120. package/test/resources/9999999/dataExtensionField/retrieve-response.xml +93 -0
  121. package/test/resources/9999999/dataExtensionTemplate/retrieve-response.xml +303 -0
  122. package/test/resources/9999999/dataFolder/retrieve-response.xml +65 -0
  123. package/test/resources/9999999/query/build-expected.json +8 -0
  124. package/test/resources/9999999/query/get-expected.json +11 -0
  125. package/test/resources/9999999/query/patch-expected.json +11 -0
  126. package/test/resources/9999999/query/post-expected.json +11 -0
  127. package/test/resources/9999999/query/template-expected.json +8 -0
  128. package/test/resources/auth.json +32 -0
  129. package/test/resources/rest404-response.json +5 -0
  130. package/test/resources/retrieve-response.xml +21 -0
  131. package/test/utils.js +107 -0
  132. package/types/mcdev.d.js +301 -0
  133. package/CHANGELOG.md +0 -126
  134. package/PULL_REQUEST_TEMPLATE.md +0 -19
  135. package/test/util/file.js +0 -51
@@ -1,8 +1,10 @@
1
+ const TYPE = require('../../types/mcdev.d');
1
2
  const File = require('./file');
2
- const path = require('path');
3
+ const path = require('node:path');
3
4
  const inquirer = require('inquirer');
4
5
  const Util = require('./util');
5
- const git = require('simple-git/promise')();
6
+ const git = require('simple-git')();
7
+ const Builder = require('../Builder');
6
8
  const MetadataType = require('../MetadataTypeInfo');
7
9
  const jsonToTable = require('json-to-table');
8
10
  /**
@@ -13,25 +15,23 @@ const DevOps = {
13
15
  /**
14
16
  * Extracts the delta between a commit and the current state for deployment.
15
17
  * Interactive commit selection if no commits are passed.
16
- * @param {Object} properties central properties object
17
- * @param {String} [range] git commit range
18
+ *
19
+ * @param {TYPE.Mcdevrc} properties central properties object
20
+ * @param {string} [range] git commit range
18
21
  * @param {boolean} [saveToDeployDir] if true, copy metadata changes into deploy directory
19
- * @param {String} [filterPaths] filter file paths that start with any specified path (comma separated)
20
- * @returns {Promise<Object>} -
22
+ * @param {string} [filterPaths] filter file paths that start with any specified path (comma separated)
23
+ * @returns {Promise.<TYPE.DeltaPkgItem[]>} -
21
24
  */
22
- async createDeltaPkg(properties, range, saveToDeployDir, filterPaths) {
25
+ async getDeltaList(properties, range, saveToDeployDir, filterPaths) {
23
26
  const rangeUserInput = range;
24
- Util.logger.info('Create Delta Package ::');
25
- if (filterPaths) {
26
- filterPaths = filterPaths.split(',').map((filePath) =>
27
- path
28
- .normalize(properties.directories.retrieve + filePath)
29
- .split('\\')
30
- .join('/')
31
- );
32
- } else {
33
- filterPaths = [properties.directories.retrieve];
34
- }
27
+ filterPaths = filterPaths
28
+ ? filterPaths.split(',').map((filePath) =>
29
+ path
30
+ .normalize(properties.directories.retrieve + filePath)
31
+ .split('\\')
32
+ .join('/')
33
+ )
34
+ : [properties.directories.retrieve];
35
35
  if (range) {
36
36
  if (!range.includes('..')) {
37
37
  // we limit the user here somewhat by always comparing to current branch if no range was given
@@ -54,7 +54,7 @@ const DevOps = {
54
54
  }));
55
55
  display.push(new inquirer.Separator(' ==== '));
56
56
 
57
- const questions = [
57
+ const responses = await inquirer.prompt([
58
58
  {
59
59
  type: 'list',
60
60
  message: 'Select base commit for comparison with current commit',
@@ -62,12 +62,7 @@ const DevOps = {
62
62
  pageSize: 10,
63
63
  choices: display,
64
64
  },
65
- ];
66
- const responses = await new Promise((resolve) => {
67
- inquirer.prompt(questions).then((answers) => {
68
- resolve(answers);
69
- });
70
- });
65
+ ]);
71
66
  range = `${responses.commit}..HEAD`;
72
67
  }
73
68
 
@@ -78,18 +73,22 @@ const DevOps = {
78
73
  'add/update': 0,
79
74
  move: 0,
80
75
  };
76
+ /**
77
+ * @type {TYPE.DeltaPkgItem[]}
78
+ */
81
79
  const delta = (await git.diffSummary([range])).files
82
80
  // populate additional info for all changed files
83
- .map((file) => {
81
+ .map((/** @type {TYPE.DeltaPkgItem} */ file) => {
84
82
  // If file was moved it's path needs to be parsed
85
83
  file.moved = file.file.includes('=>');
86
84
  if (file.moved) {
87
85
  const p = file.file;
86
+ // TODO: rewrite remaining substring() to slice()
88
87
  const paths = {
89
- base: p.substring(0, p.indexOf('{')),
90
- before: p.substring(p.indexOf('{') + 1, p.indexOf('=>') - 1),
91
- after: p.substring(p.indexOf('=>') + 3, p.indexOf('}')),
92
- end: p.substring(p.indexOf('}') + 1),
88
+ base: p.slice(0, Math.max(0, p.indexOf('{'))),
89
+ before: p.substring(p.indexOf('{') + 1, p.indexOf('=>') - 1), // eslint-disable-line unicorn/prefer-string-slice
90
+ after: p.substring(p.indexOf('=>') + 3, p.indexOf('}')), // eslint-disable-line unicorn/prefer-string-slice
91
+ end: p.slice(Math.max(0, p.indexOf('}') + 1)),
93
92
  };
94
93
  file.fromPath = path
95
94
  .normalize(`${paths.base}${paths.before}${paths.end}`)
@@ -113,7 +112,7 @@ const DevOps = {
113
112
  // ! Filter happens after initial parse, because if file was moved its new path has to be calculated
114
113
  .filter((file) => filterPaths.some((path) => file.file.startsWith(path)))
115
114
  // ensure badly named files on unsupported metadata types are not in our subset
116
- .filter((file) => {
115
+ .filter((/** @type {TYPE.DeltaPkgItem} */ file) => {
117
116
  if (!MetadataType[file.type]) {
118
117
  Util.logger.debug(
119
118
  `Unknown metadata-type found for (${file.file}): ` + file.type
@@ -123,7 +122,7 @@ const DevOps = {
123
122
  return true;
124
123
  }
125
124
  })
126
- .map((file) => {
125
+ .map((/** @type {TYPE.DeltaPkgItem} */ file) => {
127
126
  // Gets external key based on file name und the assumption that filename = externalKey
128
127
  if (file.type === 'folder') {
129
128
  file.externalKey = null;
@@ -135,7 +134,7 @@ const DevOps = {
135
134
 
136
135
  // Check if file doesn't exist in reported path, that means it was a git deletion
137
136
  // TODO: improve git action detection by switching from diffSummary to diff with --summary result parsing
138
- if (!File.existsSync(file.file)) {
137
+ if (!File.pathExistsSync(file.file)) {
139
138
  file.gitAction = 'delete';
140
139
  } else if (file.moved) {
141
140
  file.gitAction = 'move';
@@ -189,8 +188,12 @@ const DevOps = {
189
188
  return [];
190
189
  }
191
190
  // Write into delta.json to serve as documentation
192
- File.writeJSONToFile(properties.directories.deltaPackage, 'delta_package', delta);
193
- this.document(properties.directories.deltaPackage, delta);
191
+ const directoryDeltaPkg = File.normalizePath([
192
+ properties.directories.docs,
193
+ 'deltaPackage/',
194
+ ]);
195
+ await File.writeJSONToFile(directoryDeltaPkg, 'delta_package', delta);
196
+ this.document(directoryDeltaPkg, delta);
194
197
  Util.logger.info(
195
198
  `- ✔️ Identified changes: Add/Update=${gitActionsCounter['add/update']}, Move=${gitActionsCounter['move']}, Delete=${gitActionsCounter['delete']}`
196
199
  );
@@ -199,12 +202,11 @@ const DevOps = {
199
202
  'Please note that deletions have to be done manually on the SFMC website.'
200
203
  );
201
204
  }
202
- Util.logger.info(
203
- `Saved report in ${path.join(properties.directories.deltaPackage, 'delta_package.md')}`
204
- );
205
+ Util.logger.info(`Saved report in ${path.join(directoryDeltaPkg, 'delta_package.md')}`);
205
206
 
206
207
  // Copy filtered list of files into deploy directory
207
208
  if (saveToDeployDir) {
209
+ /** @type {TYPE.DeltaPkgItem} */
208
210
  const copied = delta.map((file) =>
209
211
  File.copyFile(
210
212
  file.file,
@@ -229,42 +231,215 @@ const DevOps = {
229
231
  } copied, ${skipped.length} skipped, ${failed.length} failed)`
230
232
  );
231
233
  if (skipped.length > 0) {
232
- skipped.forEach((file) =>
233
- Util.logger.debug(`Skipped - ${file.statusMessage} - ${file.file}`)
234
- );
234
+ for (const file of skipped) {
235
+ Util.logger.debug(`Skipped - ${file.statusMessage} - ${file.file}`);
236
+ }
235
237
  }
236
238
  if (failed.length > 0) {
237
- failed.forEach((file) =>
238
- Util.logger.error(`Failed - ${file.statusMessage} - ${file.file}`)
239
- );
239
+ for (const file of failed) {
240
+ Util.logger.error(`Failed - ${file.statusMessage} - ${file.file}`);
241
+ }
240
242
  }
241
243
  }
242
244
  return delta;
243
245
  },
246
+ /**
247
+ * wrapper around DevOps.getDeltaList, Builder.buildTemplate and M
248
+ *
249
+ * @param {TYPE.Mcdevrc} properties project config file
250
+ * @param {string} range git commit range
251
+ * @param {TYPE.SkipInteraction} [skipInteraction] allows to skip interactive wizard
252
+ */
253
+ async buildDeltaDefinitions(properties, range, skipInteraction) {
254
+ // check if sourceTargetMapping is valid
255
+ if (
256
+ !properties.options.deployment.sourceTargetMapping ||
257
+ !Object.keys(properties.options.deployment.sourceTargetMapping).length
258
+ ) {
259
+ Util.logger.error('Bad configuration of options.deployment.sourceTargetMapping');
260
+ return;
261
+ }
262
+ const sourceMarketListArr = Object.keys(properties.options.deployment.sourceTargetMapping);
263
+
264
+ for (const sourceML of sourceMarketListArr) {
265
+ // check if sourceTargetMapping has valid values
266
+ // #1 check source marketlist
267
+ try {
268
+ Util.verifyMarketList(sourceML, properties);
269
+ // remove potentially existing "description"-entry
270
+ delete properties.marketList[sourceML].description;
271
+
272
+ const sourceMarketBuArr = Object.keys(properties.marketList[sourceML]);
273
+ if (sourceMarketBuArr.length !== 1) {
274
+ throw new Error('Only 1 BU is allowed per source marketList');
275
+ }
276
+ if ('string' !== typeof properties.marketList[sourceML][sourceMarketBuArr[0]]) {
277
+ throw new TypeError('Only 1 market per BU is allowed per source marketList');
278
+ }
279
+ } catch (ex) {
280
+ Util.logger.error('Deployment Source: ' + ex.message);
281
+ return;
282
+ }
283
+ // #2 check corresponding target marketList
284
+ let targetML;
285
+ try {
286
+ targetML = properties.options.deployment.sourceTargetMapping[sourceML];
287
+ if ('string' !== typeof targetML) {
288
+ throw new TypeError(
289
+ 'Please define one target marketList per source in deployment.sourceTargetMapping (No arrays allowed)'
290
+ );
291
+ }
292
+ Util.verifyMarketList(targetML, properties);
293
+ // remove potentially existing "description"-entry
294
+ delete properties.marketList[targetML].description;
295
+ } catch (ex) {
296
+ Util.logger.error('Deployment Target: ' + ex.message);
297
+ }
298
+ }
299
+ // all good let's loop a second time for actual execution
300
+ for (const sourceMlName of sourceMarketListArr) {
301
+ const targetMlName = properties.options.deployment.sourceTargetMapping[sourceMlName];
302
+ const sourceBU = Object.keys(properties.marketList[sourceMlName])[0];
303
+ const sourceMarket = Object.values(properties.marketList[sourceMlName])[0];
304
+
305
+ const delta = await DevOps.getDeltaList(properties, range, false, sourceBU);
306
+ // If only chaing templating and buildDefinition if required
307
+ if (!delta || delta.length === 0) {
308
+ // info/error messages was printed by DevOps.createDeltaPkg() already
309
+ return;
310
+ }
311
+ Util.logger.info('=============');
312
+
313
+ // Put files into maps. One map with BU -> type -> file (for retrieveAsTemplate)
314
+ // Other map only with type -> file (for buildDefinitionBulk)
315
+ const buTypeDelta = {}; // for bt, with BU info
316
+ const typeDelta = {}; // for bdb, without BU info - thats taken from the marketList
317
+ let deltaCounter = 0;
318
+ for (const file of delta
319
+ // Only template/build files that were added/updated/moved. no deletions
320
+ // ! doesn't work for folder, because their name parsing doesnt work at the moment
321
+ .filter((file) => file.gitAction !== 'delete' && file.name)) {
322
+ const buPath = `${file._credential}/${file._businessUnit}`;
323
+ if (!buTypeDelta[buPath]) {
324
+ // init object
325
+ buTypeDelta[buPath] = {};
326
+ }
327
+ if (!buTypeDelta[buPath][file.type]) {
328
+ // init array
329
+ buTypeDelta[buPath][file.type] = [];
330
+ }
331
+ buTypeDelta[buPath][file.type].push(file.externalKey);
332
+ }
333
+
334
+ // Run buildTemplate for each business unit for each type
335
+ Util.logger.info('Retrieve template from Git delta');
336
+ // ! needs to be for (.. in ..) loop so that it gets executed in series
337
+ for (const bu in buTypeDelta) {
338
+ for (const type in buTypeDelta[bu]) {
339
+ // get unique list (original search might include more than one entry for types with docs or extracted code)
340
+ const keyArr = [...new Set(buTypeDelta[bu][type])];
341
+ Util.logger.info(
342
+ `⚡ mcdev bt ${bu} ${type} "${keyArr.join(',')}" ${sourceMarket}`
343
+ );
344
+ await Builder.buildTemplate(bu, type, keyArr, sourceMarket);
345
+ // ensure we have the right key for bd/bdb that matches the name used for rt
346
+ if (keyArr.length) {
347
+ if (!typeDelta[type]) {
348
+ // init array
349
+ typeDelta[type] = [];
350
+ }
351
+ typeDelta[type].push(...keyArr);
352
+ deltaCounter += keyArr.length;
353
+ }
354
+ }
355
+ }
356
+ if (deltaCounter) {
357
+ Util.logger.info(`- ✔️ Templates created: ${deltaCounter}`);
358
+ } else {
359
+ Util.logger.error(
360
+ '- ❌ No Templates or Deploy Definitions created. Check if you expected no changes.'
361
+ );
362
+ return;
363
+ }
364
+
365
+ // Run build definitions bulk for each type
366
+ Util.logger.info('=============');
367
+ Util.logger.info('Build deploy definitions from delta templates');
368
+ if (
369
+ properties.directories.templateBuilds == properties.directories.deploy ||
370
+ (Array.isArray(properties.directories.templateBuilds) &&
371
+ properties.directories.templateBuilds.includes(properties.directories.deploy))
372
+ ) {
373
+ let responses;
374
+ if (!skipInteraction) {
375
+ // deploy folder is in targets for definition creation
376
+ // recommend to purge their content first
377
+ responses = await inquirer.prompt([
378
+ {
379
+ type: 'confirm',
380
+ name: 'isPurgeDeployFolder',
381
+ message:
382
+ 'Do you want to empty the deploy folder (ensures no files from previous deployments remain)?',
383
+ default: true,
384
+ },
385
+ ]);
386
+ }
387
+ if (skipInteraction || responses.isPurgeDeployFolders) {
388
+ // Clear output folder structure for selected sub-type
389
+ await File.remove(File.normalizePath([properties.directories.deploy]));
390
+ }
391
+ }
392
+ const bdPromises = [];
393
+ for (const type in typeDelta) {
394
+ Util.logger.info(
395
+ `⚡ mcdev bdb ${targetMlName} ${type} "${typeDelta[type].join(',')}"`
396
+ );
397
+ // omitting "await" to speed up creation
398
+ bdPromises.push(
399
+ Builder.buildDefinitionBulk(targetMlName, type, typeDelta[type].join(','))
400
+ );
401
+ }
402
+ await Promise.all(bdPromises);
403
+ Util.logger.info(`- ✔️ Deploy defintions created`);
404
+ if (
405
+ properties.directories.templateBuilds == properties.directories.deploy ||
406
+ (Array.isArray(properties.directories.templateBuilds) &&
407
+ properties.directories.templateBuilds.includes(properties.directories.deploy))
408
+ ) {
409
+ Util.logger.info(`You can now run deploy on the prepared BUs`);
410
+ } else {
411
+ Util.logger.info(
412
+ `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.`
413
+ );
414
+ }
415
+ }
416
+ },
417
+
244
418
  /**
245
419
  * create markdown file for deployment listing
246
- * @param {String} directory -
247
- * @param {Object} jsonReport -
420
+ *
421
+ * @param {string} directory -
422
+ * @param {object} jsonReport -
248
423
  * @returns {void}
249
424
  */
250
425
  document(directory, jsonReport) {
251
426
  const tabled = jsonToTable(jsonReport);
252
427
  let output = `# Deployment Report\n\n`;
253
428
  let tableSeparator = '';
254
- tabled[0].forEach((column) => {
429
+ for (const column of tabled[0]) {
255
430
  if (column !== '') {
256
431
  output += `| ${column} `;
257
432
  tableSeparator += '| --- ';
258
433
  }
259
- });
434
+ }
260
435
  output += `|\n${tableSeparator}|\n`;
261
436
  for (let i = 1; i < tabled.length; i++) {
262
- tabled[i].forEach((field) => {
437
+ for (let field of tabled[i]) {
263
438
  if (field !== '') {
264
439
  field = field === true ? '✓' : field === false ? '✗' : field;
265
440
  output += `| ${field} `;
266
441
  }
267
- });
442
+ }
268
443
  output += '|\n';
269
444
  }
270
445
  try {
@@ -274,6 +449,21 @@ const DevOps = {
274
449
  Util.logger.error(`DevOps.document():: error | ` + ex.message);
275
450
  }
276
451
  },
452
+ /**
453
+ * should return only the json for all but asset, query and script that are saved as multiple files
454
+ * additionally, the documentation for dataExtension and automation should be returned
455
+ *
456
+ * @param {TYPE.Mcdevrc} properties central properties object
457
+ * @param {TYPE.BuObject} buObject references credentials
458
+ * @param {string} metadataType metadata type to build
459
+ * @param {string[]} keyArr customerkey of the metadata
460
+ * @returns {Promise.<string[]>} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
461
+ */
462
+ getFilesToCommit(properties, buObject, metadataType, keyArr) {
463
+ MetadataType[metadataType].properties = properties;
464
+ MetadataType[metadataType].buObject = buObject;
465
+ return MetadataType[metadataType].getFilesToCommit(keyArr);
466
+ },
277
467
  };
278
468
 
279
469
  module.exports = DevOps;