mcdev 7.0.3 → 7.0.4

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 (185) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +1 -0
  2. package/.github/PULL_REQUEST_TEMPLATE/pr_template_release.md +1 -0
  3. package/.github/workflows/code-test.yml +48 -0
  4. package/.husky/pre-commit +1 -1
  5. package/@types/lib/Builder.d.ts +13 -13
  6. package/@types/lib/Builder.d.ts.map +1 -1
  7. package/@types/lib/Deployer.d.ts +16 -16
  8. package/@types/lib/Deployer.d.ts.map +1 -1
  9. package/@types/lib/Retriever.d.ts +15 -15
  10. package/@types/lib/Retriever.d.ts.map +1 -1
  11. package/@types/lib/cli.d.ts +1 -1
  12. package/@types/lib/cli.d.ts.map +1 -1
  13. package/@types/lib/index.d.ts +34 -34
  14. package/@types/lib/index.d.ts.map +1 -1
  15. package/@types/lib/metadataTypes/Asset.d.ts +18 -43
  16. package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
  17. package/@types/lib/metadataTypes/AttributeGroup.d.ts +3 -8
  18. package/@types/lib/metadataTypes/AttributeGroup.d.ts.map +1 -1
  19. package/@types/lib/metadataTypes/AttributeSet.d.ts +5 -21
  20. package/@types/lib/metadataTypes/AttributeSet.d.ts.map +1 -1
  21. package/@types/lib/metadataTypes/Automation.d.ts +36 -49
  22. package/@types/lib/metadataTypes/Automation.d.ts.map +1 -1
  23. package/@types/lib/metadataTypes/Campaign.d.ts +11 -18
  24. package/@types/lib/metadataTypes/Campaign.d.ts.map +1 -1
  25. package/@types/lib/metadataTypes/ContentArea.d.ts +12 -31
  26. package/@types/lib/metadataTypes/ContentArea.d.ts.map +1 -1
  27. package/@types/lib/metadataTypes/DataExtension.d.ts +23 -31
  28. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
  29. package/@types/lib/metadataTypes/DataExtensionField.d.ts +12 -18
  30. package/@types/lib/metadataTypes/DataExtensionField.d.ts.map +1 -1
  31. package/@types/lib/metadataTypes/DataExtensionTemplate.d.ts +11 -11
  32. package/@types/lib/metadataTypes/DataExtensionTemplate.d.ts.map +1 -1
  33. package/@types/lib/metadataTypes/DataExtract.d.ts +12 -28
  34. package/@types/lib/metadataTypes/DataExtract.d.ts.map +1 -1
  35. package/@types/lib/metadataTypes/DataExtractType.d.ts +10 -10
  36. package/@types/lib/metadataTypes/DataExtractType.d.ts.map +1 -1
  37. package/@types/lib/metadataTypes/DeliveryProfile.d.ts +10 -10
  38. package/@types/lib/metadataTypes/DeliveryProfile.d.ts.map +1 -1
  39. package/@types/lib/metadataTypes/Discovery.d.ts +10 -10
  40. package/@types/lib/metadataTypes/Discovery.d.ts.map +1 -1
  41. package/@types/lib/metadataTypes/Email.d.ts +13 -34
  42. package/@types/lib/metadataTypes/Email.d.ts.map +1 -1
  43. package/@types/lib/metadataTypes/EmailSend.d.ts +10 -10
  44. package/@types/lib/metadataTypes/EmailSend.d.ts.map +1 -1
  45. package/@types/lib/metadataTypes/Event.d.ts +19 -16
  46. package/@types/lib/metadataTypes/Event.d.ts.map +1 -1
  47. package/@types/lib/metadataTypes/FileLocation.d.ts +10 -10
  48. package/@types/lib/metadataTypes/FileLocation.d.ts.map +1 -1
  49. package/@types/lib/metadataTypes/FileTransfer.d.ts +11 -18
  50. package/@types/lib/metadataTypes/FileTransfer.d.ts.map +1 -1
  51. package/@types/lib/metadataTypes/Filter.d.ts +10 -10
  52. package/@types/lib/metadataTypes/Filter.d.ts.map +1 -1
  53. package/@types/lib/metadataTypes/Folder.d.ts +10 -10
  54. package/@types/lib/metadataTypes/Folder.d.ts.map +1 -1
  55. package/@types/lib/metadataTypes/ImportFile.d.ts +11 -15
  56. package/@types/lib/metadataTypes/ImportFile.d.ts.map +1 -1
  57. package/@types/lib/metadataTypes/Journey.d.ts +13 -32
  58. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  59. package/@types/lib/metadataTypes/List.d.ts +12 -12
  60. package/@types/lib/metadataTypes/List.d.ts.map +1 -1
  61. package/@types/lib/metadataTypes/MetadataType.d.ts +23 -23
  62. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
  63. package/@types/lib/metadataTypes/MobileCode.d.ts +11 -20
  64. package/@types/lib/metadataTypes/MobileCode.d.ts.map +1 -1
  65. package/@types/lib/metadataTypes/MobileKeyword.d.ts +14 -14
  66. package/@types/lib/metadataTypes/MobileKeyword.d.ts.map +1 -1
  67. package/@types/lib/metadataTypes/MobileMessage.d.ts +14 -42
  68. package/@types/lib/metadataTypes/MobileMessage.d.ts.map +1 -1
  69. package/@types/lib/metadataTypes/Query.d.ts +12 -12
  70. package/@types/lib/metadataTypes/Query.d.ts.map +1 -1
  71. package/@types/lib/metadataTypes/Role.d.ts +11 -11
  72. package/@types/lib/metadataTypes/Role.d.ts.map +1 -1
  73. package/@types/lib/metadataTypes/Script.d.ts +15 -24
  74. package/@types/lib/metadataTypes/Script.d.ts.map +1 -1
  75. package/@types/lib/metadataTypes/SendClassification.d.ts +10 -10
  76. package/@types/lib/metadataTypes/SendClassification.d.ts.map +1 -1
  77. package/@types/lib/metadataTypes/SenderProfile.d.ts +11 -19
  78. package/@types/lib/metadataTypes/SenderProfile.d.ts.map +1 -1
  79. package/@types/lib/metadataTypes/TransactionalEmail.d.ts +12 -12
  80. package/@types/lib/metadataTypes/TransactionalEmail.d.ts.map +1 -1
  81. package/@types/lib/metadataTypes/TransactionalMessage.d.ts +11 -32
  82. package/@types/lib/metadataTypes/TransactionalMessage.d.ts.map +1 -1
  83. package/@types/lib/metadataTypes/TransactionalPush.d.ts +12 -21
  84. package/@types/lib/metadataTypes/TransactionalPush.d.ts.map +1 -1
  85. package/@types/lib/metadataTypes/TransactionalSMS.d.ts +12 -18
  86. package/@types/lib/metadataTypes/TransactionalSMS.d.ts.map +1 -1
  87. package/@types/lib/metadataTypes/TriggeredSend.d.ts +13 -18
  88. package/@types/lib/metadataTypes/TriggeredSend.d.ts.map +1 -1
  89. package/@types/lib/metadataTypes/User.d.ts +22 -40
  90. package/@types/lib/metadataTypes/User.d.ts.map +1 -1
  91. package/@types/lib/metadataTypes/Verification.d.ts +13 -13
  92. package/@types/lib/metadataTypes/Verification.d.ts.map +1 -1
  93. package/@types/lib/metadataTypes/definitions/Event.definition.d.ts +7 -1
  94. package/@types/lib/util/auth.d.ts +19 -18
  95. package/@types/lib/util/auth.d.ts.map +1 -1
  96. package/@types/lib/util/businessUnit.d.ts +17 -17
  97. package/@types/lib/util/businessUnit.d.ts.map +1 -1
  98. package/@types/lib/util/cache.d.ts +24 -32
  99. package/@types/lib/util/cache.d.ts.map +1 -1
  100. package/@types/lib/util/cli.d.ts +27 -27
  101. package/@types/lib/util/cli.d.ts.map +1 -1
  102. package/@types/lib/util/config.d.ts +20 -20
  103. package/@types/lib/util/config.d.ts.map +1 -1
  104. package/@types/lib/util/devops.d.ts +21 -21
  105. package/@types/lib/util/devops.d.ts.map +1 -1
  106. package/@types/lib/util/file.d.ts +179 -18
  107. package/@types/lib/util/file.d.ts.map +1 -1
  108. package/@types/lib/util/init.config.d.ts +19 -19
  109. package/@types/lib/util/init.config.d.ts.map +1 -1
  110. package/@types/lib/util/init.d.ts +20 -20
  111. package/@types/lib/util/init.d.ts.map +1 -1
  112. package/@types/lib/util/init.git.d.ts +2 -2
  113. package/@types/lib/util/init.git.d.ts.map +1 -1
  114. package/@types/lib/util/init.npm.d.ts +2 -2
  115. package/@types/lib/util/init.npm.d.ts.map +1 -1
  116. package/@types/lib/util/replaceContentBlockReference.d.ts +28 -28
  117. package/@types/lib/util/replaceContentBlockReference.d.ts.map +1 -1
  118. package/@types/lib/util/util.d.ts +33 -37
  119. package/@types/lib/util/util.d.ts.map +1 -1
  120. package/@types/types/mcdev.d.d.ts +73 -52
  121. package/@types/types/mcdev.d.d.ts.map +1 -1
  122. package/eslint.config.js +0 -1
  123. package/lib/Deployer.js +1 -1
  124. package/lib/Retriever.js +4 -1
  125. package/lib/cli.js +209 -278
  126. package/lib/index.js +89 -43
  127. package/lib/metadataTypes/Asset.js +1 -0
  128. package/lib/metadataTypes/DataExtension.js +3 -3
  129. package/lib/metadataTypes/DataExtensionField.js +0 -1
  130. package/lib/metadataTypes/Event.js +199 -35
  131. package/lib/metadataTypes/Folder.js +1 -1
  132. package/lib/metadataTypes/Journey.js +10 -2
  133. package/lib/metadataTypes/MetadataType.js +3 -4
  134. package/lib/metadataTypes/Role.js +9 -7
  135. package/lib/metadataTypes/SenderProfile.js +1 -0
  136. package/lib/metadataTypes/TransactionalEmail.js +2 -2
  137. package/lib/metadataTypes/TriggeredSend.js +1 -0
  138. package/lib/metadataTypes/User.js +31 -27
  139. package/lib/metadataTypes/definitions/Event.definition.js +8 -8
  140. package/lib/util/auth.js +0 -1
  141. package/lib/util/cli.js +0 -1
  142. package/lib/util/devops.js +2 -3
  143. package/lib/util/file.js +7 -10
  144. package/lib/util/replaceContentBlockReference.js +3 -1
  145. package/package.json +11 -9
  146. package/test/general.test.js +20 -2
  147. package/test/mockRoot/.mcdevrc.json +1 -1
  148. package/test/mockRoot/deploy/testInstance/testBU/event/testExisting_event.event-meta.json +201 -0
  149. package/test/mockRoot/deploy/testInstance/testBU/event/testNew_event_withExistingDE.event-meta.json +232 -0
  150. package/test/mockRoot/deploy/testInstance/testBU/event/testNew_event_withSchema.event-meta.json +193 -0
  151. package/test/resources/9999999/dataExtension/retrieve-CustomerKey=testNew_event_withSchema-response.xml +50 -0
  152. package/test/resources/9999999/dataExtension/retrieve-createdViaEvent-response.xml +50 -0
  153. package/test/resources/9999999/dataExtension/retrieve-response.xml +48 -0
  154. package/test/resources/9999999/dataExtension/retrieve_event_withSchema-expected.json +219 -0
  155. package/test/resources/9999999/dataExtension/update-afterCreatedViaEvent-response.xml +55 -0
  156. package/test/resources/9999999/dataExtension/update-callout-afterCreatedViaEvent-expected.xml +1 -0
  157. package/test/resources/9999999/dataExtensionField/retrieve-DataExtension.CustomerKey=testNew_event_withSchema-response.xml +461 -0
  158. package/test/resources/9999999/dataExtensionField/retrieve-DataExtension.CustomerKeyINtestExisting_dataExtension,testNew_dataExtension-response.xml +133 -0
  159. package/test/resources/9999999/dataExtensionField/retrieve-response.xml +870 -0
  160. package/test/resources/9999999/event/build-expected.json +228 -0
  161. package/test/resources/9999999/event/get-expected.json +235 -0
  162. package/test/resources/9999999/event/post_withExistingDE-callout-expected.json +17 -0
  163. package/test/resources/9999999/event/post_withExistingDE-expected.json +21 -0
  164. package/test/resources/9999999/event/post_withSchema-callout-expected.json +196 -0
  165. package/test/resources/9999999/event/post_withSchema-expected.json +232 -0
  166. package/test/resources/9999999/event/put-callout-expected.json +202 -0
  167. package/test/resources/9999999/event/put-expected.json +233 -0
  168. package/test/resources/9999999/event/template-expected.json +228 -0
  169. package/test/resources/9999999/interaction/v1/eventDefinitions/get-response.json +252 -2
  170. package/test/resources/9999999/interaction/v1/eventDefinitions/key_testExisting_event/delete-response.txt +0 -0
  171. package/test/resources/9999999/interaction/v1/eventDefinitions/key_testExisting_event/put-response.json +241 -0
  172. package/test/resources/9999999/interaction/v1/eventDefinitions/post_withExistingDE-response.json +26 -0
  173. package/test/resources/9999999/interaction/v1/eventDefinitions/post_withSchema-response.json +241 -0
  174. package/test/resources/9999999/interaction/v1/interactions/0175b971-71a3-4d8e-98ac-48121f3fbf4f/delete-response.txt +1 -0
  175. package/test/type.asset.test.js +0 -3
  176. package/test/type.automation.test.js +1 -1
  177. package/test/type.dataExtension.test.js +4 -4
  178. package/test/type.event.test.js +287 -6
  179. package/test/type.journey.test.js +13 -0
  180. package/test/type.user.test.js +1 -1
  181. package/test/utils.js +18 -6
  182. package/tsconfig.json +6 -1
  183. package/{jsconfig.json → tsconfig.npmScripts.json} +1 -1
  184. package/tsconfig.precommit.json +26 -0
  185. package/types/mcdev.d.js +12 -13
package/lib/index.js CHANGED
@@ -557,39 +557,74 @@ class Mcdev {
557
557
  * deletes metadata from MC instance by key
558
558
  *
559
559
  * @param {string} businessUnit references credentials from properties.json
560
- * @param {string} type supported metadata type
561
- * @param {string} customerKey Identifier of metadata
560
+ * @param {string | TypeKeyCombo} selectedTypes supported metadata type (single) or complex object
561
+ * @param {string[] | string} [keyArr] Identifier of metadata
562
562
  * @returns {Promise.<boolean>} true if successful, false otherwise
563
563
  */
564
- static async deleteByKey(businessUnit, type, customerKey) {
564
+ static async deleteByKey(businessUnit, selectedTypes, keyArr) {
565
565
  Util.startLogger();
566
566
  Util.logger.info('mcdev:: delete');
567
- if (!Util._isValidType(type)) {
568
- return;
567
+ /** @typedef {string[]} */
568
+ let selectedTypesArr;
569
+ /** @typedef {TypeKeyCombo} */
570
+ let selectedTypesObj;
571
+ if ('string' === typeof keyArr) {
572
+ keyArr = [keyArr];
573
+ }
574
+ if ('string' === typeof selectedTypes) {
575
+ selectedTypesArr = [selectedTypes];
576
+ } else {
577
+ selectedTypesObj = selectedTypes;
578
+ // reset keys array because it will be overriden by values from selectedTypesObj
579
+ keyArr = null;
580
+ }
581
+ // check if types are valid
582
+ for (const selectedType of selectedTypesArr || Object.keys(selectedTypesObj)) {
583
+ if (!Util._isValidType(selectedType)) {
584
+ return;
585
+ }
569
586
  }
570
587
  const properties = await config.getProperties();
571
588
  if (!(await config.checkProperties(properties))) {
572
589
  return null;
573
590
  }
574
591
  const buObject = await Cli.getCredentialObject(properties, businessUnit);
575
- if (buObject !== null) {
576
- try {
577
- MetadataTypeInfo[type].client = auth.getSDK(buObject);
578
- } catch (ex) {
579
- Util.logger.error(ex.message);
592
+ if (!buObject) {
593
+ return;
594
+ }
595
+ let client;
596
+ try {
597
+ client = auth.getSDK(buObject);
598
+ } catch (ex) {
599
+ Util.logger.error(ex.message);
600
+ return;
601
+ }
602
+ let status = true;
603
+ for (const type of selectedTypesArr || Object.keys(selectedTypesObj)) {
604
+ keyArr = selectedTypesArr ? keyArr : selectedTypesObj[type];
605
+ if (!keyArr) {
606
+ Util.logger.error(`No keys set for ${type}`);
580
607
  return;
581
608
  }
582
- Util.logger.info(
583
- Util.getGrayMsg(` - Deleting ${type} ${customerKey} on BU ${businessUnit}`)
584
- );
585
- try {
586
- MetadataTypeInfo[type].properties = properties;
587
- MetadataTypeInfo[type].buObject = buObject;
588
- return await MetadataTypeInfo[type].deleteByKey(customerKey);
589
- } catch (ex) {
590
- Util.logger.errorStack(ex, ` - Deleting ${type} failed`);
609
+ for (const key of keyArr) {
610
+ MetadataTypeInfo[type].client = client;
611
+
612
+ Util.logger.info(
613
+ Util.getGrayMsg(` - Deleting ${type} ${key} on BU ${businessUnit}`)
614
+ );
615
+ try {
616
+ MetadataTypeInfo[type].properties = properties;
617
+ MetadataTypeInfo[type].buObject = buObject;
618
+ const result = await MetadataTypeInfo[type].deleteByKey(key);
619
+ status &&= result;
620
+ } catch (ex) {
621
+ Util.logger.errorStack(ex, ` - Deleting ${type} failed`);
622
+ status = false;
623
+ }
591
624
  }
592
625
  }
626
+
627
+ return status;
593
628
  }
594
629
  /**
595
630
  * get name & key for provided id
@@ -1010,13 +1045,12 @@ class Mcdev {
1010
1045
  * Updates the key to match the name field
1011
1046
  *
1012
1047
  * @param {string} businessUnit name of BU
1013
- * @param {TypeKeyCombo|undefined} selectedTypesArr limit retrieval to given metadata type
1014
- * @param {ContentBlockConversionTypes} to what to replace with
1015
- * @param {ContentBlockConversionTypes[]} [fromList] what to replace
1048
+ * @param {TypeKeyCombo | undefined} selectedTypesObj limit retrieval to given metadata type
1049
+ * @param {string} to what to replace with
1050
+ * @param {string[]} [fromList] what to replace
1016
1051
  * @returns {Promise.<Object.<string, object>>} key1: business unit name, key2:type value: list of fixed item keys
1017
1052
  */
1018
- static async replaceCbReference(businessUnit, selectedTypesArr, to, fromList) {
1019
- /** @type {ContentBlockConversionTypes[]} */
1053
+ static async replaceCbReference(businessUnit, selectedTypesObj, to, fromList) {
1020
1054
  const allowedFromTo = ['key', 'name', 'id'];
1021
1055
  if (!allowedFromTo.includes(to)) {
1022
1056
  Util.logger.error(
@@ -1053,9 +1087,11 @@ class Mcdev {
1053
1087
  if (!Util._isValidBU(properties, businessUnit)) {
1054
1088
  return;
1055
1089
  }
1056
- if (selectedTypesArr) {
1090
+ /** @typedef {string[]} */
1091
+ let selectedTypesArr;
1092
+ if (selectedTypesObj) {
1057
1093
  // check if types are valid
1058
- for (const selectedType of Object.keys(selectedTypesArr)) {
1094
+ for (const selectedType of Object.keys(selectedTypesObj)) {
1059
1095
  if (!Util._isValidType(selectedType)) {
1060
1096
  return;
1061
1097
  }
@@ -1081,19 +1117,19 @@ class Mcdev {
1081
1117
  selectedTypesArr
1082
1118
  )
1083
1119
  ? selectedTypesArr
1084
- : Object.keys(selectedTypesArr)
1120
+ : Object.keys(selectedTypesObj)
1085
1121
  ).join(', ')}`
1086
1122
  );
1087
1123
 
1088
1124
  const response = {};
1089
1125
  for (const selectedType of Array.isArray(selectedTypesArr)
1090
1126
  ? selectedTypesArr
1091
- : Object.keys(selectedTypesArr)) {
1127
+ : Object.keys(selectedTypesObj)) {
1092
1128
  const temp = await this.#runMethod(
1093
1129
  'replaceCbReference',
1094
1130
  businessUnit,
1095
1131
  selectedType,
1096
- Array.isArray(selectedTypesArr) ? null : selectedTypesArr[selectedType]
1132
+ Array.isArray(selectedTypesArr) ? null : selectedTypesObj[selectedType]
1097
1133
  );
1098
1134
  response[businessUnit] ||= {};
1099
1135
  response[businessUnit][selectedType] = temp[businessUnit];
@@ -1106,22 +1142,31 @@ class Mcdev {
1106
1142
  * Updates the key to match the name field
1107
1143
  *
1108
1144
  * @param {string} businessUnit name of BU
1109
- * @param {string[] | TypeKeyCombo} selectedTypesArr limit retrieval to given metadata type
1145
+ * @param {string[] | TypeKeyCombo} selectedTypes limit retrieval to given metadata type
1110
1146
  * @param {string[]} [keys] customerkey of the metadata
1111
1147
  * @returns {Promise.<Object.<string, object>>} key1: business unit name, key2:type value: list of fixed item keys
1112
1148
  */
1113
- static async fixKeys(businessUnit, selectedTypesArr, keys) {
1149
+ static async fixKeys(businessUnit, selectedTypes, keys) {
1114
1150
  const properties = await config.getProperties();
1115
1151
  let reRetrieveAll = false;
1116
- if (selectedTypesArr) {
1152
+ /** @typedef {string[]} */
1153
+ let selectedTypesArr;
1154
+ /** @typedef {TypeKeyCombo} */
1155
+ let selectedTypesObj;
1156
+ if (selectedTypes) {
1117
1157
  // check if types are valid
1118
- for (const selectedType of Array.isArray(selectedTypesArr)
1119
- ? selectedTypesArr
1120
- : Object.keys(selectedTypesArr)) {
1158
+ for (const selectedType of Array.isArray(selectedTypes)
1159
+ ? selectedTypes
1160
+ : Object.keys(selectedTypes)) {
1121
1161
  if (!Util._isValidType(selectedType)) {
1122
1162
  return;
1123
1163
  }
1124
1164
  }
1165
+ if (Array.isArray(selectedTypes)) {
1166
+ selectedTypesArr = selectedTypes;
1167
+ } else {
1168
+ selectedTypesObj = selectedTypes;
1169
+ }
1125
1170
  } else {
1126
1171
  // do it for all standard retrieve types
1127
1172
  selectedTypesArr = [];
@@ -1139,7 +1184,9 @@ class Mcdev {
1139
1184
  )
1140
1185
  )
1141
1186
  );
1142
- Util.logger.info(`:: Fixing keys for ${selectedTypesArr.join(', ')}`);
1187
+ Util.logger.info(
1188
+ `:: Fixing keys for ${selectedTypesArr ? selectedTypesArr.join(', ') : Object.keys(selectedTypesObj).join(', ')}`
1189
+ );
1143
1190
  reRetrieveAll = true;
1144
1191
  this.setOptions({
1145
1192
  skipInteraction: { fixKeysReretrieve: false },
@@ -1147,17 +1194,15 @@ class Mcdev {
1147
1194
  }
1148
1195
 
1149
1196
  const response = {};
1150
- for (const selectedType of Array.isArray(selectedTypesArr)
1151
- ? selectedTypesArr
1152
- : Object.keys(selectedTypesArr)) {
1197
+ for (const selectedType of selectedTypesArr || Object.keys(selectedTypesObj)) {
1153
1198
  if (selectedType === 'event') {
1154
1199
  Util.logger.warn(
1155
1200
  `Type 'event' is not supported for fixKeys for compatibility reasons. Draft Journeys would otherwise be broken after the key change. If you do need to update an event key, use deploy --changeKeyValue or --changeKeyField instead.`
1156
1201
  );
1157
- if (Array.isArray(selectedTypesArr)) {
1158
- selectedTypesArr = selectedTypesArr.filter((type) => type !== 'event');
1202
+ if (Array.isArray(selectedTypes)) {
1203
+ selectedTypes = selectedTypes.filter((type) => type !== 'event');
1159
1204
  } else {
1160
- delete selectedTypesArr.event;
1205
+ delete selectedTypes.event;
1161
1206
  }
1162
1207
  continue;
1163
1208
  }
@@ -1165,13 +1210,14 @@ class Mcdev {
1165
1210
  'fixKeys',
1166
1211
  businessUnit,
1167
1212
  selectedType,
1168
- Array.isArray(selectedTypesArr) ? keys : selectedTypesArr[selectedType]
1213
+ selectedTypesArr ? keys : selectedTypesObj[selectedType]
1169
1214
  );
1170
1215
  response[businessUnit] ||= {};
1171
1216
  response[businessUnit][selectedType] = temp[businessUnit];
1172
1217
  }
1173
1218
 
1174
1219
  if (reRetrieveAll) {
1220
+ // only done if selectedTypesArr is set as fallback
1175
1221
  Util.logger.info(
1176
1222
  `Retrieving latest versions of ${selectedTypesArr.join(', ')} from server`
1177
1223
  );
@@ -2217,6 +2217,7 @@ class Asset extends MetadataType {
2217
2217
 
2218
2218
  if (!changes) {
2219
2219
  const ex = new Error('No changes made to the code.');
2220
+ // @ts-expect-error TODO: create custom Error object
2220
2221
  ex.code = 200;
2221
2222
  throw ex;
2222
2223
  }
@@ -278,10 +278,11 @@ class DataExtension extends MetadataType {
278
278
  * Updates a single dataExtension. Also updates their columns in 'dataExtension.columns'
279
279
  *
280
280
  * @param {DataExtensionItem} metadata single metadata entry
281
+ * @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
281
282
  * @returns {Promise} Promise
282
283
  */
283
- static async update(metadata) {
284
- return super.updateSOAP(metadata);
284
+ static async update(metadata, handleOutside) {
285
+ return super.updateSOAP(metadata, handleOutside);
285
286
  }
286
287
  /**
287
288
  * Gets executed after deployment of metadata type
@@ -678,7 +679,6 @@ class DataExtension extends MetadataType {
678
679
  ],
679
680
  },
680
681
  };
681
- // @ts-expect-error SFMC-SDK typing error
682
682
  await this.client.soap.update(Util.capitalizeFirstLetter(soapType), payload, null);
683
683
  return randomSuffix;
684
684
  }
@@ -315,7 +315,6 @@ class DataExtensionField extends MetadataType {
315
315
  };
316
316
  try {
317
317
  // ! we really do need to delete from DataExtension not DataExtensionField here!
318
- // @ts-expect-error wrong jsdoc for 2nd parameter in SFMC-SDK
319
318
  this.client.soap.delete('DataExtension', keyObj, null);
320
319
 
321
320
  if (!fieldId) {
@@ -4,6 +4,7 @@ import MetadataType from './MetadataType.js';
4
4
  import { Util } from '../util/util.js';
5
5
  import File from '../util/file.js';
6
6
  import cache from '../util/cache.js';
7
+ import deepEqual from 'deep-equal';
7
8
 
8
9
  /**
9
10
  * @typedef {import('../../types/mcdev.d.js').BuObject} BuObject
@@ -24,6 +25,7 @@ import cache from '../util/cache.js';
24
25
  * @augments MetadataType
25
26
  */
26
27
  class Event extends MetadataType {
28
+ static reCacheDataExtensions = [];
27
29
  /**
28
30
  * Retrieves Metadata of Event Definition.
29
31
  * Endpoint /interaction/v1/eventDefinitions return all Event Definitions with all details.
@@ -92,30 +94,26 @@ class Event extends MetadataType {
92
94
  );
93
95
  } else if (event?.length === 1) {
94
96
  const originalKey = event[0][this.definition.keyField];
95
- const eventDef = JSON.parse(
96
- Util.replaceByObject(
97
- JSON.stringify(this.postRetrieveTasks(event[0])),
98
- templateVariables
99
- )
97
+ const metadataItemTemplated = Util.replaceByObject(
98
+ this.postRetrieveTasks(event[0]),
99
+ templateVariables
100
100
  );
101
- if (!eventDef.dataExtensionId) {
101
+
102
+ if (!metadataItemTemplated.r__dataExtension_key) {
102
103
  throw new Error(
103
- `Event.postRetrieveTasks:: ` +
104
- `No Data Extension found for ` +
105
- `event: ${eventDef.name}. ` +
106
- `This cannot be templated`
104
+ `Event.postRetrieveTasks:: No Data Extension found for ${this.definition.type}: ${metadataItemTemplated.name}. This cannot be templated.`
107
105
  );
108
106
  }
109
107
 
110
108
  // remove all fields listed in Definition for templating
111
- this.keepTemplateFields(eventDef);
109
+ this.keepTemplateFields(metadataItemTemplated);
112
110
  await File.writeJSONToFile(
113
111
  [templateDir, this.definition.type].join('/'),
114
112
  originalKey + '.' + this.definition.type + '-meta',
115
- JSON.parse(Util.replaceByObject(JSON.stringify(eventDef), templateVariables))
113
+ metadataItemTemplated
116
114
  );
117
- Util.logger.info(`- templated ${this.definition.type}: ${name}`);
118
- return { metadata: eventDef, type: this.definition.type };
115
+ Util.logger.info(` - templated ${this.definition.type}: ${name}`);
116
+ return { metadata: metadataItemTemplated, type: this.definition.type };
119
117
  } else {
120
118
  throw new Error(
121
119
  `Encountered unknown error when retrieveing ${
@@ -175,7 +173,8 @@ class Event extends MetadataType {
175
173
  static async update(metadataEntry) {
176
174
  return super.updateREST(
177
175
  metadataEntry,
178
- '/interaction/v1/eventDefinitions/' + metadataEntry.id,
176
+ '/interaction/v1/eventDefinitions/key:' +
177
+ encodeURIComponent(metadataEntry[this.definition.keyField]),
179
178
  'put'
180
179
  );
181
180
  }
@@ -187,25 +186,8 @@ class Event extends MetadataType {
187
186
  * @returns {MetadataTypeItem} parsed version
188
187
  */
189
188
  static preDeployTasks(metadata) {
190
- if (metadata.r__dataExtension_key) {
191
- metadata.dataExtensionId = cache.searchForField(
192
- 'dataExtension',
193
- metadata.r__dataExtension_key,
194
- 'CustomerKey',
195
- 'ObjectID'
196
- );
197
- metadata.dataExtensionName = cache.searchForField(
198
- 'dataExtension',
199
- metadata.r__dataExtension_key,
200
- 'CustomerKey',
201
- 'Name'
202
- );
203
- metadata.arguments.dataExtensionId = metadata.dataExtensionId;
204
- if (metadata.schema) {
205
- metadata.schema.id = metadata.dataExtensionId;
206
- metadata.schema.name = metadata.dataExtensionName;
207
- }
208
- }
189
+ // Note: lots has to be done in createOrUpdate based on what action is required
190
+ metadata.arguments ||= {};
209
191
  metadata.arguments.eventDefinitionKey = metadata.eventDefinitionKey;
210
192
 
211
193
  // standard values
@@ -214,6 +196,187 @@ class Event extends MetadataType {
214
196
  metadata.mode = 'Production'; // potentially needs to be set to "1" instead
215
197
  return metadata;
216
198
  }
199
+ /**
200
+ * helper for {@link MetadataType.upsert}
201
+ *
202
+ * @param {MetadataTypeMap} metadataMap list of metadata
203
+ * @param {string} metadataKey key of item we are looking at
204
+ * @param {boolean} hasError error flag from previous code
205
+ * @param {MetadataTypeItemDiff[]} metadataToUpdate list of items to update
206
+ * @param {MetadataTypeItem[]} metadataToCreate list of items to create
207
+ * @returns {Promise.<'create'|'update'|'skip'>} action to take
208
+ */
209
+ static async createOrUpdate(
210
+ metadataMap,
211
+ metadataKey,
212
+ hasError,
213
+ metadataToUpdate,
214
+ metadataToCreate
215
+ ) {
216
+ const createOrUpdateAction = await super.createOrUpdate(
217
+ metadataMap,
218
+ metadataKey,
219
+ hasError,
220
+ metadataToUpdate,
221
+ metadataToCreate
222
+ );
223
+ const metadataItem = metadataMap[metadataKey];
224
+ if (createOrUpdateAction === 'update') {
225
+ if (metadataItem.r__dataExtension_key) {
226
+ metadataItem.dataExtensionId = cache.searchForField(
227
+ 'dataExtension',
228
+ metadataItem.r__dataExtension_key,
229
+ 'CustomerKey',
230
+ 'ObjectID'
231
+ );
232
+ metadataItem.dataExtensionName = cache.searchForField(
233
+ 'dataExtension',
234
+ metadataItem.r__dataExtension_key,
235
+ 'CustomerKey',
236
+ 'Name'
237
+ );
238
+ metadataItem.arguments.dataExtensionId = metadataItem.dataExtensionId;
239
+ if (metadataItem.schema) {
240
+ metadataItem.schema.id = metadataItem.dataExtensionId;
241
+ metadataItem.schema.name = metadataItem.dataExtensionName;
242
+ }
243
+ }
244
+ if (metadataItem.schema?.fields?.length) {
245
+ const normalizedKey = File.reverseFilterIllegalFilenames(
246
+ metadataMap[metadataKey][this.definition.keyField]
247
+ );
248
+ const cachedVersion = cache.getByKey(this.definition.type, normalizedKey);
249
+ if (cachedVersion?.schema?.fields?.length) {
250
+ const cacheClone = structuredClone(cachedVersion);
251
+ cacheClone.schema.fields = cacheClone.schema.fields.map((field) => {
252
+ delete field.isDevicePreference;
253
+ return field;
254
+ });
255
+ if (!deepEqual(metadataItem?.schema?.fields, cacheClone?.schema?.fields)) {
256
+ Util.logger.warn(
257
+ ` - ${this.definition.type} ${metadataItem[this.definition.keyField]}: schema fields differ from server version. Resetting as this will not be reflected on dataExtension.`
258
+ );
259
+ metadataItem.schema.fields = cacheClone.schema.fields;
260
+ }
261
+ }
262
+ }
263
+ } else if (createOrUpdateAction === 'create') {
264
+ try {
265
+ if (metadataItem.r__dataExtension_key) {
266
+ metadataItem.dataExtensionId = cache.searchForField(
267
+ 'dataExtension',
268
+ metadataItem.r__dataExtension_key,
269
+ 'CustomerKey',
270
+ 'ObjectID'
271
+ );
272
+ metadataItem.dataExtensionName = cache.searchForField(
273
+ 'dataExtension',
274
+ metadataItem.r__dataExtension_key,
275
+ 'CustomerKey',
276
+ 'Name'
277
+ );
278
+ if (metadataItem.schema) {
279
+ delete metadataItem.schema;
280
+ Util.logger.info(
281
+ ` - ${this.definition.type} ${metadataItem[this.definition.keyField]}: dataExtension ${metadataItem.r__dataExtension_key} found, ignoring schema-section in ${this.definition.type} json`
282
+ );
283
+ }
284
+ }
285
+ } catch {
286
+ // no action
287
+ }
288
+ if (metadataItem.schema) {
289
+ if (metadataItem.r__dataExtension_key) {
290
+ metadataItem.schema.name = metadataItem.r__dataExtension_key;
291
+ }
292
+ Util.logger.warn(
293
+ `Data Extension ${metadataItem.schema.name || metadataItem[this.definition.keyField]} not found on BU. Creating it automatically based on schema-definition.`
294
+ );
295
+ // we want the event api to create the DE for us based on the schema
296
+ this.reCacheDataExtensions.push({
297
+ eventKey: metadataItem[this.definition.keyField],
298
+ deKey: metadataItem.schema.name || metadataItem[this.definition.keyField],
299
+ });
300
+ }
301
+ }
302
+ return createOrUpdateAction;
303
+ }
304
+ /**
305
+ * Gets executed after deployment of metadata type
306
+ *
307
+ * @param {MetadataTypeMap} upsertResults metadata mapped by their keyField as returned by update/create
308
+ * @param {MetadataTypeMap} originalMetadata metadata to be updated (contains additioanl fields)
309
+ * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates
310
+ * @returns {Promise.<void>} -
311
+ */
312
+ static async postDeployTasks(upsertResults, originalMetadata, createdUpdated) {
313
+ // CREATE ONLY - if dataExtensions were auto-
314
+ if (this.reCacheDataExtensions.length && createdUpdated.created > 0) {
315
+ Util.logger.warn(' - Re-caching dependent Metadata: dataExtension');
316
+ const deRetrieve = await DataExtension.retrieveForCache();
317
+ cache.setMetadata('dataExtension', deRetrieve.metadata);
318
+ const reDownloadDeKeys = [];
319
+ // try to update key & name of the auto-generated dataExtension
320
+ for (const { eventKey, deKey } of this.reCacheDataExtensions) {
321
+ if (!upsertResults[eventKey]) {
322
+ continue;
323
+ }
324
+ const eventItem = upsertResults[eventKey];
325
+ const newDeKey = cache.searchForField(
326
+ 'dataExtension',
327
+ eventItem.dataExtensionId,
328
+ 'ObjectID',
329
+ 'CustomerKey'
330
+ );
331
+ // get dataExtension from cache which conveniently already has the ObjectID set
332
+ const deObj = cache.getByKey('dataExtension', newDeKey);
333
+ const oldName = deObj[DataExtension.definition.nameField];
334
+ // prepare a clone of the DE to update name & key to match the event
335
+ const clone = structuredClone(deObj);
336
+ clone[DataExtension.definition.keyField] = deKey;
337
+ clone[DataExtension.definition.nameField] = deKey;
338
+ try {
339
+ // update DE on server
340
+ await DataExtension.update(clone, true);
341
+ Util.logger.info(
342
+ ` - changed dataExtension ${newDeKey} (${oldName}) key/name to ${deKey}`
343
+ );
344
+ // update cache
345
+ deObj[DataExtension.definition.keyField] = deKey;
346
+ deObj[DataExtension.definition.nameField] = deKey;
347
+
348
+ reDownloadDeKeys.push(deObj[DataExtension.definition.keyField]);
349
+ } catch {
350
+ // fallback, set DE key to value of DE name
351
+ const clone = structuredClone(deObj);
352
+ clone[DataExtension.definition.keyField] = oldName;
353
+ try {
354
+ // update DE on server
355
+ await DataExtension.update(clone, true);
356
+ Util.logger.info(
357
+ ` - changed dataExtension ${newDeKey} (${oldName}) key to ${oldName}`
358
+ );
359
+ // update cache
360
+ deObj[DataExtension.definition.keyField] =
361
+ deObj[DataExtension.definition.nameField];
362
+
363
+ reDownloadDeKeys.push(deObj[DataExtension.definition.keyField]);
364
+ } catch {
365
+ Util.logger.debug(
366
+ ` - failed to change dataExtension ${newDeKey} (${oldName}) key/name`
367
+ );
368
+ }
369
+ }
370
+ }
371
+ this.reCacheDataExtensions.length = 0;
372
+
373
+ // ensure we have downloaded auto-created DEs
374
+ if (reDownloadDeKeys.length) {
375
+ const retriever = new Retriever(this.properties, this.buObject);
376
+ await retriever.retrieve(['dataExtension'], reDownloadDeKeys);
377
+ }
378
+ }
379
+ }
217
380
 
218
381
  /**
219
382
  * parses retrieved Metadata before saving
@@ -234,7 +397,6 @@ class Event extends MetadataType {
234
397
  delete metadata.arguments.dataExtensionId;
235
398
  if (metadata.schema) {
236
399
  delete metadata.schema.id;
237
- delete metadata.schema.name;
238
400
  }
239
401
  } catch (ex) {
240
402
  Util.logger.verbose(
@@ -249,6 +411,8 @@ class Event extends MetadataType {
249
411
 
250
412
  // Assign definition to static attributes
251
413
  import MetadataTypeDefinitions from '../MetadataTypeDefinitions.js';
414
+ import DataExtension from './DataExtension.js';
415
+ import Retriever from './../Retriever.js';
252
416
  Event.definition = MetadataTypeDefinitions.event;
253
417
 
254
418
  export default Event;
@@ -361,7 +361,7 @@ class Folder extends MetadataType {
361
361
  const response = await super.createREST(restPayload, '/email/v1/category', true);
362
362
  if (response?.objectId) {
363
363
  // convert the response to the same format as the SOAP response
364
- metadataEntry.ID = response.objectId;
364
+ metadataEntry.ID = response.categoryId;
365
365
  // the following is a bit of a hack to make the response look like the SOAP response; not sure if we actually need that anywhere like this --> future developers feel free to double check
366
366
  const returnObject = {
367
367
  Results: [
@@ -49,7 +49,11 @@ class Journey extends MetadataType {
49
49
  let singleKey = '';
50
50
  let mode = 'key';
51
51
  if (key) {
52
- if (key.startsWith('id:') || key.startsWith('%23')) {
52
+ if (key.startsWith('%23')) {
53
+ // correct the format
54
+ key = 'id:' + key.slice(3);
55
+ }
56
+ if (key.startsWith('id:')) {
53
57
  // ! allow selecting journeys by ID because that's what users see in the URL
54
58
  // if the key started with %23 assume an ID was copied from the URL but the user forgot to prefix it with id:
55
59
 
@@ -59,6 +63,8 @@ class Journey extends MetadataType {
59
63
  // in the journey URL the Id is prefixed with an HTML-encoded "#" which could accidentally be copied by users
60
64
  // despite the slicing above, this still needs testing here because users might have prefixed the ID with id: but did not know to remove the #23
61
65
  singleKey = singleKey.slice(3);
66
+ // correct the format to ensure we show sth readable in the "Downloaded" log
67
+ key = 'id:' + singleKey;
62
68
  }
63
69
  if (singleKey.includes('/')) {
64
70
  // in the journey URL the version is appended after the ID, separated by a forward-slash. Needs to be removed from the ID for the retrieve as we always aim to retrieve the latest version only
@@ -89,7 +95,7 @@ class Journey extends MetadataType {
89
95
  const results = this.definition.restPagination
90
96
  ? await this.client.rest.getBulk(uri, this.definition.restPageSize || 500)
91
97
  : await this.client.rest.get(uri);
92
- // const results = this.parseResponseBody(response);
98
+
93
99
  if (results.items?.length) {
94
100
  // empty results will come back without "items" defined
95
101
  Util.logger.info(
@@ -1182,6 +1188,7 @@ class Journey extends MetadataType {
1182
1188
  const activities = item.activities.filter((activity) => activity.type === 'EMAILV2');
1183
1189
  if (!activities) {
1184
1190
  const ex = new Error('No changes made to the code.');
1191
+ // @ts-expect-error custom error object
1185
1192
  ex.code = 200;
1186
1193
  throw ex;
1187
1194
  }
@@ -1278,6 +1285,7 @@ class Journey extends MetadataType {
1278
1285
 
1279
1286
  if (!changes) {
1280
1287
  const ex = new Error('No changes made to the code.');
1288
+ // @ts-expect-error custom error object
1281
1289
  ex.code = 200;
1282
1290
  throw ex;
1283
1291
  }
@@ -26,7 +26,7 @@ import Mustache from 'mustache';
26
26
  * @typedef {import('../../types/mcdev.d.js').MetadataTypeMapObj} MetadataTypeMapObj
27
27
  * @typedef {import('../../types/mcdev.d.js').SoapRequestParams} SoapRequestParams
28
28
  * @typedef {import('../../types/mcdev.d.js').TemplateMap} TemplateMap
29
- * @typedef {import('../../types/mcdev.d.js').SDK} SDK
29
+ * @typedef {import('sfmc-sdk').default} SDK
30
30
  * @typedef {import('../../types/mcdev.d.js').SDKError} SDKError
31
31
  * @typedef {import('../../types/mcdev.d.js').SOAPError} SOAPError
32
32
  * @typedef {import('../../types/mcdev.d.js').RestError} RestError
@@ -148,9 +148,9 @@ class MetadataType {
148
148
  * @param {MetadataTypeMap} upsertResults metadata mapped by their keyField as returned by update/create
149
149
  * @param {MetadataTypeMap} originalMetadata metadata to be updated (contains additioanl fields)
150
150
  * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates
151
- * @returns {void}
151
+ * @returns {Promise.<void>} -
152
152
  */
153
- static postDeployTasks(upsertResults, originalMetadata, createdUpdated) {}
153
+ static async postDeployTasks(upsertResults, originalMetadata, createdUpdated) {}
154
154
 
155
155
  /**
156
156
  * helper for {@link MetadataType.createREST}
@@ -2229,7 +2229,6 @@ class MetadataType {
2229
2229
  metadata[overrideKeyField || this.definition.keyField] = customerKey;
2230
2230
  const soapType = this.definition.soapType || this.definition.type;
2231
2231
  try {
2232
- // @ts-expect-error - wrong jsdoc in SFMC-SDK
2233
2232
  await this.client.soap.delete(Util.capitalizeFirstLetter(soapType), metadata, null);
2234
2233
  if (!handleOutside) {
2235
2234
  Util.logger.info(` - deleted ${this.definition.type}: ${customerKey}`);