mcdev 3.0.3 → 3.1.3

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 (41) hide show
  1. package/.eslintrc.json +1 -1
  2. package/.github/ISSUE_TEMPLATE/bug.yml +75 -0
  3. package/.github/ISSUE_TEMPLATE/task.md +1 -1
  4. package/.issuetracker +11 -3
  5. package/.vscode/settings.json +3 -3
  6. package/CHANGELOG.md +66 -0
  7. package/README.md +245 -141
  8. package/boilerplate/config.json +3 -2
  9. package/docs/dist/documentation.md +799 -338
  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 +298 -0
  15. package/lib/index.js +773 -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 +133 -97
  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/ImportFile.js +13 -12
  25. package/lib/metadataTypes/MetadataType.js +41 -33
  26. package/lib/metadataTypes/Query.js +2 -3
  27. package/lib/metadataTypes/Role.js +13 -8
  28. package/lib/metadataTypes/Script.js +2 -2
  29. package/lib/metadataTypes/definitions/AccountUser.definition.js +227 -0
  30. package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
  31. package/lib/metadataTypes/definitions/DataExtension.definition.js +1 -1
  32. package/lib/metadataTypes/definitions/ImportFile.definition.js +2 -1
  33. package/lib/metadataTypes/definitions/Script.definition.js +5 -5
  34. package/lib/retrieveChangelog.js +96 -0
  35. package/lib/util/cli.js +4 -6
  36. package/lib/util/init.git.js +2 -1
  37. package/lib/util/util.js +17 -0
  38. package/package.json +18 -22
  39. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
  40. package/img/README.md/troubleshoot-nodejs-postinstall.jpg +0 -0
  41. package/postinstall.js +0 -41
@@ -1,17 +1,21 @@
1
1
  'use strict';
2
2
 
3
+ const Util = require('../util/util');
4
+
3
5
  /**
4
6
  * @typedef {Object} DataExtensionFieldItem
5
7
  * @property {string} [ObjectID] id
6
- * @property {string} [CustomerKey] key
8
+ * @property {string} [CustomerKey] key in format [DEkey].[FieldName]
7
9
  * @property {Object} [DataExtension] -
8
10
  * @property {string} DataExtension.CustomerKey key of DE
9
- * @property {string} Name name
10
- * @property {string} DefaultValue -
11
+ * @property {string} Name name of field
12
+ * @property {string} [Name_new] custom attribute that is only used when trying to rename a field from Name to Name_new
13
+ * @property {string} DefaultValue empty string for not set
11
14
  * @property {'true'|'false'} IsRequired -
12
15
  * @property {'true'|'false'} IsPrimaryKey -
13
16
  * @property {string} Ordinal 1, 2, 3, ...
14
- * @property {'Text'|'Date'|'Number'|'Decimal'|'Email'} FieldType -
17
+ * @property {'Text'|'Number'|'Date'|'Boolean'|'Decimal'|'EmailAddress'|'Phone'|'Locale'} FieldType can only be set on create
18
+ * @property {string} Scale the number of places after the decimal that the field can hold; example: "0","1", ...
15
19
  *
16
20
  * @typedef {Object.<string, DataExtensionFieldItem>} DataExtensionFieldMap
17
21
  */
@@ -91,9 +95,135 @@ class DataExtensionField extends MetadataType {
91
95
  delete metadata.CustomerKey;
92
96
  delete metadata.DataExtension;
93
97
  delete metadata.Ordinal;
98
+ if (metadata.FieldType !== 'Decimal') {
99
+ // remove scale - it's only used for "Decimal" to define the digits behind the decimal
100
+ delete metadata.Scale;
101
+ }
94
102
  }
95
103
  return metadata;
96
104
  }
105
+ /**
106
+ * Mofifies passed deployColumns for update by mapping ObjectID to their target column's values.
107
+ * Removes FieldType field if its the same in deploy and target column, because it results in an error even if its of the same type
108
+ *
109
+ * @param {DataExtensionFieldItem[]} deployColumns Columns of data extension that will be deployed
110
+ * @param {string} deKey external/customer key of Data Extension
111
+ * @returns {Object<string,DataExtensionFieldItem>} existing fields by their original name to allow re-adding FieldType after update
112
+ */
113
+ static async prepareDeployColumnsOnUpdate(deployColumns, deKey) {
114
+ // retrieve existing fields to enable updating them
115
+ const response = await this.retrieveForCache(
116
+ {
117
+ filter: {
118
+ leftOperand: 'DataExtension.CustomerKey',
119
+ operator: 'equals',
120
+ rightOperand: deKey,
121
+ },
122
+ },
123
+ ['Name', 'ObjectID']
124
+ );
125
+
126
+ const fieldsObj = response.metadata;
127
+
128
+ // ensure fields can be updated properly by their adding ObjectId based on Name-matching
129
+ /** @type {Object<string,DataExtensionFieldItem>} */
130
+ const existingFieldByName = {};
131
+
132
+ Object.keys(fieldsObj).forEach((key) => {
133
+ existingFieldByName[fieldsObj[key].Name] = fieldsObj[key];
134
+ });
135
+ for (let i = deployColumns.length - 1; i >= 0; i--) {
136
+ const item = deployColumns[i];
137
+ const itemOld = existingFieldByName[item.Name];
138
+ if (itemOld) {
139
+ // field is getting updated ---
140
+
141
+ // Updating to a new FieldType will result in an error; warn & afterwards remove it
142
+ if (itemOld.FieldType !== item.FieldType) {
143
+ Util.logger.warn(
144
+ `- The Field Type of an existing field cannot be changed. Keeping the original value for [${deKey}].[${item.Name}]: '${itemOld.FieldType}'`
145
+ );
146
+ item.FieldType = itemOld.FieldType;
147
+ }
148
+ if (item.FieldType !== 'Decimal') {
149
+ // remove scale - it's only used for "Decimal" to define the digits behind the decimal
150
+ delete item.Scale;
151
+ }
152
+ delete item.FieldType;
153
+
154
+ if (itemOld.MaxLength > item.MaxLength) {
155
+ Util.logger.warn(
156
+ `- The length of an existing field cannot be decreased. Keeping the original value for [${deKey}].[${item.Name}]: '${itemOld.MaxLength}'`
157
+ );
158
+ item.MaxLength = itemOld.MaxLength;
159
+ }
160
+ if (Util.isFalse(itemOld.IsRequired) && Util.isTrue(item.IsRequired)) {
161
+ Util.logger.warn(
162
+ `- A field cannot be changed to be required on update after it was created to allow nulls: [${deKey}].[${item.Name}]`
163
+ );
164
+ item.IsRequired = itemOld.IsRequired;
165
+ }
166
+
167
+ // enable renaming
168
+ if (item.Name_new) {
169
+ item.Name = item.Name_new;
170
+ delete item.Name_new;
171
+ Util.logger.warn(
172
+ `Found 'Name_new' value '${item.Name_new}' for ${deKey}.${item.Name} - trying to rename.`
173
+ );
174
+ }
175
+
176
+ // check if any changes were found
177
+ let changeFound = false;
178
+ Object.keys(item).forEach((key) => {
179
+ if (item[key] !== itemOld[key]) {
180
+ changeFound = true;
181
+ }
182
+ });
183
+ if (!changeFound) {
184
+ deployColumns.splice(i, 1);
185
+ Util.logger.verbose(`no change - removed field [${deKey}].[${item.Name}]`);
186
+ continue;
187
+ }
188
+
189
+ // set the ObjectId for clear identification during update
190
+ item.ObjectID = itemOld.ObjectID;
191
+ } else {
192
+ // field is getting added ---
193
+ if (Util.isTrue(item.IsRequired) && item.DefaultValue === '') {
194
+ Util.logger.warn(
195
+ `- Adding new fields to an existing table requires that these fields are either not-required (nullable) or have a default value set. Changing [${deKey}].[${item.Name}] to be not-required`
196
+ );
197
+ item.IsRequired = 'false';
198
+ }
199
+ if (item.Name_new) {
200
+ Util.logger.warn(
201
+ `Found 'Name_new' value '${item.Name_new}' for ${deKey}.${item.Name} but could not find a corresponding DE field on the server - adding new field instead of updating.`
202
+ );
203
+ delete item.Name_new;
204
+ }
205
+ // Field doesn't exist in target, therefore Remove ObjectID if present
206
+ delete item.ObjectID;
207
+ }
208
+ if (!Util.isTrue(item.IsRequired) && !Util.isFalse(item.IsRequired)) {
209
+ Util.logger.error(
210
+ `- Invalid value for 'IsRequired' of [${deKey}].[${item.Name}]. Found '${item.IsRequired}' instead of 'true'/'false'. Removing field from deploy!`
211
+ );
212
+ deployColumns.splice(i, 1);
213
+ }
214
+ if (!Util.isTrue(item.IsPrimaryKey) && !Util.isFalse(item.IsPrimaryKey)) {
215
+ Util.logger.error(
216
+ `- Invalid value for 'IsPrimaryKey' of [${deKey}].[${item.Name}]. Found '${item.IsPrimaryKey}' instead of 'true'/'false'. Removing field from deploy!`
217
+ );
218
+ deployColumns.splice(i, 1);
219
+ }
220
+ }
221
+ Util.logger.debug(
222
+ `${deployColumns.length} Fields added/updated for [${deKey}]: ` +
223
+ deployColumns.map((item) => item.Name).join(', ')
224
+ );
225
+ return deployColumns.length ? existingFieldByName : null;
226
+ }
97
227
  }
98
228
 
99
229
  // Assign definition to static attributes
@@ -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})`
@@ -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
  }
@@ -204,6 +204,16 @@ class MetadataType {
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],
@@ -474,8 +485,12 @@ class MetadataType {
474
485
  async () => (response = await this.client.RestClient.patch(options))
475
486
  );
476
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
477
489
  Util.logger.info(
478
- `- updated ${this.definition.type}: ${metadataEntry[this.definition.keyField]}`
490
+ `- updated ${this.definition.type}: ${
491
+ metadataEntry[this.definition.keyField] ||
492
+ metadataEntry[this.definition.nameField]
493
+ }`
479
494
  );
480
495
  return response;
481
496
  } catch (ex) {
@@ -565,7 +580,6 @@ class MetadataType {
565
580
  Util.logger.info(
566
581
  `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})`
567
582
  );
568
-
569
583
  if (
570
584
  buObject &&
571
585
  this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
@@ -1058,11 +1072,11 @@ class MetadataType {
1058
1072
  const savedResults = {};
1059
1073
  const subtypeExtension = '.' + (overrideType || this.definition.type) + '-meta';
1060
1074
  let filterCounter = 0;
1061
- for (const metadataEntry in results) {
1075
+ for (const originalKey in results) {
1062
1076
  try {
1063
1077
  if (
1064
- this.isFiltered(results[metadataEntry], true) ||
1065
- this.isFiltered(results[metadataEntry], false)
1078
+ this.isFiltered(results[originalKey], true) ||
1079
+ this.isFiltered(results[originalKey], false)
1066
1080
  ) {
1067
1081
  // if current metadata entry is filtered don't save it
1068
1082
  filterCounter++;
@@ -1071,21 +1085,21 @@ class MetadataType {
1071
1085
  // define directory into which the current metdata shall be saved
1072
1086
  const baseDir = [retrieveDir, ...(overrideType || this.definition.type).split('-')];
1073
1087
 
1074
- results[metadataEntry] = await this.postRetrieveTasks(
1075
- results[metadataEntry],
1088
+ results[originalKey] = await this.postRetrieveTasks(
1089
+ results[originalKey],
1076
1090
  retrieveDir,
1077
1091
  templateVariables ? true : false
1078
1092
  );
1079
- if (!results[metadataEntry] || results[metadataEntry] === null) {
1093
+ if (!results[originalKey] || results[originalKey] === null) {
1080
1094
  // we encountered a situation in our postRetrieveTasks that made us want to filter this record
1081
- delete results[metadataEntry];
1095
+ delete results[originalKey];
1082
1096
  filterCounter++;
1083
1097
  continue;
1084
1098
  }
1085
1099
 
1086
1100
  if (
1087
- this.isFilteredFolder(results[metadataEntry], true) ||
1088
- this.isFilteredFolder(results[metadataEntry], false)
1101
+ this.isFilteredFolder(results[originalKey], true) ||
1102
+ this.isFilteredFolder(results[originalKey], false)
1089
1103
  ) {
1090
1104
  // if current metadata entry is filtered don't save it
1091
1105
  filterCounter++;
@@ -1093,20 +1107,20 @@ class MetadataType {
1093
1107
  }
1094
1108
 
1095
1109
  // for complex types like asset, script, query we need to save the scripts that were extracted from the JSON
1096
- if (results[metadataEntry].json && results[metadataEntry].codeArr) {
1110
+ if (results[originalKey].json && results[originalKey].codeArr) {
1097
1111
  // replace market values with template variable placeholders (do not do it on .codeArr)
1098
1112
  if (templateVariables) {
1099
- results[metadataEntry].json = Util.replaceByObject(
1100
- results[metadataEntry].json,
1113
+ results[originalKey].json = Util.replaceByObject(
1114
+ results[originalKey].json,
1101
1115
  templateVariables
1102
1116
  );
1103
- results[metadataEntry].subFolder = Util.replaceByObject(
1104
- results[metadataEntry].subFolder,
1117
+ results[originalKey].subFolder = Util.replaceByObject(
1118
+ results[originalKey].subFolder,
1105
1119
  templateVariables
1106
1120
  );
1107
1121
  }
1108
1122
 
1109
- const postRetrieveData = results[metadataEntry];
1123
+ const postRetrieveData = results[originalKey];
1110
1124
  if (postRetrieveData.subFolder) {
1111
1125
  // very complex types have their own subfolder
1112
1126
  baseDir.push(...postRetrieveData.subFolder);
@@ -1127,13 +1141,13 @@ class MetadataType {
1127
1141
  );
1128
1142
  }
1129
1143
  // normalize results[metadataEntry]
1130
- results[metadataEntry] = postRetrieveData.json;
1144
+ results[originalKey] = postRetrieveData.json;
1131
1145
  } else {
1132
1146
  // not a complex type, run the the entire JSON through templating
1133
1147
  // replace market values with template variable placeholders
1134
1148
  if (templateVariables) {
1135
- results[metadataEntry] = Util.replaceByObject(
1136
- results[metadataEntry],
1149
+ results[originalKey] = Util.replaceByObject(
1150
+ results[originalKey],
1137
1151
  templateVariables
1138
1152
  );
1139
1153
  }
@@ -1142,7 +1156,7 @@ class MetadataType {
1142
1156
  // we dont store Id on local disk, but we need it for caching logic,
1143
1157
  // so its in retrieve but not in save. Here we put into the clone so that the original
1144
1158
  // object used for caching doesnt have the Id removed.
1145
- const saveClone = JSON.parse(JSON.stringify(results[metadataEntry]));
1159
+ const saveClone = JSON.parse(JSON.stringify(results[originalKey]));
1146
1160
  if (!this.definition.keepId) {
1147
1161
  delete saveClone[this.definition.idField];
1148
1162
  }
@@ -1152,22 +1166,16 @@ class MetadataType {
1152
1166
  } else {
1153
1167
  this.keepRetrieveFields(saveClone);
1154
1168
  }
1155
- savedResults[metadataEntry] = saveClone;
1169
+ savedResults[originalKey] = saveClone;
1156
1170
  File.writeJSONToFile(
1157
1171
  // manage subtypes
1158
1172
  baseDir,
1159
- saveClone[this.definition.keyField] + subtypeExtension,
1173
+ originalKey + subtypeExtension,
1160
1174
  saveClone
1161
1175
  );
1162
1176
  } catch (ex) {
1163
1177
  console.log(ex.stack);
1164
- Util.metadataLogger(
1165
- 'error',
1166
- this.definition.type,
1167
- 'saveResults',
1168
- ex,
1169
- metadataEntry
1170
- );
1178
+ Util.metadataLogger('error', this.definition.type, 'saveResults', ex, originalKey);
1171
1179
  }
1172
1180
  }
1173
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