mcdev 7.6.3 → 7.7.1

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 (147) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
  2. package/.github/ISSUE_TEMPLATE/task.md +1 -1
  3. package/.github/workflows/coverage-base-update.yml +2 -2
  4. package/.github/workflows/coverage-develop-branch.yml +3 -1
  5. package/.github/workflows/coverage-main-branch.yml +3 -1
  6. package/.github/workflows/coverage.yml +5 -3
  7. package/.mcdev-validations.js +0 -0
  8. package/@types/lib/Builder.d.ts +14 -0
  9. package/@types/lib/Builder.d.ts.map +1 -1
  10. package/@types/lib/MetadataTypeDefinitions.d.ts +2 -0
  11. package/@types/lib/MetadataTypeDefinitions.d.ts.map +1 -1
  12. package/@types/lib/MetadataTypeInfo.d.ts +2 -0
  13. package/@types/lib/MetadataTypeInfo.d.ts.map +1 -1
  14. package/@types/lib/index.d.ts +17 -8
  15. package/@types/lib/index.d.ts.map +1 -1
  16. package/@types/lib/metadataTypes/Asset.d.ts +11 -3
  17. package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
  18. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
  19. package/@types/lib/metadataTypes/DomainVerification.d.ts +180 -0
  20. package/@types/lib/metadataTypes/DomainVerification.d.ts.map +1 -0
  21. package/@types/lib/metadataTypes/Event.d.ts.map +1 -1
  22. package/@types/lib/metadataTypes/Journey.d.ts +7 -4
  23. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  24. package/@types/lib/metadataTypes/MetadataType.d.ts +15 -7
  25. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
  26. package/@types/lib/metadataTypes/MobileKeyword.d.ts +2 -10
  27. package/@types/lib/metadataTypes/MobileKeyword.d.ts.map +1 -1
  28. package/@types/lib/metadataTypes/MobileMessage.d.ts +2 -10
  29. package/@types/lib/metadataTypes/MobileMessage.d.ts.map +1 -1
  30. package/@types/lib/metadataTypes/SendClassification.d.ts.map +1 -1
  31. package/@types/lib/metadataTypes/SenderProfile.d.ts +7 -0
  32. package/@types/lib/metadataTypes/SenderProfile.d.ts.map +1 -1
  33. package/@types/lib/metadataTypes/TransactionalEmail.d.ts +2 -2
  34. package/@types/lib/metadataTypes/TransactionalEmail.d.ts.map +1 -1
  35. package/@types/lib/metadataTypes/TriggeredSend.d.ts +8 -0
  36. package/@types/lib/metadataTypes/TriggeredSend.d.ts.map +1 -1
  37. package/@types/lib/metadataTypes/Verification.d.ts +0 -9
  38. package/@types/lib/metadataTypes/Verification.d.ts.map +1 -1
  39. package/@types/lib/metadataTypes/definitions/DomainVerification.definition.d.ts +100 -0
  40. package/@types/lib/metadataTypes/definitions/DomainVerification.definition.d.ts.map +1 -0
  41. package/@types/lib/metadataTypes/definitions/Journey.definition.d.ts +1 -1
  42. package/@types/lib/util/devops.d.ts.map +1 -1
  43. package/@types/lib/util/replaceContentBlockReference.d.ts +2 -1
  44. package/@types/lib/util/replaceContentBlockReference.d.ts.map +1 -1
  45. package/@types/lib/util/util.d.ts +42 -1
  46. package/@types/lib/util/util.d.ts.map +1 -1
  47. package/@types/lib/util/validations.d.ts.map +1 -1
  48. package/@types/types/mcdev.d.d.ts +34 -0
  49. package/@types/types/mcdev.d.d.ts.map +1 -1
  50. package/boilerplate/config.json +11 -0
  51. package/boilerplate/files/eslint.config.js +98 -3
  52. package/boilerplate/forcedUpdates.json +4 -0
  53. package/boilerplate/gitignore-template +1 -1
  54. package/boilerplate/npm-dependencies.json +1 -0
  55. package/eslint.config.js +4 -3
  56. package/lib/Builder.js +114 -54
  57. package/lib/Deployer.js +2 -2
  58. package/lib/MetadataTypeDefinitions.js +2 -0
  59. package/lib/MetadataTypeInfo.js +2 -0
  60. package/lib/cli.js +127 -3
  61. package/lib/index.js +217 -164
  62. package/lib/metadataTypes/Asset.js +76 -22
  63. package/lib/metadataTypes/DataExtension.js +10 -2
  64. package/lib/metadataTypes/DomainVerification.js +246 -0
  65. package/lib/metadataTypes/Event.js +21 -9
  66. package/lib/metadataTypes/Journey.js +339 -223
  67. package/lib/metadataTypes/MetadataType.js +153 -106
  68. package/lib/metadataTypes/MobileKeyword.js +5 -2
  69. package/lib/metadataTypes/MobileMessage.js +5 -2
  70. package/lib/metadataTypes/SendClassification.js +5 -0
  71. package/lib/metadataTypes/SenderProfile.js +102 -3
  72. package/lib/metadataTypes/TransactionalEmail.js +3 -1
  73. package/lib/metadataTypes/Verification.js +3 -1
  74. package/lib/metadataTypes/definitions/DomainVerification.definition.js +71 -0
  75. package/lib/metadataTypes/definitions/Journey.definition.js +7 -1
  76. package/lib/metadataTypes/definitions/SendClassification.definition.js +2 -2
  77. package/lib/metadataTypes/definitions/SenderProfile.definition.js +1 -1
  78. package/lib/util/config.js +6 -0
  79. package/lib/util/devops.js +130 -154
  80. package/lib/util/file.js +3 -3
  81. package/lib/util/replaceContentBlockReference.js +10 -3
  82. package/lib/util/util.js +96 -14
  83. package/lib/util/validations.js +34 -14
  84. package/package.json +10 -10
  85. package/test/general.test.js +339 -96
  86. package/test/mockRoot/.mcdev-validations.js +66 -0
  87. package/test/mockRoot/.mcdevrc.json +30 -2
  88. package/test/mockRoot/deploy/testInstance/testBU/asset/block/testNew_asset_htmlblock.asset-block-meta.html +1 -0
  89. package/test/mockRoot/deploy/testInstance/testBU/asset/block/testNew_asset_htmlblock.asset-block-meta.json +39 -0
  90. package/test/mockRoot/deploy/testInstance/testBU/asset/block/testNew_asset_withCBBK_notexisting.asset-block-meta.html +4 -0
  91. package/test/mockRoot/deploy/testInstance/testBU/asset/block/testNew_asset_withCBBK_notexisting.asset-block-meta.json +39 -0
  92. package/test/mockRoot/deploy/testInstance/testBU/asset/block/testNew_asset_withCBBK_preexisting.asset-block-meta.html +4 -0
  93. package/test/mockRoot/deploy/testInstance/testBU/asset/block/testNew_asset_withCBBK_preexisting.asset-block-meta.json +39 -0
  94. package/test/mockRoot/deploy/testInstance/testBU/asset/message/testNew_assetMessage/testNew_assetMessage.asset-message-meta.json +435 -0
  95. package/test/mockRoot/deploy/testInstance/testBU/asset/message/testNew_assetMessage/views.html.content.asset-message-meta.html +150 -0
  96. package/test/mockRoot/deploy/testInstance/testBU/asset/message/testNew_asset_templatebasedemail/testNew_asset_templatebasedemail.asset-message-meta.json +305 -0
  97. package/test/mockRoot/deploy/testInstance/testBU/asset/message/testNew_asset_templatebasedemail/views.html.content.asset-message-meta.html +150 -0
  98. package/test/mockRoot/deploy/testInstance/testBU/asset/template/testNew_asset_template/content.asset-template-meta.html +150 -0
  99. package/test/mockRoot/deploy/testInstance/testBU/asset/template/testNew_asset_template/testNew_asset_template.asset-template-meta.json +116 -0
  100. package/test/mockRoot/deploy/testInstance/testBU/domainVerification/joern.berkefeld.New@accenture.com.domainVerification-meta.json +6 -0
  101. package/test/mockRoot/deploy/testInstance/testBU/domainVerification/joern.berkefeld@accenture.com.domainVerification-meta.json +6 -0
  102. package/test/mockRoot/deploy/testInstance/testBU/domainVerification/mcdev.accenture.com.domainVerification-meta.json +6 -0
  103. package/test/mockRoot/deploy/testInstance/testBU/journey/testNew_temail_notPublished.journey-meta.json +213 -0
  104. package/test/mockRoot/deploy/testInstance/testBU/senderProfile/testExisting_senderProfile.senderProfile-meta.json +1 -0
  105. package/test/mockRoot/deploy/testInstance/testBU/senderProfile/testNew_senderProfile.senderProfile-meta.json +1 -0
  106. package/test/resourceFactory.js +7 -24
  107. package/test/resources/9999999/asset/v1/content/assets/post-response-key=testNew_assetMessage.json +441 -0
  108. package/test/resources/9999999/asset/v1/content/assets/post-response-key=testNew_asset_htmlblock.json +59 -0
  109. package/test/resources/9999999/asset/v1/content/assets/post-response-key=testNew_asset_template.json +147 -0
  110. package/test/resources/9999999/asset/v1/content/assets/post-response-key=testNew_asset_templatebasedemail.json +322 -0
  111. package/test/resources/9999999/asset/v1/content/assets/post-response-key=testNew_asset_withCBBK_notexisting.json +59 -0
  112. package/test/resources/9999999/asset/v1/content/assets/post-response-key=testNew_asset_withCBBK_preexisting.json +59 -0
  113. package/test/resources/9999999/asset-deploy2/block/testBlacklist_asset_htmlblock.asset-block-meta.html +1 -0
  114. package/test/resources/9999999/asset-deploy2/block/testBlacklist_asset_htmlblock.asset-block-meta.json +39 -0
  115. package/test/resources/9999999/automation/clone-expected.json +61 -0
  116. package/test/resources/9999999/dataExtension/retrieve-CustomerKey=testExisting_dataExtension-response.xml +52 -0
  117. package/test/resources/9999999/dataExtension-deploy/testBlacklist_dataExtension.dataExtension-meta.json +20 -0
  118. package/test/resources/9999999/dataFolder/retrieve-ContentTypeINasset,asset-shared,dataextension,hidden,salesforcedataextension,shared_data,shared_dataextension,shared_salesforcedataextension,synchronizeddataextension-response.xml +137 -0
  119. package/test/resources/9999999/domainVerification/create-expected.json +3 -0
  120. package/test/resources/9999999/domainVerification/get-sap-expected.json +6 -0
  121. package/test/resources/9999999/domainVerification/update-expected.json +6 -0
  122. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_temail/put-response-paused.json +219 -0
  123. package/test/resources/9999999/interaction/v1/interactions/key_testNew_temail_notPublished/get-response.json +218 -0
  124. package/test/resources/9999999/interaction/v1/interactions/post-response.json +216 -0
  125. package/test/resources/9999999/journey/create-transactionaEmail-publish-expected.json +217 -0
  126. package/test/resources/9999999/messaging/v1/domainverification/delete/post-response.txt +1 -0
  127. package/test/resources/9999999/messaging/v1/domainverification/get-response.json +43 -0
  128. package/test/resources/9999999/messaging/v1/domainverification/post-response.txt +1 -0
  129. package/test/resources/9999999/messaging/v1/domainverification/update/post-response.txt +1 -0
  130. package/test/resources/9999999/messaging/v1/email/definitions/get-response.json +7 -0
  131. package/test/resources/9999999/messaging/v1/email/definitions/testNew_temail_notPublished/get-response.json +26 -0
  132. package/test/resources/9999999/query/clone-expected.json +8 -0
  133. package/test/resources/9999999/query/clone-expected.sql +7 -0
  134. package/test/resources/9999999/senderProfile/create-response.xml +1 -1
  135. package/test/resources/9999999/senderProfile/post-expected.json +1 -1
  136. package/test/resources/9999999/transactionalEmail/create-publish-expected.json +20 -0
  137. package/test/type.asset.test.js +216 -9
  138. package/test/type.automation.test.js +1 -1
  139. package/test/type.domainVerification.test.js +169 -0
  140. package/test/type.journey.test.js +107 -21
  141. package/test/type.script.test.js +1 -1
  142. package/test/type.sendClassification.test.js +3 -3
  143. package/test/type.senderProfile.test.js +26 -6
  144. package/test/type.transactionalEmail.test.js +5 -5
  145. package/test/type.triggeredSend.test.js +1 -1
  146. package/test/utils.js +8 -0
  147. package/types/mcdev.d.js +12 -0
package/lib/index.js CHANGED
@@ -93,6 +93,7 @@ class Mcdev {
93
93
  'errorLog',
94
94
  'execute',
95
95
  'filter',
96
+ 'fix',
96
97
  'fixShared',
97
98
  'format',
98
99
  'fromRetrieve',
@@ -107,6 +108,8 @@ class Mcdev {
107
108
  'noUpdate',
108
109
  'autoMidSuffix',
109
110
  'publish',
111
+ 'purge',
112
+ 'range',
110
113
  'referenceFrom',
111
114
  'referenceTo',
112
115
  'refresh',
@@ -144,8 +147,8 @@ class Mcdev {
144
147
  * handler for 'mcdev createDeltaPkg
145
148
  *
146
149
  * @param {object} argv yargs parameters
147
- * @param {string} [argv.range] git commit range
148
- into deploy directory
150
+ * @param {string} [argv.commitrange] git commit range via positional
151
+ * @param {string} [argv.range] git commit range via option
149
152
  * @param {string} [argv.filter] filter file paths that start with any
150
153
  * @param {number} [argv.commitHistory] filter file paths that start with any
151
154
  * @param {DeltaPkgItem[]} [argv.diffArr] list of files to include in delta package (skips git diff when provided)
@@ -155,18 +158,23 @@ class Mcdev {
155
158
  Util.startLogger();
156
159
  Util.logger.info('Create Delta Package ::');
157
160
  const properties = await config.getProperties();
158
- if (!(await config.checkProperties(properties))) {
159
- return null;
161
+ if (!properties) {
162
+ return;
160
163
  }
161
-
164
+ if (argv.commitrange) {
165
+ Util.logger.warn(
166
+ `Depecation Notice: Please start using --range to define the commit range or target branch. The positional argument will be removed in the next major release.`
167
+ );
168
+ }
169
+ const range = argv.commitrange || Util.OPTIONS.range;
162
170
  try {
163
171
  return await (argv.filter
164
172
  ? // get source market and source BU from config
165
- DevOps.getDeltaList(properties, argv.range, true, argv.filter, argv.commitHistory)
173
+ DevOps.getDeltaList(properties, range, true, argv.filter, argv.commitHistory)
166
174
  : // If no custom filter was provided, use deployment marketLists & templating
167
175
  DevOps.buildDeltaDefinitions(
168
176
  properties,
169
- argv.range,
177
+ range,
170
178
  argv.diffArr,
171
179
  argv.commitHistory
172
180
  ));
@@ -181,9 +189,10 @@ class Mcdev {
181
189
  static async selectTypes() {
182
190
  Util.startLogger();
183
191
  const properties = await config.getProperties();
184
- if (!(await config.checkProperties(properties))) {
185
- return null;
192
+ if (!properties) {
193
+ return;
186
194
  }
195
+
187
196
  await Cli.selectTypes(properties);
188
197
  }
189
198
 
@@ -199,11 +208,11 @@ class Mcdev {
199
208
  */
200
209
  static async upgrade() {
201
210
  Util.startLogger();
202
- const properties = await config.getProperties();
211
+ const properties = await config.getProperties(false, true);
203
212
  if (!properties) {
204
- Util.logger.error('No config found. Please run mcdev init');
205
- return false;
213
+ return;
206
214
  }
215
+
207
216
  if ((await InitGit.initGitRepo()).status === 'error') {
208
217
  return false;
209
218
  }
@@ -221,9 +230,7 @@ class Mcdev {
221
230
  }
222
231
  Util.OPTIONS._welcomeMessageShown = true;
223
232
 
224
- const color = Util.isRunViaVSCodeExtension
225
- ? { reset: '', bgWhite: '', fgBlue: '' }
226
- : Util.color;
233
+ const color = Util.color;
227
234
  /* eslint-disable no-console */
228
235
  if (process.env['USERDNSDOMAIN'] === 'DIR.SVC.ACCENTURE.COM') {
229
236
  // Accenture internal message
@@ -262,9 +269,8 @@ class Mcdev {
262
269
  Util.startLogger();
263
270
  Util.logger.info('mcdev:: Retrieve');
264
271
  const properties = await config.getProperties();
265
- if (!(await config.checkProperties(properties))) {
266
- // return null here to avoid seeing 2 error messages for the same issue
267
- return null;
272
+ if (!properties) {
273
+ return;
268
274
  }
269
275
 
270
276
  // assume a list was passed in and check each entry's validity
@@ -390,9 +396,10 @@ class Mcdev {
390
396
  selectedTypesArr = structuredClone(selectedTypesArr);
391
397
 
392
398
  const properties = await config.getProperties();
393
- if (!(await config.checkProperties(properties))) {
394
- return null;
399
+ if (!properties) {
400
+ return;
395
401
  }
402
+
396
403
  const buObject = await Cli.getCredentialObject(
397
404
  properties,
398
405
  cred === null ? null : cred + '/' + bu,
@@ -551,9 +558,10 @@ class Mcdev {
551
558
  Util.startLogger();
552
559
  Util.logger.info('mcdev:: Load BUs');
553
560
  const properties = await config.getProperties();
554
- if (!(await config.checkProperties(properties))) {
555
- return null;
561
+ if (!properties) {
562
+ return;
556
563
  }
564
+
557
565
  const buObject = await Cli.getCredentialObject(properties, credentialsName, true);
558
566
  if (buObject !== null) {
559
567
  BuHelper.refreshBUProperties(properties, buObject.credential);
@@ -571,9 +579,10 @@ class Mcdev {
571
579
  Util.startLogger();
572
580
  Util.logger.info('mcdev:: Document');
573
581
  const properties = await config.getProperties();
574
- if (!(await config.checkProperties(properties))) {
575
- return null;
582
+ if (!properties) {
583
+ return;
576
584
  }
585
+
577
586
  if (type && !MetadataTypeInfo[type]) {
578
587
  Util.logger.error(`:: '${type}' is not a valid metadata type`);
579
588
  return;
@@ -632,9 +641,10 @@ class Mcdev {
632
641
  }
633
642
  }
634
643
  const properties = await config.getProperties();
635
- if (!(await config.checkProperties(properties))) {
636
- return null;
644
+ if (!properties) {
645
+ return;
637
646
  }
647
+
638
648
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
639
649
  if (!buObject) {
640
650
  return;
@@ -656,6 +666,9 @@ class Mcdev {
656
666
  MetadataTypeInfo[type].client = client;
657
667
  MetadataTypeInfo[type].properties = properties;
658
668
  MetadataTypeInfo[type].buObject = buObject;
669
+
670
+ await MetadataTypeInfo[type].preDeleteTasks(keyArr);
671
+
659
672
  const deleteLimit = pLimit(20);
660
673
 
661
674
  await Promise.allSettled(
@@ -700,9 +713,10 @@ class Mcdev {
700
713
  return;
701
714
  }
702
715
  const properties = await config.getProperties();
703
- if (!(await config.checkProperties(properties))) {
704
- return null;
716
+ if (!properties) {
717
+ return;
705
718
  }
719
+
706
720
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
707
721
  if (buObject !== null) {
708
722
  try {
@@ -749,9 +763,10 @@ class Mcdev {
749
763
  Util.startLogger();
750
764
  Util.logger.info('mcdev:: describe SOAP');
751
765
  const properties = await config.getProperties();
752
- if (!(await config.checkProperties(properties))) {
753
- return null;
766
+ if (!properties) {
767
+ return;
754
768
  }
769
+
755
770
  const credential = Object.keys(properties.credentials)[0];
756
771
  businessUnit ||=
757
772
  credential + '/' + Object.keys(properties.credentials[credential].businessUnits)[0];
@@ -796,9 +811,10 @@ class Mcdev {
796
811
  static async badKeys(businessUnit) {
797
812
  Util.startLogger();
798
813
  const properties = await config.getProperties();
799
- if (!(await config.checkProperties(properties))) {
800
- return null;
814
+ if (!properties) {
815
+ return;
801
816
  }
817
+
802
818
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
803
819
  if (buObject !== null) {
804
820
  Util.logger.info('Gathering list of Name<>External Key mismatches (bad keys)');
@@ -873,9 +889,10 @@ class Mcdev {
873
889
  'mcdev:: [DEPRECATED] Retrieve as Template [DEPRECATED] - use "retrieve" + "buildTemplate" instead'
874
890
  );
875
891
  const properties = await config.getProperties();
876
- if (!(await config.checkProperties(properties))) {
877
- return null;
892
+ if (!properties) {
893
+ return;
878
894
  }
895
+
879
896
  if (!Util._isValidType(selectedType)) {
880
897
  return;
881
898
  }
@@ -904,15 +921,19 @@ class Mcdev {
904
921
 
905
922
  /**
906
923
  * @param {string} businessUnit references credentials from properties.json
907
- * @param {TypeKeyCombo} selectedTypes limit retrieval to given metadata type
924
+ * @param {TypeKeyCombo} typeKeyList limit retrieval to given metadata type
908
925
  * @returns {Promise.<TypeKeyCombo>} selected types including dependencies
909
926
  */
910
- static async addDependentCbReferences(businessUnit, selectedTypes) {
927
+ static async addDependentCbReferences(businessUnit, typeKeyList) {
911
928
  if (!Util.OPTIONS.dependencies) {
912
929
  return;
913
930
  }
914
- const initialAssetNumber = selectedTypes['asset']?.length || 0;
931
+ const initialAssetNumber = typeKeyList['asset']?.length || 0;
915
932
  const properties = await config.getProperties();
933
+ if (!properties) {
934
+ return;
935
+ }
936
+
916
937
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
917
938
  Util.logger.info(
918
939
  'Searching for additional dependencies that were linked via ContentBlockByKey, ContentBlockByName and ContentBlockById'
@@ -936,7 +957,7 @@ class Mcdev {
936
957
  ])
937
958
  );
938
959
  // check all non-asset types for dependencies
939
- for (const depType in selectedTypes) {
960
+ for (const depType in typeKeyList) {
940
961
  if (
941
962
  !Object.prototype.hasOwnProperty.call(
942
963
  MetadataTypeInfo[depType],
@@ -949,7 +970,7 @@ class Mcdev {
949
970
  MetadataTypeInfo[depType].properties = properties;
950
971
  MetadataTypeInfo[depType].buObject = buObject;
951
972
  await MetadataTypeInfo[depType].getCbReferenceKeys(
952
- selectedTypes[depType],
973
+ typeKeyList[depType],
953
974
  retrieveDir,
954
975
  assetDependencies
955
976
  );
@@ -957,26 +978,26 @@ class Mcdev {
957
978
  // add dependencies to selectedTypes
958
979
  if (assetDependencies.size) {
959
980
  const depType = 'asset';
960
- if (selectedTypes[depType]) {
961
- selectedTypes[depType].push(...assetDependencies);
981
+ if (typeKeyList[depType]) {
982
+ typeKeyList[depType].push(...assetDependencies);
962
983
  } else {
963
- selectedTypes[depType] = [...assetDependencies];
984
+ typeKeyList[depType] = [...assetDependencies];
964
985
  }
965
986
  // remove duplicates in main object after adding dependencies
966
- selectedTypes[depType] = [...new Set(selectedTypes[depType])];
987
+ typeKeyList[depType] = [...new Set(typeKeyList[depType])];
967
988
  }
968
989
 
969
990
  // check all assets for dependencies recursively
970
- if (selectedTypes.asset?.length) {
991
+ if (typeKeyList.asset?.length) {
971
992
  const depType = 'asset';
972
993
  const Asset = MetadataTypeInfo[depType];
973
994
  Asset.properties = properties;
974
995
  Asset.buObject = buObject;
975
996
  const additionalAssetDependencies = [
976
997
  ...(await Asset.getCbReferenceKeys(
977
- selectedTypes[depType],
998
+ typeKeyList[depType],
978
999
  retrieveDir,
979
- new Set(selectedTypes[depType])
1000
+ new Set(typeKeyList[depType])
980
1001
  )),
981
1002
  ];
982
1003
  if (additionalAssetDependencies.length) {
@@ -988,19 +1009,19 @@ class Mcdev {
988
1009
  Asset.getJsonFromFSCache = null;
989
1010
 
990
1011
  // remove duplicates in main object after adding dependencies
991
- selectedTypes[depType] = [...new Set(selectedTypes[depType])];
1012
+ typeKeyList[depType] = [...new Set(typeKeyList[depType])];
992
1013
  }
993
1014
 
994
- return selectedTypes;
1015
+ return typeKeyList;
995
1016
  }
996
1017
 
997
1018
  /**
998
1019
  *
999
1020
  * @param {string} businessUnit references credentials from properties.json
1000
- * @param {TypeKeyCombo} selectedTypes limit retrieval to given metadata type
1021
+ * @param {TypeKeyCombo} typeKeyList limit retrieval to given metadata type
1001
1022
  * @returns {Promise.<TypeKeyCombo>} dependencies
1002
1023
  */
1003
- static async addDependencies(businessUnit, selectedTypes) {
1024
+ static async addDependencies(businessUnit, typeKeyList) {
1004
1025
  if (!Util.OPTIONS.dependencies) {
1005
1026
  return;
1006
1027
  }
@@ -1018,15 +1039,18 @@ class Mcdev {
1018
1039
  const dependencies = {};
1019
1040
  /** @type {TypeKeyCombo} */
1020
1041
  const notFoundList = {};
1021
- const initiallySelectedTypesArr = Object.keys(selectedTypes);
1042
+ const initiallySelectedTypesArr = Object.keys(typeKeyList);
1022
1043
 
1023
1044
  const properties = await config.getProperties();
1045
+ if (!properties) {
1046
+ return;
1047
+ }
1024
1048
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
1025
1049
  for (const type of initiallySelectedTypesArr) {
1026
1050
  MetadataTypeInfo[type].properties = properties;
1027
1051
  MetadataTypeInfo[type].buObject = buObject;
1028
1052
  await MetadataTypeInfo[type].getDependentFiles(
1029
- selectedTypes[type],
1053
+ typeKeyList[type],
1030
1054
  dependencies,
1031
1055
  notFoundList,
1032
1056
  true
@@ -1057,18 +1081,36 @@ class Mcdev {
1057
1081
  `Found ${Util.getTypeKeyCount(dependencies)} items across ${Object.keys(dependencies).length} types.`
1058
1082
  );
1059
1083
  for (const type in dependencies) {
1060
- if (selectedTypes[type]) {
1061
- selectedTypes[type].push(...dependencies[type]);
1084
+ if (typeKeyList[type]) {
1085
+ typeKeyList[type].push(...dependencies[type]);
1062
1086
  } else {
1063
- selectedTypes[type] = dependencies[type];
1087
+ typeKeyList[type] = dependencies[type];
1064
1088
  }
1065
1089
  // remove duplicates in main object after adding dependencies
1066
- selectedTypes[type] = [...new Set(selectedTypes[type])];
1090
+ typeKeyList[type] = [...new Set(typeKeyList[type])];
1067
1091
  }
1068
1092
  }
1069
1093
  return dependencies;
1070
1094
  }
1071
1095
 
1096
+ /**
1097
+ * Build a template based on a list of metadata files in the retrieve folder.
1098
+ *
1099
+ * @param {string} businessUnitTemplate references credentials from properties.json
1100
+ * @param {string} businessUnitDefinition references credentials from properties.json
1101
+ * @param {TypeKeyCombo} typeKeyCombo limit retrieval to given metadata type
1102
+ * @returns {Promise.<MultiMetadataTypeList | object>} response from buildDefinition
1103
+ */
1104
+ static async clone(businessUnitTemplate, businessUnitDefinition, typeKeyCombo) {
1105
+ return this.build(
1106
+ businessUnitTemplate,
1107
+ businessUnitDefinition,
1108
+ typeKeyCombo,
1109
+ ['__clone__'],
1110
+ ['__clone__']
1111
+ );
1112
+ }
1113
+
1072
1114
  /**
1073
1115
  * Build a template based on a list of metadata files in the retrieve folder.
1074
1116
  *
@@ -1107,29 +1149,45 @@ class Mcdev {
1107
1149
  }
1108
1150
  }
1109
1151
 
1152
+ // redirect templates to temporary folder when executed via build()
1153
+ const properties = await config.getProperties();
1154
+ if (!properties) {
1155
+ return;
1156
+ }
1157
+ const templateDirBackup = properties.directories.template;
1158
+ properties.directories.template = '.mcdev/template/';
1159
+
1110
1160
  Util.logger.info('mcdev:: Build Template & Build Definition');
1111
1161
  await this.buildTemplate(businessUnitTemplate, typeKeyCombo, null, marketTemplate);
1112
1162
 
1113
- let isPurgeDeployFolder;
1114
- if (!Util.skipInteraction && !bulk) {
1115
- const properties = await config.getProperties();
1163
+ if (typeof Util.OPTIONS.purge !== 'boolean') {
1116
1164
  // deploy folder is in targets for definition creation
1117
1165
  // recommend to purge their content first
1118
- isPurgeDeployFolder = await confirm({
1119
- message: `Do you want to empty the folder /${properties.directories.deploy}${businessUnitDefinition} (ensures no files from previous deployments remain)?`,
1166
+ Util.OPTIONS.purge = await confirm({
1167
+ message: `Do you want to empty relevant BU sub-folders in /${properties.directories.deploy} (ensures no files from previous deployments remain)?`,
1120
1168
  default: true,
1121
1169
  });
1122
1170
  }
1123
1171
 
1124
- return bulk
1125
- ? this.buildDefinitionBulk(marketDefinition[0], typeKeyCombo, null)
1126
- : this.buildDefinition(
1172
+ const response = bulk
1173
+ ? await this.buildDefinitionBulk(marketDefinition[0], typeKeyCombo, null)
1174
+ : await this.buildDefinition(
1127
1175
  businessUnitDefinition,
1128
1176
  typeKeyCombo,
1129
1177
  null,
1130
- marketDefinition,
1131
- isPurgeDeployFolder
1178
+ marketDefinition
1132
1179
  );
1180
+
1181
+ // reset temporary template folder
1182
+ try {
1183
+ await File.remove(properties.directories.template);
1184
+ } catch {
1185
+ // sometimes the first attempt is not successful for some operating system reason. Trying again mostly solves this
1186
+ await File.remove(properties.directories.template);
1187
+ }
1188
+ properties.directories.template = templateDirBackup;
1189
+
1190
+ return response;
1133
1191
  }
1134
1192
 
1135
1193
  /**
@@ -1147,30 +1205,25 @@ class Mcdev {
1147
1205
  Util.startLogger();
1148
1206
  Util.logger.info('mcdev:: Build Template from retrieved files');
1149
1207
  const properties = await config.getProperties();
1150
- const buObject = await Cli.getCredentialObject(properties, businessUnit);
1151
- for (const market of marketArr) {
1152
- if (!Util.checkMarket(market, properties)) {
1153
- return;
1154
- }
1208
+ if (!properties) {
1209
+ return;
1155
1210
  }
1156
- if ('string' === typeof selectedTypes) {
1157
- // ensure we have TypeKeyCombo here
1158
- selectedTypes = Util.createTypeKeyCombo(selectedTypes, keyArr);
1211
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
1212
+ if (!Util.checkMarketList(marketArr, properties)) {
1213
+ return;
1159
1214
  }
1160
- // check if types are valid
1161
- for (const type of Object.keys(selectedTypes)) {
1162
- if (!Util._isValidType(type)) {
1163
- return;
1164
- }
1165
- if (!Array.isArray(selectedTypes[type])) {
1166
- Util.logger.error('You need to define keys, not just types to run buildTemplate');
1167
- // we need an array of keys here
1168
- return;
1169
- }
1215
+
1216
+ const typeKeyList = Util.checkAndPrepareTypeKeyCombo(
1217
+ selectedTypes,
1218
+ keyArr,
1219
+ 'buildTemplate'
1220
+ );
1221
+ if (!typeKeyList) {
1222
+ return;
1170
1223
  }
1171
1224
 
1172
1225
  if (!Util.OPTIONS.dependencies) {
1173
- await this._reRetrieve(businessUnit, false, selectedTypes);
1226
+ await this._reRetrieve(businessUnit, false, typeKeyList);
1174
1227
  }
1175
1228
  // convert names to keys
1176
1229
  const retrieveDir = File.normalizePath([
@@ -1178,14 +1231,14 @@ class Mcdev {
1178
1231
  buObject.credential,
1179
1232
  buObject.businessUnit,
1180
1233
  ]);
1181
- for (const type of Object.keys(selectedTypes)) {
1182
- const keyArr = selectedTypes[type];
1234
+ for (const type of Object.keys(typeKeyList)) {
1235
+ const keyArr = typeKeyList[type];
1183
1236
  if (keyArr.some((key) => key.startsWith('name:'))) {
1184
1237
  // at least one key was provided as a name -> load all files from disk to try and find that key
1185
1238
  const builTemplateCache = Object.values(
1186
1239
  await MetadataTypeInfo[type].getJsonFromFS(retrieveDir + path.sep + type)
1187
1240
  );
1188
- selectedTypes[type] = keyArr
1241
+ typeKeyList[type] = keyArr
1189
1242
  .map((key) => {
1190
1243
  if (key.startsWith('name:')) {
1191
1244
  // key was defined by name. try and find matching item on disk
@@ -1220,16 +1273,19 @@ class Mcdev {
1220
1273
  }
1221
1274
 
1222
1275
  // if dependencies are enabled, we need to search for them and add them to our
1223
- await this.addDependencies(businessUnit, selectedTypes);
1224
- await this.addDependentCbReferences(businessUnit, selectedTypes);
1276
+ await this.addDependencies(businessUnit, typeKeyList);
1277
+ await this.addDependentCbReferences(businessUnit, typeKeyList);
1225
1278
 
1226
1279
  /** @type {MultiMetadataTypeList} */
1227
1280
  const returnObj = {};
1228
- for (const type of Object.keys(selectedTypes)) {
1281
+ for (const type of Object.keys(typeKeyList)) {
1282
+ // ensure keys are sorted again, after finding dependencies, to enhance log readability
1283
+ typeKeyList[type].sort();
1284
+
1229
1285
  const result = await Builder.buildTemplate(
1230
1286
  businessUnit,
1231
1287
  type,
1232
- selectedTypes[type],
1288
+ typeKeyList[type],
1233
1289
  marketArr
1234
1290
  );
1235
1291
  returnObj[type] = result[type];
@@ -1271,61 +1327,42 @@ class Mcdev {
1271
1327
  * @param {string | TypeKeyCombo} selectedTypes limit retrieval to given metadata type
1272
1328
  * @param {string[] | undefined} nameArr name of the metadata
1273
1329
  * @param {string[]} marketArr market localizations
1274
- * @param {boolean} [isPurgeDeployFolder] whether to purge the deploy folder
1275
1330
  * @returns {Promise.<MultiMetadataTypeList>} -
1276
1331
  */
1277
- static async buildDefinition(
1278
- businessUnit,
1279
- selectedTypes,
1280
- nameArr,
1281
- marketArr,
1282
- isPurgeDeployFolder
1283
- ) {
1332
+ static async buildDefinition(businessUnit, selectedTypes, nameArr, marketArr) {
1284
1333
  this.#welcomeMessage();
1285
1334
 
1286
1335
  Util.startLogger();
1287
1336
  Util.logger.info('mcdev:: Build Definition from Template');
1288
1337
  const properties = await config.getProperties();
1289
- for (const market of marketArr) {
1290
- if (!Util.checkMarket(market, properties)) {
1291
- return;
1292
- }
1338
+ if (!properties) {
1339
+ return;
1293
1340
  }
1294
- if ('string' === typeof selectedTypes) {
1295
- // ensure we have TypeKeyCombo here
1296
- selectedTypes = Util.createTypeKeyCombo(selectedTypes, nameArr);
1341
+ if (!Util.checkMarketList(marketArr, properties)) {
1342
+ return;
1297
1343
  }
1298
-
1299
- // check if types are valid
1300
- for (const type of Object.keys(selectedTypes)) {
1301
- if (!Util._isValidType(type)) {
1302
- return;
1303
- }
1304
- if (!Array.isArray(selectedTypes[type])) {
1305
- Util.logger.error('You need to define keys, not just types to run buildDefinition');
1306
- // we need an array of keys here
1307
- return;
1308
- }
1344
+ const typeKeyList = Util.checkAndPrepareTypeKeyCombo(
1345
+ selectedTypes,
1346
+ nameArr,
1347
+ 'buildDefinition'
1348
+ );
1349
+ if (!typeKeyList) {
1350
+ return;
1309
1351
  }
1310
- // standard deploy folder
1311
- const buObject = await Cli.getCredentialObject(properties, businessUnit);
1312
- const deployDir = File.normalizePath([
1313
- properties.directories.deploy,
1314
- buObject.credential,
1315
- buObject.businessUnit,
1316
- ]);
1317
- if (isPurgeDeployFolder) {
1318
- // Clear output folder structure for selected sub-type
1319
- // only run this if the standard deploy folder is a target of buildDefinition (which technically could be changed)
1320
- await File.remove(deployDir);
1352
+
1353
+ if (Util.OPTIONS.purge) {
1354
+ const buObject = await Cli.getCredentialObject(properties, businessUnit);
1355
+ await Builder.purgeDeployFolder(buObject.credential + '/' + buObject.businessUnit);
1356
+ } else {
1357
+ Util.logger.info(` ☇ skipping purge of folder`);
1321
1358
  }
1322
1359
  /** @type {MultiMetadataTypeList} */
1323
1360
  const returnObj = {};
1324
- for (const type of Object.keys(selectedTypes)) {
1361
+ for (const type of Object.keys(typeKeyList)) {
1325
1362
  const result = await Builder.buildDefinition(
1326
1363
  businessUnit,
1327
1364
  type,
1328
- selectedTypes[type],
1365
+ typeKeyList[type],
1329
1366
  marketArr
1330
1367
  );
1331
1368
  returnObj[type] = result[type];
@@ -1347,37 +1384,36 @@ class Mcdev {
1347
1384
 
1348
1385
  Util.startLogger();
1349
1386
  Util.logger.info('mcdev:: Build Definition from Template Bulk');
1350
- let selectedTypesArr;
1351
- if ('string' === typeof selectedTypes) {
1352
- selectedTypesArr = [selectedTypes];
1387
+
1388
+ const properties = await config.getProperties();
1389
+ if (!properties) {
1390
+ return;
1391
+ }
1392
+ try {
1393
+ Util.verifyMarketList(listName, properties);
1394
+ } catch (ex) {
1395
+ Util.logger.error(ex.message);
1396
+ return;
1397
+ }
1398
+ const typeKeyList = Util.checkAndPrepareTypeKeyCombo(
1399
+ selectedTypes,
1400
+ nameArr,
1401
+ 'buildDefinitionBulk'
1402
+ );
1403
+ if (!typeKeyList) {
1404
+ return;
1405
+ }
1406
+ if (Util.OPTIONS.purge) {
1407
+ await Builder.purgeDeployFolderList(listName);
1353
1408
  } else {
1354
- selectedTypesArr = Object.keys(selectedTypes);
1355
- // check if types are valid
1356
- for (const selectedType of selectedTypesArr) {
1357
- if (!Util._isValidType(selectedType)) {
1358
- return;
1359
- }
1360
- if (!Array.isArray(selectedTypes[selectedType])) {
1361
- Util.logger.error(
1362
- 'You need to define keys, not just types to run buildDefinitionBulk'
1363
- );
1364
- // we need an array of keys here
1365
- return;
1366
- }
1367
- }
1409
+ Util.logger.info(` skipping purge of folder`);
1368
1410
  }
1369
1411
  /** @type {MultiMetadataTypeList} */
1370
1412
  const returnObj = {};
1371
- for (const selectedType of selectedTypesArr) {
1372
- if (selectedTypesArr.length > 1) {
1373
- Util.logger.info(Util.getGrayMsg(`buildDefinitionBulk for ${selectedType}`));
1374
- }
1375
- const result = await Builder.buildDefinitionBulk(
1376
- listName,
1377
- selectedType,
1378
- 'string' === typeof selectedTypes ? nameArr : selectedTypes[selectedType]
1379
- );
1380
- returnObj[selectedType] = result;
1413
+ for (const type of Object.keys(typeKeyList)) {
1414
+ Util.logger.info(Util.getGrayMsg(`buildDefinitionBulk for ${type}`));
1415
+ const result = await Builder.buildDefinitionBulk(listName, type, typeKeyList[type]);
1416
+ returnObj[type] = result;
1381
1417
  }
1382
1418
  Util.logger.info('Done');
1383
1419
  return returnObj;
@@ -1394,8 +1430,8 @@ class Mcdev {
1394
1430
  Util.startLogger();
1395
1431
  Util.logger.info('mcdev:: getFilesToCommit');
1396
1432
  const properties = await config.getProperties();
1397
- if (!(await config.checkProperties(properties))) {
1398
- return null;
1433
+ if (!properties) {
1434
+ return;
1399
1435
  }
1400
1436
  if (!Util._isValidType(selectedType)) {
1401
1437
  return;
@@ -1527,7 +1563,10 @@ class Mcdev {
1527
1563
  this.setOptions({ referenceFrom, referenceTo });
1528
1564
 
1529
1565
  const properties = await config.getProperties();
1530
- if (!Util._isValidBU(properties, businessUnit)) {
1566
+ if (!properties) {
1567
+ return;
1568
+ }
1569
+ if (!Util.isValidBU(properties, businessUnit)) {
1531
1570
  return;
1532
1571
  }
1533
1572
 
@@ -1584,6 +1623,9 @@ class Mcdev {
1584
1623
  */
1585
1624
  static async fixKeys(businessUnit, selectedTypes, keys) {
1586
1625
  const properties = await config.getProperties();
1626
+ if (!properties) {
1627
+ return;
1628
+ }
1587
1629
  // make sure validation rules dont keep us from fixing the keys
1588
1630
  this.setOptions({ skipValidation: true });
1589
1631
  let reRetrieveAll = false;
@@ -1785,9 +1827,8 @@ class Mcdev {
1785
1827
  return resultObj;
1786
1828
  }
1787
1829
  const properties = await config.getProperties();
1788
- if (!(await config.checkProperties(properties))) {
1789
- // return null here to avoid seeing 2 error messages for the same issue
1790
- return resultObj;
1830
+ if (!properties) {
1831
+ return;
1791
1832
  }
1792
1833
  for (const selectedType of selectedTypesArr || Object.keys(selectedTypesObj)) {
1793
1834
  Util.logger.info(`mcdev:: ${methodName} ${selectedType}`);
@@ -1912,6 +1953,9 @@ class Mcdev {
1912
1953
  */
1913
1954
  static async #runOnBU(methodName, cred, bu, type, keyArr) {
1914
1955
  const properties = await config.getProperties();
1956
+ if (!properties) {
1957
+ return;
1958
+ }
1915
1959
  const resultArr = [];
1916
1960
  const buObject = await Cli.getCredentialObject(
1917
1961
  properties,
@@ -1971,6 +2015,9 @@ class Mcdev {
1971
2015
  */
1972
2016
  static async #retrieveKeysWithLike(selectedType, buObject) {
1973
2017
  const properties = await config.getProperties();
2018
+ if (!properties) {
2019
+ return;
2020
+ }
1974
2021
 
1975
2022
  // cache depenencies
1976
2023
  const deployOrder = Util.getMetadataHierachy([selectedType]);
@@ -2038,6 +2085,9 @@ class Mcdev {
2038
2085
  */
2039
2086
  static async #fixKeys(cred, bu, type, keyArr) {
2040
2087
  const properties = await config.getProperties();
2088
+ if (!properties) {
2089
+ return;
2090
+ }
2041
2091
  let actuallyFixedKeys = [];
2042
2092
  const resultArr = [];
2043
2093
 
@@ -2121,6 +2171,9 @@ class Mcdev {
2121
2171
  */
2122
2172
  static async #replaceCbReference(cred, bu, type, keyArr) {
2123
2173
  const properties = await config.getProperties();
2174
+ if (!properties) {
2175
+ return;
2176
+ }
2124
2177
  let updatedKeys = [];
2125
2178
  const resultArr = [];
2126
2179