mcdev 7.7.1 → 7.8.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 (199) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
  2. package/.github/workflows/coverage-base-update.yml +2 -2
  3. package/.github/workflows/coverage.yml +1 -1
  4. package/@types/lib/Builder.d.ts.map +1 -1
  5. package/@types/lib/index.d.ts +1 -0
  6. package/@types/lib/index.d.ts.map +1 -1
  7. package/@types/lib/metadataTypes/Asset.d.ts +1 -0
  8. package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
  9. package/@types/lib/metadataTypes/AttributeGroup.d.ts +1 -0
  10. package/@types/lib/metadataTypes/AttributeGroup.d.ts.map +1 -1
  11. package/@types/lib/metadataTypes/AttributeSet.d.ts +1 -0
  12. package/@types/lib/metadataTypes/AttributeSet.d.ts.map +1 -1
  13. package/@types/lib/metadataTypes/Automation.d.ts +1 -0
  14. package/@types/lib/metadataTypes/Automation.d.ts.map +1 -1
  15. package/@types/lib/metadataTypes/Campaign.d.ts +1 -0
  16. package/@types/lib/metadataTypes/Campaign.d.ts.map +1 -1
  17. package/@types/lib/metadataTypes/ContentArea.d.ts +1 -0
  18. package/@types/lib/metadataTypes/ContentArea.d.ts.map +1 -1
  19. package/@types/lib/metadataTypes/DataExtension.d.ts +1 -0
  20. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
  21. package/@types/lib/metadataTypes/DataExtensionField.d.ts +1 -0
  22. package/@types/lib/metadataTypes/DataExtensionField.d.ts.map +1 -1
  23. package/@types/lib/metadataTypes/DataExtensionTemplate.d.ts +1 -0
  24. package/@types/lib/metadataTypes/DataExtensionTemplate.d.ts.map +1 -1
  25. package/@types/lib/metadataTypes/DataExtract.d.ts +1 -0
  26. package/@types/lib/metadataTypes/DataExtract.d.ts.map +1 -1
  27. package/@types/lib/metadataTypes/DataExtractType.d.ts +1 -0
  28. package/@types/lib/metadataTypes/DataExtractType.d.ts.map +1 -1
  29. package/@types/lib/metadataTypes/DeliveryProfile.d.ts +1 -0
  30. package/@types/lib/metadataTypes/DeliveryProfile.d.ts.map +1 -1
  31. package/@types/lib/metadataTypes/Discovery.d.ts +1 -0
  32. package/@types/lib/metadataTypes/Discovery.d.ts.map +1 -1
  33. package/@types/lib/metadataTypes/DomainVerification.d.ts +1 -0
  34. package/@types/lib/metadataTypes/DomainVerification.d.ts.map +1 -1
  35. package/@types/lib/metadataTypes/Email.d.ts +1 -0
  36. package/@types/lib/metadataTypes/Email.d.ts.map +1 -1
  37. package/@types/lib/metadataTypes/EmailSend.d.ts +1 -0
  38. package/@types/lib/metadataTypes/EmailSend.d.ts.map +1 -1
  39. package/@types/lib/metadataTypes/Event.d.ts +1 -0
  40. package/@types/lib/metadataTypes/Event.d.ts.map +1 -1
  41. package/@types/lib/metadataTypes/FileLocation.d.ts +1 -0
  42. package/@types/lib/metadataTypes/FileLocation.d.ts.map +1 -1
  43. package/@types/lib/metadataTypes/FileTransfer.d.ts +1 -0
  44. package/@types/lib/metadataTypes/FileTransfer.d.ts.map +1 -1
  45. package/@types/lib/metadataTypes/Filter.d.ts +1 -0
  46. package/@types/lib/metadataTypes/Filter.d.ts.map +1 -1
  47. package/@types/lib/metadataTypes/Folder.d.ts +1 -0
  48. package/@types/lib/metadataTypes/Folder.d.ts.map +1 -1
  49. package/@types/lib/metadataTypes/ImportFile.d.ts +1 -0
  50. package/@types/lib/metadataTypes/ImportFile.d.ts.map +1 -1
  51. package/@types/lib/metadataTypes/Journey.d.ts +7 -1
  52. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  53. package/@types/lib/metadataTypes/List.d.ts +1 -0
  54. package/@types/lib/metadataTypes/List.d.ts.map +1 -1
  55. package/@types/lib/metadataTypes/MetadataType.d.ts +3 -3
  56. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
  57. package/@types/lib/metadataTypes/MobileCode.d.ts +1 -0
  58. package/@types/lib/metadataTypes/MobileCode.d.ts.map +1 -1
  59. package/@types/lib/metadataTypes/MobileKeyword.d.ts +1 -0
  60. package/@types/lib/metadataTypes/MobileKeyword.d.ts.map +1 -1
  61. package/@types/lib/metadataTypes/MobileMessage.d.ts +1 -0
  62. package/@types/lib/metadataTypes/MobileMessage.d.ts.map +1 -1
  63. package/@types/lib/metadataTypes/Query.d.ts +1 -0
  64. package/@types/lib/metadataTypes/Query.d.ts.map +1 -1
  65. package/@types/lib/metadataTypes/Role.d.ts +1 -0
  66. package/@types/lib/metadataTypes/Role.d.ts.map +1 -1
  67. package/@types/lib/metadataTypes/Script.d.ts +1 -0
  68. package/@types/lib/metadataTypes/Script.d.ts.map +1 -1
  69. package/@types/lib/metadataTypes/SendClassification.d.ts +1 -0
  70. package/@types/lib/metadataTypes/SendClassification.d.ts.map +1 -1
  71. package/@types/lib/metadataTypes/SenderProfile.d.ts +1 -0
  72. package/@types/lib/metadataTypes/SenderProfile.d.ts.map +1 -1
  73. package/@types/lib/metadataTypes/TransactionalEmail.d.ts +1 -0
  74. package/@types/lib/metadataTypes/TransactionalEmail.d.ts.map +1 -1
  75. package/@types/lib/metadataTypes/TransactionalMessage.d.ts +1 -0
  76. package/@types/lib/metadataTypes/TransactionalMessage.d.ts.map +1 -1
  77. package/@types/lib/metadataTypes/TransactionalPush.d.ts +1 -0
  78. package/@types/lib/metadataTypes/TransactionalPush.d.ts.map +1 -1
  79. package/@types/lib/metadataTypes/TransactionalSMS.d.ts +1 -0
  80. package/@types/lib/metadataTypes/TransactionalSMS.d.ts.map +1 -1
  81. package/@types/lib/metadataTypes/TriggeredSend.d.ts +1 -0
  82. package/@types/lib/metadataTypes/TriggeredSend.d.ts.map +1 -1
  83. package/@types/lib/metadataTypes/User.d.ts +1 -0
  84. package/@types/lib/metadataTypes/User.d.ts.map +1 -1
  85. package/@types/lib/metadataTypes/Verification.d.ts +1 -0
  86. package/@types/lib/metadataTypes/Verification.d.ts.map +1 -1
  87. package/@types/lib/metadataTypes/definitions/Asset.definition.d.ts +1 -0
  88. package/@types/lib/metadataTypes/definitions/AttributeGroup.definition.d.ts +1 -0
  89. package/@types/lib/metadataTypes/definitions/AttributeSet.definition.d.ts +1 -0
  90. package/@types/lib/metadataTypes/definitions/Automation.definition.d.ts +1 -0
  91. package/@types/lib/metadataTypes/definitions/Campaign.definition.d.ts +1 -0
  92. package/@types/lib/metadataTypes/definitions/ContentArea.definition.d.ts +1 -0
  93. package/@types/lib/metadataTypes/definitions/DataExtension.definition.d.ts +1 -0
  94. package/@types/lib/metadataTypes/definitions/DataExtensionField.definition.d.ts +1 -0
  95. package/@types/lib/metadataTypes/definitions/DataExtensionTemplate.definition.d.ts +1 -0
  96. package/@types/lib/metadataTypes/definitions/DataExtract.definition.d.ts +1 -0
  97. package/@types/lib/metadataTypes/definitions/DataExtractType.definition.d.ts +1 -0
  98. package/@types/lib/metadataTypes/definitions/DeliveryProfile.definition.d.ts +1 -0
  99. package/@types/lib/metadataTypes/definitions/Discovery.definition.d.ts +1 -0
  100. package/@types/lib/metadataTypes/definitions/DomainVerification.definition.d.ts +1 -0
  101. package/@types/lib/metadataTypes/definitions/Email.definition.d.ts +1 -0
  102. package/@types/lib/metadataTypes/definitions/EmailSend.definition.d.ts +1 -0
  103. package/@types/lib/metadataTypes/definitions/Event.definition.d.ts +1 -0
  104. package/@types/lib/metadataTypes/definitions/FileLocation.definition.d.ts +1 -0
  105. package/@types/lib/metadataTypes/definitions/FileTransfer.definition.d.ts +1 -0
  106. package/@types/lib/metadataTypes/definitions/Filter.definition.d.ts +1 -0
  107. package/@types/lib/metadataTypes/definitions/Folder.definition.d.ts +1 -0
  108. package/@types/lib/metadataTypes/definitions/ImportFile.definition.d.ts +1 -0
  109. package/@types/lib/metadataTypes/definitions/Journey.definition.d.ts +7 -0
  110. package/@types/lib/metadataTypes/definitions/List.definition.d.ts +1 -0
  111. package/@types/lib/metadataTypes/definitions/MobileCode.definition.d.ts +1 -0
  112. package/@types/lib/metadataTypes/definitions/MobileKeyword.definition.d.ts +1 -0
  113. package/@types/lib/metadataTypes/definitions/MobileMessage.definition.d.ts +1 -0
  114. package/@types/lib/metadataTypes/definitions/Query.definition.d.ts +1 -0
  115. package/@types/lib/metadataTypes/definitions/Role.definition.d.ts +1 -0
  116. package/@types/lib/metadataTypes/definitions/Script.definition.d.ts +1 -0
  117. package/@types/lib/metadataTypes/definitions/SendClassification.definition.d.ts +1 -0
  118. package/@types/lib/metadataTypes/definitions/SenderProfile.definition.d.ts +1 -0
  119. package/@types/lib/metadataTypes/definitions/TransactionalEmail.definition.d.ts +1 -0
  120. package/@types/lib/metadataTypes/definitions/TransactionalMessage.definition.d.ts +1 -0
  121. package/@types/lib/metadataTypes/definitions/TransactionalPush.definition.d.ts +1 -0
  122. package/@types/lib/metadataTypes/definitions/TransactionalSMS.definition.d.ts +1 -0
  123. package/@types/lib/metadataTypes/definitions/TriggeredSend.definition.d.ts +1 -0
  124. package/@types/lib/metadataTypes/definitions/User.definition.d.ts +1 -0
  125. package/@types/lib/metadataTypes/definitions/Verification.definition.d.ts +1 -0
  126. package/@types/lib/util/config.d.ts.map +1 -1
  127. package/@types/lib/util/devops.d.ts +9 -0
  128. package/@types/lib/util/devops.d.ts.map +1 -1
  129. package/@types/lib/util/util.d.ts +2 -2
  130. package/@types/lib/util/util.d.ts.map +1 -1
  131. package/@types/lib/util/validations.d.ts +3 -2
  132. package/@types/lib/util/validations.d.ts.map +1 -1
  133. package/@types/types/mcdev.d.d.ts +6 -0
  134. package/@types/types/mcdev.d.d.ts.map +1 -1
  135. package/boilerplate/config.json +2 -1
  136. package/boilerplate/files/.vscode/extensions.json +1 -0
  137. package/boilerplate/forcedUpdates.json +4 -0
  138. package/lib/Builder.js +7 -0
  139. package/lib/cli.js +5 -0
  140. package/lib/index.js +57 -44
  141. package/lib/metadataTypes/Asset.js +32 -1
  142. package/lib/metadataTypes/Automation.js +5 -2
  143. package/lib/metadataTypes/DataExtension.js +5 -1
  144. package/lib/metadataTypes/Event.js +1 -0
  145. package/lib/metadataTypes/ImportFile.js +1 -0
  146. package/lib/metadataTypes/Journey.js +3 -1
  147. package/lib/metadataTypes/MetadataType.js +45 -9
  148. package/lib/metadataTypes/MobileKeyword.js +1 -0
  149. package/lib/metadataTypes/MobileMessage.js +1 -0
  150. package/lib/metadataTypes/TransactionalMessage.js +3 -0
  151. package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
  152. package/lib/metadataTypes/definitions/AttributeGroup.definition.js +1 -0
  153. package/lib/metadataTypes/definitions/AttributeSet.definition.js +1 -0
  154. package/lib/metadataTypes/definitions/Automation.definition.js +1 -0
  155. package/lib/metadataTypes/definitions/Campaign.definition.js +1 -0
  156. package/lib/metadataTypes/definitions/ContentArea.definition.js +1 -0
  157. package/lib/metadataTypes/definitions/DataExtension.definition.js +1 -0
  158. package/lib/metadataTypes/definitions/DataExtensionField.definition.js +1 -0
  159. package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +1 -0
  160. package/lib/metadataTypes/definitions/DataExtract.definition.js +1 -0
  161. package/lib/metadataTypes/definitions/DataExtractType.definition.js +1 -0
  162. package/lib/metadataTypes/definitions/DeliveryProfile.definition.js +1 -0
  163. package/lib/metadataTypes/definitions/Discovery.definition.js +1 -0
  164. package/lib/metadataTypes/definitions/DomainVerification.definition.js +1 -0
  165. package/lib/metadataTypes/definitions/Email.definition.js +1 -0
  166. package/lib/metadataTypes/definitions/EmailSend.definition.js +1 -0
  167. package/lib/metadataTypes/definitions/Event.definition.js +1 -0
  168. package/lib/metadataTypes/definitions/FileLocation.definition.js +1 -0
  169. package/lib/metadataTypes/definitions/FileTransfer.definition.js +1 -0
  170. package/lib/metadataTypes/definitions/Filter.definition.js +1 -0
  171. package/lib/metadataTypes/definitions/Folder.definition.js +1 -0
  172. package/lib/metadataTypes/definitions/ImportFile.definition.js +1 -0
  173. package/lib/metadataTypes/definitions/Journey.definition.js +1 -0
  174. package/lib/metadataTypes/definitions/List.definition.js +1 -0
  175. package/lib/metadataTypes/definitions/MobileCode.definition.js +1 -0
  176. package/lib/metadataTypes/definitions/MobileKeyword.definition.js +1 -0
  177. package/lib/metadataTypes/definitions/MobileMessage.definition.js +1 -0
  178. package/lib/metadataTypes/definitions/Query.definition.js +1 -0
  179. package/lib/metadataTypes/definitions/Role.definition.js +1 -0
  180. package/lib/metadataTypes/definitions/Script.definition.js +1 -0
  181. package/lib/metadataTypes/definitions/SendClassification.definition.js +1 -0
  182. package/lib/metadataTypes/definitions/SenderProfile.definition.js +1 -0
  183. package/lib/metadataTypes/definitions/TransactionalEmail.definition.js +1 -0
  184. package/lib/metadataTypes/definitions/TransactionalMessage.definition.js +1 -0
  185. package/lib/metadataTypes/definitions/TransactionalPush.definition.js +1 -0
  186. package/lib/metadataTypes/definitions/TransactionalSMS.definition.js +1 -0
  187. package/lib/metadataTypes/definitions/TriggeredSend.definition.js +1 -0
  188. package/lib/metadataTypes/definitions/User.definition.js +1 -0
  189. package/lib/metadataTypes/definitions/Verification.definition.js +1 -0
  190. package/lib/util/config.js +3 -1
  191. package/lib/util/devops.js +131 -1
  192. package/lib/util/util.js +4 -3
  193. package/lib/util/validations.js +25 -3
  194. package/package.json +9 -9
  195. package/test/general.test.js +28 -0
  196. package/test/mockRoot/.mcdev-validations.js +40 -12
  197. package/test/mockRoot/.mcdevrc.json +23 -1
  198. package/test/utils.js +10 -38
  199. package/types/mcdev.d.js +7 -4
@@ -266,7 +266,22 @@ const DevOps = {
266
266
  'Please note that deletions have to be done manually on the SFMC website.'
267
267
  );
268
268
  }
269
- Util.logger.info(`Saved report in ${path.join(directoryDeltaPkg, 'delta_package.md')}`);
269
+ Util.logger.info(
270
+ `Saved report in ./${directoryDeltaPkg}${Util.logFileName}-delta_package.md`
271
+ );
272
+
273
+ // const deletedTypeKeys = {};
274
+ // for (const file of delta.filter((file) => file.gitAction === 'delete')) {
275
+ // if (deletedTypeKeys[file.type]) {
276
+ // deletedTypeKeys[file.type].add(file.externalKey);
277
+ // } else {
278
+ // deletedTypeKeys[file.type] = new Set([file.externalKey]);
279
+ // }
280
+ // }
281
+ // for (const deletedType in deletedTypeKeys) {
282
+ // const keyArr = [...deletedTypeKeys[deletedType]];
283
+ // Util.logger.warn(`Found deleted ${deletedType} keys: ${keyArr.join(', ')}`);
284
+ // }
270
285
 
271
286
  // Copy filtered list of files into deploy directory
272
287
  // only do that if we do not use templating
@@ -506,6 +521,17 @@ const DevOps = {
506
521
  }
507
522
  }
508
523
  for (const buFrom in buTypeDelta) {
524
+ for (const type of Object.keys(buTypeDelta[buFrom])) {
525
+ if (
526
+ Array.isArray(properties.metaDataTypes.createDeltaPkg) &&
527
+ !properties.metaDataTypes.createDeltaPkg.includes(type)
528
+ ) {
529
+ Util.logger.warn(
530
+ `Skipping ${type} for build based on config.metaDataTypes.createDeltaPkg: ${buTypeDelta[buFrom][type].join(', ')}`
531
+ );
532
+ delete buTypeDelta[buFrom][type];
533
+ }
534
+ }
509
535
  // Run buildTemplate for each business unit for each type
510
536
  await mcdev.build(
511
537
  buFrom,
@@ -516,6 +542,7 @@ const DevOps = {
516
542
  true
517
543
  );
518
544
  }
545
+ this._generateDeleteInstructions(delta, sourceMarket, properties, targetMlName);
519
546
  }
520
547
  if (!deltaDeployAll.length) {
521
548
  Util.logger.error(
@@ -575,6 +602,109 @@ const DevOps = {
575
602
  MetadataType[metadataType].buObject = buObject;
576
603
  return MetadataType[metadataType].getFilesToCommit(keyArr);
577
604
  },
605
+ /**
606
+ * helper for {@link DevOps.buildDeltaDefinitions}
607
+ *
608
+ * @param {DeltaPkgItem[]} delta git delta
609
+ * @param {string} sourceMarket market for the source BU
610
+ * @param {Mcdevrc} properties mcdev config
611
+ * @param {string} targetMlName marketList used to build for the target BU
612
+ */
613
+ _generateDeleteInstructions(delta, sourceMarket, properties, targetMlName) {
614
+ const deltaDelete = delta
615
+ .filter((file) => file.gitAction === 'delete')
616
+ .filter(
617
+ // in case of assets we need to be careful because extracted code files might get deleted but that doesnt mean the asset was
618
+ (file) =>
619
+ file.type !== 'asset' || (file.type === 'asset' && file.file.endsWith('.json'))
620
+ );
621
+ const buTypeDeltaDelete = {}; // for bt, with BU info
622
+
623
+ for (const file of deltaDelete) {
624
+ const buFrom = `${file._credential}/${file._businessUnit}`;
625
+ if (!buTypeDeltaDelete[buFrom]) {
626
+ // init object
627
+ /** @type {TypeKeyCombo} */
628
+ buTypeDeltaDelete[buFrom] = {};
629
+ }
630
+ if (!buTypeDeltaDelete[buFrom][file.type]) {
631
+ // init array
632
+ buTypeDeltaDelete[buFrom][file.type] = [];
633
+ }
634
+ if (!buTypeDeltaDelete[buFrom][file.type].includes(file.externalKey)) {
635
+ buTypeDeltaDelete[buFrom][file.type].push(file.externalKey);
636
+ }
637
+ }
638
+ const deleteByBU = {};
639
+ for (const buFrom in buTypeDeltaDelete) {
640
+ /** @type {TemplateMap} */
641
+ const sourceVariables = {};
642
+ if (Util.checkMarket(sourceMarket, properties)) {
643
+ Object.assign(sourceVariables, properties.markets[sourceMarket]);
644
+ }
645
+ const typeKeyFrom = {};
646
+ const typeKeyDelete = {};
647
+ for (const type of Object.keys(buTypeDeltaDelete[buFrom])) {
648
+ if (
649
+ Array.isArray(properties.metaDataTypes.createDeltaPkg) &&
650
+ !properties.metaDataTypes.createDeltaPkg.includes(type)
651
+ ) {
652
+ Util.logger.warn(
653
+ `Skipping ${type} for delete based on config.metaDataTypes.createDeltaPkg: ${buTypeDeltaDelete[buFrom][type].join(', ')}`
654
+ );
655
+ delete buTypeDeltaDelete[buFrom][type];
656
+ continue;
657
+ }
658
+ typeKeyFrom[type] = new Set();
659
+ if (Object.keys(sourceVariables).length > 0) {
660
+ for (let i = 0; i < buTypeDeltaDelete[buFrom][type].length; i++) {
661
+ // add templating variables to keys
662
+ typeKeyFrom[type].add(
663
+ Util.replaceByObject(
664
+ buTypeDeltaDelete[buFrom][type][i],
665
+ sourceVariables
666
+ )
667
+ );
668
+ }
669
+ }
670
+ typeKeyFrom[type] = Array.from(typeKeyFrom[type]);
671
+ for (const targetBu in properties.marketList[targetMlName]) {
672
+ typeKeyDelete[targetBu] ||= {};
673
+ typeKeyDelete[targetBu][type] ||= [];
674
+ deleteByBU[targetBu] || {};
675
+ const market = properties.marketList[targetMlName][targetBu];
676
+ const markets = 'string' === typeof market ? [market] : market;
677
+ for (const marketArr of markets) {
678
+ const templateVariables = {};
679
+ for (const market of Array.isArray(marketArr) ? marketArr : [marketArr]) {
680
+ if (Util.checkMarket(market, properties)) {
681
+ Object.assign(templateVariables, properties.markets[market]);
682
+ }
683
+ }
684
+ typeKeyDelete[targetBu][type].push(
685
+ ...typeKeyFrom[type].map((key) =>
686
+ MetadataType[type].applyTemplateValues(key, templateVariables)
687
+ )
688
+ );
689
+ }
690
+ }
691
+ }
692
+ for (const targetBu in typeKeyDelete) {
693
+ const metadataList = [];
694
+ for (const type in typeKeyDelete[targetBu]) {
695
+ for (const key of typeKeyDelete[targetBu][type]) {
696
+ metadataList.push(type + ':"' + key + '"');
697
+ }
698
+ }
699
+ if (metadataList.length) {
700
+ Util.logger.warn(
701
+ `Run the following to delete ${metadataList.length} potentially existing old files in ${targetBu}`
702
+ );
703
+ Util.logger.warn(`mcdev delete ${targetBu} -m ${metadataList.join(' ')}`);
704
+ }
705
+ }
706
+ }
707
+ },
578
708
  };
579
709
 
580
710
  export default DevOps;
package/lib/util/util.js CHANGED
@@ -309,14 +309,15 @@ export const Util = {
309
309
  /**
310
310
  * helper for getDefaultProperties()
311
311
  *
312
+ * @param {string} field relevant field in type definition
312
313
  * @returns {string[]} type choices
313
314
  */
314
- getRetrieveTypeChoices() {
315
+ getTypeChoices(field) {
315
316
  const typeChoices = [];
316
317
  for (const el in MetadataDefinitions) {
317
318
  if (
318
- Array.isArray(MetadataDefinitions[el].typeRetrieveByDefault) ||
319
- MetadataDefinitions[el].typeRetrieveByDefault === true
319
+ Array.isArray(MetadataDefinitions[el][field]) ||
320
+ MetadataDefinitions[el][field] === true
320
321
  ) {
321
322
  // complex types like assets are saved as array but to ease upgradability we
322
323
  // save the main type only unless the user deviates from our pre-selection.
@@ -4,6 +4,7 @@ import { Util } from './util.js';
4
4
 
5
5
  /**
6
6
  * @typedef {import('../../types/mcdev.d.js').validationRuleList} validationRuleList
7
+ * @typedef {import('../../types/mcdev.d.js').validationRuleFix} validationRuleFix
7
8
  * @typedef {import('../../types/mcdev.d.js').validationRuleTest} validationRuleTest
8
9
  * @typedef {import('../../types/mcdev.d.js').CodeExtract} CodeExtract
9
10
  */
@@ -40,7 +41,7 @@ async function loadCustomRules() {
40
41
  * @param {any} item MetadataItem
41
42
  * @param {string} targetDir folder in which the MetadataItem is deployed from (deploy/cred/bu)
42
43
  * @param {CodeExtract[]} [codeExtractItemArr] array of code snippets
43
- * @returns {Promise.<any>} MetadataItem
44
+ * @returns {Promise.<validationRuleList>} MetadataItem
44
45
  */
45
46
  export default async function validation(definition, item, targetDir, codeExtractItemArr) {
46
47
  try {
@@ -53,8 +54,8 @@ export default async function validation(definition, item, targetDir, codeExtrac
53
54
  definition,
54
55
  item,
55
56
  targetDir,
56
- Util,
57
- codeExtractItemArr
57
+ codeExtractItemArr,
58
+ Util
58
59
  )
59
60
  : {};
60
61
  } catch (ex) {
@@ -67,6 +68,13 @@ export default async function validation(definition, item, targetDir, codeExtrac
67
68
  const defaultRules = {
68
69
  noGuidKeys: {
69
70
  failedMsg: 'Please update the key to a readable value. Currently still in GUID format.',
71
+ /**
72
+ * @type {validationRuleFix}
73
+ */
74
+ fix: function () {
75
+ // to fix we skip the component
76
+ return this.passed() || null;
77
+ },
70
78
  /**
71
79
  * @type {validationRuleTest}
72
80
  */
@@ -83,6 +91,13 @@ export default async function validation(definition, item, targetDir, codeExtrac
83
91
  },
84
92
  noRootFolder: {
85
93
  failedMsg: 'Root folder not allowed. Current folder: ' + item.r__folder_Path,
94
+ /**
95
+ * @type {validationRuleFix}
96
+ */
97
+ fix: function () {
98
+ // to fix we skip the component
99
+ return this.passed() || null;
100
+ },
86
101
  /**
87
102
  * @type {validationRuleTest}
88
103
  */
@@ -108,6 +123,13 @@ export default async function validation(definition, item, targetDir, codeExtrac
108
123
  noAmpscriptHtmlTag: {
109
124
  failedMsg:
110
125
  'Please use %%[]%% instead of <script runat="server" language="ampscript"></script> for AMPscript',
126
+ /**
127
+ * @type {validationRuleFix}
128
+ */
129
+ fix: function () {
130
+ // to fix we skip the component
131
+ return this.passed() || null;
132
+ },
111
133
  /**
112
134
  * @type {validationRuleTest}
113
135
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcdev",
3
- "version": "7.7.1",
3
+ "version": "7.8.0",
4
4
  "description": "Accenture Salesforce Marketing Cloud DevTools",
5
5
  "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
6
6
  "license": "MIT",
@@ -74,11 +74,11 @@
74
74
  "console.table": "0.10.0",
75
75
  "deep-equal": "2.2.3",
76
76
  "fs-extra": "11.3.0",
77
- "inquirer": "12.4.1",
77
+ "inquirer": "12.4.2",
78
78
  "json-to-table": "4.2.1",
79
79
  "mustache": "4.2.0",
80
80
  "p-limit": "6.2.0",
81
- "prettier": "3.5.0",
81
+ "prettier": "3.5.3",
82
82
  "prettier-plugin-sql": "0.18.1",
83
83
  "semver": "7.7.1",
84
84
  "sfmc-sdk": "2.1.2",
@@ -90,26 +90,26 @@
90
90
  "yocto-spinner": "0.2.0"
91
91
  },
92
92
  "devDependencies": {
93
- "@eslint/js": "9.20.0",
93
+ "@eslint/js": "9.21.0",
94
94
  "@types/fs-extra": "11.0.4",
95
95
  "@types/inquirer": "9.0.7",
96
96
  "@types/mocha": "10.0.8",
97
- "@types/node": "22.13.1",
97
+ "@types/node": "22.13.9",
98
98
  "@types/yargs": "17.0.33",
99
99
  "assert": "2.1.0",
100
100
  "axios-mock-adapter": "2.0.0",
101
101
  "c8": "10.1.3",
102
- "chai": "5.1.2",
102
+ "chai": "5.2.0",
103
103
  "chai-files": "1.4.0",
104
- "eslint": "9.20.0",
104
+ "eslint": "9.21.0",
105
105
  "eslint-config-prettier": "10.0.1",
106
106
  "eslint-config-ssjs": "2.0.0",
107
107
  "eslint-plugin-jsdoc": "50.6.3",
108
108
  "eslint-plugin-mocha": "10.5.0",
109
109
  "eslint-plugin-prettier": "5.2.3",
110
110
  "eslint-plugin-unicorn": "56.0.1",
111
- "fast-xml-parser": "4.5.1",
112
- "globals": "15.14.0",
111
+ "fast-xml-parser": "5.0.8",
112
+ "globals": "16.0.0",
113
113
  "husky": "9.1.7",
114
114
  "lint-staged": "15.4.3",
115
115
  "mocha": "11.1.0",
@@ -2212,6 +2212,34 @@ describe('GENERAL', () => {
2212
2212
  );
2213
2213
  });
2214
2214
 
2215
+ it('build but error after buildTemplate step because keys were not found', async () => {
2216
+ const buName = 'testInstance/testBU';
2217
+ const typeKeyCombo = {
2218
+ asset: ['404'],
2219
+ dataExtension: ['404'],
2220
+ };
2221
+
2222
+ // handler.setOptions({ skipInteraction: true, purge: false, fix: true });
2223
+
2224
+ await handler.build(
2225
+ buName,
2226
+ 'ignored',
2227
+ typeKeyCombo,
2228
+ ['testSourceMarket'],
2229
+ ['parent'],
2230
+ true
2231
+ );
2232
+
2233
+ // THEN
2234
+ assert.equal(process.exitCode, 1, 'build should not have thrown an error');
2235
+
2236
+ assert.equal(
2237
+ testUtils.getAPIHistoryLength(),
2238
+ 0,
2239
+ 'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
2240
+ );
2241
+ });
2242
+
2215
2243
  it('skip build based on validation rule "filterPrefixByBu" with --fix without error', async () => {
2216
2244
  const buName = 'testInstance/testBU';
2217
2245
  const typeKeyCombo = {
@@ -1,4 +1,27 @@
1
1
  'use strict';
2
+ /**
3
+ * @typedef {Object.<string, any>} MetadataTypeItem generic metadata item
4
+ *
5
+ * @typedef {object} CodeExtract
6
+ * @property {string[]} subFolder mostly set to null, otherwise subfolders path split into elements
7
+ * @property {string} fileName name of file w/o extension
8
+ * @property {string} fileExt file extension
9
+ * @property {string} content file content
10
+ * @property {'base64'} [encoding] optional for binary files
11
+ *
12
+ * @callback validationRuleFix
13
+ * @returns {boolean|null} true = test passed; false = test failed & fixed; null = test failed & item removed to fix
14
+ *
15
+ * @callback validationRuleTest
16
+ * @returns {boolean} true = test passed; false = test failed
17
+ *
18
+ * @typedef {object} validationRule
19
+ * @property {string} failedMsg error message to display in case of a failed test
20
+ * @property {validationRuleTest} passed test to run
21
+ * @property {validationRuleFix} [fix] test to run
22
+ *
23
+ * @typedef {Object.<string, validationRule>} validationRuleList key=rule name
24
+ */
2
25
 
3
26
  /** @type {Object.<string, string[]>} */
4
27
  const buPrefixBlacklistMap = {
@@ -8,14 +31,16 @@ const buPrefixBlacklistMap = {
8
31
 
9
32
  /**
10
33
  *
11
- * @param {any} definition - type defintiion
12
- * @param {any} item - metadata json
13
- * @param {string} targetDir - where the metadata is stored ("deploy/cred/bu")
14
- * @param {any} Util - helper methods
15
- * @param {{subFolder: string[],fileName: string,fileExt: string,content: string,encoding?: "base64"}[]} [codeExtractItemArr] - actual code blocks
16
- * @returns {any} validation rule
34
+ * @param {any} definition type definition
35
+ * @param {MetadataTypeItem} item MetadataItem
36
+ * @param {string} targetDir folder in which the MetadataItem is deployed from (deploy/cred/bu)
37
+ * @param {CodeExtract[]} codeExtractItemArr array of code snippets
38
+ * @param {any} Util utility functions
39
+ * @returns {validationRuleList} MetadataItem
17
40
  */
18
- export function validation(definition, item, targetDir, Util, codeExtractItemArr) {
41
+ export function validation(definition, item, targetDir, codeExtractItemArr, Util) {
42
+ Util.logger.verbose('just here to not get a warning about the unused property Util');
43
+
19
44
  const bu =
20
45
  (targetDir.includes('/') ? targetDir.split('/').pop() : targetDir.split('\\').pop()) ||
21
46
  'not-found';
@@ -31,9 +56,12 @@ export function validation(definition, item, targetDir, Util, codeExtractItemArr
31
56
  get failedMsg() {
32
57
  return `Prefix not allowed on this BU. Blacklisted prefixes: ${prefixBlacklist.join(', ')}`;
33
58
  },
34
- /**
35
- * @returns {boolean|null} true=test passed, false=issue error, null=skip item for current activity due to --fix (build/deploy)
36
- */
59
+ /** @type {validationRuleFix} */
60
+ fix: function () {
61
+ // to fix we skip the component
62
+ return this.passed() || null;
63
+ },
64
+ /** @type {validationRuleTest} */
37
65
  passed: function () {
38
66
  // this rule aims to prevent deploying things that dont belong on a BU
39
67
  if (prefixBlacklist.length === 0) {
@@ -47,7 +75,7 @@ export function validation(definition, item, targetDir, Util, codeExtractItemArr
47
75
  ('' + item[definition.keyField]).startsWith(prefix)
48
76
  ) {
49
77
  // return false to issue an error or null to skip the item entirely (which is the "fix" of this rule)
50
- return Util.OPTIONS.fix ? null : false;
78
+ return false;
51
79
  }
52
80
  // some components have unreadable keys and hence only have the brand prefix in the name
53
81
  if (
@@ -55,7 +83,7 @@ export function validation(definition, item, targetDir, Util, codeExtractItemArr
55
83
  ('' + item[definition.nameField]).startsWith(prefix)
56
84
  ) {
57
85
  // return false to issue an error or null to skip the item entirely (which is the "fix" of this rule)
58
- return Util.OPTIONS.fix ? null : false;
86
+ return false;
59
87
  }
60
88
  }
61
89
  // not found
@@ -153,7 +153,29 @@
153
153
  "transactionalSMS",
154
154
  "triggeredSend",
155
155
  "verification"
156
+ ],
157
+ "createDeltaPkg": [
158
+ "asset",
159
+ "automation",
160
+ "dataExtension",
161
+ "dataExtract",
162
+ "emailSend",
163
+ "event",
164
+ "fileLocation",
165
+ "fileTransfer",
166
+ "importFile",
167
+ "journey",
168
+ "mobileCode",
169
+ "mobileKeyword",
170
+ "mobileMessage",
171
+ "query",
172
+ "script",
173
+ "sendClassification",
174
+ "senderProfile",
175
+ "transactionalPush",
176
+ "transactionalSMS",
177
+ "verification"
156
178
  ]
157
179
  },
158
- "version": "7.7.1"
180
+ "version": "7.8.0"
159
181
  }
package/test/utils.js CHANGED
@@ -192,44 +192,16 @@ export function mockSetup(isDeploy) {
192
192
  // no need to execute this again if we ran it a 2nd time for deploy - already done in standard setup
193
193
  if (!isDeploy) {
194
194
  // reset all options to default
195
- handler.setOptions({
196
- // test config
197
- debug: true,
198
- noLogFile: true,
199
- // reset
200
- api: undefined,
201
- autoMidSuffix: undefined,
202
- changeKeyField: undefined,
203
- changeKeyValue: undefined,
204
- commitHistory: undefined,
205
- dependencies: undefined,
206
- errorLog: undefined,
207
- execute: undefined,
208
- filter: undefined,
209
- fix: undefined,
210
- fixShared: undefined,
211
- fromRetrieve: undefined,
212
- json: undefined,
213
- keySuffix: undefined,
214
- like: undefined,
215
- matchName: undefined,
216
- noUpdate: undefined,
217
- publish: undefined,
218
- purge: undefined,
219
- range: undefined,
220
- referenceFrom: undefined,
221
- referenceTo: undefined,
222
- refresh: undefined,
223
- retrieve: undefined,
224
- schedule: undefined,
225
- skipDeploy: undefined,
226
- skipInteraction: undefined,
227
- skipRetrieve: undefined,
228
- skipStatusCheck: undefined,
229
- skipValidation: undefined,
230
- _runningTest: undefined,
231
- _welcomeMessageShown: undefined,
232
- });
195
+ const resetOptions = {};
196
+ // get known options and make sure none are set
197
+ for (const option in handler.knownOptions) {
198
+ resetOptions[option] = undefined;
199
+ }
200
+ // test config
201
+ resetOptions.debug = true;
202
+ resetOptions.noLogFile = true;
203
+
204
+ handler.setOptions(resetOptions);
233
205
  }
234
206
  apimock = new MockAdapter(axiosInstance, { onNoMatch: 'throwException' });
235
207
  // set access_token to mid to allow for autorouting of mock to correct resources
package/types/mcdev.d.js CHANGED
@@ -463,6 +463,7 @@ complex
463
463
  * @property {object} marketList combination of markets and BUs for streamlined deployments
464
464
  * @property {object} metaDataTypes templating variables grouped by markets
465
465
  * @property {string[]} metaDataTypes.retrieve define what types shall be downloaded by default during retrieve
466
+ * @property {string[]} metaDataTypes.createDeltaPkg defines what types shall be passed to build() as part of running cdp
466
467
  * @property {string[]} metaDataTypes.documentOnRetrieve which types should be parsed & documented after retrieve
467
468
  * @property {string} version mcdev version that last updated the config file
468
469
  */
@@ -628,16 +629,18 @@ complex
628
629
  * @property {string} text seems to be equal to name-attribute?; "Email"
629
630
  */
630
631
  /**
632
+ *
633
+ * @callback validationRuleFix
634
+ * @returns {boolean|null} true = test passed; false = test failed & fixed; null = test failed & item removed to fix
631
635
  *
632
636
  * @callback validationRuleTest
633
637
  * @returns {boolean} true = test passed; false = test failed
634
- */
635
- /**
638
+ *
636
639
  * @typedef {object} validationRule
637
640
  * @property {string} failedMsg error message to display in case of a failed test
638
641
  * @property {validationRuleTest} passed test to run
639
- */
640
- /**
642
+ * @property {validationRuleFix} [fix] test to run
643
+ *
641
644
  * @typedef {Object.<string, validationRule>} validationRuleList key=rule name
642
645
  */
643
646