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
@@ -194,16 +194,26 @@ class MetadataType {
194
194
  /**
195
195
  * Gets metadata from Marketing Cloud
196
196
  * @param {string} retrieveDir Directory where retrieved metadata directory will be saved
197
- * @param {string[]} [overrideFields] Returns specified fields even if their retrieve definition is not set to true
197
+ * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
198
198
  * @param {Util.BuObject} buObject properties for auth
199
199
  * @param {string} [subType] optionally limit to a single subtype
200
200
  * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} metadata
201
201
  */
202
- static retrieve(retrieveDir, overrideFields, buObject, subType) {
202
+ static retrieve(retrieveDir, additionalFields, buObject, subType) {
203
203
  Util.metadataLogger('error', this.definition.type, 'retrieve', `Not Supported`);
204
204
  const metadata = {};
205
205
  return { metadata: null, type: this.definition.type };
206
206
  }
207
+ /**
208
+ * Gets metadata from Marketing Cloud
209
+ * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
210
+ * @param {Util.BuObject} buObject properties for auth
211
+ * @param {string} [subType] optionally limit to a single subtype
212
+ * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} metadata
213
+ */
214
+ static retrieveChangelog(additionalFields, buObject, subType) {
215
+ return this.retrieve(null, additionalFields, buObject, subType);
216
+ }
207
217
 
208
218
  /**
209
219
  * Gets metadata cache with limited fields and does not store value to disk
@@ -304,9 +314,10 @@ class MetadataType {
304
314
  });
305
315
  } else if (this.cache[this.definition.type][normalizedKey]) {
306
316
  // normal way of processing update files
307
- metadata[metadataKey][this.definition.idField] = this.cache[
308
- this.definition.type
309
- ][normalizedKey][this.definition.idField];
317
+ metadata[metadataKey][this.definition.idField] =
318
+ this.cache[this.definition.type][normalizedKey][
319
+ this.definition.idField
320
+ ];
310
321
  metadataToUpdate.push({
311
322
  before: this.cache[this.definition.type][normalizedKey],
312
323
  after: metadata[metadataKey],
@@ -403,31 +414,54 @@ class MetadataType {
403
414
  /**
404
415
  * Creates a single metadata entry via fuel-soap (generic lib not wrapper)
405
416
  * @param {MetadataTypeItem} metadataEntry single metadata entry
417
+ * @param {string} [overrideType] can be used if the API type differs from the otherwise used type identifier
418
+ * @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
406
419
  * @returns {Promise} Promise
407
420
  */
408
- static async createSOAP(metadataEntry) {
421
+ static async createSOAP(metadataEntry, overrideType, handleOutside) {
409
422
  try {
410
- const res = await new Promise((resolve, reject) => {
411
- this.removeNotCreateableFields(metadataEntry);
412
- this.client.SoapClient.create(
413
- this.definition.type.charAt(0).toUpperCase() + this.definition.type.slice(1),
414
- metadataEntry,
415
- null,
416
- (error, response) => (error ? reject(error) : resolve(response))
417
- );
418
- });
419
- Util.logger.info(
420
- `- created ${this.definition.type}: ${metadataEntry[this.definition.keyField]}`
423
+ this.removeNotCreateableFields(metadataEntry);
424
+ let response;
425
+ await Util.retryOnError(
426
+ `Retrying to create ${this.definition.type}: ${
427
+ metadataEntry[this.definition.nameField]
428
+ }`,
429
+ async () =>
430
+ (response = await new Promise((resolve, reject) => {
431
+ this.client.SoapClient.create(
432
+ overrideType ||
433
+ this.definition.type.charAt(0).toUpperCase() +
434
+ this.definition.type.slice(1),
435
+ metadataEntry,
436
+ null,
437
+ (error, response) => (error ? reject(error) : resolve(response))
438
+ );
439
+ }))
421
440
  );
422
- return res;
441
+ if (!handleOutside) {
442
+ Util.logger.info(
443
+ `- created ${this.definition.type}: ${metadataEntry[this.definition.keyField]}`
444
+ );
445
+ }
446
+ return response;
423
447
  } catch (ex) {
424
- Util.logger.error(
425
- `- error creating ${this.definition.type}: ${
426
- metadataEntry[this.definition.keyField]
427
- } (${ex.message})`
428
- );
448
+ if (!handleOutside) {
449
+ let errorMsg;
450
+ if (ex.results && ex.results.length) {
451
+ errorMsg = `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})`;
452
+ } else {
453
+ errorMsg = ex.message;
454
+ }
455
+ Util.logger.error(
456
+ `- error creating ${this.definition.type} '${
457
+ metadataEntry[this.definition.keyField]
458
+ }': ${errorMsg}`
459
+ );
460
+ } else {
461
+ throw ex;
462
+ }
429
463
 
430
- return null;
464
+ return {};
431
465
  }
432
466
  }
433
467
 
@@ -451,8 +485,12 @@ class MetadataType {
451
485
  async () => (response = await this.client.RestClient.patch(options))
452
486
  );
453
487
  this.checkForErrors(response);
488
+ // some times, e.g. automation dont return a key in their update response and hence we need to fall back to name
454
489
  Util.logger.info(
455
- `- updated ${this.definition.type}: ${metadataEntry[this.definition.keyField]}`
490
+ `- updated ${this.definition.type}: ${
491
+ metadataEntry[this.definition.keyField] ||
492
+ metadataEntry[this.definition.nameField]
493
+ }`
456
494
  );
457
495
  return response;
458
496
  } catch (ex) {
@@ -468,31 +506,54 @@ class MetadataType {
468
506
  /**
469
507
  * Updates a single metadata entry via fuel-soap (generic lib not wrapper)
470
508
  * @param {MetadataTypeItem} metadataEntry single metadata entry
509
+ * @param {string} [overrideType] can be used if the API type differs from the otherwise used type identifier
510
+ * @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
471
511
  * @returns {Promise} Promise
472
512
  */
473
- static async updateSOAP(metadataEntry) {
513
+ static async updateSOAP(metadataEntry, overrideType, handleOutside) {
514
+ let response;
474
515
  try {
475
- const res = await new Promise((resolve, reject) => {
476
- this.removeNotUpdateableFields(metadataEntry);
477
- this.client.SoapClient.update(
478
- this.definition.type.charAt(0).toUpperCase() + this.definition.type.slice(1),
479
- metadataEntry,
480
- null,
481
- (error, response) => (error ? reject(error) : resolve(response))
482
- );
483
- });
484
- Util.logger.info(
485
- `- updated ${this.definition.type}: ${metadataEntry[this.definition.keyField]}`
516
+ this.removeNotUpdateableFields(metadataEntry);
517
+ await Util.retryOnError(
518
+ `Retrying to update ${this.definition.type}: ${
519
+ metadataEntry[this.definition.nameField]
520
+ }`,
521
+ async () =>
522
+ (response = await new Promise((resolve, reject) => {
523
+ this.client.SoapClient.update(
524
+ overrideType ||
525
+ this.definition.type.charAt(0).toUpperCase() +
526
+ this.definition.type.slice(1),
527
+ metadataEntry,
528
+ null,
529
+ (error, response) => (error ? reject(error) : resolve(response))
530
+ );
531
+ }))
486
532
  );
487
- return res;
533
+ if (!handleOutside) {
534
+ Util.logger.info(
535
+ `- updated ${this.definition.type}: ${metadataEntry[this.definition.keyField]}`
536
+ );
537
+ }
538
+ return response;
488
539
  } catch (ex) {
489
- Util.logger.error(
490
- `- error updating ${this.definition.type}: ${
491
- metadataEntry[this.definition.keyField]
492
- } (${ex.message})`
493
- );
540
+ if (!handleOutside) {
541
+ let errorMsg;
542
+ if (ex.results && ex.results.length) {
543
+ errorMsg = `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})`;
544
+ } else {
545
+ errorMsg = ex.message;
546
+ }
547
+ Util.logger.error(
548
+ `- error updating ${this.definition.type} '${
549
+ metadataEntry[this.definition.keyField]
550
+ }': ${errorMsg}`
551
+ );
552
+ } else {
553
+ throw ex;
554
+ }
494
555
 
495
- return null;
556
+ return {};
496
557
  }
497
558
  }
498
559
  /**
@@ -501,18 +562,24 @@ class MetadataType {
501
562
  * @param {Util.BuObject} buObject properties for auth
502
563
  * @param {Object} [options] required for the specific request (filter for example)
503
564
  * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
565
+ * @param {string} [overrideType] can be used if the API type differs from the otherwise used type identifier
504
566
  * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} Promise of item map
505
567
  */
506
- static async retrieveSOAPgeneric(retrieveDir, buObject, options, additionalFields) {
568
+ static async retrieveSOAPgeneric(
569
+ retrieveDir,
570
+ buObject,
571
+ options,
572
+ additionalFields,
573
+ overrideType
574
+ ) {
507
575
  const fields = this.getFieldNamesToRetrieve(additionalFields);
508
576
 
509
- const metadata = await this.retrieveSOAPBody(fields, options);
577
+ const metadata = await this.retrieveSOAPBody(fields, options, overrideType);
510
578
  if (retrieveDir) {
511
579
  const savedMetadata = await this.saveResults(metadata, retrieveDir, null);
512
580
  Util.logger.info(
513
581
  `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})`
514
582
  );
515
-
516
583
  if (
517
584
  buObject &&
518
585
  this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
@@ -538,7 +605,7 @@ class MetadataType {
538
605
  do {
539
606
  let resultsBatch;
540
607
  await Util.retryOnError(`Retrying ${this.definition.type}`, async () => {
541
- resultsBatch = await new Promise((resolve) => {
608
+ resultsBatch = await new Promise((resolve, reject) => {
542
609
  this.client.SoapClient.retrieve(
543
610
  type || this.definition.type,
544
611
  fields,
@@ -546,7 +613,7 @@ class MetadataType {
546
613
  (error, response) => {
547
614
  if (error) {
548
615
  Util.logger.debug(`SOAP.retrieve Error: ${error.message}`);
549
- throw error;
616
+ reject(error);
550
617
  }
551
618
  if (response) {
552
619
  resolve(response.body);
@@ -1005,11 +1072,11 @@ class MetadataType {
1005
1072
  const savedResults = {};
1006
1073
  const subtypeExtension = '.' + (overrideType || this.definition.type) + '-meta';
1007
1074
  let filterCounter = 0;
1008
- for (const metadataEntry in results) {
1075
+ for (const originalKey in results) {
1009
1076
  try {
1010
1077
  if (
1011
- this.isFiltered(results[metadataEntry], true) ||
1012
- this.isFiltered(results[metadataEntry], false)
1078
+ this.isFiltered(results[originalKey], true) ||
1079
+ this.isFiltered(results[originalKey], false)
1013
1080
  ) {
1014
1081
  // if current metadata entry is filtered don't save it
1015
1082
  filterCounter++;
@@ -1018,21 +1085,21 @@ class MetadataType {
1018
1085
  // define directory into which the current metdata shall be saved
1019
1086
  const baseDir = [retrieveDir, ...(overrideType || this.definition.type).split('-')];
1020
1087
 
1021
- results[metadataEntry] = await this.postRetrieveTasks(
1022
- results[metadataEntry],
1088
+ results[originalKey] = await this.postRetrieveTasks(
1089
+ results[originalKey],
1023
1090
  retrieveDir,
1024
1091
  templateVariables ? true : false
1025
1092
  );
1026
- if (!results[metadataEntry] || results[metadataEntry] === null) {
1093
+ if (!results[originalKey] || results[originalKey] === null) {
1027
1094
  // we encountered a situation in our postRetrieveTasks that made us want to filter this record
1028
- delete results[metadataEntry];
1095
+ delete results[originalKey];
1029
1096
  filterCounter++;
1030
1097
  continue;
1031
1098
  }
1032
1099
 
1033
1100
  if (
1034
- this.isFilteredFolder(results[metadataEntry], true) ||
1035
- this.isFilteredFolder(results[metadataEntry], false)
1101
+ this.isFilteredFolder(results[originalKey], true) ||
1102
+ this.isFilteredFolder(results[originalKey], false)
1036
1103
  ) {
1037
1104
  // if current metadata entry is filtered don't save it
1038
1105
  filterCounter++;
@@ -1040,20 +1107,20 @@ class MetadataType {
1040
1107
  }
1041
1108
 
1042
1109
  // for complex types like asset, script, query we need to save the scripts that were extracted from the JSON
1043
- if (results[metadataEntry].json && results[metadataEntry].codeArr) {
1110
+ if (results[originalKey].json && results[originalKey].codeArr) {
1044
1111
  // replace market values with template variable placeholders (do not do it on .codeArr)
1045
1112
  if (templateVariables) {
1046
- results[metadataEntry].json = Util.replaceByObject(
1047
- results[metadataEntry].json,
1113
+ results[originalKey].json = Util.replaceByObject(
1114
+ results[originalKey].json,
1048
1115
  templateVariables
1049
1116
  );
1050
- results[metadataEntry].subFolder = Util.replaceByObject(
1051
- results[metadataEntry].subFolder,
1117
+ results[originalKey].subFolder = Util.replaceByObject(
1118
+ results[originalKey].subFolder,
1052
1119
  templateVariables
1053
1120
  );
1054
1121
  }
1055
1122
 
1056
- const postRetrieveData = results[metadataEntry];
1123
+ const postRetrieveData = results[originalKey];
1057
1124
  if (postRetrieveData.subFolder) {
1058
1125
  // very complex types have their own subfolder
1059
1126
  baseDir.push(...postRetrieveData.subFolder);
@@ -1074,13 +1141,13 @@ class MetadataType {
1074
1141
  );
1075
1142
  }
1076
1143
  // normalize results[metadataEntry]
1077
- results[metadataEntry] = postRetrieveData.json;
1144
+ results[originalKey] = postRetrieveData.json;
1078
1145
  } else {
1079
1146
  // not a complex type, run the the entire JSON through templating
1080
1147
  // replace market values with template variable placeholders
1081
1148
  if (templateVariables) {
1082
- results[metadataEntry] = Util.replaceByObject(
1083
- results[metadataEntry],
1149
+ results[originalKey] = Util.replaceByObject(
1150
+ results[originalKey],
1084
1151
  templateVariables
1085
1152
  );
1086
1153
  }
@@ -1089,7 +1156,7 @@ class MetadataType {
1089
1156
  // we dont store Id on local disk, but we need it for caching logic,
1090
1157
  // so its in retrieve but not in save. Here we put into the clone so that the original
1091
1158
  // object used for caching doesnt have the Id removed.
1092
- const saveClone = JSON.parse(JSON.stringify(results[metadataEntry]));
1159
+ const saveClone = JSON.parse(JSON.stringify(results[originalKey]));
1093
1160
  if (!this.definition.keepId) {
1094
1161
  delete saveClone[this.definition.idField];
1095
1162
  }
@@ -1099,22 +1166,16 @@ class MetadataType {
1099
1166
  } else {
1100
1167
  this.keepRetrieveFields(saveClone);
1101
1168
  }
1102
- savedResults[metadataEntry] = saveClone;
1169
+ savedResults[originalKey] = saveClone;
1103
1170
  File.writeJSONToFile(
1104
1171
  // manage subtypes
1105
1172
  baseDir,
1106
- saveClone[this.definition.keyField] + subtypeExtension,
1173
+ originalKey + subtypeExtension,
1107
1174
  saveClone
1108
1175
  );
1109
1176
  } catch (ex) {
1110
1177
  console.log(ex.stack);
1111
- Util.metadataLogger(
1112
- 'error',
1113
- this.definition.type,
1114
- 'saveResults',
1115
- ex,
1116
- metadataEntry
1117
- );
1178
+ Util.metadataLogger('error', this.definition.type, 'saveResults', ex, originalKey);
1118
1179
  }
1119
1180
  }
1120
1181
  if (filterCounter) {
@@ -144,9 +144,8 @@ class Query extends MetadataType {
144
144
  } catch (ex) {
145
145
  throw new Error(`Query '${metadata.key}': ${ex.message}`);
146
146
  }
147
- metadata.targetUpdateTypeId = this.definition.targetUpdateTypeMapping[
148
- metadata.targetUpdateTypeName
149
- ];
147
+ metadata.targetUpdateTypeId =
148
+ this.definition.targetUpdateTypeMapping[metadata.targetUpdateTypeName];
150
149
  return metadata;
151
150
  }
152
151
 
@@ -107,14 +107,19 @@ class Role extends MetadataType {
107
107
  return;
108
108
  }
109
109
  if (!metadata) {
110
- metadata = this.readBUMetadataForType(
111
- File.normalizePath([
112
- this.properties.directories.retrieve,
113
- buObject.credential,
114
- Util.parentBuName,
115
- ]),
116
- true
117
- ).role;
110
+ try {
111
+ metadata = this.readBUMetadataForType(
112
+ File.normalizePath([
113
+ this.properties.directories.retrieve,
114
+ buObject.credential,
115
+ Util.parentBuName,
116
+ ]),
117
+ true
118
+ ).role;
119
+ } catch (ex) {
120
+ Util.logger.error(ex.message);
121
+ return;
122
+ }
118
123
  }
119
124
  const directory = this.properties.directories.roles;
120
125
 
@@ -178,12 +178,12 @@ class Script extends MetadataType {
178
178
  templateName
179
179
  ) {
180
180
  // get SSJS from filesystem
181
- let code = this._mergeCode(metadata, templateDir, templateName);
182
-
181
+ let code = await this._mergeCode(metadata, templateDir, templateName);
183
182
  // replace template variables with their values
184
183
  try {
185
184
  code = Mustache.render(code, variables);
186
185
  } catch (ex) {
186
+ Util.logger.debug('script.buildDefinitionForExtracts: ' + ex.message);
187
187
  throw new Error(
188
188
  `${this.definition.type}:: Error applying template variables on ${
189
189
  metadata[this.definition.keyField] + '.' + this.definition.type
@@ -0,0 +1,227 @@
1
+ module.exports = {
2
+ bodyIteratorField: 'Results',
3
+ dependencies: [],
4
+ folderType: null,
5
+ hasExtended: false,
6
+ idField: 'ID',
7
+ keyField: 'CustomerKey',
8
+ nameField: 'Name',
9
+ type: 'accountUser',
10
+ typeDescription: 'Marketing Cloud users',
11
+ typeName: 'User',
12
+ typeRetrieveByDefault: false,
13
+ fields: {
14
+ AccountUserID: {
15
+ isCreateable: null,
16
+ isUpdateable: null,
17
+ retrieving: true,
18
+ template: false,
19
+ },
20
+ ActiveFlag: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
21
+ AssociatedBusinessUnits: {
22
+ isCreateable: null,
23
+ isUpdateable: null,
24
+ retrieving: false,
25
+ template: null,
26
+ },
27
+ BusinessUnit: {
28
+ isCreateable: null,
29
+ isUpdateable: null,
30
+ retrieving: false,
31
+ template: null,
32
+ },
33
+ ChallengeAnswer: {
34
+ isCreateable: null,
35
+ isUpdateable: null,
36
+ retrieving: true,
37
+ template: false,
38
+ },
39
+ ChallengePhrase: {
40
+ isCreateable: null,
41
+ isUpdateable: null,
42
+ retrieving: true,
43
+ template: false,
44
+ },
45
+ Client: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
46
+ CorrelationID: {
47
+ isCreateable: null,
48
+ isUpdateable: null,
49
+ retrieving: false,
50
+ template: null,
51
+ },
52
+ CreatedDate: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
53
+ CustomerKey: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
54
+ DefaultApplication: {
55
+ isCreateable: null,
56
+ isUpdateable: null,
57
+ retrieving: false,
58
+ template: null,
59
+ },
60
+ DefaultBusinessUnit: {
61
+ isCreateable: null,
62
+ isUpdateable: null,
63
+ retrieving: true,
64
+ template: false,
65
+ },
66
+ DefaultBusinessUnitObject: {
67
+ isCreateable: null,
68
+ isUpdateable: null,
69
+ retrieving: false,
70
+ template: null,
71
+ },
72
+ Delete: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
73
+ Email: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
74
+ ID: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
75
+ IsAPIUser: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
76
+ IsLocked: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
77
+ LanguageLocale: {
78
+ isCreateable: null,
79
+ isUpdateable: null,
80
+ retrieving: false,
81
+ template: null,
82
+ },
83
+ LastSuccessfulLogin: {
84
+ isCreateable: null,
85
+ isUpdateable: null,
86
+ retrieving: true,
87
+ template: false,
88
+ },
89
+ Locale: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
90
+ ModifiedDate: {
91
+ isCreateable: null,
92
+ isUpdateable: null,
93
+ retrieving: true,
94
+ template: false,
95
+ },
96
+ MustChangePassword: {
97
+ isCreateable: null,
98
+ isUpdateable: null,
99
+ retrieving: true,
100
+ template: false,
101
+ },
102
+ Name: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
103
+ NotificationEmailAddress: {
104
+ isCreateable: null,
105
+ isUpdateable: null,
106
+ retrieving: true,
107
+ template: false,
108
+ },
109
+ ObjectID: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
110
+ ObjectState: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
111
+ Owner: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
112
+ PartnerKey: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
113
+ PartnerProperties: {
114
+ isCreateable: null,
115
+ isUpdateable: null,
116
+ retrieving: false,
117
+ template: null,
118
+ },
119
+ Password: { isCreateable: null, isUpdateable: null, retrieving: false, template: false },
120
+ Roles: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
121
+ 'Roles.Role': {
122
+ skipValidation: false,
123
+ },
124
+ 'Roles.Role[].Client': {
125
+ skipValidation: false,
126
+ },
127
+ SsoIdentities: {
128
+ isCreateable: null,
129
+ isUpdateable: null,
130
+ retrieving: false,
131
+ template: null,
132
+ },
133
+ TimeZone: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
134
+ Unlock: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
135
+ UserID: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
136
+ UserPermissions: {
137
+ isCreateable: null,
138
+ isUpdateable: null,
139
+ retrieving: true,
140
+ template: false,
141
+ },
142
+ 'UserPermissions.PartnerKey': {
143
+ isCreateable: null,
144
+ isUpdateable: null,
145
+ retrieving: false,
146
+ template: false,
147
+ },
148
+ 'UserPermissions.ID': {
149
+ skipValidation: true,
150
+ },
151
+ 'UserPermissions.ObjectID': {
152
+ isCreateable: null,
153
+ isUpdateable: null,
154
+ retrieving: false,
155
+ template: false,
156
+ },
157
+ 'UserPermissions.Name': {
158
+ isCreateable: null,
159
+ isUpdateable: null,
160
+ retrieving: false,
161
+ template: false,
162
+ },
163
+ 'UserPermissions.Value': {
164
+ isCreateable: null,
165
+ isUpdateable: null,
166
+ retrieving: false,
167
+ template: false,
168
+ },
169
+ 'UserPermissions.Description': {
170
+ isCreateable: null,
171
+ isUpdateable: null,
172
+ retrieving: false,
173
+ template: false,
174
+ },
175
+ 'UserPermissions.Delete': {
176
+ isCreateable: null,
177
+ isUpdateable: null,
178
+ retrieving: false,
179
+ template: false,
180
+ },
181
+ 'UserPermissions[].PartnerKey': {
182
+ isCreateable: null,
183
+ isUpdateable: null,
184
+ retrieving: false,
185
+ template: false,
186
+ },
187
+ 'UserPermissions[].ID': {
188
+ skipValidation: true,
189
+ },
190
+ 'UserPermissions[].ObjectID': {
191
+ isCreateable: null,
192
+ isUpdateable: null,
193
+ retrieving: false,
194
+ template: false,
195
+ },
196
+ 'UserPermissions[].Name': {
197
+ isCreateable: null,
198
+ isUpdateable: null,
199
+ retrieving: false,
200
+ template: false,
201
+ },
202
+ 'UserPermissions[].Value': {
203
+ isCreateable: null,
204
+ isUpdateable: null,
205
+ retrieving: false,
206
+ template: false,
207
+ },
208
+ 'UserPermissions[].Description': {
209
+ isCreateable: null,
210
+ isUpdateable: null,
211
+ retrieving: false,
212
+ template: false,
213
+ },
214
+ 'UserPermissions[].Delete': {
215
+ isCreateable: null,
216
+ isUpdateable: null,
217
+ retrieving: false,
218
+ template: false,
219
+ },
220
+ type__c: {
221
+ skipValidation: true,
222
+ },
223
+ AssociatedBusinessUnits__c: {
224
+ skipValidation: true,
225
+ },
226
+ },
227
+ };
@@ -677,6 +677,7 @@ module.exports = {
677
677
  ],
678
678
  },
679
679
  typeMapping: {
680
+ ai: 16,
680
681
  psd: 17,
681
682
  pdd: 18,
682
683
  eps: 19,
@@ -167,7 +167,7 @@ module.exports = {
167
167
  ModifiedDate: {
168
168
  isCreateable: false,
169
169
  isUpdateable: false,
170
- retrieving: false,
170
+ retrieving: true,
171
171
  template: false,
172
172
  },
173
173
  Name: {