mcdev 7.3.1 → 7.4.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 (71) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
  2. package/@types/lib/Deployer.d.ts.map +1 -1
  3. package/@types/lib/index.d.ts.map +1 -1
  4. package/@types/lib/metadataTypes/Asset.d.ts +25 -0
  5. package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
  6. package/@types/lib/metadataTypes/DataExtension.d.ts +3 -2
  7. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
  8. package/@types/lib/metadataTypes/DataExtensionField.d.ts.map +1 -1
  9. package/@types/lib/metadataTypes/Event.d.ts +39 -7
  10. package/@types/lib/metadataTypes/Event.d.ts.map +1 -1
  11. package/@types/lib/metadataTypes/Journey.d.ts +4 -3
  12. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  13. package/@types/lib/metadataTypes/MetadataType.d.ts +22 -3
  14. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
  15. package/@types/lib/util/init.config.d.ts.map +1 -1
  16. package/@types/lib/util/replaceContentBlockReference.d.ts +4 -5
  17. package/@types/lib/util/replaceContentBlockReference.d.ts.map +1 -1
  18. package/@types/lib/util/util.d.ts +18 -2
  19. package/@types/lib/util/util.d.ts.map +1 -1
  20. package/@types/lib/util/validations.d.ts +9 -0
  21. package/@types/lib/util/validations.d.ts.map +1 -0
  22. package/@types/types/mcdev.d.d.ts +195 -0
  23. package/@types/types/mcdev.d.d.ts.map +1 -1
  24. package/boilerplate/config.json +22 -0
  25. package/boilerplate/files/.gitattributes +1 -1
  26. package/boilerplate/files/README.md +1 -1
  27. package/boilerplate/forcedUpdates.json +8 -0
  28. package/boilerplate/gitignore-template +1 -0
  29. package/lib/Deployer.js +5 -0
  30. package/lib/cli.js +28 -3
  31. package/lib/index.js +3 -2
  32. package/lib/metadataTypes/Asset.js +87 -7
  33. package/lib/metadataTypes/DataExtension.js +74 -17
  34. package/lib/metadataTypes/DataExtensionField.js +11 -3
  35. package/lib/metadataTypes/Event.js +171 -105
  36. package/lib/metadataTypes/Journey.js +207 -89
  37. package/lib/metadataTypes/MetadataType.js +182 -37
  38. package/lib/metadataTypes/definitions/TriggeredSend.definition.js +4 -2
  39. package/lib/util/config.js +4 -4
  40. package/lib/util/init.config.js +10 -6
  41. package/lib/util/replaceContentBlockReference.js +15 -11
  42. package/lib/util/util.js +43 -9
  43. package/lib/util/validations.js +66 -0
  44. package/package.json +8 -8
  45. package/test/general.test.js +4 -4
  46. package/test/mockRoot/.mcdevrc.json +15 -1
  47. package/test/mockRoot/deploy/testInstance/testBU/triggeredSend/testExisting_triggeredSend.triggeredSend-meta.json +0 -1
  48. package/test/mockRoot/deploy/testInstance/testBU/triggeredSend/testNew_triggeredSend.triggeredSend-meta.json +0 -1
  49. package/test/resources/9999999/journey/build-expected.json +4 -2
  50. package/test/resources/9999999/journey/get-multistep-expected.json +61 -61
  51. package/test/resources/9999999/journey/get-quicksend-expected.json +4 -2
  52. package/test/resources/9999999/journey/get-quicksend-rcb-id-expected.json +2 -2
  53. package/test/resources/9999999/journey/get-quicksend-rcb-key-expected.json +2 -2
  54. package/test/resources/9999999/journey/get-quicksend-rcb-name-expected.json +6 -2
  55. package/test/resources/9999999/journey/get-transactionalEmail-expected.json +2 -2
  56. package/test/resources/9999999/journey/template-expected.json +4 -2
  57. package/test/resources/9999999/triggeredSend/build-expected.json +0 -1
  58. package/test/resources/9999999/triggeredSend/get-expected.json +0 -1
  59. package/test/resources/9999999/triggeredSend/get-rcb-id-expected.json +0 -1
  60. package/test/resources/9999999/triggeredSend/get-rcb-key-expected.json +0 -1
  61. package/test/resources/9999999/triggeredSend/get-rcb-name-expected.json +0 -1
  62. package/test/resources/9999999/triggeredSend/patch-expected.json +0 -1
  63. package/test/resources/9999999/triggeredSend/post-expected.json +0 -1
  64. package/test/resources/9999999/triggeredSend/template-expected.json +0 -1
  65. package/test/resources/9999999/triggeredSendDefinition/create-response.xml +0 -1
  66. package/test/resources/9999999/triggeredSendDefinition/retrieve-TriggeredSendStatusINNew,Active,Inactive,Moved,Canceled-response.xml +0 -2
  67. package/test/resources/9999999/triggeredSendDefinition/update-response.xml +0 -1
  68. package/test/type.dataExtension.test.js +3 -3
  69. package/test/type.journey.test.js +2 -2
  70. package/test/utils.js +1 -0
  71. package/types/mcdev.d.js +66 -0
@@ -185,7 +185,7 @@ class Journey extends MetadataType {
185
185
  // if the interaction does not exist, the API returns an error code which would otherwise bring execution to a hold
186
186
  if (
187
187
  [
188
- 'Journey matching key not found.',
188
+ 'Interaction matching key not found.',
189
189
  'Must provide a valid ID or Key parameter',
190
190
  ].includes(ex.message)
191
191
  ) {
@@ -419,6 +419,7 @@ class Journey extends MetadataType {
419
419
  metadata.triggers[0].type,
420
420
  metadata.triggers[0].configurationArguments,
421
421
  metadata.key,
422
+ metadata.status === 'Published',
422
423
  this.definition.type
423
424
  );
424
425
  } catch (ex) {
@@ -562,7 +563,6 @@ class Journey extends MetadataType {
562
563
  switch (activity.type) {
563
564
  case 'EMAILV2': {
564
565
  // triggeredSend + email+asset
565
- // TODO email / asset
566
566
  const configurationArguments = activity.configurationArguments;
567
567
  if (configurationArguments) {
568
568
  try {
@@ -630,49 +630,79 @@ class Journey extends MetadataType {
630
630
  'triggeredSend',
631
631
  triggeredSend.r__triggeredSend_key
632
632
  );
633
- triggeredSend.emailId = linkedTS?.Email?.ID;
634
- triggeredSend.dynamicEmailSubject = linkedTS.DynamicEmailSubject;
635
- triggeredSend.emailSubject = linkedTS.EmailSubject;
636
- triggeredSend.bccEmail = linkedTS.BccEmail;
637
- triggeredSend.isMultipart = linkedTS.IsMultipart;
638
- triggeredSend.autoAddSubscribers = linkedTS.AutoAddSubscribers;
639
- triggeredSend.autoUpdateSubscribers = linkedTS.AutoUpdateSubscribers;
640
- triggeredSend.isTrackingClicks = !linkedTS.SuppressTracking;
641
- // from name & email are set in the senderProfile, not in the triggeredSend
642
- // triggeredSend.fromName = linkedTS.FromName;
643
- // triggeredSend.fromAddress = linkedTS.FromAddress;
644
-
645
- if (linkedTS.SenderProfile?.CustomerKey) {
646
- try {
647
- const spKey = cache.searchForField(
648
- 'senderProfile',
649
- linkedTS.SenderProfile.ObjectID,
650
- 'ObjectID',
651
- 'CustomerKey'
652
- );
653
- triggeredSend.r__senderProfile_key = spKey;
654
- delete triggeredSend.senderProfileId;
655
- } catch (ex) {
656
- Util.logger.warn(
657
- ` - triggeredSend ${linkedTS.CustomerKey}: ${ex.message} (senderProfile key ${linkedTS.SenderProfile.CustomerKey})`
658
- );
633
+ if (linkedTS) {
634
+ triggeredSend.emailId = linkedTS.Email?.ID;
635
+ triggeredSend.dynamicEmailSubject = linkedTS.DynamicEmailSubject;
636
+ triggeredSend.emailSubject = linkedTS.EmailSubject;
637
+ // only the bccEmail field can be retrieved for triggeredSends, not the ccEmail field; for some reason BccEmail can be retrieved but does not return a value even if stored correctly in the journey.
638
+ // triggeredSend.bccEmail = linkedTS.BccEmail;
639
+ triggeredSend.isMultipart = linkedTS.IsMultipart;
640
+ triggeredSend.autoAddSubscribers = linkedTS.AutoAddSubscribers;
641
+ triggeredSend.autoUpdateSubscribers =
642
+ linkedTS.AutoUpdateSubscribers;
643
+ triggeredSend.isTrackingClicks = !linkedTS.SuppressTracking;
644
+ // from name & email are set in the senderProfile, not in the triggeredSend
645
+ // triggeredSend.fromName = linkedTS.FromName;
646
+ // triggeredSend.fromAddress = linkedTS.FromAddress;
647
+
648
+ if (linkedTS.List?.ID) {
649
+ triggeredSend.publicationListId = linkedTS.List.ID;
650
+ } else if (linkedTS.r__list_PathName) {
651
+ delete triggeredSend.publicationListId;
652
+ triggeredSend.r__list_PathName = {
653
+ publicationList: linkedTS.r__list_PathName,
654
+ };
659
655
  }
660
- }
661
- // send classification
662
- if (linkedTS.SendClassification?.CustomerKey) {
663
- try {
664
- const scKey = cache.searchForField(
665
- 'sendClassification',
666
- linkedTS.SendClassification.ObjectID,
667
- 'ObjectID',
668
- 'CustomerKey'
669
- );
670
- triggeredSend.r__sendClassification_key = scKey;
671
- delete triggeredSend.sendClassificationId;
672
- } catch (ex) {
673
- Util.logger.warn(
674
- ` - triggeredSend ${linkedTS.CustomerKey}: ${ex.message} (sendClassification key ${linkedTS.SendClassification.CustomerKey})`
675
- );
656
+ if (linkedTS.SenderProfile?.CustomerKey) {
657
+ try {
658
+ const spKey = cache.searchForField(
659
+ 'senderProfile',
660
+ linkedTS.SenderProfile.ObjectID,
661
+ 'ObjectID',
662
+ 'CustomerKey'
663
+ );
664
+ triggeredSend.r__senderProfile_key = spKey;
665
+ delete triggeredSend.senderProfileId;
666
+ } catch (ex) {
667
+ Util.logger.warn(
668
+ ` - triggeredSend ${linkedTS.CustomerKey}: ${ex.message} (senderProfile key ${linkedTS.SenderProfile.CustomerKey})`
669
+ );
670
+ }
671
+ } else if (linkedTS.r__senderProfile_key) {
672
+ triggeredSend.r__senderProfile_key =
673
+ linkedTS.r__senderProfile_key;
674
+ }
675
+ // send classification
676
+ if (linkedTS.SendClassification?.CustomerKey) {
677
+ try {
678
+ const scKey = cache.searchForField(
679
+ 'sendClassification',
680
+ linkedTS.SendClassification.ObjectID,
681
+ 'ObjectID',
682
+ 'CustomerKey'
683
+ );
684
+ triggeredSend.r__sendClassification_key = scKey;
685
+ delete triggeredSend.sendClassificationId;
686
+ } catch (ex) {
687
+ Util.logger.warn(
688
+ ` - triggeredSend ${linkedTS.CustomerKey}: ${ex.message} (sendClassification key ${linkedTS.SendClassification.CustomerKey})`
689
+ );
690
+ }
691
+ } else if (linkedTS.r__sendClassification_key) {
692
+ triggeredSend.r__sendClassification_key =
693
+ linkedTS.r__sendClassification_key;
694
+ }
695
+ if (linkedTS.c__priority) {
696
+ delete triggeredSend.priority;
697
+ triggeredSend.c__priority = linkedTS.c__priority;
698
+ }
699
+ if (linkedTS.Email?.ID) {
700
+ triggeredSend.emailId = linkedTS.Email.ID;
701
+ } else if (linkedTS.r__asset_key) {
702
+ delete triggeredSend.emailId;
703
+ triggeredSend.r__asset_name_readOnly =
704
+ linkedTS.r__asset_name_readOnly;
705
+ triggeredSend.r__asset_key = linkedTS.r__asset_key;
676
706
  }
677
707
  }
678
708
  } else if (triggeredSend.id) {
@@ -706,8 +736,15 @@ class Journey extends MetadataType {
706
736
  delete triggeredSend.key;
707
737
  }
708
738
 
739
+ triggeredSend.ccEmail = triggeredSend.ccEmail
740
+ .split(';')
741
+ .filter((el) => el !== '');
742
+ triggeredSend.bccEmail = triggeredSend.bccEmail
743
+ .split(';')
744
+ .filter((el) => el !== '');
745
+
709
746
  // List (optional)
710
- triggeredSend.r__list_PathName = {};
747
+ triggeredSend.r__list_PathName ||= {};
711
748
  if (triggeredSend.publicationListId) {
712
749
  try {
713
750
  triggeredSend.r__list_PathName.publicationList =
@@ -827,39 +864,45 @@ class Journey extends MetadataType {
827
864
  delete triggeredSend.priority;
828
865
  }
829
866
  // email
830
- try {
831
- // content builder
832
- triggeredSend.r__asset_name_readOnly = cache.searchForField(
833
- 'asset',
834
- triggeredSend.emailId,
835
- 'legacyData.legacyId',
836
- 'name'
837
- );
838
- triggeredSend.r__asset_key = cache.searchForField(
839
- 'asset',
840
- triggeredSend.emailId,
841
- 'legacyData.legacyId',
842
- 'customerKey'
843
- );
844
- delete triggeredSend.emailId;
845
- } catch {
867
+ if (triggeredSend.emailId) {
846
868
  try {
847
- // classic
848
- triggeredSend.r__email_name = cache.searchForField(
849
- 'email',
869
+ // content builder
870
+ triggeredSend.r__asset_name_readOnly = cache.searchForField(
871
+ 'asset',
872
+ triggeredSend.emailId,
873
+ 'legacyData.legacyId',
874
+ 'name'
875
+ );
876
+ triggeredSend.r__asset_key = cache.searchForField(
877
+ 'asset',
850
878
  triggeredSend.emailId,
851
- 'ID',
852
- 'Name'
879
+ 'legacyData.legacyId',
880
+ 'customerKey'
853
881
  );
854
882
  delete triggeredSend.emailId;
855
883
  } catch {
856
- Util.logger.warn(
857
- ` - ${this.definition.type} '${metadata[this.definition.nameField]}' (${
858
- metadata[this.definition.keyField]
859
- }): Could not find email with ID ${triggeredSend.emailId} in Classic nor in Content Builder.`
860
- );
884
+ try {
885
+ // classic
886
+ triggeredSend.r__email_name = cache.searchForField(
887
+ 'email',
888
+ triggeredSend.emailId,
889
+ 'ID',
890
+ 'Name'
891
+ );
892
+ delete triggeredSend.emailId;
893
+ } catch {
894
+ Util.logger.warn(
895
+ ` - ${this.definition.type} '${metadata[this.definition.nameField]}' (${
896
+ metadata[this.definition.keyField]
897
+ }): Could not find email with ID ${triggeredSend.emailId} in Classic nor in Content Builder.`
898
+ );
899
+ }
861
900
  }
862
901
  }
902
+
903
+ // sort attributes of triggeredSend alphabetically to allow for easier pull request reviews
904
+ configurationArguments.triggeredSend =
905
+ Util.sortObjectAttributes(triggeredSend);
863
906
  }
864
907
  break;
865
908
  }
@@ -978,12 +1021,10 @@ class Journey extends MetadataType {
978
1021
  break;
979
1022
  }
980
1023
  }
981
- // TODO: Filters / activities[].type === 'MULTICRITERIADECISION'
982
- // - activities[].arguments.filterResult
983
- // - activities[].arguments.configurationArguments.criteria
984
-
985
- // TODO: wait activity / activities[].type === 'WAIT'
986
1024
  }
1025
+
1026
+ // apply sorting by activity key to work around the API shuffling activities around
1027
+ metadata.activities = metadata.activities.sort((a, b) => a.key.localeCompare(b.key));
987
1028
  }
988
1029
 
989
1030
  /**
@@ -1190,6 +1231,15 @@ class Journey extends MetadataType {
1190
1231
  delete triggeredSend.r__triggeredSend_key;
1191
1232
  }
1192
1233
 
1234
+ triggeredSend.ccEmail =
1235
+ typeof triggeredSend.ccEmail === 'string'
1236
+ ? triggeredSend.ccEmail
1237
+ : triggeredSend.ccEmail.join(';');
1238
+ triggeredSend.bccEmail =
1239
+ typeof triggeredSend.bccEmail === 'string'
1240
+ ? triggeredSend.bccEmail
1241
+ : triggeredSend.bccEmail.join(';');
1242
+
1193
1243
  // List (optional)
1194
1244
  if (triggeredSend.r__list_PathName) {
1195
1245
  if (triggeredSend.r__list_PathName.publicationList) {
@@ -1444,11 +1494,16 @@ class Journey extends MetadataType {
1444
1494
  if (triggeredSend) {
1445
1495
  // the following is very similar but not equal to the variables in TriggeredSend.js
1446
1496
  try {
1447
- triggeredSend.bccEmail = ReplaceCbReference.replaceReference(
1448
- triggeredSend.bccEmail,
1497
+ let bccEmail =
1498
+ typeof triggeredSend.bccEmail === 'string'
1499
+ ? triggeredSend.bccEmail
1500
+ : triggeredSend.bccEmail.join(';');
1501
+ bccEmail = ReplaceCbReference.replaceReference(
1502
+ bccEmail,
1449
1503
  parentName,
1450
1504
  findAssetKeys
1451
1505
  );
1506
+ triggeredSend.bccEmail = bccEmail.split(';').filter((el) => el !== '');
1452
1507
  changes = true;
1453
1508
  } catch (ex) {
1454
1509
  if (ex.code !== 200) {
@@ -1456,11 +1511,16 @@ class Journey extends MetadataType {
1456
1511
  }
1457
1512
  }
1458
1513
  try {
1459
- triggeredSend.ccEmail = ReplaceCbReference.replaceReference(
1460
- triggeredSend.ccEmail,
1514
+ let ccEmail =
1515
+ typeof triggeredSend.ccEmail === 'string'
1516
+ ? triggeredSend.ccEmail
1517
+ : triggeredSend.ccEmail.join(';');
1518
+ ccEmail = ReplaceCbReference.replaceReference(
1519
+ ccEmail,
1461
1520
  parentName,
1462
1521
  findAssetKeys
1463
1522
  );
1523
+ triggeredSend.ccEmail = ccEmail.split(';').filter((el) => el !== '');
1464
1524
  changes = true;
1465
1525
  } catch (ex) {
1466
1526
  if (ex.code !== 200) {
@@ -1691,7 +1751,7 @@ class Journey extends MetadataType {
1691
1751
  ); // payload is empty for this request
1692
1752
  if (response.statusUrl && response.statusId) {
1693
1753
  Util.logger.info(
1694
- ` - ${this.definition.type} queued for publishing : ${key}/${version}`
1754
+ ` - ${this.definition.type} queued for publishing: ${journey[this.definition.keyField]}/${version} / ${journey[this.definition.nameField]}`
1695
1755
  );
1696
1756
  statusUrl = response.statusUrl;
1697
1757
  } else {
@@ -1704,7 +1764,12 @@ class Journey extends MetadataType {
1704
1764
 
1705
1765
  await Util.sleep(1000);
1706
1766
  executedKeyArr.push(
1707
- await this._checkPublishStatus(statusUrl, key, spinner)
1767
+ await this._checkPublishStatus(
1768
+ statusUrl,
1769
+ journey[this.definition.keyField],
1770
+ journey[this.definition.nameField],
1771
+ spinner
1772
+ )
1708
1773
  );
1709
1774
  } else {
1710
1775
  // no guarantees if the journey was actually published
@@ -1804,35 +1869,44 @@ class Journey extends MetadataType {
1804
1869
  * helper for {@link Journey.publish}
1805
1870
  *
1806
1871
  * @param {string} statusUrl URL to check the status of the publish request
1807
- * @param {string} key key or id for log messages
1808
- * @param {any} spinner key or id for log messages
1872
+ * @param {string} key journey-key or id for log messages
1873
+ * @param {string} name journey-name for log messages
1874
+ * @param {import('yocto-spinner').Spinner} spinner reference to spinner to allow stopping it when done
1809
1875
  * @param {number} [tries] number of tries used to check the status
1810
1876
  * @returns {Promise.<string>} key of the item that was published successfully
1811
1877
  */
1812
- static async _checkPublishStatus(statusUrl, key, spinner, tries = 1) {
1878
+ static async _checkPublishStatus(statusUrl, key, name, spinner, tries = 1) {
1813
1879
  try {
1814
1880
  const response = await this.client.rest.get(statusUrl);
1815
1881
  switch (response.status) {
1816
1882
  case 'PublishCompleted': {
1817
1883
  spinner.success('done.');
1818
- Util.logger.info(` - ${this.definition.type} ${key}: publishing succeeded`);
1884
+ Util.logger.info(` - published ${this.definition.type}: ${key} / ${name}`);
1819
1885
  this._showPublishStatusDetails(response);
1820
1886
  return key;
1821
1887
  }
1822
1888
  case 'PublishInProcess': {
1823
1889
  Util.logger.debug(
1824
- ` - ${this.definition.type} ${key}: publishing still in progress`
1890
+ ` - publishing ${this.definition.type} still in progress: ${key} / ${name}`
1825
1891
  );
1826
1892
  if (tries < 50) {
1827
1893
  await (tries < 10 ? Util.sleep(2000) : Util.sleep(5000));
1828
- return await this._checkPublishStatus(statusUrl, key, spinner, tries + 1);
1894
+ return await this._checkPublishStatus(
1895
+ statusUrl,
1896
+ key,
1897
+ name,
1898
+ spinner,
1899
+ tries + 1
1900
+ );
1829
1901
  } else {
1830
1902
  return;
1831
1903
  }
1832
1904
  }
1833
1905
  case 'Error': {
1834
1906
  spinner.success('failed.');
1835
- Util.logger.error(` - ${this.definition.type} ${key}: publishing failed.`);
1907
+ Util.logger.error(
1908
+ ` - publishing ${this.definition.type} failed: ${key} / ${name}`
1909
+ );
1836
1910
  this._showPublishStatusDetails(response);
1837
1911
  return;
1838
1912
  }
@@ -1969,6 +2043,50 @@ class Journey extends MetadataType {
1969
2043
 
1970
2044
  return stoppedKeyArr;
1971
2045
  }
2046
+ /**
2047
+ * resumes selected journey versions
2048
+ *
2049
+ * @param {string[]} keyArr customerkey of the metadata
2050
+ * @returns {Promise.<string[]>} Returns list of keys that were paused
2051
+ */
2052
+ static async execute(keyArr) {
2053
+ let version;
2054
+ const endpoint = '/interaction/v1/interactions/resume/';
2055
+ const resumedKeyArr = [];
2056
+ const apiLimit = pLimit(20);
2057
+ const journeyCache = await this.retrieveForCache();
2058
+
2059
+ await Promise.allSettled(
2060
+ keyArr.map((key) =>
2061
+ apiLimit(async () => {
2062
+ [key, version] = key.split('/');
2063
+ if (journeyCache.metadata[key]) {
2064
+ version ||= journeyCache.metadata[key].version;
2065
+ try {
2066
+ await this.client.rest.post(
2067
+ endpoint +
2068
+ journeyCache.metadata[key].id +
2069
+ (version === '*'
2070
+ ? '?allVersions=true'
2071
+ : `?versionNumber=${version}`),
2072
+ {}
2073
+ );
2074
+ Util.logger.info(
2075
+ ` - Resumed ${this.definition.type} ${key}/${version}`
2076
+ );
2077
+ resumedKeyArr.push(key);
2078
+ } catch (ex) {
2079
+ Util.logger.error(
2080
+ ` - Resuming ${this.definition.type} ${key} failed: ${ex.message}`
2081
+ );
2082
+ }
2083
+ }
2084
+ })
2085
+ )
2086
+ );
2087
+
2088
+ return resumedKeyArr;
2089
+ }
1972
2090
  }
1973
2091
 
1974
2092
  // Assign definition to static attributes