mcdev 4.1.11 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/.eslintrc.json +1 -1
  2. package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +2 -3
  4. package/.nycrc.json +5 -0
  5. package/README.md +34 -1528
  6. package/boilerplate/config.json +2 -6
  7. package/boilerplate/files/.vscode/extensions.json +1 -0
  8. package/boilerplate/files/.vscode/settings.json +13 -9
  9. package/boilerplate/forcedUpdates.json +4 -0
  10. package/docs/dist/documentation.md +434 -29
  11. package/lib/Deployer.js +10 -8
  12. package/lib/MetadataTypeDefinitions.js +3 -0
  13. package/lib/MetadataTypeInfo.js +3 -0
  14. package/lib/Retriever.js +16 -8
  15. package/lib/cli.js +1 -0
  16. package/lib/index.js +5 -5
  17. package/lib/metadataTypes/AccountUser.js +2 -2
  18. package/lib/metadataTypes/Asset.js +45 -35
  19. package/lib/metadataTypes/Automation.js +4 -4
  20. package/lib/metadataTypes/DataExtension.js +14 -8
  21. package/lib/metadataTypes/DataExtensionField.js +44 -9
  22. package/lib/metadataTypes/Discovery.js +5 -5
  23. package/lib/metadataTypes/Folder.js +30 -6
  24. package/lib/metadataTypes/List.js +115 -17
  25. package/lib/metadataTypes/MetadataType.js +73 -40
  26. package/lib/metadataTypes/Query.js +2 -2
  27. package/lib/metadataTypes/Script.js +2 -2
  28. package/lib/metadataTypes/TransactionalEmail.js +163 -0
  29. package/lib/metadataTypes/TransactionalMessage.js +127 -0
  30. package/lib/metadataTypes/TransactionalPush.js +77 -0
  31. package/lib/metadataTypes/TransactionalSMS.js +354 -0
  32. package/lib/metadataTypes/TriggeredSendDefinition.js +11 -9
  33. package/lib/metadataTypes/definitions/TransactionalEmail.definition.js +145 -0
  34. package/lib/metadataTypes/definitions/TransactionalPush.definition.js +109 -0
  35. package/lib/metadataTypes/definitions/TransactionalSMS.definition.js +103 -0
  36. package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +36 -36
  37. package/lib/util/auth.js +2 -2
  38. package/lib/util/businessUnit.js +1 -1
  39. package/lib/util/cli.js +19 -20
  40. package/lib/util/config.js +10 -10
  41. package/lib/util/devops.js +4 -4
  42. package/lib/util/init.config.js +7 -7
  43. package/lib/util/init.git.js +12 -24
  44. package/lib/util/init.js +65 -1
  45. package/lib/util/util.js +25 -14
  46. package/package.json +19 -12
  47. package/test/dataExtension.test.js +36 -19
  48. package/test/mockRoot/.mcdevrc.json +13 -2
  49. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +27 -7
  50. package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +1 -1
  51. package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +3 -1
  52. package/test/mockRoot/deploy/testInstance/testBU/query/{testQuery.query-meta.json → testNewQuery.query-meta.json} +3 -3
  53. package/test/mockRoot/deploy/testInstance/testBU/query/{testQuery.query-meta.sql → testNewQuery.query-meta.sql} +0 -0
  54. package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json +24 -0
  55. package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json +24 -0
  56. package/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testExisting_tpush.transactionalPush-meta.json +18 -0
  57. package/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testNew_tpush.transactionalPush-meta.json +18 -0
  58. package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.amp +4 -0
  59. package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json +15 -0
  60. package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.amp +4 -0
  61. package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json +15 -0
  62. package/test/query.test.js +57 -23
  63. package/test/resources/1111111/businessUnit/retrieve-response.xml +33 -0
  64. package/test/resources/1111111/list/retrieve-response.xml +39 -0
  65. package/test/resources/9999999/asset/v1/content/assets/query/post-response.json +72 -0
  66. package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +2 -2
  67. package/test/resources/9999999/automation/v1/queries/get-response.json +2 -2
  68. package/test/resources/9999999/automation/v1/queries/post-response.json +3 -3
  69. package/test/resources/9999999/dataExtension/build-expected.json +2 -2
  70. package/test/resources/9999999/dataFolder/retrieve-response.xml +95 -2
  71. package/test/resources/9999999/interaction/v1/interactions/get-response.json +296 -0
  72. package/test/resources/9999999/legacy/v1/beta/mobile/code/get-response.json +32 -0
  73. package/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json +46 -0
  74. package/test/resources/9999999/list/retrieve-response.xml +78 -0
  75. package/test/resources/9999999/messaging/v1/email/definitions/get-response.json +15 -0
  76. package/test/resources/9999999/messaging/v1/email/definitions/post-response.json +19 -0
  77. package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json +26 -0
  78. package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/patch-response.json +26 -0
  79. package/test/resources/9999999/messaging/v1/push/definitions/get-response.json +15 -0
  80. package/test/resources/9999999/messaging/v1/push/definitions/post-response.json +13 -0
  81. package/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json +13 -0
  82. package/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/patch-response.json +13 -0
  83. package/test/resources/9999999/messaging/v1/sms/definitions/get-response.json +15 -0
  84. package/test/resources/9999999/messaging/v1/sms/definitions/post-response.json +17 -0
  85. package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json +18 -0
  86. package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json +18 -0
  87. package/test/resources/9999999/query/build-expected.json +2 -2
  88. package/test/resources/9999999/query/build-expected.sql +6 -0
  89. package/test/resources/9999999/query/get-expected.json +1 -1
  90. package/test/resources/9999999/query/get-expected.sql +6 -0
  91. package/test/resources/9999999/query/patch-expected.json +1 -1
  92. package/test/resources/9999999/query/patch-expected.sql +6 -0
  93. package/test/resources/9999999/query/post-expected.json +3 -3
  94. package/test/resources/9999999/query/post-expected.sql +4 -0
  95. package/test/resources/9999999/query/template-expected.json +1 -1
  96. package/test/resources/9999999/query/template-expected.sql +6 -0
  97. package/test/resources/9999999/transactionalEmail/build-expected.json +22 -0
  98. package/test/resources/9999999/transactionalEmail/get-expected.json +24 -0
  99. package/test/resources/9999999/transactionalEmail/patch-expected.json +24 -0
  100. package/test/resources/9999999/transactionalEmail/post-expected.json +24 -0
  101. package/test/resources/9999999/transactionalEmail/template-expected.json +22 -0
  102. package/test/resources/9999999/transactionalPush/build-expected.json +8 -0
  103. package/test/resources/9999999/transactionalPush/get-expected.json +11 -0
  104. package/test/resources/9999999/transactionalPush/patch-expected.json +16 -0
  105. package/test/resources/9999999/transactionalPush/post-expected.json +16 -0
  106. package/test/resources/9999999/transactionalPush/template-expected.json +8 -0
  107. package/test/resources/9999999/transactionalSMS/build-expected.amp +4 -0
  108. package/test/resources/9999999/transactionalSMS/build-expected.json +13 -0
  109. package/test/resources/9999999/transactionalSMS/get-expected.amp +4 -0
  110. package/test/resources/9999999/transactionalSMS/get-expected.json +15 -0
  111. package/test/resources/9999999/transactionalSMS/patch-expected.amp +4 -0
  112. package/test/resources/9999999/transactionalSMS/patch-expected.json +15 -0
  113. package/test/resources/9999999/transactionalSMS/post-expected.amp +4 -0
  114. package/test/resources/9999999/transactionalSMS/post-expected.json +15 -0
  115. package/test/resources/9999999/transactionalSMS/template-expected.amp +4 -0
  116. package/test/resources/9999999/transactionalSMS/template-expected.json +13 -0
  117. package/test/transactionalEmail.test.js +120 -0
  118. package/test/transactionalPush.test.js +120 -0
  119. package/test/transactionalSMS.test.js +149 -0
  120. package/test/utils.js +57 -8
@@ -0,0 +1,127 @@
1
+ 'use strict';
2
+
3
+ const TYPE = require('../../types/mcdev.d');
4
+ const MetadataType = require('./MetadataType');
5
+ const Util = require('../util/util');
6
+
7
+ /**
8
+ * TransactionalMessage MetadataType
9
+ *
10
+ * @augments MetadataType
11
+ */
12
+ class TransactionalMessage extends MetadataType {
13
+ // define this.subType as string here for intellisense; requires to be redefined in child class
14
+ static subType;
15
+ /**
16
+ * Retrieves Metadata of Mobile Keywords
17
+ * Endpoint /legacy/v1/beta/mobile/code/ return all Mobile Codes with all details.
18
+ *
19
+ * @param {string} retrieveDir Directory where retrieved metadata directory will be saved
20
+ * @param {void} [_] unused parameter
21
+ * @param {void} [__] unused parameter
22
+ * @param {void} [___] unused parameter
23
+ * @param {string} [key] customer key of single item to retrieve
24
+ * @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
25
+ */
26
+ static async retrieve(retrieveDir, _, __, ___, key) {
27
+ let keyList;
28
+ const baseUri = '/messaging/v1/' + this.subType + '/definitions/';
29
+ if (key) {
30
+ // Retrieve single
31
+ keyList = [key];
32
+ } else {
33
+ // Retrieve all
34
+ const response = this.definition.restPagination
35
+ ? await this.client.rest.getBulk(baseUri)
36
+ : await this.client.rest.get(baseUri);
37
+ const parsed = this.parseResponseBody(response);
38
+ keyList = Object.keys(parsed).filter((item) => parsed[item].status !== 'Deleted');
39
+ const filteredCount = Object.keys(parsed).length - keyList.length;
40
+ if (filteredCount) {
41
+ Util.logger.info(
42
+ ` - Filtered ${this.definition.type} with status 'deleted': ${filteredCount} (downloaded but not saved to disk)`
43
+ );
44
+ }
45
+ }
46
+
47
+ // get all sms with additional details not given by the list endpoint
48
+ const details = (
49
+ await Promise.all(
50
+ keyList.map(async (key) => {
51
+ try {
52
+ return await this.client.rest.get(baseUri + (key || ''));
53
+ } catch {
54
+ return null;
55
+ }
56
+ })
57
+ )
58
+ ).filter(Boolean);
59
+ const parsed = this.parseResponseBody({ definitions: details });
60
+
61
+ // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that)
62
+ const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null);
63
+ // defined colors for optionally printing the keys we filtered by
64
+ const color = {
65
+ reset: '\x1B[0m',
66
+ dim: '\x1B[2m',
67
+ };
68
+ Util.logger.info(
69
+ `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
70
+ (key === null ? '' : ` ${color.dim}(Key: ${key})${color.reset}`)
71
+ );
72
+
73
+ return { metadata: savedMetadata, type: this.definition.type };
74
+ }
75
+
76
+ /**
77
+ * Retrieves event definition metadata for caching
78
+ *
79
+ * @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
80
+ */
81
+ static retrieveForCache() {
82
+ return super.retrieveREST(null, '/messaging/v1/' + this.subType + '/definitions/');
83
+ }
84
+ /**
85
+ * Updates a single item
86
+ *
87
+ * @param {TYPE.MetadataTypeItem} metadata a single item
88
+ * @returns {Promise} Promise
89
+ */
90
+ static update(metadata) {
91
+ return super.updateREST(
92
+ metadata,
93
+ '/messaging/v1/' + this.subType + '/definitions/' + metadata[this.definition.keyField]
94
+ );
95
+ }
96
+
97
+ /**
98
+ * Creates a single item
99
+ *
100
+ * @param {TYPE.MetadataTypeItem} metadata a single item
101
+ * @returns {Promise} Promise
102
+ */
103
+ static create(metadata) {
104
+ return super.createREST(metadata, '/messaging/v1/' + this.subType + '/definitions');
105
+ }
106
+ /**
107
+ * Delete a metadata item from the specified business unit
108
+ *
109
+ * @param {TYPE.BuObject} buObject references credentials
110
+ * @param {string} key Identifier of item
111
+ * @returns {Promise.<boolean>} deletion success status
112
+ */
113
+ static deleteByKey(buObject, key) {
114
+ return super.deleteByKeyREST(
115
+ buObject,
116
+ '/messaging/v1/' + this.subType + '/definitions/' + key,
117
+ key,
118
+ false
119
+ );
120
+ }
121
+ }
122
+
123
+ // Assign definition to static attributes
124
+ // ! using SMS definitions here as placeholder to have auto completion
125
+ TransactionalMessage.definition = require('../MetadataTypeDefinitions').transactionalSMS;
126
+
127
+ module.exports = TransactionalMessage;
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ const TYPE = require('../../types/mcdev.d');
4
+ const TransactionalMessage = require('./TransactionalMessage');
5
+ const Util = require('../util/util');
6
+ const cache = require('../util/cache');
7
+
8
+ /**
9
+ * TransactionalPush TransactionalMessage
10
+ *
11
+ * @augments TransactionalMessage
12
+ */
13
+ class TransactionalPush extends TransactionalMessage {
14
+ static subType = 'push';
15
+
16
+ /**
17
+ * prepares for deployment
18
+ *
19
+ * @param {TYPE.MetadataTypeItem} metadata a single item
20
+ * @returns {TYPE.MetadataTypeItem} Promise
21
+ */
22
+ static async preDeployTasks(metadata) {
23
+ // asset
24
+ if (metadata.content?.customerKey) {
25
+ // we merely want to be able to show an error if it does not exist
26
+ cache.searchForField(
27
+ 'asset',
28
+ metadata.content.customerKey,
29
+ 'customerKey',
30
+ 'customerKey'
31
+ );
32
+ }
33
+ if (metadata.options?.badge && typeof metadata.options?.badge !== 'string') {
34
+ // ensure it's a string, or else the API will return an error. Our SDK turns numbers in strings into actual numbers
35
+ metadata.options.badge += '';
36
+ }
37
+
38
+ return metadata;
39
+ }
40
+ /**
41
+ * manages post retrieve steps
42
+ *
43
+ * @param {TYPE.MetadataTypeItem} metadata a single item
44
+ * @returns {TYPE.MetadataTypeItem} a single item
45
+ */
46
+ static postRetrieveTasks(metadata) {
47
+ // asset
48
+ if (metadata.content?.customerKey) {
49
+ try {
50
+ // we merely want to be able to show an error if it does not exist
51
+ cache.searchForField(
52
+ 'asset',
53
+ metadata.content.customerKey,
54
+ 'customerKey',
55
+ 'customerKey'
56
+ );
57
+ } catch (ex) {
58
+ Util.logger.warn(
59
+ ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
60
+ metadata[this.definition.keyField]
61
+ }): ${ex.message}.`
62
+ );
63
+ }
64
+ }
65
+ if (metadata.options?.badge && typeof metadata.options?.badge !== 'string') {
66
+ // ensure it's a string, or else the API will return an error. Our SDK turns numbers in strings into actual numbers
67
+ metadata.options.badge += '';
68
+ }
69
+
70
+ return metadata;
71
+ }
72
+ }
73
+
74
+ // Assign definition to static attributes
75
+ TransactionalPush.definition = require('../MetadataTypeDefinitions').transactionalPush;
76
+
77
+ module.exports = TransactionalPush;
@@ -0,0 +1,354 @@
1
+ 'use strict';
2
+
3
+ const TYPE = require('../../types/mcdev.d');
4
+ const TransactionalMessage = require('./TransactionalMessage');
5
+ const Util = require('../util/util');
6
+ const File = require('../util/file');
7
+ const beautifier = require('beauty-amp-core');
8
+ const cache = require('../util/cache');
9
+
10
+ /**
11
+ * TransactionalSMS MetadataType
12
+ *
13
+ * @augments TransactionalMessage
14
+ */
15
+ class TransactionalSMS extends TransactionalMessage {
16
+ static subType = 'sms';
17
+ /**
18
+ * clean up after deleting a metadata item
19
+ *
20
+ * @param {TYPE.BuObject} buObject references credentials
21
+ * @param {string} customerKey Identifier of metadata item
22
+ * @returns {void}
23
+ */
24
+ static async postDeleteTasks(buObject, customerKey) {
25
+ // delete local copy: retrieve/cred/bu/type/...json
26
+ const fileName = File.normalizePath([
27
+ this.properties.directories.retrieve,
28
+ buObject.credential,
29
+ buObject.businessUnit,
30
+ this.definition.type,
31
+ `${customerKey}.${this.definition.type}-meta.`,
32
+ ]);
33
+ await File.remove(fileName + 'json');
34
+ await File.remove(fileName + 'amp');
35
+ }
36
+
37
+ /**
38
+ * prepares for deployment
39
+ *
40
+ * @param {TYPE.MetadataTypeItem} metadata a single item
41
+ * @param {string} dir directory of deploy files
42
+ * @returns {TYPE.MetadataTypeItem} Promise
43
+ */
44
+ static async preDeployTasks(metadata, dir) {
45
+ // code
46
+ metadata.content = {
47
+ message: await this._mergeCode(metadata, dir),
48
+ };
49
+
50
+ if (this._isHTML(metadata.content?.message)) {
51
+ // keep this as a non-blocking warning because the test not 100% accurate
52
+ Util.logger.warn(
53
+ ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
54
+ metadata[this.definition.keyField]
55
+ }): HTML detected`
56
+ );
57
+ }
58
+
59
+ // subscriptions: mobileCode
60
+ if (metadata.subscriptions?.shortCode) {
61
+ // we merely want to be able to show an error if it does not exist
62
+ cache.searchForField('mobileCode', metadata.subscriptions.shortCode, 'code', 'code');
63
+ }
64
+ // subscriptions: mobileKeyword
65
+ if (metadata.subscriptions?.keyword) {
66
+ // we merely want to be able to show an error if it does not exist
67
+ cache.searchForField(
68
+ 'mobileKeyword',
69
+ metadata.subscriptions.keyword,
70
+ 'keyword',
71
+ 'keyword'
72
+ );
73
+ }
74
+ return metadata;
75
+ }
76
+ /**
77
+ * helper for {@link preDeployTasks} that loads extracted code content back into JSON
78
+ *
79
+ * @param {TYPE.MetadataTypeItem} metadata a single definition
80
+ * @param {string} deployDir directory of deploy files
81
+ * @param {string} [templateName] name of the template used to built defintion (prior applying templating)
82
+ * @returns {Promise.<string>} content for metadata.script
83
+ */
84
+ static async _mergeCode(metadata, deployDir, templateName) {
85
+ templateName = templateName || metadata[this.definition.keyField];
86
+ const codePath = File.normalizePath([
87
+ deployDir,
88
+ this.definition.type,
89
+ templateName + '.' + this.definition.type + '-meta',
90
+ ]);
91
+
92
+ if (await File.pathExists(codePath + '.amp')) {
93
+ return await File.readFilteredFilename(
94
+ [deployDir, this.definition.type],
95
+ templateName + '.' + this.definition.type + '-meta',
96
+ 'amp'
97
+ );
98
+ } else {
99
+ throw new Error(`Could not find ${codePath}.amp`);
100
+ }
101
+ }
102
+ /**
103
+ * manages post retrieve steps
104
+ *
105
+ * @param {TYPE.MetadataTypeItem} metadata a single item
106
+ * @returns {TYPE.CodeExtractItem} Array with one metadata object and one ssjs string
107
+ */
108
+ static postRetrieveTasks(metadata) {
109
+ // extract message body
110
+ const codeArr = [];
111
+ // keep between tags
112
+ const { fileExt, code } = this.prepExtractedCode(metadata.content?.message);
113
+ delete metadata.content;
114
+ codeArr.push({
115
+ subFolder: null,
116
+ fileName: metadata[this.definition.keyField],
117
+ fileExt: fileExt,
118
+ content: code,
119
+ });
120
+
121
+ if (this._isHTML(code)) {
122
+ Util.logger.warn(
123
+ ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
124
+ metadata[this.definition.keyField]
125
+ }): HTML detected`
126
+ );
127
+ }
128
+
129
+ // subscriptions: mobileCode
130
+ if (metadata.subscriptions?.shortCode) {
131
+ try {
132
+ // we merely want to be able to show a warning if it does not exist
133
+ cache.searchForField(
134
+ 'mobileCode',
135
+ metadata.subscriptions.shortCode,
136
+ 'code',
137
+ 'code'
138
+ );
139
+ } catch {
140
+ Util.logger.warn(
141
+ ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
142
+ metadata[this.definition.keyField]
143
+ }): Could not find mobileCode ${metadata.subscriptions.shortCode}.`
144
+ );
145
+ }
146
+ }
147
+ // subscriptions: mobileKeyword
148
+ if (metadata.subscriptions?.keyword) {
149
+ try {
150
+ // we merely want to be able to show a warning if it does not exist
151
+ cache.searchForField(
152
+ 'mobileKeyword',
153
+ metadata.subscriptions.keyword,
154
+ 'keyword',
155
+ 'keyword'
156
+ );
157
+ } catch {
158
+ Util.logger.warn(
159
+ ` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
160
+ metadata[this.definition.keyField]
161
+ }): Could not find mobileKeyword ${metadata.subscriptions.keyword}.`
162
+ );
163
+ }
164
+ }
165
+
166
+ return { json: metadata, codeArr: codeArr, subFolder: null };
167
+ }
168
+ /**
169
+ * helper for {@link parseMetadata} and {@link _buildForNested}
170
+ *
171
+ * @param {string} metadataScript the code of the file
172
+ * @returns {{fileExt:string,code:string}} returns found extension and file content
173
+ */
174
+ static prepExtractedCode(metadataScript) {
175
+ // immutable at the moment:
176
+ const ampscript = {
177
+ capitalizeAndOrNot: true,
178
+ capitalizeIfFor: true,
179
+ capitalizeSet: true,
180
+ capitalizeVar: true,
181
+ maxParametersPerLine: 4,
182
+ };
183
+ // immutable at the moment:
184
+ const editor = {
185
+ insertSpaces: true,
186
+ tabSize: 4,
187
+ };
188
+ // logs trough console only for the moment.
189
+ const logs = {
190
+ loggerOn: false, // <= disable logging
191
+ };
192
+
193
+ beautifier.setup(ampscript, editor, logs);
194
+ const code = beautifier.beautify(metadataScript);
195
+ const fileExt = 'amp';
196
+
197
+ return { fileExt, code };
198
+ }
199
+ /**
200
+ * helper for {@link MetadataType.buildDefinition}
201
+ * handles extracted code if any are found for complex types
202
+ *
203
+ * @param {string} templateDir Directory where metadata templates are stored
204
+ * @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
205
+ * @param {TYPE.MetadataTypeItem} metadata main JSON file that was read from file system
206
+ * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
207
+ * @param {string} templateName name of the template to be built
208
+ * @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
209
+ */
210
+ static buildDefinitionForNested(
211
+ templateDir,
212
+ targetDir,
213
+ metadata,
214
+ templateVariables,
215
+ templateName
216
+ ) {
217
+ return this._buildForNested(
218
+ templateDir,
219
+ targetDir,
220
+ metadata,
221
+ templateVariables,
222
+ templateName,
223
+ 'definition'
224
+ );
225
+ }
226
+ /**
227
+ * helper for {@link MetadataType.buildTemplate}
228
+ * handles extracted code if any are found for complex types
229
+ *
230
+ * @example scripts are saved as 1 json and 1 ssjs file. both files need to be run through templating
231
+ * @param {string} templateDir Directory where metadata templates are stored
232
+ * @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
233
+ * @param {TYPE.MetadataTypeItem} metadata main JSON file that was read from file system
234
+ * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
235
+ * @param {string} templateName name of the template to be built
236
+ * @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
237
+ */
238
+ static buildTemplateForNested(
239
+ templateDir,
240
+ targetDir,
241
+ metadata,
242
+ templateVariables,
243
+ templateName
244
+ ) {
245
+ return this._buildForNested(
246
+ templateDir,
247
+ targetDir,
248
+ metadata,
249
+ templateVariables,
250
+ templateName,
251
+ 'template'
252
+ );
253
+ }
254
+
255
+ /**
256
+ * helper for {@link buildTemplateForNested} / {@link buildDefinitionForNested}
257
+ * handles extracted code if any are found for complex types
258
+ *
259
+ * @param {string} templateDir Directory where metadata templates are stored
260
+ * @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
261
+ * @param {TYPE.MetadataTypeItem} metadata main JSON file that was read from file system
262
+ * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
263
+ * @param {string} templateName name of the template to be built
264
+ * @param {'definition'|'template'} mode defines what we use this helper for
265
+ * @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
266
+ */
267
+ static async _buildForNested(
268
+ templateDir,
269
+ targetDir,
270
+ metadata,
271
+ templateVariables,
272
+ templateName,
273
+ mode
274
+ ) {
275
+ // get code from filesystem
276
+ let code = await this._mergeCode(metadata, templateDir, templateName);
277
+ const file = this.prepExtractedCode(code, metadata.name);
278
+ const fileExt = file.fileExt;
279
+ code = file.code;
280
+ // apply templating
281
+ try {
282
+ if (mode === 'definition') {
283
+ // replace template variables with their values
284
+ code = this.applyTemplateValues(code, templateVariables);
285
+ } else if (mode === 'template') {
286
+ // replace template values with corresponding variable names
287
+ code = this.applyTemplateNames(code, templateVariables);
288
+ }
289
+ } catch {
290
+ throw new Error(
291
+ `${this.definition.type}:: Error applying template variables on ${
292
+ templateName + '.' + this.definition.type
293
+ }-meta.${fileExt}.`
294
+ );
295
+ }
296
+
297
+ // write to file
298
+ const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir];
299
+ const nestedFilePaths = [];
300
+
301
+ // keep old name if creating templates, otherwise use new name
302
+ const fileName = mode === 'definition' ? metadata[this.definition.keyField] : templateName;
303
+
304
+ for (const targetDir of targetDirArr) {
305
+ File.writeToFile(
306
+ [targetDir, this.definition.type],
307
+ fileName + '.' + this.definition.type + '-meta',
308
+ fileExt,
309
+ code
310
+ );
311
+ nestedFilePaths.push([
312
+ targetDir,
313
+ this.definition.type,
314
+ fileName + '.' + this.definition.type + '-meta.' + fileExt,
315
+ ]);
316
+ }
317
+ return nestedFilePaths;
318
+ }
319
+ /**
320
+ * very simplified test for HTML code in our SMS
321
+ *
322
+ * @param {string} code sms source code
323
+ * @returns {boolean} true if HTML is found
324
+ */
325
+ static _isHTML(code) {
326
+ return /(<([^>]+)>)/gi.test(code);
327
+ }
328
+ /**
329
+ * should return only the json for all but asset, query and script that are saved as multiple files
330
+ * additionally, the documentation for dataExtension and automation should be returned
331
+ *
332
+ * @param {string[]} keyArr customerkey of the metadata
333
+ * @returns {string[]} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
334
+ */
335
+ static getFilesToCommit(keyArr) {
336
+ const path = File.normalizePath([
337
+ this.properties.directories.retrieve,
338
+ this.buObject.credential,
339
+ this.buObject.businessUnit,
340
+ this.definition.type,
341
+ ]);
342
+
343
+ const fileList = keyArr.flatMap((key) => [
344
+ File.normalizePath([path, `${key}.${this.definition.type}-meta.json`]),
345
+ File.normalizePath([path, `${key}.${this.definition.type}-meta.amp`]),
346
+ ]);
347
+ return fileList;
348
+ }
349
+ }
350
+
351
+ // Assign definition to static attributes
352
+ TransactionalSMS.definition = require('../MetadataTypeDefinitions').transactionalSMS;
353
+
354
+ module.exports = TransactionalSMS;
@@ -169,14 +169,16 @@ class TriggeredSendDefinition extends MetadataType {
169
169
  );
170
170
  }
171
171
  }
172
- // List
173
- try {
174
- metadata.r__list_PathName = cache.getListPathName(metadata.List.ID, 'ID');
175
- delete metadata.List;
176
- } catch (ex) {
177
- Util.logger.warn(
178
- ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
179
- );
172
+ // List (optional)
173
+ if (metadata.List) {
174
+ try {
175
+ metadata.r__list_PathName = cache.getListPathName(metadata.List.ID, 'ID');
176
+ delete metadata.List;
177
+ } catch (ex) {
178
+ Util.logger.warn(
179
+ ` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
180
+ );
181
+ }
180
182
  }
181
183
 
182
184
  return metadata;
@@ -227,7 +229,7 @@ class TriggeredSendDefinition extends MetadataType {
227
229
  `r__assetMessage_Key / r__email_Name not defined but instead found Email.ID. Please try re-retrieving this TSD from your BU.`
228
230
  );
229
231
  }
230
- // get List
232
+ // get List (optional)
231
233
  if (metadata.r__list_PathName) {
232
234
  metadata.List = {
233
235
  ID: cache.getListObjectId(metadata.r__list_PathName, 'ID'),