mcdev 3.1.1 → 4.0.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.
- package/.eslintrc.json +67 -7
- package/.github/ISSUE_TEMPLATE/bug.yml +5 -1
- package/.github/ISSUE_TEMPLATE/task.md +1 -1
- package/.github/PULL_REQUEST_TEMPLATE.md +5 -3
- package/.github/dependabot.yml +14 -0
- package/.github/workflows/code-analysis.yml +57 -0
- package/.husky/commit-msg +10 -0
- package/.husky/post-checkout +5 -0
- package/.husky/pre-commit +2 -1
- package/.prettierrc +8 -0
- package/.vscode/settings.json +1 -1
- package/LICENSE +2 -2
- package/README.md +134 -45
- package/boilerplate/config.json +5 -11
- package/boilerplate/files/.prettierrc +8 -0
- package/boilerplate/files/.vscode/extensions.json +0 -1
- package/boilerplate/files/.vscode/settings.json +28 -2
- package/boilerplate/files/README.md +2 -2
- package/boilerplate/forcedUpdates.json +10 -0
- package/boilerplate/npm-dependencies.json +5 -5
- package/docs/dist/documentation.md +2795 -1724
- package/jsconfig.json +1 -1
- package/lib/Builder.js +166 -75
- package/lib/Deployer.js +244 -96
- package/lib/MetadataTypeDefinitions.js +2 -0
- package/lib/MetadataTypeInfo.js +2 -0
- package/lib/Retriever.js +61 -84
- package/lib/cli.js +133 -25
- package/lib/index.js +242 -563
- package/lib/metadataTypes/AccountUser.js +101 -95
- package/lib/metadataTypes/Asset.js +677 -248
- package/lib/metadataTypes/AttributeGroup.js +23 -12
- package/lib/metadataTypes/Automation.js +456 -357
- package/lib/metadataTypes/Campaign.js +33 -93
- package/lib/metadataTypes/ContentArea.js +31 -11
- package/lib/metadataTypes/DataExtension.js +391 -376
- package/lib/metadataTypes/DataExtensionField.js +131 -54
- package/lib/metadataTypes/DataExtensionTemplate.js +22 -4
- package/lib/metadataTypes/DataExtract.js +67 -50
- package/lib/metadataTypes/DataExtractType.js +14 -8
- package/lib/metadataTypes/Discovery.js +21 -16
- package/lib/metadataTypes/Email.js +32 -12
- package/lib/metadataTypes/EmailSendDefinition.js +85 -80
- package/lib/metadataTypes/EventDefinition.js +69 -47
- package/lib/metadataTypes/FileTransfer.js +78 -54
- package/lib/metadataTypes/Filter.js +11 -4
- package/lib/metadataTypes/Folder.js +149 -117
- package/lib/metadataTypes/FtpLocation.js +14 -8
- package/lib/metadataTypes/ImportFile.js +69 -69
- package/lib/metadataTypes/Interaction.js +19 -4
- package/lib/metadataTypes/List.js +54 -13
- package/lib/metadataTypes/MetadataType.js +687 -479
- package/lib/metadataTypes/MobileCode.js +46 -0
- package/lib/metadataTypes/MobileKeyword.js +114 -0
- package/lib/metadataTypes/Query.js +204 -103
- package/lib/metadataTypes/Role.js +76 -61
- package/lib/metadataTypes/Script.js +146 -82
- package/lib/metadataTypes/SetDefinition.js +20 -8
- package/lib/metadataTypes/TriggeredSendDefinition.js +78 -58
- package/lib/metadataTypes/definitions/Asset.definition.js +21 -10
- package/lib/metadataTypes/definitions/AttributeGroup.definition.js +12 -0
- package/lib/metadataTypes/definitions/Automation.definition.js +10 -5
- package/lib/metadataTypes/definitions/Campaign.definition.js +44 -1
- package/lib/metadataTypes/definitions/DataExtension.definition.js +4 -0
- package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +6 -0
- package/lib/metadataTypes/definitions/DataExtract.definition.js +18 -14
- package/lib/metadataTypes/definitions/Discovery.definition.js +12 -0
- package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +4 -0
- package/lib/metadataTypes/definitions/EventDefinition.definition.js +22 -0
- package/lib/metadataTypes/definitions/FileTransfer.definition.js +4 -0
- package/lib/metadataTypes/definitions/Filter.definition.js +4 -0
- package/lib/metadataTypes/definitions/Folder.definition.js +6 -0
- package/lib/metadataTypes/definitions/FtpLocation.definition.js +4 -0
- package/lib/metadataTypes/definitions/ImportFile.definition.js +10 -5
- package/lib/metadataTypes/definitions/Interaction.definition.js +4 -0
- package/lib/metadataTypes/definitions/MobileCode.definition.js +163 -0
- package/lib/metadataTypes/definitions/MobileKeyword.definition.js +253 -0
- package/lib/metadataTypes/definitions/Query.definition.js +4 -0
- package/lib/metadataTypes/definitions/Role.definition.js +5 -0
- package/lib/metadataTypes/definitions/Script.definition.js +4 -0
- package/lib/metadataTypes/definitions/SetDefinition.definition.js +28 -0
- package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +4 -0
- package/lib/retrieveChangelog.js +7 -6
- package/lib/util/auth.js +117 -0
- package/lib/util/businessUnit.js +55 -66
- package/lib/util/cache.js +194 -0
- package/lib/util/cli.js +90 -116
- package/lib/util/config.js +302 -0
- package/lib/util/devops.js +240 -50
- package/lib/util/file.js +120 -191
- package/lib/util/init.config.js +195 -69
- package/lib/util/init.git.js +45 -50
- package/lib/util/init.js +72 -59
- package/lib/util/init.npm.js +48 -39
- package/lib/util/util.js +280 -564
- package/package.json +44 -33
- package/test/dataExtension.test.js +152 -0
- package/test/mockRoot/.mcdev-auth.json +8 -0
- package/test/mockRoot/.mcdevrc.json +67 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +39 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testDataExtension.dataExtension-meta.json +23 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +4 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testQuery.query-meta.sql +4 -0
- package/test/query.test.js +149 -0
- package/test/resourceFactory.js +142 -0
- package/test/resources/1111111/dataFolder/retrieve-response.xml +43 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +18 -0
- package/test/resources/9999999/automation/v1/queries/get-response.json +24 -0
- package/test/resources/9999999/automation/v1/queries/post-response.json +18 -0
- package/test/resources/9999999/dataExtension/build-expected.json +51 -0
- package/test/resources/9999999/dataExtension/create-expected.json +23 -0
- package/test/resources/9999999/dataExtension/create-response.xml +54 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.json +51 -0
- package/test/resources/9999999/dataExtension/retrieve-response.xml +47 -0
- package/test/resources/9999999/dataExtension/template-expected.json +51 -0
- package/test/resources/9999999/dataExtension/update-expected.json +55 -0
- package/test/resources/9999999/dataExtension/update-response.xml +52 -0
- package/test/resources/9999999/dataExtensionField/retrieve-response.xml +93 -0
- package/test/resources/9999999/dataExtensionTemplate/retrieve-response.xml +303 -0
- package/test/resources/9999999/dataFolder/retrieve-response.xml +65 -0
- package/test/resources/9999999/query/build-expected.json +8 -0
- package/test/resources/9999999/query/get-expected.json +11 -0
- package/test/resources/9999999/query/patch-expected.json +11 -0
- package/test/resources/9999999/query/post-expected.json +11 -0
- package/test/resources/9999999/query/template-expected.json +8 -0
- package/test/resources/auth.json +32 -0
- package/test/resources/rest404-response.json +5 -0
- package/test/resources/retrieve-response.xml +21 -0
- package/test/utils.js +107 -0
- package/types/mcdev.d.js +301 -0
- package/CHANGELOG.md +0 -126
- package/PULL_REQUEST_TEMPLATE.md +0 -19
- package/test/util/file.js +0 -51
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const TYPE = require('../../types/mcdev.d');
|
|
3
4
|
const MetadataType = require('./MetadataType');
|
|
4
5
|
const Util = require('../util/util');
|
|
5
6
|
const File = require('../util/file');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* ImportFile MetadataType
|
|
10
|
+
*
|
|
9
11
|
* @augments MetadataType
|
|
10
12
|
*/
|
|
11
13
|
class Role extends MetadataType {
|
|
12
14
|
/**
|
|
13
15
|
* Gets metadata from Marketing Cloud
|
|
14
|
-
*
|
|
15
|
-
* @param {
|
|
16
|
-
* @param {
|
|
17
|
-
* @
|
|
16
|
+
*
|
|
17
|
+
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
18
|
+
* @param {string[]} _ Returns specified fields even if their retrieve definition is not set to true
|
|
19
|
+
* @param {TYPE.BuObject} buObject properties for auth
|
|
20
|
+
* @param {void} [___] unused parameter
|
|
21
|
+
* @param {string} [key] customer key of single item to retrieve
|
|
22
|
+
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Metadata store object
|
|
18
23
|
*/
|
|
19
|
-
static async retrieve(retrieveDir, _, buObject) {
|
|
20
|
-
if (retrieveDir) {
|
|
24
|
+
static async retrieve(retrieveDir, _, buObject, ___, key) {
|
|
25
|
+
if (retrieveDir && buObject.eid !== buObject.mid) {
|
|
21
26
|
// don't run for BUs other than Parent BU
|
|
22
27
|
// this check does not work during caching
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
28
|
+
Util.logger.info(' - Skipping Role retrieval on non-parent BU');
|
|
29
|
+
return;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
const fields = Object.keys(this.definition.fields).reduce((accumulator, currentValue) => {
|
|
@@ -32,7 +35,10 @@ class Role extends MetadataType {
|
|
|
32
35
|
}
|
|
33
36
|
return accumulator;
|
|
34
37
|
}, []);
|
|
35
|
-
|
|
38
|
+
// manually add ObjectID for retrieves as it is no longer automatically returned, and is needed for updates
|
|
39
|
+
fields.push('ObjectID');
|
|
40
|
+
/** @type {TYPE.SoapRequestParams} */
|
|
41
|
+
let requestParams = {
|
|
36
42
|
// filter individual roles
|
|
37
43
|
filter: {
|
|
38
44
|
leftOperand: 'IsPrivate',
|
|
@@ -40,18 +46,26 @@ class Role extends MetadataType {
|
|
|
40
46
|
rightOperand: false,
|
|
41
47
|
},
|
|
42
48
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if (key) {
|
|
50
|
+
// move original filter down one level into rightOperand and add key filter into leftOperand
|
|
51
|
+
requestParams = {
|
|
52
|
+
filter: {
|
|
53
|
+
leftOperand: {
|
|
54
|
+
leftOperand: 'CustomerKey',
|
|
55
|
+
operator: 'equals',
|
|
56
|
+
rightOperand: key,
|
|
57
|
+
},
|
|
58
|
+
operator: 'AND',
|
|
59
|
+
rightOperand: requestParams.filter,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const results = await this.client.soap.retrieve('Role', fields, requestParams);
|
|
65
|
+
|
|
52
66
|
const parsed = this.parseResponseBody(results);
|
|
53
67
|
if (retrieveDir) {
|
|
54
|
-
const savedMetadata = await
|
|
68
|
+
const savedMetadata = await super.saveResults(parsed, retrieveDir, null);
|
|
55
69
|
Util.logger.info(
|
|
56
70
|
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})`
|
|
57
71
|
);
|
|
@@ -63,10 +77,11 @@ class Role extends MetadataType {
|
|
|
63
77
|
}
|
|
64
78
|
/**
|
|
65
79
|
* Gets executed before deploying metadata
|
|
66
|
-
*
|
|
67
|
-
* @
|
|
80
|
+
*
|
|
81
|
+
* @param {TYPE.MetadataTypeItem} metadata a single metadata item
|
|
82
|
+
* @returns {TYPE.MetadataTypeItem} Promise of a single metadata item
|
|
68
83
|
*/
|
|
69
|
-
static
|
|
84
|
+
static preDeployTasks(metadata) {
|
|
70
85
|
if (this.definition.deployBlacklist.includes(metadata.CustomerKey)) {
|
|
71
86
|
throw new Error(
|
|
72
87
|
`Skipped ${metadata.Name} (${metadata.CustomerKey}) because its CustomerKey is reserved for a default system role.`
|
|
@@ -77,7 +92,8 @@ class Role extends MetadataType {
|
|
|
77
92
|
|
|
78
93
|
/**
|
|
79
94
|
* Create a single Role.
|
|
80
|
-
*
|
|
95
|
+
*
|
|
96
|
+
* @param {TYPE.MetadataTypeItem} metadata single metadata entry
|
|
81
97
|
* @returns {Promise} Promise
|
|
82
98
|
*/
|
|
83
99
|
static create(metadata) {
|
|
@@ -86,7 +102,8 @@ class Role extends MetadataType {
|
|
|
86
102
|
|
|
87
103
|
/**
|
|
88
104
|
* Updates a single Role.
|
|
89
|
-
*
|
|
105
|
+
*
|
|
106
|
+
* @param {TYPE.MetadataTypeItem} metadata single metadata entry
|
|
90
107
|
* @returns {Promise} Promise
|
|
91
108
|
*/
|
|
92
109
|
static update(metadata) {
|
|
@@ -95,9 +112,10 @@ class Role extends MetadataType {
|
|
|
95
112
|
|
|
96
113
|
/**
|
|
97
114
|
* Creates markdown documentation of all roles
|
|
98
|
-
*
|
|
99
|
-
* @param {
|
|
100
|
-
* @
|
|
115
|
+
*
|
|
116
|
+
* @param {TYPE.BuObject} buObject properties for auth
|
|
117
|
+
* @param {TYPE.MetadataTypeMap} [metadata] role definitions
|
|
118
|
+
* @returns {Promise.<void>} -
|
|
101
119
|
*/
|
|
102
120
|
static async document(buObject, metadata) {
|
|
103
121
|
if (buObject.eid !== buObject.mid) {
|
|
@@ -121,15 +139,24 @@ class Role extends MetadataType {
|
|
|
121
139
|
return;
|
|
122
140
|
}
|
|
123
141
|
}
|
|
124
|
-
const directory = this.properties.directories.
|
|
142
|
+
const directory = File.normalizePath([this.properties.directories.docs, 'role']);
|
|
125
143
|
|
|
126
144
|
// initialize permission object
|
|
127
145
|
this.allPermissions = {};
|
|
128
146
|
// traverse all permissions recursively and write them into allPermissions object once it has reached the end
|
|
129
147
|
for (const role in metadata) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
148
|
+
// filter standard rules
|
|
149
|
+
if (
|
|
150
|
+
this.properties.options?.documentStandardRoles === false &&
|
|
151
|
+
'string' === typeof metadata[role]?.CustomerKey && // key could be numeric
|
|
152
|
+
metadata[role].CustomerKey.startsWith('SYS_DEF')
|
|
153
|
+
) {
|
|
154
|
+
delete metadata[role];
|
|
155
|
+
} else {
|
|
156
|
+
for (const element of metadata[role].PermissionSets.PermissionSet) {
|
|
157
|
+
this._traverseRoles(role, element);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
133
160
|
}
|
|
134
161
|
// Create output markdown
|
|
135
162
|
let output = `# Permission Overview - ${buObject.credential}\n\n`;
|
|
@@ -143,7 +170,7 @@ class Role extends MetadataType {
|
|
|
143
170
|
>
|
|
144
171
|
> **+** = _Allow_: User has access to the application or functionality
|
|
145
172
|
>
|
|
146
|
-
>
|
|
173
|
+
> ** •** = _Not Set_: User permission for this app or functionality is not explicitely granted nor denied, but defaults to Deny
|
|
147
174
|
>
|
|
148
175
|
> **-** = _Deny_: User does not have access to the app or functionality
|
|
149
176
|
>
|
|
@@ -156,7 +183,7 @@ class Role extends MetadataType {
|
|
|
156
183
|
let separator = '| --- |';
|
|
157
184
|
for (const role in metadata) {
|
|
158
185
|
output +=
|
|
159
|
-
metadata[role].IsSystemDefined ===
|
|
186
|
+
metadata[role].IsSystemDefined === true
|
|
160
187
|
? ` [${metadata[role].Name}] |`
|
|
161
188
|
: ` ${metadata[role].Name} |`;
|
|
162
189
|
separator += ' --- |';
|
|
@@ -167,14 +194,14 @@ class Role extends MetadataType {
|
|
|
167
194
|
for (const permission in this.allPermissions[permGroup]) {
|
|
168
195
|
output += '| ' + permission + ' |';
|
|
169
196
|
for (const role in this.allPermissions[permGroup][permission]) {
|
|
170
|
-
if (this.allPermissions[permGroup][permission][role] ===
|
|
197
|
+
if (this.allPermissions[permGroup][permission][role] === true) {
|
|
171
198
|
output += ' + |';
|
|
172
|
-
} else if (this.allPermissions[permGroup][permission][role] ===
|
|
199
|
+
} else if (this.allPermissions[permGroup][permission][role] === false) {
|
|
173
200
|
output += ' - |';
|
|
174
201
|
} else if (
|
|
175
202
|
'undefined' === typeof this.allPermissions[permGroup][permission][role]
|
|
176
203
|
) {
|
|
177
|
-
output += '
|
|
204
|
+
output += ' • |';
|
|
178
205
|
} else {
|
|
179
206
|
output += ' ' + this.allPermissions[permGroup][permission][role] + ' |';
|
|
180
207
|
}
|
|
@@ -185,15 +212,11 @@ class Role extends MetadataType {
|
|
|
185
212
|
}
|
|
186
213
|
try {
|
|
187
214
|
const filename = buObject.credential;
|
|
188
|
-
// ensure docs/roles folder is existing (depends on setup in .mcdevrc.json)
|
|
189
|
-
if (!File.existsSync(directory)) {
|
|
190
|
-
File.mkdirpSync(directory);
|
|
191
|
-
}
|
|
192
215
|
// write to disk
|
|
193
216
|
await File.writeToFile(directory, filename + '.roles', 'md', output);
|
|
194
|
-
Util.logger.info(`Created ${directory
|
|
217
|
+
Util.logger.info(`Created ${File.normalizePath([directory, filename])}.roles.md`);
|
|
195
218
|
if (['html', 'both'].includes(this.properties.options.documentType)) {
|
|
196
|
-
Util.logger.warn('HTML-based documentation of roles currently not supported.');
|
|
219
|
+
Util.logger.warn(' - HTML-based documentation of roles currently not supported.');
|
|
197
220
|
}
|
|
198
221
|
} catch (ex) {
|
|
199
222
|
Util.logger.error(`Roles.document():: error | `, ex.message);
|
|
@@ -212,12 +235,7 @@ class Role extends MetadataType {
|
|
|
212
235
|
* @returns {void}
|
|
213
236
|
*/
|
|
214
237
|
static _traverseRoles(role, element, permission, isAllowed) {
|
|
215
|
-
|
|
216
|
-
if (permission) {
|
|
217
|
-
_permission = permission + ' > ' + element.Name;
|
|
218
|
-
} else {
|
|
219
|
-
_permission = element.Name;
|
|
220
|
-
}
|
|
238
|
+
const _permission = permission ? permission + ' > ' + element.Name : element.Name;
|
|
221
239
|
// Reached end: write permission into this.allPermissions
|
|
222
240
|
if (element.Operation) {
|
|
223
241
|
const permSplit = _permission.split(' > ');
|
|
@@ -229,15 +247,14 @@ class Role extends MetadataType {
|
|
|
229
247
|
if (!this.allPermissions[basePermission][permissionName]) {
|
|
230
248
|
this.allPermissions[basePermission][permissionName] = {};
|
|
231
249
|
}
|
|
232
|
-
this.allPermissions[basePermission][permissionName][role] =
|
|
233
|
-
|
|
234
|
-
: isAllowed;
|
|
250
|
+
this.allPermissions[basePermission][permissionName][role] =
|
|
251
|
+
element.IsAllowed || isAllowed;
|
|
235
252
|
// Not at end: Traverse more
|
|
236
253
|
} else if (element.PermissionSets) {
|
|
237
254
|
if (Array.isArray(element.PermissionSets.PermissionSet)) {
|
|
238
|
-
element.PermissionSets.PermissionSet
|
|
255
|
+
for (const nextElement of element.PermissionSets.PermissionSet) {
|
|
239
256
|
this._traverseRoles(role, nextElement, _permission);
|
|
240
|
-
}
|
|
257
|
+
}
|
|
241
258
|
} else {
|
|
242
259
|
this._traverseRoles(
|
|
243
260
|
role,
|
|
@@ -250,20 +267,20 @@ class Role extends MetadataType {
|
|
|
250
267
|
// Not at end: Traverse more
|
|
251
268
|
} else if (element.Permissions) {
|
|
252
269
|
if (Array.isArray(element.Permissions.Permission)) {
|
|
253
|
-
element.Permissions.Permission
|
|
270
|
+
for (const nextElement of element.Permissions.Permission) {
|
|
254
271
|
this._traverseRoles(
|
|
255
272
|
role,
|
|
256
273
|
nextElement,
|
|
257
274
|
_permission,
|
|
258
|
-
element.IsAllowed
|
|
275
|
+
element.IsAllowed || isAllowed
|
|
259
276
|
);
|
|
260
|
-
}
|
|
277
|
+
}
|
|
261
278
|
} else {
|
|
262
279
|
this._traverseRoles(
|
|
263
280
|
role,
|
|
264
281
|
element.Permissions.Permission,
|
|
265
282
|
_permission,
|
|
266
|
-
element.IsAllowed
|
|
283
|
+
element.IsAllowed || isAllowed
|
|
267
284
|
);
|
|
268
285
|
}
|
|
269
286
|
}
|
|
@@ -272,7 +289,5 @@ class Role extends MetadataType {
|
|
|
272
289
|
|
|
273
290
|
// Assign definition to static attributes
|
|
274
291
|
Role.definition = require('../MetadataTypeDefinitions').role;
|
|
275
|
-
Role.cache = {};
|
|
276
|
-
Role.client = undefined;
|
|
277
292
|
|
|
278
293
|
module.exports = Role;
|
|
@@ -1,64 +1,54 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const TYPE = require('../../types/mcdev.d');
|
|
3
4
|
const MetadataType = require('./MetadataType');
|
|
4
5
|
const Util = require('../util/util');
|
|
5
6
|
const File = require('../util/file');
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {Object} ScriptItem
|
|
10
|
-
* @property {string} name name
|
|
11
|
-
* @property {string} key key
|
|
12
|
-
* @property {string} description -
|
|
13
|
-
* @property {string} createdDate e.g. "2020-09-14T01:42:03.017"
|
|
14
|
-
* @property {string} modifiedDate e.g. "2020-09-14T01:42:03.017"
|
|
15
|
-
* @property {string} [script] contains script with line breaks converted to '\n'. The content is extracted during retrieval and written into a separate *.ssjs file
|
|
16
|
-
* @property {string} [categoryId] holds folder ID, replaced with r__folder_Path during retrieve
|
|
17
|
-
* @property {string} r__folder_Path folder path in which this DE is saved
|
|
18
|
-
*
|
|
19
|
-
* @typedef {Object.<string, ScriptItem>} ScriptMap
|
|
20
|
-
*
|
|
21
|
-
* @typedef {Object} CodeExtractItem
|
|
22
|
-
* @property {ScriptItem} json metadata of one item w/o code
|
|
23
|
-
* @property {MetadataType.CodeExtract[]} codeArr list of code snippets in this item
|
|
24
|
-
* @property {string[]} subFolder mostly set to null, otherwise list of subfolders
|
|
25
|
-
*/
|
|
7
|
+
const cache = require('../util/cache');
|
|
26
8
|
|
|
27
9
|
/**
|
|
28
10
|
* Script MetadataType
|
|
11
|
+
*
|
|
29
12
|
* @augments MetadataType
|
|
30
13
|
*/
|
|
31
14
|
class Script extends MetadataType {
|
|
32
15
|
/**
|
|
33
16
|
* Retrieves Metadata of Script
|
|
34
17
|
* Endpoint /automation/v1/scripts/ return all Scripts with all details.
|
|
18
|
+
*
|
|
35
19
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
36
|
-
* @
|
|
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.<{metadata: TYPE.ScriptMap, type: string}>} Promise
|
|
37
25
|
*/
|
|
38
|
-
static async retrieve(retrieveDir) {
|
|
26
|
+
static async retrieve(retrieveDir, _, __, ___, key) {
|
|
39
27
|
await File.initPrettier('ssjs');
|
|
40
|
-
return super.retrieveREST(retrieveDir, '/automation/v1/scripts/', null);
|
|
28
|
+
return super.retrieveREST(retrieveDir, '/automation/v1/scripts/', null, null, key);
|
|
41
29
|
}
|
|
42
30
|
/**
|
|
43
31
|
* Retrieves script metadata for caching
|
|
44
|
-
*
|
|
32
|
+
*
|
|
33
|
+
* @returns {Promise.<{metadata: TYPE.ScriptMap, type: string}>} Promise
|
|
45
34
|
*/
|
|
46
35
|
static async retrieveForCache() {
|
|
47
|
-
return super.retrieveREST(null, '/automation/v1/scripts/'
|
|
36
|
+
return super.retrieveREST(null, '/automation/v1/scripts/');
|
|
48
37
|
}
|
|
49
38
|
|
|
50
39
|
/**
|
|
51
40
|
* Retrieve a specific Script by Name
|
|
41
|
+
*
|
|
52
42
|
* @param {string} templateDir Directory where retrieved metadata directory will be saved
|
|
53
43
|
* @param {string} name name of the metadata file
|
|
54
|
-
* @param {
|
|
55
|
-
* @returns {Promise
|
|
44
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
45
|
+
* @returns {Promise.<{metadata: TYPE.Script, type: string}>} Promise
|
|
56
46
|
*/
|
|
57
47
|
static async retrieveAsTemplate(templateDir, name, templateVariables) {
|
|
58
48
|
await File.initPrettier('ssjs');
|
|
59
49
|
return super.retrieveREST(
|
|
60
50
|
templateDir,
|
|
61
|
-
'/automation/v1/scripts/?$filter=name%20eq%20' + name
|
|
51
|
+
'/automation/v1/scripts/?$filter=name%20eq%20' + encodeURIComponent(name),
|
|
62
52
|
null,
|
|
63
53
|
templateVariables
|
|
64
54
|
);
|
|
@@ -66,24 +56,18 @@ class Script extends MetadataType {
|
|
|
66
56
|
|
|
67
57
|
/**
|
|
68
58
|
* manages post retrieve steps
|
|
69
|
-
*
|
|
70
|
-
* @param {
|
|
71
|
-
* @
|
|
72
|
-
* @returns {CodeExtractItem} Array with one metadata object and one ssjs string
|
|
59
|
+
*
|
|
60
|
+
* @param {TYPE.ScriptItem} metadata a single script
|
|
61
|
+
* @returns {TYPE.CodeExtractItem} Array with one metadata object and one ssjs string
|
|
73
62
|
*/
|
|
74
|
-
static postRetrieveTasks(metadata
|
|
75
|
-
// if retrieving template, replace the name with customer key if that wasn't already the case
|
|
76
|
-
if (isTemplating) {
|
|
77
|
-
const warningMsg =
|
|
78
|
-
'Ensure that Automations using this script are updated with the new script-key before deployment.';
|
|
79
|
-
this.overrideKeyWithName(metadata, warningMsg);
|
|
80
|
-
}
|
|
63
|
+
static postRetrieveTasks(metadata) {
|
|
81
64
|
return this.parseMetadata(metadata);
|
|
82
65
|
}
|
|
83
66
|
|
|
84
67
|
/**
|
|
85
68
|
* Updates a single Script
|
|
86
|
-
*
|
|
69
|
+
*
|
|
70
|
+
* @param {TYPE.MetadataTypeItem} script a single Script
|
|
87
71
|
* @returns {Promise} Promise
|
|
88
72
|
*/
|
|
89
73
|
static update(script) {
|
|
@@ -92,7 +76,8 @@ class Script extends MetadataType {
|
|
|
92
76
|
|
|
93
77
|
/**
|
|
94
78
|
* Creates a single Script
|
|
95
|
-
*
|
|
79
|
+
*
|
|
80
|
+
* @param {TYPE.MetadataTypeItem} script a single Script
|
|
96
81
|
* @returns {Promise} Promise
|
|
97
82
|
*/
|
|
98
83
|
static create(script) {
|
|
@@ -101,10 +86,11 @@ class Script extends MetadataType {
|
|
|
101
86
|
|
|
102
87
|
/**
|
|
103
88
|
* helper for this.preDeployTasks() that loads extracted code content back into JSON
|
|
104
|
-
*
|
|
89
|
+
*
|
|
90
|
+
* @param {TYPE.ScriptItem} metadata a single asset definition
|
|
105
91
|
* @param {string} deployDir directory of deploy files
|
|
106
92
|
* @param {string} [templateName] name of the template used to built defintion (prior applying templating)
|
|
107
|
-
* @returns {Promise
|
|
93
|
+
* @returns {Promise.<string>} content for metadata.script
|
|
108
94
|
*/
|
|
109
95
|
static async _mergeCode(metadata, deployDir, templateName) {
|
|
110
96
|
templateName = templateName || metadata.key;
|
|
@@ -115,15 +101,15 @@ class Script extends MetadataType {
|
|
|
115
101
|
templateName + '.' + this.definition.type + '-meta',
|
|
116
102
|
]);
|
|
117
103
|
|
|
118
|
-
if (File.
|
|
119
|
-
code = await File.
|
|
104
|
+
if (await File.pathExists(codePath + '.ssjs')) {
|
|
105
|
+
code = await File.readFilteredFilename(
|
|
120
106
|
[deployDir, this.definition.type],
|
|
121
107
|
templateName + '.' + this.definition.type + '-meta',
|
|
122
108
|
'ssjs'
|
|
123
109
|
);
|
|
124
110
|
code = `<script runat="server">\n${code}</script>`;
|
|
125
|
-
} else if (File.
|
|
126
|
-
code = await File.
|
|
111
|
+
} else if (await File.pathExists(codePath + '.html')) {
|
|
112
|
+
code = await File.readFilteredFilename(
|
|
127
113
|
[deployDir, this.definition.type],
|
|
128
114
|
templateName + '.' + this.definition.type + '-meta',
|
|
129
115
|
'html'
|
|
@@ -135,55 +121,108 @@ class Script extends MetadataType {
|
|
|
135
121
|
}
|
|
136
122
|
/**
|
|
137
123
|
* prepares a Script for deployment
|
|
138
|
-
*
|
|
124
|
+
*
|
|
125
|
+
* @param {TYPE.ScriptItem} metadata a single script activity definition
|
|
139
126
|
* @param {string} dir directory of deploy files
|
|
140
|
-
* @returns {ScriptItem} Promise
|
|
127
|
+
* @returns {TYPE.ScriptItem} Promise
|
|
141
128
|
*/
|
|
142
129
|
static async preDeployTasks(metadata, dir) {
|
|
143
130
|
// folder
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.cache,
|
|
147
|
-
'folder',
|
|
148
|
-
metadata.r__folder_Path,
|
|
149
|
-
'Path',
|
|
150
|
-
'ID'
|
|
151
|
-
);
|
|
152
|
-
delete metadata.r__folder_Path;
|
|
153
|
-
} catch (ex) {
|
|
154
|
-
Util.logger.error(`Script '${metadata.key}': ${ex.message}`);
|
|
155
|
-
}
|
|
131
|
+
metadata.categoryId = cache.searchForField('folder', metadata.r__folder_Path, 'Path', 'ID');
|
|
132
|
+
delete metadata.r__folder_Path;
|
|
156
133
|
|
|
157
134
|
// code
|
|
158
135
|
metadata.script = await this._mergeCode(metadata, dir);
|
|
159
136
|
|
|
160
137
|
return metadata;
|
|
161
138
|
}
|
|
162
|
-
|
|
163
139
|
/**
|
|
164
140
|
* helper for buildDefinition
|
|
165
141
|
* handles extracted code if any are found for complex types
|
|
142
|
+
*
|
|
166
143
|
* @param {string} templateDir Directory where metadata templates are stored
|
|
167
144
|
* @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
|
|
168
|
-
* @param {ScriptItem} metadata main JSON file that was read from file system
|
|
169
|
-
* @param {
|
|
145
|
+
* @param {TYPE.ScriptItem} metadata main JSON file that was read from file system
|
|
146
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
170
147
|
* @param {string} templateName name of the template to be built
|
|
171
|
-
* @returns {Promise}
|
|
148
|
+
* @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
|
|
172
149
|
*/
|
|
173
|
-
static
|
|
150
|
+
static buildDefinitionForNested(
|
|
174
151
|
templateDir,
|
|
175
152
|
targetDir,
|
|
176
153
|
metadata,
|
|
177
|
-
|
|
154
|
+
templateVariables,
|
|
178
155
|
templateName
|
|
179
156
|
) {
|
|
180
|
-
|
|
181
|
-
|
|
157
|
+
return this._buildForNested(
|
|
158
|
+
templateDir,
|
|
159
|
+
targetDir,
|
|
160
|
+
metadata,
|
|
161
|
+
templateVariables,
|
|
162
|
+
templateName,
|
|
163
|
+
'definition'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* helper for buildTemplate
|
|
168
|
+
* handles extracted code if any are found for complex types
|
|
169
|
+
*
|
|
170
|
+
* @example scripts are saved as 1 json and 1 ssjs file. both files need to be run through templating
|
|
171
|
+
* @param {string} templateDir Directory where metadata templates are stored
|
|
172
|
+
* @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
|
|
173
|
+
* @param {TYPE.ScriptItem} metadata main JSON file that was read from file system
|
|
174
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
175
|
+
* @param {string} templateName name of the template to be built
|
|
176
|
+
* @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
|
|
177
|
+
*/
|
|
178
|
+
static buildTemplateForNested(
|
|
179
|
+
templateDir,
|
|
180
|
+
targetDir,
|
|
181
|
+
metadata,
|
|
182
|
+
templateVariables,
|
|
183
|
+
templateName
|
|
184
|
+
) {
|
|
185
|
+
return this._buildForNested(
|
|
186
|
+
templateDir,
|
|
187
|
+
targetDir,
|
|
188
|
+
metadata,
|
|
189
|
+
templateVariables,
|
|
190
|
+
templateName,
|
|
191
|
+
'template'
|
|
192
|
+
);
|
|
193
|
+
}
|
|
182
194
|
|
|
183
|
-
|
|
195
|
+
/**
|
|
196
|
+
* helper for buildTemplateForNested / buildDefinitionForNested
|
|
197
|
+
* handles extracted code if any are found for complex types
|
|
198
|
+
*
|
|
199
|
+
* @param {string} templateDir Directory where metadata templates are stored
|
|
200
|
+
* @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
|
|
201
|
+
* @param {TYPE.ScriptItem} metadata main JSON file that was read from file system
|
|
202
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
203
|
+
* @param {string} templateName name of the template to be built
|
|
204
|
+
* @param {'definition'|'template'} mode defines what we use this helper for
|
|
205
|
+
* @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
|
|
206
|
+
*/
|
|
207
|
+
static async _buildForNested(
|
|
208
|
+
templateDir,
|
|
209
|
+
targetDir,
|
|
210
|
+
metadata,
|
|
211
|
+
templateVariables,
|
|
212
|
+
templateName,
|
|
213
|
+
mode
|
|
214
|
+
) {
|
|
215
|
+
// get SSJS from filesystem
|
|
216
|
+
let code = await this._mergeCode(metadata, templateDir, templateName);
|
|
184
217
|
try {
|
|
185
|
-
|
|
186
|
-
|
|
218
|
+
if (mode === 'definition') {
|
|
219
|
+
// replace template variables with their values
|
|
220
|
+
code = this.applyTemplateValues(code, templateVariables);
|
|
221
|
+
} else if (mode === 'template') {
|
|
222
|
+
// replace template values with corresponding variable names
|
|
223
|
+
code = this.applyTemplateNames(code, templateVariables);
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
187
226
|
throw new Error(
|
|
188
227
|
`${this.definition.type}:: Error applying template variables on ${
|
|
189
228
|
metadata[this.definition.keyField] + '.' + this.definition.type
|
|
@@ -193,6 +232,7 @@ class Script extends MetadataType {
|
|
|
193
232
|
|
|
194
233
|
// write to file
|
|
195
234
|
const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir];
|
|
235
|
+
const nestedFilePaths = [];
|
|
196
236
|
|
|
197
237
|
for (const targetDir of targetDirArr) {
|
|
198
238
|
File.writeToFile(
|
|
@@ -201,19 +241,25 @@ class Script extends MetadataType {
|
|
|
201
241
|
'ssjs',
|
|
202
242
|
code
|
|
203
243
|
);
|
|
244
|
+
nestedFilePaths.push([
|
|
245
|
+
targetDir,
|
|
246
|
+
this.definition.type,
|
|
247
|
+
metadata[this.definition.keyField] + '.' + this.definition.type + '-meta.ssjs',
|
|
248
|
+
]);
|
|
204
249
|
}
|
|
250
|
+
return nestedFilePaths;
|
|
205
251
|
}
|
|
206
252
|
|
|
207
253
|
/**
|
|
208
254
|
* Splits the script metadata into two parts and parses in a standard manner
|
|
209
|
-
*
|
|
210
|
-
* @
|
|
255
|
+
*
|
|
256
|
+
* @param {TYPE.ScriptItem} metadata a single script activity definition
|
|
257
|
+
* @returns {TYPE.CodeExtractItem} a single item with code parts extracted
|
|
211
258
|
*/
|
|
212
259
|
static parseMetadata(metadata) {
|
|
213
260
|
// folder
|
|
214
261
|
try {
|
|
215
|
-
metadata.r__folder_Path =
|
|
216
|
-
this.cache,
|
|
262
|
+
metadata.r__folder_Path = cache.searchForField(
|
|
217
263
|
'folder',
|
|
218
264
|
metadata.categoryId,
|
|
219
265
|
'ID',
|
|
@@ -221,7 +267,7 @@ class Script extends MetadataType {
|
|
|
221
267
|
);
|
|
222
268
|
delete metadata.categoryId;
|
|
223
269
|
} catch (ex) {
|
|
224
|
-
Util.logger.warn(`Script '${metadata.key}': ${ex.message}`);
|
|
270
|
+
Util.logger.warn(` - Script '${metadata.key}': ${ex.message}`);
|
|
225
271
|
}
|
|
226
272
|
|
|
227
273
|
// extract SSJS
|
|
@@ -231,15 +277,14 @@ class Script extends MetadataType {
|
|
|
231
277
|
let ssjs;
|
|
232
278
|
let fileExt;
|
|
233
279
|
const regexMatches = regex.exec(metadata.script);
|
|
234
|
-
if (regexMatches
|
|
280
|
+
if (regexMatches?.length > 1) {
|
|
235
281
|
ssjs = regexMatches[1];
|
|
236
282
|
fileExt = 'ssjs';
|
|
237
283
|
} else {
|
|
238
284
|
ssjs = metadata.script;
|
|
239
285
|
fileExt = 'html';
|
|
240
286
|
Util.logger.warn(
|
|
241
|
-
'
|
|
242
|
-
metadata.name
|
|
287
|
+
' - Could not find script tags, saving whole text in SSJS: ' + metadata.name
|
|
243
288
|
);
|
|
244
289
|
}
|
|
245
290
|
delete metadata.script;
|
|
@@ -252,11 +297,30 @@ class Script extends MetadataType {
|
|
|
252
297
|
|
|
253
298
|
return { json: metadata, codeArr: codeArr, subFolder: null };
|
|
254
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* should return only the json for all but asset, query and script that are saved as multiple files
|
|
302
|
+
* additionally, the documentation for dataExtension and automation should be returned
|
|
303
|
+
*
|
|
304
|
+
* @param {string[]} keyArr customerkey of the metadata
|
|
305
|
+
* @returns {string[]} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
|
|
306
|
+
*/
|
|
307
|
+
static getFilesToCommit(keyArr) {
|
|
308
|
+
const path = File.normalizePath([
|
|
309
|
+
this.properties.directories.retrieve,
|
|
310
|
+
this.buObject.credential,
|
|
311
|
+
this.buObject.businessUnit,
|
|
312
|
+
this.definition.type,
|
|
313
|
+
]);
|
|
314
|
+
|
|
315
|
+
const fileList = keyArr.flatMap((key) => [
|
|
316
|
+
File.normalizePath([path, `${key}.${this.definition.type}-meta.json`]),
|
|
317
|
+
File.normalizePath([path, `${key}.${this.definition.type}-meta.ssjs`]),
|
|
318
|
+
]);
|
|
319
|
+
return fileList;
|
|
320
|
+
}
|
|
255
321
|
}
|
|
256
322
|
|
|
257
323
|
// Assign definition & cache to static attributes
|
|
258
324
|
Script.definition = require('../MetadataTypeDefinitions').script;
|
|
259
|
-
Script.cache = {};
|
|
260
|
-
Script.client = undefined;
|
|
261
325
|
|
|
262
326
|
module.exports = Script;
|