mcdev 7.2.0 → 7.3.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 (76) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +1 -0
  2. package/.vscode/settings.json +1 -1
  3. package/@types/lib/Builder.d.ts +4 -4
  4. package/@types/lib/Builder.d.ts.map +1 -1
  5. package/@types/lib/index.d.ts +26 -13
  6. package/@types/lib/index.d.ts.map +1 -1
  7. package/@types/lib/metadataTypes/Event.d.ts +52 -4
  8. package/@types/lib/metadataTypes/Event.d.ts.map +1 -1
  9. package/@types/lib/metadataTypes/Journey.d.ts +2 -3
  10. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  11. package/@types/lib/metadataTypes/MetadataType.d.ts +7 -0
  12. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
  13. package/@types/lib/metadataTypes/SendClassification.d.ts.map +1 -1
  14. package/@types/lib/metadataTypes/TriggeredSend.d.ts.map +1 -1
  15. package/@types/types/mcdev.d.d.ts +123 -0
  16. package/@types/types/mcdev.d.d.ts.map +1 -1
  17. package/boilerplate/files/.vscode/settings.json +2 -3
  18. package/boilerplate/forcedUpdates.json +4 -0
  19. package/eslint.config.js +1 -1
  20. package/lib/Builder.js +45 -26
  21. package/lib/cli.js +67 -5
  22. package/lib/index.js +117 -48
  23. package/lib/metadataTypes/Asset.js +1 -1
  24. package/lib/metadataTypes/Automation.js +5 -5
  25. package/lib/metadataTypes/DataExtract.js +1 -1
  26. package/lib/metadataTypes/Event.js +525 -5
  27. package/lib/metadataTypes/FileTransfer.js +1 -1
  28. package/lib/metadataTypes/ImportFile.js +1 -1
  29. package/lib/metadataTypes/Journey.js +144 -33
  30. package/lib/metadataTypes/MetadataType.js +16 -2
  31. package/lib/metadataTypes/MobileKeyword.js +1 -1
  32. package/lib/metadataTypes/Query.js +1 -1
  33. package/lib/metadataTypes/Script.js +1 -1
  34. package/lib/metadataTypes/SendClassification.js +10 -8
  35. package/lib/metadataTypes/TriggeredSend.js +18 -16
  36. package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -4
  37. package/lib/metadataTypes/definitions/EmailSend.definition.js +4 -4
  38. package/lib/metadataTypes/definitions/Event.definition.js +92 -92
  39. package/lib/metadataTypes/definitions/Journey.definition.js +1 -0
  40. package/lib/metadataTypes/definitions/Role.definition.js +4 -4
  41. package/lib/metadataTypes/definitions/SendClassification.definition.js +7 -7
  42. package/lib/metadataTypes/definitions/SenderProfile.definition.js +4 -4
  43. package/lib/metadataTypes/definitions/TriggeredSend.definition.js +11 -11
  44. package/package.json +1 -1
  45. package/test/general.test.js +20 -20
  46. package/test/mockRoot/.mcdevrc.json +1 -1
  47. package/test/resourceFactory.js +5 -1
  48. package/test/resources/9999999/dataExtension/update-callout-afterCreatedViaEvent-expected.xml +1 -1
  49. package/test/resources/9999999/sendClassification/create-response.xml +1 -1
  50. package/test/resources/9999999/sendClassification/retrieve-response.xml +2 -2
  51. package/test/resources/9999999/sendClassification/update-response.xml +1 -1
  52. package/test/resources/9999999/triggeredSendDefinition/create-response.xml +2 -2
  53. package/test/resources/9999999/triggeredSendDefinition/retrieve-TriggeredSendStatusINNew,Active,Inactive,Moved,Canceled-response.xml +4 -4
  54. package/test/resources/9999999/triggeredSendDefinition/update-response.xml +2 -2
  55. package/test/type.asset.test.js +3 -3
  56. package/test/type.automation.test.js +4 -4
  57. package/test/type.dataExtension.test.js +3 -3
  58. package/test/type.dataExtract.test.js +3 -3
  59. package/test/type.emailSend.test.js +2 -2
  60. package/test/type.event.test.js +3 -3
  61. package/test/type.fileTransfer.test.js +3 -3
  62. package/test/type.importFile.test.js +3 -3
  63. package/test/type.journey.test.js +19 -5
  64. package/test/type.mobileKeyword.test.js +2 -2
  65. package/test/type.mobileMessage.test.js +2 -2
  66. package/test/type.query.test.js +3 -3
  67. package/test/type.script.test.js +4 -4
  68. package/test/type.sendClassification.test.js +2 -2
  69. package/test/type.senderProfile.test.js +3 -3
  70. package/test/type.transactionalEmail.test.js +2 -2
  71. package/test/type.transactionalPush.test.js +2 -2
  72. package/test/type.transactionalSMS.test.js +2 -2
  73. package/test/type.triggeredSend.test.js +3 -3
  74. package/test/type.user.test.js +2 -2
  75. package/test/type.verification.test.js +2 -2
  76. package/types/mcdev.d.js +37 -0
package/lib/Builder.js CHANGED
@@ -102,10 +102,10 @@ saved
102
102
  * @param {string} businessUnit references credentials from properties.json
103
103
  * @param {string} selectedType supported metadata type
104
104
  * @param {string[]} keyArr customerkey of the metadata
105
- * @param {string} market market localizations
105
+ * @param {string[]} marketArr market localizations
106
106
  * @returns {Promise.<MultiMetadataTypeList>} -
107
107
  */
108
- static async buildTemplate(businessUnit, selectedType, keyArr, market) {
108
+ static async buildTemplate(businessUnit, selectedType, keyArr, marketArr) {
109
109
  const properties = await config.getProperties();
110
110
  if (!(await config.checkProperties(properties))) {
111
111
  return null;
@@ -119,12 +119,20 @@ saved
119
119
  );
120
120
  return;
121
121
  }
122
+ /** @type {TemplateMap} */
123
+ const templateVariables = {};
124
+ for (const market of marketArr) {
125
+ if (Util.checkMarket(market, properties)) {
126
+ Object.assign(templateVariables, properties.markets[market]);
127
+ } else {
128
+ // do not execute the rest of this method if a market was invalid
129
+ return;
130
+ }
131
+ }
122
132
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
123
133
  if (buObject !== null) {
124
134
  const builder = new Builder(properties, buObject);
125
- if (Util.checkMarket(market, properties)) {
126
- return builder._buildTemplate(selectedType, keyArr, properties.markets[market]);
127
- }
135
+ return builder._buildTemplate(selectedType, keyArr, templateVariables);
128
136
  }
129
137
  }
130
138
 
@@ -175,10 +183,10 @@ saved
175
183
  * @param {string} businessUnit references credentials from properties.json
176
184
  * @param {string} selectedType supported metadata type
177
185
  * @param {string[]} nameArr name of the metadata
178
- * @param {string} market market localizations
186
+ * @param {string[]} marketArr market localizations
179
187
  * @returns {Promise.<MultiMetadataTypeList>} -
180
188
  */
181
- static async buildDefinition(businessUnit, selectedType, nameArr, market) {
189
+ static async buildDefinition(businessUnit, selectedType, nameArr, marketArr) {
182
190
  const properties = await config.getProperties();
183
191
  if (!(await config.checkProperties(properties))) {
184
192
  return null;
@@ -192,12 +200,20 @@ saved
192
200
  );
193
201
  return;
194
202
  }
203
+ /** @type {TemplateMap} */
204
+ const templateVariables = {};
205
+ for (const market of marketArr) {
206
+ if (Util.checkMarket(market, properties)) {
207
+ Object.assign(templateVariables, properties.markets[market]);
208
+ } else {
209
+ // do not execute the rest of this method if a market was invalid
210
+ return;
211
+ }
212
+ }
195
213
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
196
214
  if (buObject !== null) {
197
215
  const builder = new Builder(properties, buObject);
198
- if (Util.checkMarket(market, properties)) {
199
- return builder._buildDefinition(selectedType, nameArr, properties.markets[market]);
200
- }
216
+ return builder._buildDefinition(selectedType, nameArr, templateVariables);
201
217
  }
202
218
  }
203
219
 
@@ -233,25 +249,28 @@ saved
233
249
  continue;
234
250
  }
235
251
  i++;
252
+ /** @type {string | string[] | string[][]} */
236
253
  const market = properties.marketList[listName][businessUnit];
237
- let marketList = [];
238
- if ('string' === typeof market) {
239
- marketList.push(market);
240
- } else {
241
- marketList = market;
242
- }
254
+ const marketList = 'string' === typeof market ? [market] : market;
255
+
243
256
  for (const market of marketList) {
244
- if (Util.checkMarket(market, properties)) {
245
- Util.logger.info(`Executing for '${businessUnit}': '${market}'`);
246
- // omitting "await" to speed up creation
247
- responseObj[businessUnit] ||= {};
248
- responseObj[businessUnit][market] = await this.buildDefinition(
249
- businessUnit,
250
- type,
251
- nameArr,
252
- market
253
- );
257
+ // one can now send multiple markets to buildTemplate/buildDefinition and hence that also needs to work for marketLists
258
+ const marketArr = 'string' === typeof market ? [market] : market;
259
+ for (const market of marketArr) {
260
+ if (!Util.checkMarket(market, properties)) {
261
+ return;
262
+ }
254
263
  }
264
+
265
+ Util.logger.info(`Executing for '${businessUnit}': '${marketArr.join('-')}'`);
266
+ // omitting "await" to speed up creation
267
+ responseObj[businessUnit] ||= {};
268
+ responseObj[businessUnit][marketArr.join('-')] = await this.buildDefinition(
269
+ businessUnit,
270
+ type,
271
+ nameArr,
272
+ marketArr
273
+ );
255
274
  }
256
275
  }
257
276
 
package/lib/cli.js CHANGED
@@ -363,6 +363,7 @@ yargs(hideBin(process.argv))
363
363
  })
364
364
  .option('marketFrom', {
365
365
  type: 'string',
366
+ array: true,
366
367
  alias: 'mf',
367
368
  group: 'Required parameters for build:',
368
369
  describe: 'market used for reverse building template',
@@ -370,6 +371,7 @@ yargs(hideBin(process.argv))
370
371
  })
371
372
  .option('marketTo', {
372
373
  type: 'string',
374
+ array: true,
373
375
  alias: 'mt',
374
376
  group: 'Required parameters for build:',
375
377
  describe: 'market used for building deployable definition',
@@ -440,6 +442,7 @@ yargs(hideBin(process.argv))
440
442
  })
441
443
  .option('market', {
442
444
  type: 'string',
445
+ array: true,
443
446
  group: 'Options for buildTemplate:',
444
447
  describe: 'market used for reverse building template',
445
448
  })
@@ -472,10 +475,15 @@ yargs(hideBin(process.argv))
472
475
  argv.BU,
473
476
  argv.TYPE,
474
477
  csvToArray(argv.KEY),
475
- argv.MARKET || argv.market
478
+ argv.MARKET ? [argv.MARKET] : argv.market
476
479
  );
477
480
  } else {
478
- Mcdev.buildTemplate(argv.BU, typeKeyCombo, null, argv.MARKET || argv.market);
481
+ Mcdev.buildTemplate(
482
+ argv.BU,
483
+ typeKeyCombo,
484
+ null,
485
+ argv.MARKET ? [argv.MARKET] : argv.market
486
+ );
479
487
  }
480
488
  }
481
489
  )
@@ -509,6 +517,7 @@ yargs(hideBin(process.argv))
509
517
  })
510
518
  .option('market', {
511
519
  type: 'string',
520
+ array: true,
512
521
  group: 'Options for buildDefinition:',
513
522
  describe: 'market used for building deployable definition',
514
523
  })
@@ -528,10 +537,15 @@ yargs(hideBin(process.argv))
528
537
  argv.BU,
529
538
  argv.TYPE,
530
539
  csvToArray(argv.FILENAME),
531
- argv.MARKET || argv.market
540
+ argv.MARKET ? [argv.MARKET] : argv.market
532
541
  );
533
542
  } else {
534
- Mcdev.buildDefinition(argv.BU, typeKeyCombo, null, argv.MARKET || argv.market);
543
+ Mcdev.buildDefinition(
544
+ argv.BU,
545
+ typeKeyCombo,
546
+ null,
547
+ argv.MARKET ? [argv.MARKET] : argv.market
548
+ );
535
549
  }
536
550
  }
537
551
  )
@@ -813,7 +827,7 @@ yargs(hideBin(process.argv))
813
827
  }
814
828
  )
815
829
  .command(
816
- ['pause <BU> [TYPE] [KEY]', 'p', 'stop'],
830
+ ['pause <BU> [TYPE] [KEY]', 'p'],
817
831
  'pauses the entity (automation etc.)',
818
832
  (yargs) =>
819
833
  yargs
@@ -860,6 +874,54 @@ yargs(hideBin(process.argv))
860
874
  }
861
875
  }
862
876
  )
877
+ .command(
878
+ ['stop <BU> [TYPE] [KEY]'],
879
+ 'stops the entity (journey etc.)',
880
+ (yargs) =>
881
+ yargs
882
+ .positional('BU', {
883
+ type: 'string',
884
+ describe: 'the business unit where to start an item',
885
+ })
886
+ .positional('TYPE', {
887
+ type: 'string',
888
+ describe: 'metadata type',
889
+ })
890
+ .positional('KEY', {
891
+ type: 'string',
892
+ describe: 'key(s) of the metadata component(s)',
893
+ })
894
+ .option('metadata', {
895
+ type: 'string',
896
+ array: true,
897
+ alias: 'm',
898
+ group: 'Options for pause:',
899
+ describe: 'type or type:key or type:i:id or type:n:name to fix',
900
+ })
901
+ .option('like', {
902
+ type: 'string',
903
+ group: 'Options for pause:',
904
+ describe:
905
+ 'filter metadata components (can include % as wildcard or _ for a single character)',
906
+ })
907
+ .check((argv) => {
908
+ if (!argv.TYPE && !argv.metadata) {
909
+ throw new Error(
910
+ 'Error: Either specify the metadata type after the BU or use --metadata'
911
+ );
912
+ }
913
+ return true;
914
+ }),
915
+ (argv) => {
916
+ Mcdev.setOptions(argv);
917
+ const typeKeyCombo = Mcdev.metadataToTypeKey(argv.metadata);
918
+ if ('undefined' === typeof typeKeyCombo) {
919
+ Mcdev.stop(argv.BU, csvToArray(argv.TYPE), csvToArray(argv.KEY));
920
+ } else {
921
+ Mcdev.stop(argv.BU, typeKeyCombo);
922
+ }
923
+ }
924
+ )
863
925
  .command(
864
926
  ['fixKeys <BU> [TYPE] [KEY]', 'fx'],
865
927
  'changes the key of the items to match the name',
package/lib/index.js CHANGED
@@ -16,6 +16,8 @@ import MetadataTypeDefinitions from './MetadataTypeDefinitions.js';
16
16
  import Retriever from './Retriever.js';
17
17
  import cache from './util/cache.js';
18
18
  import ReplaceContentBlockReference from './util/replaceContentBlockReference.js';
19
+ import pLimit from 'p-limit';
20
+ import path from 'node:path';
19
21
 
20
22
  import { confirm } from '@inquirer/prompts';
21
23
 
@@ -597,10 +599,10 @@ class Mcdev {
597
599
  *
598
600
  * @param {string} businessUnit references credentials from properties.json
599
601
  * @param {string | TypeKeyCombo} selectedTypes supported metadata type (single) or complex object
600
- * @param {string[] | string} [keyArr] Identifier of metadata
602
+ * @param {string[] | string} [keys] Identifier of metadata
601
603
  * @returns {Promise.<boolean>} true if successful, false otherwise
602
604
  */
603
- static async deleteByKey(businessUnit, selectedTypes, keyArr) {
605
+ static async deleteByKey(businessUnit, selectedTypes, keys) {
604
606
  Util.startLogger();
605
607
  Util.logger.info('mcdev:: delete');
606
608
 
@@ -609,9 +611,8 @@ class Mcdev {
609
611
 
610
612
  /** @typedef {TypeKeyCombo} */
611
613
  let selectedTypesObj;
612
- if ('string' === typeof keyArr) {
613
- keyArr = [keyArr];
614
- }
614
+ let keyArr;
615
+ keyArr = 'string' === typeof keys ? [keys] : keys;
615
616
  if ('string' === typeof selectedTypes) {
616
617
  selectedTypesArr = [selectedTypes];
617
618
  } else {
@@ -647,22 +648,31 @@ class Mcdev {
647
648
  Util.logger.error(`No keys set for ${type}`);
648
649
  return;
649
650
  }
650
- for (const key of keyArr) {
651
- MetadataTypeInfo[type].client = client;
652
-
653
- Util.logger.info(
654
- Util.getGrayMsg(` - Deleting ${type} ${key} on BU ${businessUnit}`)
655
- );
656
- try {
657
- MetadataTypeInfo[type].properties = properties;
658
- MetadataTypeInfo[type].buObject = buObject;
659
- const result = await MetadataTypeInfo[type].deleteByKey(key);
660
- status &&= result;
661
- } catch (ex) {
662
- Util.logger.errorStack(ex, ` - Deleting ${type} failed`);
663
- status = false;
664
- }
665
- }
651
+ MetadataTypeInfo[type].client = client;
652
+ MetadataTypeInfo[type].properties = properties;
653
+ MetadataTypeInfo[type].buObject = buObject;
654
+ const deleteLimit = pLimit(20);
655
+
656
+ await Promise.allSettled(
657
+ keyArr.map((key) =>
658
+ deleteLimit(async () => {
659
+ // Util.logger.info(
660
+ // Util.getGrayMsg(` - Deleting ${type} ${key} on BU ${businessUnit}`)
661
+ // );
662
+ try {
663
+ const result = await MetadataTypeInfo[type].deleteByKey(key);
664
+ status &&= result;
665
+ } catch (ex) {
666
+ Util.logger.errorStack(
667
+ ex,
668
+ ` - Deleting ${type} ${key} on BU ${businessUnit} failed`
669
+ );
670
+ status = false;
671
+ }
672
+ return status;
673
+ })
674
+ )
675
+ );
666
676
  }
667
677
 
668
678
  return status;
@@ -1086,8 +1096,8 @@ class Mcdev {
1086
1096
  * @param {string} businessUnitTemplate references credentials from properties.json
1087
1097
  * @param {string} businessUnitDefinition references credentials from properties.json
1088
1098
  * @param {TypeKeyCombo} typeKeyCombo limit retrieval to given metadata type
1089
- * @param {string} marketTemplate market localizations
1090
- * @param {string} marketDefinition market localizations
1099
+ * @param {string[]} marketTemplate market localizations
1100
+ * @param {string[]} marketDefinition market localizations
1091
1101
  * @param {boolean} [bulk] runs buildDefinitionBulk instead of buildDefinition; requires marketList to be defined and given via marketDefinition
1092
1102
  * @returns {Promise.<MultiMetadataTypeList | object>} response from buildDefinition
1093
1103
  */
@@ -1105,10 +1115,7 @@ class Mcdev {
1105
1115
  );
1106
1116
  return;
1107
1117
  }
1108
- const properties = await config.getProperties();
1109
- if (!Util.checkMarket(marketTemplate, properties)) {
1110
- return;
1111
- }
1118
+
1112
1119
  // check if types are valid
1113
1120
  for (const type of Object.keys(typeKeyCombo)) {
1114
1121
  if (!Util._isValidType(type)) {
@@ -1123,7 +1130,7 @@ class Mcdev {
1123
1130
  Util.logger.info('mcdev:: Build Template & Build Definition');
1124
1131
  await this.buildTemplate(businessUnitTemplate, typeKeyCombo, null, marketTemplate);
1125
1132
  return bulk
1126
- ? this.buildDefinitionBulk(marketDefinition, typeKeyCombo, null)
1133
+ ? this.buildDefinitionBulk(marketDefinition[0], typeKeyCombo, null)
1127
1134
  : this.buildDefinition(businessUnitDefinition, typeKeyCombo, null, marketDefinition);
1128
1135
  }
1129
1136
 
@@ -1133,17 +1140,20 @@ class Mcdev {
1133
1140
  * @param {string} businessUnit references credentials from properties.json
1134
1141
  * @param {string | TypeKeyCombo} selectedTypes limit retrieval to given metadata type
1135
1142
  * @param {string[] | undefined} keyArr customerkey of the metadata
1136
- * @param {string} market market localizations
1143
+ * @param {string[]} marketArr market localizations
1137
1144
  * @returns {Promise.<MultiMetadataTypeList>} -
1138
1145
  */
1139
- static async buildTemplate(businessUnit, selectedTypes, keyArr, market) {
1146
+ static async buildTemplate(businessUnit, selectedTypes, keyArr, marketArr) {
1140
1147
  this.#welcomeMessage();
1141
1148
 
1142
1149
  Util.startLogger();
1143
1150
  Util.logger.info('mcdev:: Build Template from retrieved files');
1144
1151
  const properties = await config.getProperties();
1145
- if (!Util.checkMarket(market, properties)) {
1146
- return;
1152
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
1153
+ for (const market of marketArr) {
1154
+ if (!Util.checkMarket(market, properties)) {
1155
+ return;
1156
+ }
1147
1157
  }
1148
1158
  if ('string' === typeof selectedTypes) {
1149
1159
  // ensure we have TypeKeyCombo here
@@ -1163,6 +1173,52 @@ class Mcdev {
1163
1173
  if (!Util.OPTIONS.dependencies) {
1164
1174
  await this._reRetrieve(businessUnit, false, selectedTypes);
1165
1175
  }
1176
+ // convert names to keys
1177
+ const retrieveDir = File.normalizePath([
1178
+ properties.directories.retrieve,
1179
+ buObject.credential,
1180
+ buObject.businessUnit,
1181
+ ]);
1182
+ for (const type of Object.keys(selectedTypes)) {
1183
+ const keyArr = selectedTypes[type];
1184
+ if (keyArr.some((key) => key.startsWith('name:'))) {
1185
+ // at least one key was provided as a name -> load all files from disk to try and find that key
1186
+ const builTemplateCache = Object.values(
1187
+ await MetadataTypeInfo[type].getJsonFromFS(retrieveDir + path.sep + type)
1188
+ );
1189
+ selectedTypes[type] = keyArr
1190
+ .map((key) => {
1191
+ if (key.startsWith('name:')) {
1192
+ // key was defined by name. try and find matching item on disk
1193
+ const name = key.slice(5);
1194
+ const foundKeysByName = builTemplateCache
1195
+ .filter(
1196
+ (item) =>
1197
+ name == item[MetadataTypeInfo[type].definition.nameField]
1198
+ )
1199
+ .map((item) => item[MetadataTypeInfo[type].definition.keyField]);
1200
+ if (foundKeysByName.length === 1) {
1201
+ key = foundKeysByName[0];
1202
+ Util.logger.debug(
1203
+ `- found ${type} key '${key}' for name '${name}'`
1204
+ );
1205
+ return key;
1206
+ } else if (foundKeysByName.length > 1) {
1207
+ Util.logger.error(
1208
+ `Found multiple keys (${foundKeysByName.join(', ')}) for name: ${key}`
1209
+ );
1210
+ return;
1211
+ } else {
1212
+ Util.logger.error(`Could not find any keys for name: ${name}`);
1213
+ return;
1214
+ }
1215
+ } else {
1216
+ return key;
1217
+ }
1218
+ })
1219
+ .filter(Boolean);
1220
+ }
1221
+ }
1166
1222
 
1167
1223
  // if dependencies are enabled, we need to search for them and add them to our
1168
1224
  await this.addDependencies(businessUnit, selectedTypes);
@@ -1175,19 +1231,11 @@ class Mcdev {
1175
1231
  businessUnit,
1176
1232
  type,
1177
1233
  selectedTypes[type],
1178
- market
1234
+ marketArr
1179
1235
  );
1180
1236
  returnObj[type] = result[type];
1181
1237
  }
1182
1238
  Util.logger.info(`Templated ${Util.getTypeKeyCount(returnObj)} items`);
1183
- if (Util.OPTIONS.dependencies) {
1184
- Util.logger.info(
1185
- `You can now run buildDefinition to create the deployment package. Don't forget to adjust the BU and market!`
1186
- );
1187
- Util.logger.info(
1188
- ` mcdev bd ${businessUnit} ${Util.convertTypeKeyToCli(selectedTypes)} --market ${market}`
1189
- );
1190
- }
1191
1239
  return returnObj;
1192
1240
  }
1193
1241
 
@@ -1223,17 +1271,19 @@ class Mcdev {
1223
1271
  * @param {string} businessUnit references credentials from properties.json
1224
1272
  * @param {string | TypeKeyCombo} selectedTypes limit retrieval to given metadata type
1225
1273
  * @param {string[] | undefined} nameArr name of the metadata
1226
- * @param {string} market market localizations
1274
+ * @param {string[]} marketArr market localizations
1227
1275
  * @returns {Promise.<MultiMetadataTypeList>} -
1228
1276
  */
1229
- static async buildDefinition(businessUnit, selectedTypes, nameArr, market) {
1277
+ static async buildDefinition(businessUnit, selectedTypes, nameArr, marketArr) {
1230
1278
  this.#welcomeMessage();
1231
1279
 
1232
1280
  Util.startLogger();
1233
1281
  Util.logger.info('mcdev:: Build Definition from Template');
1234
1282
  const properties = await config.getProperties();
1235
- if (!Util.checkMarket(market, properties)) {
1236
- return;
1283
+ for (const market of marketArr) {
1284
+ if (!Util.checkMarket(market, properties)) {
1285
+ return;
1286
+ }
1237
1287
  }
1238
1288
  if ('string' === typeof selectedTypes) {
1239
1289
  // ensure we have TypeKeyCombo here
@@ -1257,7 +1307,7 @@ class Mcdev {
1257
1307
  businessUnit,
1258
1308
  type,
1259
1309
  selectedTypes[type],
1260
- market
1310
+ marketArr
1261
1311
  );
1262
1312
  returnObj[type] = result[type];
1263
1313
  }
@@ -1389,6 +1439,18 @@ class Mcdev {
1389
1439
  return this.#runMethod('pause', businessUnit, selectedTypes, keys);
1390
1440
  }
1391
1441
 
1442
+ /**
1443
+ * stop an item
1444
+ *
1445
+ * @param {string} businessUnit name of BU
1446
+ * @param {string[] | TypeKeyCombo} [selectedTypes] limit to given metadata types
1447
+ * @param {string[]} [keys] customerkey of the metadata
1448
+ * @returns {Promise.<Object.<string, Object.<string, string[]>>>} key: business unit name, key2: type, value: list of affected item keys
1449
+ */
1450
+ static async stop(businessUnit, selectedTypes, keys) {
1451
+ return this.#runMethod('stop', businessUnit, selectedTypes, keys);
1452
+ }
1453
+
1392
1454
  /**
1393
1455
  * Updates the key to match the name field
1394
1456
  *
@@ -1564,7 +1626,7 @@ class Mcdev {
1564
1626
  /**
1565
1627
  * run a method across BUs
1566
1628
  *
1567
- * @param {'execute'|'pause'|'publish'|'fixKeys'|'replaceCbReference'} methodName what to run
1629
+ * @param {'execute'|'pause'|'stop'|'publish'|'fixKeys'|'replaceCbReference'} methodName what to run
1568
1630
  * @param {string} businessUnit name of BU
1569
1631
  * @param {string[] | TypeKeyCombo} [selectedTypes] limit to given metadata types
1570
1632
  * @param {string[]} [keys] customerkey of the metadata
@@ -1594,6 +1656,13 @@ class Mcdev {
1594
1656
  checkMetadataSupport = true;
1595
1657
  break;
1596
1658
  }
1659
+ case 'stop': {
1660
+ lang_past = 'stopped';
1661
+ lang_present = 'stopping';
1662
+ requireKeyOrLike = true;
1663
+ checkMetadataSupport = true;
1664
+ break;
1665
+ }
1597
1666
  case 'publish': {
1598
1667
  lang_past = 'published';
1599
1668
  lang_present = 'publishing';
@@ -1768,7 +1837,7 @@ class Mcdev {
1768
1837
  /**
1769
1838
  * helper for Mcdev.#runMethod
1770
1839
  *
1771
- * @param {'execute'|'pause'|'publish'|'fixKeys'|'replaceCbReference'} methodName what to run
1840
+ * @param {'execute'|'pause'|'stop'|'publish'|'fixKeys'|'replaceCbReference'} methodName what to run
1772
1841
  * @param {string} cred name of Credential
1773
1842
  * @param {string} bu name of BU
1774
1843
  * @param {string} [type] limit execution to given metadata type
@@ -2089,7 +2089,7 @@ class Asset extends MetadataType {
2089
2089
  id = key ? await this._getIdForSingleRetrieve(key) : null;
2090
2090
  }
2091
2091
  if (!id) {
2092
- Util.logger.error(` - ${this.definition.type} not found`);
2092
+ Util.logger.error(` - ${this.definition.type} ${key} not found`);
2093
2093
  return false;
2094
2094
  }
2095
2095
  return super.deleteByKeyREST('/asset/v1/content/assets/' + id, key);
@@ -1612,17 +1612,17 @@ class Automation extends MetadataType {
1612
1612
  /**
1613
1613
  * Delete a metadata item from the specified business unit
1614
1614
  *
1615
- * @param {string} customerKey Identifier of data extension
1615
+ * @param {string} key Identifier of data extension
1616
1616
  * @returns {Promise.<boolean>} deletion success status
1617
1617
  */
1618
- static async deleteByKey(customerKey) {
1618
+ static async deleteByKey(key) {
1619
1619
  // the delete endpoint returns a general exception if the automation does not exist; handle it gracefully instead by adding a retrieve first
1620
- const objectId = customerKey ? await this.#getObjectIdForSingleRetrieve(customerKey) : null;
1620
+ const objectId = key ? await this.#getObjectIdForSingleRetrieve(key) : null;
1621
1621
  if (!objectId) {
1622
- Util.logger.error(` - automation not found`);
1622
+ Util.logger.error(` - automation ${key} not found`);
1623
1623
  return false;
1624
1624
  }
1625
- return super.deleteByKeySOAP(customerKey, 'CustomerKey');
1625
+ return super.deleteByKeySOAP(key, 'CustomerKey');
1626
1626
  }
1627
1627
 
1628
1628
  /**
@@ -190,7 +190,7 @@ class DataExtract extends MetadataType {
190
190
  // delete only works with the query's object id
191
191
  const objectId = customerKey ? await this._getObjectIdForSingleRetrieve(customerKey) : null;
192
192
  if (!objectId) {
193
- Util.logger.error(` - dataExtract not found`);
193
+ Util.logger.error(` - dataExtract ${customerKey} not found`);
194
194
  return false;
195
195
  }
196
196
  return super.deleteByKeyREST('/automation/v1/dataextracts/' + objectId, customerKey);