mcdev 3.0.0 → 3.1.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 +1 -1
- package/.github/ISSUE_TEMPLATE/bug.yml +72 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/ISSUE_TEMPLATE/task.md +10 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +11 -0
- package/.github/workflows/npm-publish.yml +33 -0
- package/.issuetracker +11 -3
- package/.vscode/extensions.json +1 -2
- package/.vscode/settings.json +19 -4
- package/CHANGELOG.md +98 -0
- package/README.md +247 -142
- package/boilerplate/config.json +3 -2
- package/docs/dist/considerations.md +66 -0
- package/docs/dist/documentation.md +5794 -0
- package/lib/Deployer.js +4 -1
- package/lib/MetadataTypeDefinitions.js +1 -0
- package/lib/MetadataTypeInfo.js +1 -0
- package/lib/Retriever.js +32 -17
- package/lib/cli.js +295 -0
- package/lib/index.js +774 -1019
- package/lib/metadataTypes/AccountUser.js +389 -0
- package/lib/metadataTypes/Asset.js +140 -116
- package/lib/metadataTypes/Automation.js +119 -54
- package/lib/metadataTypes/DataExtension.js +172 -131
- package/lib/metadataTypes/DataExtensionField.js +134 -4
- package/lib/metadataTypes/Folder.js +66 -69
- package/lib/metadataTypes/ImportFile.js +4 -6
- package/lib/metadataTypes/MetadataType.js +168 -80
- package/lib/metadataTypes/Query.js +54 -25
- package/lib/metadataTypes/Role.js +13 -8
- package/lib/metadataTypes/Script.js +43 -24
- package/lib/metadataTypes/definitions/AccountUser.definition.js +227 -0
- package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
- package/lib/metadataTypes/definitions/Campaign.definition.js +1 -1
- package/lib/metadataTypes/definitions/DataExtension.definition.js +1 -1
- package/lib/metadataTypes/definitions/DataExtensionField.definition.js +1 -1
- package/lib/metadataTypes/definitions/Folder.definition.js +1 -1
- package/lib/metadataTypes/definitions/ImportFile.definition.js +2 -1
- package/lib/metadataTypes/definitions/Script.definition.js +5 -5
- package/lib/retrieveChangelog.js +96 -0
- package/lib/util/cli.js +4 -6
- package/lib/util/init.config.js +3 -0
- package/lib/util/init.git.js +2 -1
- package/lib/util/util.js +35 -18
- package/package.json +20 -24
- package/test/util/file.js +51 -0
- package/img/README.md/troubleshoot-nodejs-postinstall.jpg +0 -0
- package/postinstall.js +0 -41
- package/test/deployer.js +0 -16
- package/test/util.js +0 -26
|
@@ -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[]} [
|
|
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,
|
|
21
|
-
const queryAllFolders = await this.retrieveHelper(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
if (
|
|
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::
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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[]} [
|
|
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(
|
|
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
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
this.
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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);
|
|
@@ -147,12 +147,10 @@ class ImportFile extends MetadataType {
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
// When the destinationObjectTypeId is 584 is refers to Mobile Connect which is not supported as an Import Type
|
|
150
|
-
metadata.destinationObjectTypeId =
|
|
151
|
-
metadata.c__destinationType
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
metadata.c__subscriberImportType
|
|
155
|
-
];
|
|
150
|
+
metadata.destinationObjectTypeId =
|
|
151
|
+
this.definition.destinationObjectTypeMapping[metadata.c__destinationType];
|
|
152
|
+
metadata.subscriberImportTypeId =
|
|
153
|
+
this.definition.subscriberImportTypeMapping[metadata.c__subscriberImportType];
|
|
156
154
|
metadata.updateTypeId = this.definition.updateTypeMapping[metadata.c__dataAction];
|
|
157
155
|
return metadata;
|
|
158
156
|
}
|
|
@@ -9,6 +9,19 @@
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @typedef {Object.<string, any>} MetadataTypeItem
|
|
12
|
+
*
|
|
13
|
+
* @typedef {Object} CodeExtractItem
|
|
14
|
+
* @property {MetadataTypeItem} json metadata of one item w/o code
|
|
15
|
+
* @property {CodeExtract[]} codeArr list of code snippets in this item
|
|
16
|
+
* @property {string[]} subFolder mostly set to null, otherwise list of subfolders
|
|
17
|
+
*
|
|
18
|
+
* @typedef {Object} CodeExtract
|
|
19
|
+
* @property {string[]} subFolder mostly set to null, otherwise subfolders path split into elements
|
|
20
|
+
* @property {string} fileName name of file w/o extension
|
|
21
|
+
* @property {string} fileExt file extension
|
|
22
|
+
* @property {string} content file content
|
|
23
|
+
* @property {'base64'} [encoding] optional for binary files
|
|
24
|
+
|
|
12
25
|
*
|
|
13
26
|
* @typedef {Object.<string, MetadataTypeItem>} MetadataTypeMap
|
|
14
27
|
*
|
|
@@ -181,16 +194,26 @@ class MetadataType {
|
|
|
181
194
|
/**
|
|
182
195
|
* Gets metadata from Marketing Cloud
|
|
183
196
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
184
|
-
* @param {string[]} [
|
|
197
|
+
* @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
|
|
185
198
|
* @param {Util.BuObject} buObject properties for auth
|
|
186
199
|
* @param {string} [subType] optionally limit to a single subtype
|
|
187
200
|
* @returns {Promise<{metadata:MetadataTypeMap,type:string}>} metadata
|
|
188
201
|
*/
|
|
189
|
-
static retrieve(retrieveDir,
|
|
202
|
+
static retrieve(retrieveDir, additionalFields, buObject, subType) {
|
|
190
203
|
Util.metadataLogger('error', this.definition.type, 'retrieve', `Not Supported`);
|
|
191
204
|
const metadata = {};
|
|
192
205
|
return { metadata: null, type: this.definition.type };
|
|
193
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
|
+
}
|
|
194
217
|
|
|
195
218
|
/**
|
|
196
219
|
* Gets metadata cache with limited fields and does not store value to disk
|
|
@@ -210,8 +233,9 @@ class MetadataType {
|
|
|
210
233
|
* @returns {Promise<{metadata:MetadataTypeMap,type:string}>} metadata
|
|
211
234
|
*/
|
|
212
235
|
static retrieveAsTemplate(templateDir, name, templateVariables, subType) {
|
|
236
|
+
Util.logger.error('retrieveAsTemplate is not supported yet for ' + this.definition.type);
|
|
213
237
|
Util.metadataLogger(
|
|
214
|
-
'
|
|
238
|
+
'debug',
|
|
215
239
|
this.definition.type,
|
|
216
240
|
'retrieveAsTemplate',
|
|
217
241
|
`(${templateDir}, ${name}, ${templateVariables}) no templating function`
|
|
@@ -290,9 +314,10 @@ class MetadataType {
|
|
|
290
314
|
});
|
|
291
315
|
} else if (this.cache[this.definition.type][normalizedKey]) {
|
|
292
316
|
// normal way of processing update files
|
|
293
|
-
metadata[metadataKey][this.definition.idField] =
|
|
294
|
-
this.definition.type
|
|
295
|
-
|
|
317
|
+
metadata[metadataKey][this.definition.idField] =
|
|
318
|
+
this.cache[this.definition.type][normalizedKey][
|
|
319
|
+
this.definition.idField
|
|
320
|
+
];
|
|
296
321
|
metadataToUpdate.push({
|
|
297
322
|
before: this.cache[this.definition.type][normalizedKey],
|
|
298
323
|
after: metadata[metadataKey],
|
|
@@ -389,31 +414,54 @@ class MetadataType {
|
|
|
389
414
|
/**
|
|
390
415
|
* Creates a single metadata entry via fuel-soap (generic lib not wrapper)
|
|
391
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
|
|
392
419
|
* @returns {Promise} Promise
|
|
393
420
|
*/
|
|
394
|
-
static async createSOAP(metadataEntry) {
|
|
421
|
+
static async createSOAP(metadataEntry, overrideType, handleOutside) {
|
|
395
422
|
try {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
metadataEntry
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
+
}))
|
|
407
440
|
);
|
|
408
|
-
|
|
441
|
+
if (!handleOutside) {
|
|
442
|
+
Util.logger.info(
|
|
443
|
+
`- created ${this.definition.type}: ${metadataEntry[this.definition.keyField]}`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
return response;
|
|
409
447
|
} catch (ex) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
+
}
|
|
415
463
|
|
|
416
|
-
return
|
|
464
|
+
return {};
|
|
417
465
|
}
|
|
418
466
|
}
|
|
419
467
|
|
|
@@ -437,8 +485,12 @@ class MetadataType {
|
|
|
437
485
|
async () => (response = await this.client.RestClient.patch(options))
|
|
438
486
|
);
|
|
439
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
|
|
440
489
|
Util.logger.info(
|
|
441
|
-
`- updated ${this.definition.type}: ${
|
|
490
|
+
`- updated ${this.definition.type}: ${
|
|
491
|
+
metadataEntry[this.definition.keyField] ||
|
|
492
|
+
metadataEntry[this.definition.nameField]
|
|
493
|
+
}`
|
|
442
494
|
);
|
|
443
495
|
return response;
|
|
444
496
|
} catch (ex) {
|
|
@@ -454,31 +506,54 @@ class MetadataType {
|
|
|
454
506
|
/**
|
|
455
507
|
* Updates a single metadata entry via fuel-soap (generic lib not wrapper)
|
|
456
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
|
|
457
511
|
* @returns {Promise} Promise
|
|
458
512
|
*/
|
|
459
|
-
static async updateSOAP(metadataEntry) {
|
|
513
|
+
static async updateSOAP(metadataEntry, overrideType, handleOutside) {
|
|
514
|
+
let response;
|
|
460
515
|
try {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
this.
|
|
464
|
-
this.definition.
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
(
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
+
}))
|
|
472
532
|
);
|
|
473
|
-
|
|
533
|
+
if (!handleOutside) {
|
|
534
|
+
Util.logger.info(
|
|
535
|
+
`- updated ${this.definition.type}: ${metadataEntry[this.definition.keyField]}`
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
return response;
|
|
474
539
|
} catch (ex) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
+
}
|
|
480
555
|
|
|
481
|
-
return
|
|
556
|
+
return {};
|
|
482
557
|
}
|
|
483
558
|
}
|
|
484
559
|
/**
|
|
@@ -487,18 +562,24 @@ class MetadataType {
|
|
|
487
562
|
* @param {Util.BuObject} buObject properties for auth
|
|
488
563
|
* @param {Object} [options] required for the specific request (filter for example)
|
|
489
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
|
|
490
566
|
* @returns {Promise<{metadata:MetadataTypeMap,type:string}>} Promise of item map
|
|
491
567
|
*/
|
|
492
|
-
static async retrieveSOAPgeneric(
|
|
568
|
+
static async retrieveSOAPgeneric(
|
|
569
|
+
retrieveDir,
|
|
570
|
+
buObject,
|
|
571
|
+
options,
|
|
572
|
+
additionalFields,
|
|
573
|
+
overrideType
|
|
574
|
+
) {
|
|
493
575
|
const fields = this.getFieldNamesToRetrieve(additionalFields);
|
|
494
576
|
|
|
495
|
-
const metadata = await this.retrieveSOAPBody(fields, options);
|
|
577
|
+
const metadata = await this.retrieveSOAPBody(fields, options, overrideType);
|
|
496
578
|
if (retrieveDir) {
|
|
497
579
|
const savedMetadata = await this.saveResults(metadata, retrieveDir, null);
|
|
498
580
|
Util.logger.info(
|
|
499
581
|
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})`
|
|
500
582
|
);
|
|
501
|
-
|
|
502
583
|
if (
|
|
503
584
|
buObject &&
|
|
504
585
|
this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
|
|
@@ -522,19 +603,27 @@ class MetadataType {
|
|
|
522
603
|
options = options || {};
|
|
523
604
|
let metadata = {};
|
|
524
605
|
do {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
606
|
+
let resultsBatch;
|
|
607
|
+
await Util.retryOnError(`Retrying ${this.definition.type}`, async () => {
|
|
608
|
+
resultsBatch = await new Promise((resolve, reject) => {
|
|
609
|
+
this.client.SoapClient.retrieve(
|
|
610
|
+
type || this.definition.type,
|
|
611
|
+
fields,
|
|
612
|
+
options || {},
|
|
613
|
+
(error, response) => {
|
|
614
|
+
if (error) {
|
|
615
|
+
Util.logger.debug(`SOAP.retrieve Error: ${error.message}`);
|
|
616
|
+
reject(error);
|
|
617
|
+
}
|
|
618
|
+
if (response) {
|
|
619
|
+
resolve(response.body);
|
|
620
|
+
} else {
|
|
621
|
+
// fallback, lets make sure surrounding methods know we got an empty result back
|
|
622
|
+
resolve({});
|
|
623
|
+
}
|
|
535
624
|
}
|
|
536
|
-
|
|
537
|
-
);
|
|
625
|
+
);
|
|
626
|
+
});
|
|
538
627
|
});
|
|
539
628
|
status = resultsBatch.OverallStatus;
|
|
540
629
|
if (status === 'MoreDataAvailable') {
|
|
@@ -572,7 +661,10 @@ class MetadataType {
|
|
|
572
661
|
let results = {};
|
|
573
662
|
do {
|
|
574
663
|
options.uri = this.paginate(options.uri, lastPage);
|
|
575
|
-
|
|
664
|
+
let response;
|
|
665
|
+
await Util.retryOnError(`Retrying ${this.definition.type}`, async () => {
|
|
666
|
+
response = await this.client.RestClient.get(options);
|
|
667
|
+
});
|
|
576
668
|
const metadata = this.parseResponseBody(response.body);
|
|
577
669
|
results = Object.assign(results, metadata);
|
|
578
670
|
if (
|
|
@@ -621,28 +713,24 @@ class MetadataType {
|
|
|
621
713
|
/**
|
|
622
714
|
* Builds map of metadata entries mapped to their keyfields
|
|
623
715
|
* @param {Object} body json of response body
|
|
624
|
-
* @param {string} [bodyIteratorField] overrides bodyIteratorField of definition
|
|
625
|
-
* @param {string} [keyField] overrides keyField of definition
|
|
626
716
|
* @returns {Promise<MetadataTypeMap>} keyField => metadata map
|
|
627
717
|
*/
|
|
628
|
-
static parseResponseBody(body
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
if (!keyField) {
|
|
633
|
-
keyField = this.definition.keyField;
|
|
634
|
-
}
|
|
718
|
+
static parseResponseBody(body) {
|
|
719
|
+
const bodyIteratorField = this.definition.bodyIteratorField;
|
|
720
|
+
const keyField = this.definition.keyField;
|
|
635
721
|
const metadataStructure = {};
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const
|
|
645
|
-
|
|
722
|
+
if (body !== null) {
|
|
723
|
+
// in some cases data is just an array
|
|
724
|
+
if (Array.isArray(bodyIteratorField)) {
|
|
725
|
+
for (const item of body) {
|
|
726
|
+
const key = item[keyField];
|
|
727
|
+
metadataStructure[key] = item;
|
|
728
|
+
}
|
|
729
|
+
} else if (body[bodyIteratorField]) {
|
|
730
|
+
for (const item of body[bodyIteratorField]) {
|
|
731
|
+
const key = item[keyField];
|
|
732
|
+
metadataStructure[key] = item;
|
|
733
|
+
}
|
|
646
734
|
}
|
|
647
735
|
}
|
|
648
736
|
return metadataStructure;
|