mcdev 3.0.2 → 3.1.2

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 (44) hide show
  1. package/.eslintrc.json +1 -1
  2. package/.github/ISSUE_TEMPLATE/bug.yml +75 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +3 -2
  4. package/.issuetracker +11 -3
  5. package/.vscode/settings.json +3 -3
  6. package/CHANGELOG.md +88 -0
  7. package/README.md +245 -141
  8. package/boilerplate/config.json +3 -2
  9. package/docs/dist/documentation.md +818 -352
  10. package/lib/Deployer.js +4 -1
  11. package/lib/MetadataTypeDefinitions.js +1 -0
  12. package/lib/MetadataTypeInfo.js +1 -0
  13. package/lib/Retriever.js +30 -14
  14. package/lib/cli.js +295 -0
  15. package/lib/index.js +774 -1019
  16. package/lib/metadataTypes/AccountUser.js +389 -0
  17. package/lib/metadataTypes/Asset.js +8 -7
  18. package/lib/metadataTypes/Automation.js +121 -56
  19. package/lib/metadataTypes/DataExtension.js +167 -121
  20. package/lib/metadataTypes/DataExtensionField.js +134 -4
  21. package/lib/metadataTypes/DataExtract.js +9 -5
  22. package/lib/metadataTypes/EventDefinition.js +9 -5
  23. package/lib/metadataTypes/FileTransfer.js +9 -5
  24. package/lib/metadataTypes/Folder.js +66 -69
  25. package/lib/metadataTypes/ImportFile.js +13 -12
  26. package/lib/metadataTypes/MetadataType.js +138 -77
  27. package/lib/metadataTypes/Query.js +2 -3
  28. package/lib/metadataTypes/Role.js +13 -8
  29. package/lib/metadataTypes/Script.js +2 -2
  30. package/lib/metadataTypes/definitions/AccountUser.definition.js +227 -0
  31. package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
  32. package/lib/metadataTypes/definitions/DataExtension.definition.js +1 -1
  33. package/lib/metadataTypes/definitions/DataExtensionField.definition.js +1 -1
  34. package/lib/metadataTypes/definitions/Folder.definition.js +1 -1
  35. package/lib/metadataTypes/definitions/ImportFile.definition.js +2 -1
  36. package/lib/metadataTypes/definitions/Script.definition.js +5 -5
  37. package/lib/retrieveChangelog.js +96 -0
  38. package/lib/util/cli.js +4 -6
  39. package/lib/util/init.git.js +2 -1
  40. package/lib/util/util.js +20 -3
  41. package/package.json +19 -23
  42. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
  43. package/img/README.md/troubleshoot-nodejs-postinstall.jpg +0 -0
  44. package/postinstall.js +0 -41
@@ -30,10 +30,10 @@ class DataExtract extends MetadataType {
30
30
  * Retrieve a specific dataExtract Definition by Name
31
31
  * @param {String} templateDir Directory where retrieved metadata directory will be saved
32
32
  * @param {String} name name of the metadata file
33
- * @param {Object} variables variables to be replaced in the metadata
33
+ * @param {Object} templateVariables variables to be replaced in the metadata
34
34
  * @returns {Promise<Object>} Promise of metadata
35
35
  */
36
- static async retrieveAsTemplate(templateDir, name, variables) {
36
+ static async retrieveAsTemplate(templateDir, name, templateVariables) {
37
37
  const options = {
38
38
  uri: '/automation/v1/dataextracts/?$filter=name%20eq%20' + name.split(' ').join('%20'),
39
39
  };
@@ -51,16 +51,20 @@ class DataExtract extends MetadataType {
51
51
  const extended = await this.client.RestClient.get({
52
52
  uri: '/automation/v1/dataextracts/' + metadata.id,
53
53
  });
54
+ const originalKey = extended.body[this.definition.keyField];
54
55
  const val = JSON.parse(
55
- Util.replaceByObject(JSON.stringify(this.parseMetadata(extended.body)), variables)
56
+ Util.replaceByObject(
57
+ JSON.stringify(this.parseMetadata(extended.body)),
58
+ templateVariables
59
+ )
56
60
  );
57
61
 
58
62
  // remove all fields listed in Definition for templating
59
63
  this.keepTemplateFields(val);
60
64
  File.writeJSONToFile(
61
65
  [templateDir, this.definition.type].join('/'),
62
- val.customerKey + '.' + this.definition.type + '-meta',
63
- JSON.parse(Util.replaceByObject(JSON.stringify(val), variables))
66
+ originalKey + '.' + this.definition.type + '-meta',
67
+ JSON.parse(Util.replaceByObject(JSON.stringify(val), templateVariables))
64
68
  );
65
69
  Util.logger.info(
66
70
  `Dataextracts.retrieveAsTemplate:: Written Metadata to filesystem (${name})`
@@ -32,10 +32,10 @@ class EventDefinition extends MetadataType {
32
32
  * Retrieve a specific Event Definition by Name
33
33
  * @param {String} templateDir Directory where retrieved metadata directory will be saved
34
34
  * @param {String} name name of the metadata file
35
- * @param {Object} variables variables to be replaced in the metadata
35
+ * @param {Object} templateVariables variables to be replaced in the metadata
36
36
  * @returns {Promise<Object>} Promise of metadata
37
37
  */
38
- static async retrieveAsTemplate(templateDir, name, variables) {
38
+ static async retrieveAsTemplate(templateDir, name, templateVariables) {
39
39
  // todo template based on name
40
40
  const options = {
41
41
  uri: '/interaction/v1/EventDefinitions?name=' + encodeURIComponent(name),
@@ -51,8 +51,12 @@ class EventDefinition extends MetadataType {
51
51
  `please rename to be unique to avoid issues`
52
52
  );
53
53
  } else if (event && event.length === 1) {
54
+ const originalKey = event[0][this.definition.keyField];
54
55
  const eventDef = JSON.parse(
55
- Util.replaceByObject(JSON.stringify(this.parseMetadata(event[0])), variables)
56
+ Util.replaceByObject(
57
+ JSON.stringify(this.parseMetadata(event[0])),
58
+ templateVariables
59
+ )
56
60
  );
57
61
  if (!eventDef.dataExtensionId) {
58
62
  throw new Error(
@@ -67,8 +71,8 @@ class EventDefinition extends MetadataType {
67
71
  this.keepTemplateFields(eventDef);
68
72
  File.writeJSONToFile(
69
73
  [templateDir, this.definition.type].join('/'),
70
- eventDef.customerKey + '.' + this.definition.type + '-meta',
71
- JSON.parse(Util.replaceByObject(JSON.stringify(eventDef), variables))
74
+ originalKey + '.' + this.definition.type + '-meta',
75
+ JSON.parse(Util.replaceByObject(JSON.stringify(eventDef), templateVariables))
72
76
  );
73
77
  Util.logger.info(
74
78
  `EventDefinition.retrieveAsTemplate:: Written Metadata to filesystem (${name})`
@@ -30,10 +30,10 @@ class FileTransfer extends MetadataType {
30
30
  * Retrieve a specific File Transfer Definition by Name
31
31
  * @param {String} templateDir Directory where retrieved metadata directory will be saved
32
32
  * @param {String} name name of the metadata file
33
- * @param {Object} variables variables to be replaced in the metadata
33
+ * @param {Object} templateVariables variables to be replaced in the metadata
34
34
  * @returns {Promise} Promise
35
35
  */
36
- static async retrieveAsTemplate(templateDir, name, variables) {
36
+ static async retrieveAsTemplate(templateDir, name, templateVariables) {
37
37
  const options = {
38
38
  uri: '/automation/v1/filetransfers/?$filter=name%20eq%20' + name.split(' ').join('%20'),
39
39
  };
@@ -51,16 +51,20 @@ class FileTransfer extends MetadataType {
51
51
  const extended = await this.client.RestClient.get({
52
52
  uri: '/automation/v1/filetransfers/' + metadata.id,
53
53
  });
54
+ const originalKey = extended.body[this.definition.keyField];
54
55
  const val = JSON.parse(
55
- Util.replaceByObject(JSON.stringify(this.parseMetadata(extended.body)), variables)
56
+ Util.replaceByObject(
57
+ JSON.stringify(this.parseMetadata(extended.body)),
58
+ templateVariables
59
+ )
56
60
  );
57
61
 
58
62
  // remove all fields listed in Definition for templating
59
63
  this.keepTemplateFields(val);
60
64
  File.writeJSONToFile(
61
65
  [templateDir, this.definition.type].join('/'),
62
- val.customerKey + '.' + this.definition.type + '-meta',
63
- JSON.parse(Util.replaceByObject(JSON.stringify(val), variables))
66
+ originalKey + '.' + this.definition.type + '-meta',
67
+ JSON.parse(Util.replaceByObject(JSON.stringify(val), templateVariables))
64
68
  );
65
69
  Util.logger.info(
66
70
  `FileTransfer.retrieveAsTemplate:: Written Metadata to filesystem (${name})`
@@ -13,16 +13,16 @@ class Folder extends MetadataType {
13
13
  /**
14
14
  * Retrieves metadata of metadata type into local filesystem. executes callback with retrieved metadata
15
15
  * @param {String} retrieveDir Directory where retrieved metadata directory will be saved
16
- * @param {String[]} [overrideFields] Returns specified fields even if their retrieve definition is not set to true
16
+ * @param {String[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
17
17
  * @param {Object} buObject properties for auth
18
18
  * @returns {Promise} Promise
19
19
  */
20
- static async retrieve(retrieveDir, overrideFields, buObject) {
21
- const queryAllFolders = await this.retrieveHelper(overrideFields, true);
20
+ static async retrieve(retrieveDir, additionalFields, buObject) {
21
+ const queryAllFolders = await this.retrieveHelper(additionalFields, true);
22
22
  // if this is the parent, no need to query twice as QueryAllAccounts works.
23
23
 
24
24
  if (buObject.eid !== buObject.mid) {
25
- queryAllFolders.push(...(await this.retrieveHelper(overrideFields, false)));
25
+ queryAllFolders.push(...(await this.retrieveHelper(additionalFields, false)));
26
26
  }
27
27
  const sortPairs = toposort(queryAllFolders.map((a) => [a.ParentFolder.ID, a.ID]));
28
28
  const idMap = {};
@@ -258,39 +258,36 @@ class Folder extends MetadataType {
258
258
  * @param {Object} metadata metadata of the folder
259
259
  * @returns {Promise} Promise
260
260
  */
261
- static create(metadata) {
262
- const tempCustomerKey = metadata.CustomerKey;
261
+ static async create(metadata) {
262
+ if (metadata.Parent && metadata.Parent.ID && metadata.Parent.ID === 0) {
263
+ Util.logger.error(
264
+ `${this.definition.type}-${metadata.ContentType}.create:: Cannot create Root Folder: ${metadata.Name}`
265
+ );
266
+ return {};
267
+ }
263
268
  const path = metadata.Path;
264
- this.removeNotCreateableFields(metadata);
265
- return new Promise((resolve) => {
266
- if (metadata.Parent && metadata.Parent.ID && metadata.Parent.ID === 0) {
269
+ try {
270
+ const response = await super.createSOAP(metadata, 'DataFolder', true);
271
+ if (response) {
272
+ response.body.Results[0].Object = metadata;
273
+ response.body.Results[0].Object.ID = response.body.Results[0].NewID;
274
+ response.body.Results[0].Object.CustomerKey = metadata.CustomerKey;
275
+ delete response.body.Results[0].Object.$;
276
+
277
+ Util.logger.info(`- created folder: ${path}`);
278
+ return response;
279
+ }
280
+ } catch (ex) {
281
+ if (ex && ex.results) {
267
282
  Util.logger.error(
268
- `${this.definition.type}-${metadata.ContentType}.create:: Cannot create Root Folder: ${metadata.Name}`
283
+ `${this.definition.type}-${metadata.ContentType}.create:: error creating: '${path}'. ${ex.results[0].StatusMessage}`
284
+ );
285
+ } else if (ex) {
286
+ Util.logger.error(
287
+ `${this.definition.type}-${metadata.ContentType}.create:: error creating: '${path}'. ${ex.message}`
269
288
  );
270
- resolve({});
271
- } else {
272
- this.client.SoapClient.create('DataFolder', metadata, null, (ex, response) => {
273
- if (ex && ex.results) {
274
- Util.logger.error(
275
- `${this.definition.type}-${metadata.ContentType}.create:: error creating: ${path}. ${ex.results[0].StatusMessage}`
276
- );
277
- resolve();
278
- } else if (ex) {
279
- Util.logger.error(
280
- `${this.definition.type}-${metadata.ContentType}.create:: error creating: ${path}. ${ex.message}`
281
- );
282
- resolve();
283
- } else {
284
- response.body.Results[0].Object = metadata;
285
- response.body.Results[0].Object.ID = response.body.Results[0].NewID;
286
- response.body.Results[0].Object.CustomerKey = tempCustomerKey;
287
- delete response.body.Results[0].Object.$;
288
- Util.logger.info(`- created folder: ${path}`);
289
- resolve(response);
290
- }
291
- });
292
289
  }
293
- });
290
+ }
294
291
  }
295
292
 
296
293
  /**
@@ -299,30 +296,27 @@ class Folder extends MetadataType {
299
296
  * @returns {Promise} Promise
300
297
  */
301
298
  static async update(metadata) {
302
- return await new Promise((resolve) => {
303
- const tempCustomerKey = metadata.CustomerKey;
304
- const path = metadata.Path;
305
- this.removeNotUpdateableFields(metadata);
306
- this.client.SoapClient.update('DataFolder', metadata, null, (ex, response) => {
307
- if (ex && ex.results) {
308
- Util.logger.error(
309
- `${this.definition.type}-${metadata.ContentType}.update:: error updating: '${path}'. ${ex.results[0].StatusMessage}`
310
- );
311
- resolve();
312
- } else if (ex) {
313
- Util.logger.error(
314
- `${this.definition.type}-${metadata.ContentType}.update:: error updating: '${path}'. ${ex.message}`
315
- );
316
- resolve();
317
- } else {
318
- response.body.Results[0].Object = metadata;
319
- response.body.Results[0].Object.CustomerKey = tempCustomerKey;
320
- delete response.body.Results[0].Object.$;
321
- Util.logger.info(`- updated folder: ${path}`);
322
- resolve(response);
323
- }
324
- });
325
- });
299
+ const path = metadata.Path;
300
+ try {
301
+ const response = await super.updateSOAP(metadata, 'DataFolder', true);
302
+ if (response) {
303
+ response.body.Results[0].Object = metadata;
304
+ response.body.Results[0].Object.CustomerKey = metadata.CustomerKey;
305
+ delete response.body.Results[0].Object.$;
306
+ Util.logger.info(`- updated folder: ${path}`);
307
+ return response;
308
+ }
309
+ } catch (ex) {
310
+ if (ex && ex.results) {
311
+ Util.logger.error(
312
+ `${this.definition.type}-${metadata.ContentType}.update:: error updating: '${path}'. ${ex.results[0].StatusMessage}`
313
+ );
314
+ } else if (ex) {
315
+ Util.logger.error(
316
+ `${this.definition.type}-${metadata.ContentType}.update:: error updating: '${path}'. ${ex.message}`
317
+ );
318
+ }
319
+ }
326
320
  }
327
321
 
328
322
  /**
@@ -451,25 +445,28 @@ class Folder extends MetadataType {
451
445
 
452
446
  /**
453
447
  * Helper to retrieve the folders as promise
454
- * @param {String[]} [overrideFields] Returns specified fields even if their retrieve definition is not set to true
448
+ * @param {String[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
455
449
  * @param {Boolean} [queryAllAccounts] which queryAllAccounts setting to use
456
450
  * @returns {Promise<Object>} soap object
457
451
  */
458
- static async retrieveHelper(overrideFields, queryAllAccounts) {
452
+ static async retrieveHelper(additionalFields, queryAllAccounts) {
459
453
  const options = { queryAllAccounts: !!queryAllAccounts };
460
454
  let status = 'MoreDataAvailable';
461
455
  const Results = [];
462
456
  do {
463
- const response = await new Promise((resolve, reject) => {
464
- // filtered out path as we need them to be stored locally, but do not want to try and retrieve
465
- this.client.SoapClient.retrieve(
466
- 'DataFolder',
467
- this.getFieldNamesToRetrieve(overrideFields).filter(
468
- (field) => !field.includes('Path')
469
- ),
470
- options,
471
- (ex, response) => (ex ? reject(ex) : resolve(response))
472
- );
457
+ let response;
458
+ await Util.retryOnError(`Retrying ${this.definition.type}`, async () => {
459
+ response = await new Promise((resolve, reject) => {
460
+ // filtered out path as we need them to be stored locally, but do not want to try and retrieve
461
+ this.client.SoapClient.retrieve(
462
+ 'DataFolder',
463
+ this.getFieldNamesToRetrieve(additionalFields).filter(
464
+ (field) => !field.includes('Path')
465
+ ),
466
+ options,
467
+ (ex, response) => (ex ? reject(ex) : resolve(response))
468
+ );
469
+ });
473
470
  });
474
471
  // merge results with existing
475
472
  Results.push(...response.body.Results);
@@ -32,10 +32,10 @@ class ImportFile extends MetadataType {
32
32
  * Retrieve a specific Import Definition by Name
33
33
  * @param {String} templateDir Directory where retrieved metadata directory will be saved
34
34
  * @param {String} name name of the metadata file
35
- * @param {Object} variables variables to be replaced in the metadata
35
+ * @param {Object} templateVariables variables to be replaced in the metadata
36
36
  * @returns {Promise} Promise
37
37
  */
38
- static async retrieveAsTemplate(templateDir, name, variables) {
38
+ static async retrieveAsTemplate(templateDir, name, templateVariables) {
39
39
  const options = {
40
40
  uri: '/automation/v1/imports/?$filter=name%20eq%20' + name.split(' ').join('%20'),
41
41
  };
@@ -48,17 +48,20 @@ class ImportFile extends MetadataType {
48
48
  Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`);
49
49
  return;
50
50
  }
51
-
51
+ const originalKey = metadata[this.definition.keyField];
52
52
  const val = JSON.parse(
53
- Util.replaceByObject(JSON.stringify(this.parseMetadata(metadata)), variables)
53
+ Util.replaceByObject(
54
+ JSON.stringify(this.parseMetadata(metadata)),
55
+ templateVariables
56
+ )
54
57
  );
55
58
 
56
59
  // remove all fields listed in Definition for templating
57
60
  this.keepTemplateFields(val);
58
61
  File.writeJSONToFile(
59
62
  [templateDir, this.definition.type].join('/'),
60
- val.customerKey + '.' + this.definition.type + '-meta',
61
- JSON.parse(Util.replaceByObject(JSON.stringify(val), variables))
63
+ originalKey + '.' + this.definition.type + '-meta',
64
+ JSON.parse(Util.replaceByObject(JSON.stringify(val), templateVariables))
62
65
  );
63
66
  Util.logger.info(
64
67
  `ImportFile.retrieveAsTemplate:: Written Metadata to filesystem (${name})`
@@ -147,12 +150,10 @@ class ImportFile extends MetadataType {
147
150
  }
148
151
  }
149
152
  // When the destinationObjectTypeId is 584 is refers to Mobile Connect which is not supported as an Import Type
150
- metadata.destinationObjectTypeId = this.definition.destinationObjectTypeMapping[
151
- metadata.c__destinationType
152
- ];
153
- metadata.subscriberImportTypeId = this.definition.subscriberImportTypeMapping[
154
- metadata.c__subscriberImportType
155
- ];
153
+ metadata.destinationObjectTypeId =
154
+ this.definition.destinationObjectTypeMapping[metadata.c__destinationType];
155
+ metadata.subscriberImportTypeId =
156
+ this.definition.subscriberImportTypeMapping[metadata.c__subscriberImportType];
156
157
  metadata.updateTypeId = this.definition.updateTypeMapping[metadata.c__dataAction];
157
158
  return metadata;
158
159
  }