@eluvio/elv-client-js 4.0.143 → 4.0.145

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/dist/ElvClient-min.js +15 -14
  2. package/dist/ElvClient-node-min.js +14 -13
  3. package/dist/ElvFrameClient-min.js +10 -10
  4. package/dist/ElvPermissionsClient-min.js +9 -9
  5. package/dist/ElvWalletClient-min.js +15 -14
  6. package/dist/ElvWalletClient-node-min.js +14 -13
  7. package/dist/src/AuthorizationClient.js +11 -14
  8. package/dist/src/ContentObjectAudit.js +3 -3
  9. package/dist/src/ContentObjectVerification.js +3 -3
  10. package/dist/src/Crypto.js +2 -2
  11. package/dist/src/ElvClient.js +532 -401
  12. package/dist/src/ElvWallet.js +7 -5
  13. package/dist/src/EthClient.js +9 -10
  14. package/dist/src/FrameClient.js +10 -11
  15. package/dist/src/HttpClient.js +5 -5
  16. package/dist/src/Id.js +1 -2
  17. package/dist/src/PermissionsClient.js +31 -19
  18. package/dist/src/RemoteSigner.js +10 -11
  19. package/dist/src/UserProfileClient.js +35 -20
  20. package/dist/src/Utils.js +2 -3
  21. package/dist/src/Validation.js +10 -2
  22. package/dist/src/client/ABRPublishing.js +438 -238
  23. package/dist/src/client/AccessGroups.js +2 -2
  24. package/dist/src/client/ContentAccess.js +281 -344
  25. package/dist/src/client/ContentManagement.js +100 -84
  26. package/dist/src/client/Contracts.js +430 -2
  27. package/dist/src/client/Files.js +2 -2
  28. package/dist/src/client/LiveConf.js +43 -26
  29. package/dist/src/client/LiveStream.js +65 -63
  30. package/dist/src/client/NFT.js +2 -2
  31. package/dist/src/client/Shares.js +47 -24
  32. package/dist/src/walletClient/ClientMethods.js +2 -2
  33. package/dist/src/walletClient/Profile.js +2 -2
  34. package/dist/src/walletClient/Utils.js +2 -2
  35. package/dist/src/walletClient/index.js +359 -280
  36. package/package.json +2 -2
  37. package/src/FrameClient.js +1 -0
  38. package/src/Validation.js +9 -3
  39. package/src/client/ABRPublishing.js +176 -160
  40. package/src/client/Contracts.js +258 -0
  41. package/src/walletClient/index.js +13 -0
@@ -98,13 +98,15 @@ exports.CreateProductionMaster = async function({
98
98
  structLogLevel="none"
99
99
  }) {
100
100
  ValidateLibrary(libraryId);
101
- let id;
101
+ let objectId;
102
102
  const finalize = !writeToken;
103
103
 
104
104
  if(writeToken) {
105
- id = this.utils.DecodeWriteToken(writeToken).objectId;
105
+ // if write token passed in, don't create a new object
106
+ objectId = this.utils.DecodeWriteToken(writeToken).objectId;
106
107
  } else {
107
- ({id, writeToken} = await this.CreateContentObject({
108
+ // no write token supplied - create a new object and get write token
109
+ ({objectId, writeToken} = await this.CreateContentObject({
108
110
  libraryId,
109
111
  options: type ? { type } : {}
110
112
  }));
@@ -166,7 +168,7 @@ exports.CreateProductionMaster = async function({
166
168
 
167
169
  await this.UploadFilesFromS3({
168
170
  libraryId,
169
- objectId: id,
171
+ objectId,
170
172
  writeToken,
171
173
  fileInfo: credentialSet.matched,
172
174
  region,
@@ -183,7 +185,7 @@ exports.CreateProductionMaster = async function({
183
185
  } else {
184
186
  await this.UploadFiles({
185
187
  libraryId,
186
- objectId: id,
188
+ objectId,
187
189
  writeToken,
188
190
  fileInfo,
189
191
  callback,
@@ -192,11 +194,11 @@ exports.CreateProductionMaster = async function({
192
194
  }
193
195
  }
194
196
 
195
- await this.CreateEncryptionConk({libraryId, objectId: id, writeToken, createKMSConk: true});
197
+ await this.CreateEncryptionConk({libraryId, objectId, writeToken, createKMSConk: true});
196
198
 
197
199
  const { logs, errors, warnings } = await this.CallBitcodeMethod({
198
200
  libraryId,
199
- objectId: id,
201
+ objectId,
200
202
  writeToken,
201
203
  method: UrlJoin("media", "production_master", "init"),
202
204
  queryParams: {
@@ -211,7 +213,7 @@ exports.CreateProductionMaster = async function({
211
213
 
212
214
  await this.MergeMetadata({
213
215
  libraryId,
214
- objectId: id,
216
+ objectId,
215
217
  writeToken,
216
218
  metadata: {
217
219
  ...(metadata || {}),
@@ -227,22 +229,22 @@ exports.CreateProductionMaster = async function({
227
229
  }
228
230
  });
229
231
 
230
- let finalizeResponse;
232
+ let additionalReturnVals;
231
233
 
232
234
  if(finalize) {
233
- finalizeResponse = await this.FinalizeContentObject({
235
+ additionalReturnVals = await this.FinalizeContentObject({
234
236
  libraryId,
235
- objectId: id,
237
+ objectId,
236
238
  writeToken,
237
239
  commitMessage: "Create master",
238
240
  awaitCommitConfirmation: false
239
241
  });
240
242
  } else {
241
- finalizeResponse = {
243
+ additionalReturnVals = {
242
244
  write_token: writeToken,
243
245
  type,
244
246
  qlib_id: libraryId,
245
- id
247
+ id: objectId
246
248
  };
247
249
  }
248
250
 
@@ -250,7 +252,7 @@ exports.CreateProductionMaster = async function({
250
252
  errors: errors || [],
251
253
  logs: logs || [],
252
254
  warnings: warnings || [],
253
- ...finalizeResponse
255
+ ...additionalReturnVals
254
256
  };
255
257
  };
256
258
 
@@ -265,8 +267,8 @@ exports.CreateProductionMaster = async function({
265
267
  * @param {boolean=} keepOtherStreams=false - If objectId is specified, whether to preserve existing streams with keys other than the ones specified in production master
266
268
  * @param {string} libraryId - ID of the mezzanine library
267
269
  * @param {string} masterVersionHash - The version hash of the production master content object
268
- * @param {string=} masterWriteToken - The write token of the production master content object draft. If provided, the object will not be finalized
269
- * @param {string=} writeToken - The write token of the mezzanine object draft. If specified, the object will not be finalized
270
+ * @param {string=} masterWriteToken - The write token of the production master content object draft.
271
+ * @param {string=} writeToken - The write token of the mezzanine object draft. If specified, the object will not be finalized, and no extra version of the object will be created to hold "bread crumb" metadata entry /lro_draft
270
272
  * @param {Object=} metadata - Additional metadata for mezzanine content object
271
273
  * @param {string} name - Name for mezzanine content object
272
274
  * @param {string=} objectId - ID of existing object (if not specified, new object will be created)
@@ -298,91 +300,94 @@ exports.CreateABRMezzanine = async function({
298
300
  structLogLevel="none",
299
301
  streamKeys
300
302
  }) {
303
+
304
+ if(!masterVersionHash && !masterWriteToken) {
305
+ throw Error("Neither master version hash nor master write token specified. One must be provided");
306
+ }
307
+
308
+ if(masterVersionHash && masterWriteToken) {
309
+ throw Error("Both master version hash and master write token specified. Only one can be provided");
310
+ }
311
+
312
+ // double-check library id for mez
301
313
  ValidateLibrary(libraryId);
302
314
 
315
+ // The following 2 vars are used only if masterWriteToken is passed in (to retrieve master name)
303
316
  let masterObjectId;
317
+ let masterLibId;
304
318
  if(masterVersionHash) {
319
+ // NOTE: masterObjectId is left undefined if masterWriteToken not passed in
305
320
  ValidateVersion(masterVersionHash);
306
321
  } else if(masterWriteToken) {
322
+ // determine master object id if a master write token was passed in
307
323
  ValidateWriteToken(masterWriteToken);
308
-
309
324
  masterObjectId = this.utils.DecodeWriteToken(masterWriteToken).objectId;
325
+ masterLibId = await client.ContentObjectLibraryId({masterObjectId});
310
326
  }
311
327
 
312
- if(writeToken) {
313
- ValidateWriteToken(writeToken);
314
-
315
- if(!objectId) {
316
- objectId = this.utils.DecodeWriteToken(writeToken).objectId;
317
- }
318
- }
328
+ // if pre-existing mez object id passed in, validate
329
+ if(objectId) ValidateObject(objectId);
319
330
 
320
- if(!masterVersionHash && !masterWriteToken) {
321
- throw Error("Master version hash and master write token not specified. One must be provided");
322
- }
331
+ // if mez write token supplied, validate it
332
+ if(writeToken) {
333
+ ValidateWriteToken(writeToken, objectId);
323
334
 
324
- if(!objectId && (keepOtherStreams)) {
325
- throw Error("Existing mezzanine object ID required in order to use 'keepOtherStreams'");
335
+ // if pre-existing mez object id not passed in, decode it from write token
336
+ objectId = objectId || this.utils.DecodeWriteToken(writeToken).objectId;
326
337
  }
338
+ // After above, if objectId is undefined then no mez writeToken was passed in, no mez objectId was passed in,
339
+ // meaning we must create a new object
340
+ const useExistingMez = !!objectId; // type coerce string or undefined to boolean: "" -> false, undefined -> false, all else -> true
327
341
 
328
- if(addlOfferingSpecs && !abrProfile) {
329
- throw Error("abrProfile required when using addlOfferingSpecs");
330
- }
342
+ if(!objectId && (keepOtherStreams)) throw Error("Existing mezzanine object ID required in order to use 'keepOtherStreams'");
331
343
 
332
- const existingMez = !!objectId;
344
+ if(addlOfferingSpecs && !abrProfile) throw Error("abrProfile required when using addlOfferingSpecs");
333
345
 
334
346
  let options = type ? { type } : {};
335
347
 
336
- let id;
337
348
  const finalize = !writeToken;
338
349
 
339
- if(existingMez) {
340
- // Edit existing
341
- if(writeToken) {
342
- id = objectId;
343
- } else {
344
- ({writeToken, id} = await this.EditContentObject({
345
- libraryId,
346
- objectId,
347
- options
348
- }));
349
- }
350
- } else {
351
- // Create new
352
- const createResponse = await this.CreateContentObject({
350
+ // are we ingesting to an existing mez (and did NOT supply a write token?)
351
+ if(useExistingMez && !writeToken) {
352
+ // then we need to obtain a write token for the existing object
353
+ ({writeToken} = await this.EditContentObject({
353
354
  libraryId,
355
+ objectId,
354
356
  options
355
- });
357
+ }));
358
+ }
356
359
 
357
- id = createResponse.id;
358
- writeToken = createResponse.write_token;
360
+ // if we are not using an existing mez, we need to create a brand new object (in draft form)
361
+ if(!useExistingMez) {
362
+ // Create new mez object
363
+ ({objectId, writeToken} = await this.CreateContentObject({
364
+ libraryId,
365
+ options
366
+ }));
359
367
  }
360
368
 
361
- await this.CreateEncryptionConk({libraryId, objectId: id, writeToken, createKMSConk: true});
369
+ // from here forwards, objectId and writeToken can be assumed to be populated (both refer to mez)
362
370
 
371
+ // ensure this object has encryption/kms conk
372
+ await this.CreateEncryptionConk({libraryId, objectId, writeToken, createKMSConk: true});
363
373
 
364
- let nameMetaPayload;
365
- if(masterWriteToken) {
366
- nameMetaPayload = {
367
- libraryId,
368
- objectId: masterObjectId,
369
- writeToken: masterWriteToken
370
- };
371
- } else if(masterVersionHash) {
372
- nameMetaPayload = {
373
- versionHash: masterVersionHash
374
- };
375
- }
374
+ // figure out where to read master metadata from
375
+ const masterMetaSource = masterWriteToken ? {
376
+ libraryId: masterLibId,
377
+ objectId: masterObjectId,
378
+ writeToken: masterWriteToken
379
+ } : {versionHash: masterVersionHash};
376
380
 
377
381
  const masterName = await this.ContentObjectMetadata({
378
- ...nameMetaPayload,
382
+ ...masterMetaSource,
379
383
  metadataSubtree: "public/name"
380
384
  });
381
385
 
382
386
  // Include authorization for library, master, and mezzanine
383
387
  let authorizationTokens = [];
384
- authorizationTokens.push(await this.authClient.AuthorizationToken({libraryId, objectId: id, update: true}));
388
+ authorizationTokens.push(await this.authClient.AuthorizationToken({libraryId, objectId, update: true}));
385
389
  authorizationTokens.push(await this.authClient.AuthorizationToken({libraryId}));
390
+ // only 1 of the vars masterVersionHash and masterObjectId below will contain a value
386
391
  authorizationTokens.push(await this.authClient.AuthorizationToken({versionHash: masterVersionHash, objectId: masterObjectId}));
387
392
 
388
393
  const headers = {
@@ -398,7 +403,7 @@ exports.CreateABRMezzanine = async function({
398
403
  variant_key: variant
399
404
  };
400
405
 
401
- let storeClear = false;
406
+ let storeClear;
402
407
  if(abrProfile) {
403
408
  body.abr_profile = abrProfile;
404
409
  storeClear = abrProfile.store_clear;
@@ -415,14 +420,14 @@ exports.CreateABRMezzanine = async function({
415
420
  // If mez parts are to be encrypted, generate encryption conks
416
421
  await this.EncryptionConk({
417
422
  libraryId,
418
- objectId: id,
423
+ objectId,
419
424
  writeToken
420
425
  });
421
426
  }
422
427
 
423
428
  const {logs, errors, warnings} = await this.CallBitcodeMethod({
424
429
  libraryId,
425
- objectId: id,
430
+ objectId,
426
431
  writeToken,
427
432
  method: UrlJoin("media", "abr_mezzanine", "init"),
428
433
  queryParams: {
@@ -465,12 +470,12 @@ exports.CreateABRMezzanine = async function({
465
470
  ...metadata.public.asset_metadata
466
471
  };
467
472
 
468
- if(name || !existingMez) {
473
+ if(name || !useExistingMez) {
469
474
  metadata.name = name || `${masterName} Mezzanine`;
470
475
  metadata.public.name = name || `${masterName} Mezzanine`;
471
476
  }
472
477
 
473
- if(description || !existingMez) {
478
+ if(description || !useExistingMez) {
474
479
  metadata.description = description || "";
475
480
  metadata.public.description = description || "";
476
481
  }
@@ -478,14 +483,14 @@ exports.CreateABRMezzanine = async function({
478
483
  // retrieve existing metadata to merge with updated metadata
479
484
  const existingMetadata = await this.ContentObjectMetadata({
480
485
  libraryId,
481
- objectId: id,
486
+ objectId,
482
487
  writeToken,
483
488
  });
484
489
  // newer metadata values replace existing metadata, unless both new and old values are objects,
485
490
  // in which case their keys are merged recursively
486
491
  metadata = R.mergeDeepRight(existingMetadata, metadata);
487
492
 
488
- if(!existingMez) {
493
+ if(!useExistingMez) {
489
494
  // set creation date
490
495
  metadata.elv_created_at = new Date().getTime();
491
496
  }
@@ -493,25 +498,25 @@ exports.CreateABRMezzanine = async function({
493
498
  // write metadata to mezzanine object
494
499
  await this.ReplaceMetadata({
495
500
  libraryId,
496
- objectId: id,
501
+ objectId,
497
502
  writeToken,
498
503
  metadata
499
504
  });
500
505
 
501
- let finalizeResponse;
506
+ let additionalReturnVals;
502
507
  if(finalize) {
503
- finalizeResponse = await this.FinalizeContentObject({
508
+ additionalReturnVals = await this.FinalizeContentObject({
504
509
  libraryId,
505
- objectId: id,
510
+ objectId,
506
511
  writeToken,
507
512
  commitMessage: "Create ABR mezzanine"
508
513
  });
509
514
  } else {
510
- finalizeResponse = {
515
+ additionalReturnVals = {
511
516
  write_token: writeToken,
512
517
  type,
513
518
  qlib_id: libraryId,
514
- id
519
+ id: objectId
515
520
  };
516
521
  }
517
522
 
@@ -519,7 +524,7 @@ exports.CreateABRMezzanine = async function({
519
524
  logs: logs || [],
520
525
  warnings: warnings || [],
521
526
  errors: errors || [],
522
- ...finalizeResponse
527
+ ...additionalReturnVals
523
528
  };
524
529
  };
525
530
 
@@ -530,7 +535,7 @@ exports.CreateABRMezzanine = async function({
530
535
  * @namedParams
531
536
  * @param {string} libraryId - ID of the mezzanine library
532
537
  * @param {string} objectId - ID of the mezzanine object
533
- * @param {string=} writeToken - Write token of the mezzanine object draft. If provided, the object will not be finalized
538
+ * @param {string=} writeToken - Write token of the mezzanine object draft. If provided, no "bread crumb" metadata /lro_draft will be created, nor saved as an extra finalized version
534
539
  * @param {Array<Object>=} access - Array of S3 credentials, along with path matching regexes - Required if any files in the masters are S3 references (See CreateProductionMaster method)
535
540
  * - Format: {region, bucket, accessKey, secret}
536
541
  * @param {number[]} jobIndexes - Array of LRO job indexes to start. LROs are listed in a map under metadata key /abr_mezzanine/offerings/(offeringKey)/mez_prep_specs/, and job indexes start with 0, corresponding to map keys in alphabetical order
@@ -546,12 +551,11 @@ exports.StartABRMezzanineJobs = async function({
546
551
  }) {
547
552
  ValidateParameters({libraryId, objectId});
548
553
 
549
- if(writeToken) {
550
- ValidateWriteToken(writeToken);
554
+ const writeTokenSupplied = !!writeToken;
551
555
 
552
- if(!objectId) {
553
- objectId = this.utils.DecodeWriteToken(writeToken).objectId;
554
- }
556
+ if(writeTokenSupplied) {
557
+ ValidateWriteToken(writeToken, objectId);
558
+ objectId = objectId || this.utils.DecodeWriteToken(writeToken).objectId;
555
559
  }
556
560
 
557
561
  const lastJobOfferingsInfo = await this.ContentObjectMetadata({
@@ -595,60 +599,51 @@ exports.StartABRMezzanineJobs = async function({
595
599
  ...authorizationTokens
596
600
  ];
597
601
 
598
- const headers = {
599
- Authorization: authorizationTokens.map(token => `Bearer ${token}`).join(",")
600
- };
601
-
602
- let processingDraft;
603
- if(writeToken) {
604
- const nodeUrl = await this.WriteTokenNodeUrlNetwork({writeToken});
605
-
606
- processingDraft = {
607
- write_token: writeToken,
608
- nodeUrl
602
+ const headers = {Authorization: authorizationTokens.map(token => `Bearer ${token}`).join(",")};
603
+
604
+ let breadCrumb;
605
+ let breadCrumbHash;
606
+ // If we did not have a write token passed in, get a new write token for the LROs to write to
607
+ if(!writeTokenSupplied) {
608
+ const lroWorkingDraft = await this.EditContentObject({libraryId, objectId});
609
+ writeToken = lroWorkingDraft.writeToken;
610
+
611
+ // Save this write token (and node url, and offering key) to ANOTHER, NEW write token in order to
612
+ // record "bread crumb" metadata at /lro_draft, and save (finalize) this second write token
613
+ const breadCrumbDraft = await this.EditContentObject({libraryId, objectId});
614
+ breadCrumb = {
615
+ write_token: lroWorkingDraft.writeToken,
616
+ node: lroWorkingDraft.nodeUrl,
617
+ offering: offeringKey
609
618
  };
610
- } else {
611
- processingDraft = await this.EditContentObject({libraryId, objectId});
612
- }
613
-
614
- const lroInfo = {
615
- write_token: processingDraft.write_token,
616
- node: processingDraft.nodeUrl,
617
- offering: offeringKey
618
- };
619
619
 
620
- // Update metadata with LRO version write token
621
- let finalizeResponse;
622
- if(writeToken) {
623
- await this.ReplaceMetadata({
624
- libraryId,
625
- objectId,
626
- writeToken,
627
- metadataSubtree: "lro_draft",
628
- metadata: lroInfo
629
- });
630
- } else {
631
- const statusDraft = await this.EditContentObject({libraryId, objectId});
632
620
  await this.ReplaceMetadata({
633
621
  libraryId,
634
622
  objectId,
635
- writeToken: statusDraft.write_token,
623
+ writeToken: breadCrumbDraft.writeToken,
636
624
  metadataSubtree: "lro_draft",
637
- metadata: lroInfo
625
+ metadata: breadCrumb
638
626
  });
639
627
 
640
- finalizeResponse = await this.FinalizeContentObject({
628
+ const finalizeResponse = await this.FinalizeContentObject({
641
629
  libraryId,
642
630
  objectId,
643
- writeToken: statusDraft.write_token,
644
- commitMessage: "Mezzanine LRO status"
631
+ writeToken: breadCrumbDraft.write_token,
632
+ commitMessage: "Save mez LRO write token to /lro_draft"
645
633
  });
634
+ breadCrumbHash = finalizeResponse.hash;
646
635
  }
647
636
 
637
+ // make sure we have correct nodeUrl for this token
638
+ const nodeUrl = await this.WriteTokenNodeUrlNetwork({writeToken});
639
+ // tell http client what node to contact for this write token
640
+ this.RecordWriteToken({writeToken, fabricNodeUrl: nodeUrl});
641
+
642
+ // Make the API call that actually starts the LROs
648
643
  const {data, errors, warnings, logs} = await this.CallBitcodeMethod({
649
644
  libraryId,
650
645
  objectId,
651
- writeToken: processingDraft.write_token,
646
+ writeToken,
652
647
  headers,
653
648
  method: UrlJoin("media", "abr_mezzanine", "prep_start"),
654
649
  constant: false,
@@ -660,10 +655,10 @@ exports.StartABRMezzanineJobs = async function({
660
655
  });
661
656
 
662
657
  return {
663
- hash: finalizeResponse ? finalizeResponse.hash : "",
664
- lro_draft: lroInfo,
665
- writeToken: processingDraft.write_token,
666
- nodeUrl: processingDraft.nodeUrl,
658
+ hash: breadCrumbHash,
659
+ lro_draft: breadCrumb,
660
+ writeToken,
661
+ nodeUrl,
667
662
  data,
668
663
  logs: logs || [],
669
664
  warnings: warnings || [],
@@ -675,6 +670,10 @@ exports.StartABRMezzanineJobs = async function({
675
670
  * Retrieve node and write token for a mezzanine's current offering preparation job (if any).
676
671
  * Also returns the offering key.
677
672
  *
673
+ * This information is contained in metadata at /lro_draft, in an extra version of the object
674
+ * that gets created and finalized as part of creating a mezzanine. This serves as a "bread crumb trail" to
675
+ * allow users to find the active draft/write token that the ingest LROs are writing to.
676
+ *
678
677
  * @methodGroup ABR Publishing
679
678
  * @namedParams
680
679
  * @param {string} libraryId - ID of the library
@@ -682,11 +681,10 @@ exports.StartABRMezzanineJobs = async function({
682
681
  *
683
682
  * @return {Promise<Object>} - LRO status
684
683
  */
685
- exports.LRODraftInfo = async function({libraryId, objectId, writeToken}) {
684
+ exports.LRODraftInfo = async function({libraryId, objectId}) {
686
685
  const standardPathContents = await this.ContentObjectMetadata({
687
686
  libraryId,
688
687
  objectId,
689
- writeToken,
690
688
  metadataSubtree: "lro_draft"
691
689
  });
692
690
 
@@ -728,53 +726,74 @@ exports.LRODraftInfo = async function({libraryId, objectId, writeToken}) {
728
726
 
729
727
  /**
730
728
  * Retrieve status information for mezzanine transcoding jobs, aka long running operations (LROs) on the given object.
729
+ * If a write token is passed in, its draft will be checked directly by lookiing at /lro_status.
730
+ * If a write token is not passed in, then the latest version of objectId will be checked for "bread crumb" entry
731
+ * at /lro_draft, to determine the write token that current ingest LRO(s) are writing to.
731
732
  *
732
733
  * @methodGroup ABR Publishing
733
734
  * @namedParams
734
735
  * @param {string} libraryId - ID of the library
735
736
  * @param {string} objectId - ID of the object
737
+ * @param {string=} writeToken - Write token of the active draft (that the transcode LROs are writing to)
736
738
  *
737
739
  * @return {Promise<Object>} - LRO status
738
740
  */
739
- exports.LROStatus = async function({libraryId, objectId, writeToken}) {
741
+ exports.LROStatus = async function({libraryId, objectId, writeToken= ""}) {
740
742
  ValidateParameters({libraryId, objectId});
741
743
 
742
- const lroDraft = await this.LRODraftInfo({libraryId, objectId, writeToken});
743
-
744
- this.RecordWriteToken({writeToken: lroDraft.write_token, fabricNodeUrl: lroDraft.node});
744
+ let activeWriteToken;
745
+ if(writeToken) {
746
+ ValidateWriteToken(writeToken, objectId);
747
+ activeWriteToken = writeToken;
748
+ } else {
749
+ const lroDraft = await this.LRODraftInfo({libraryId, objectId});
750
+ activeWriteToken = lroDraft.write_token;
751
+ const fabricNodeUrl = await this.WriteTokenNodeUrlNetwork({writeToken: activeWriteToken});
752
+ this.RecordWriteToken({writeToken: lroDraft.write_token, fabricNodeUrl});
753
+ }
745
754
 
746
755
  return await this.ContentObjectMetadata({
747
756
  libraryId,
748
757
  objectId,
749
- writeToken,
758
+ writeToken: activeWriteToken,
750
759
  metadataSubtree: "lro_status"
751
760
  });
752
761
  };
753
762
 
754
763
  /**
755
- * Finalize a mezzanine object after all jobs have finished
764
+ * Process completed mezzanine transcoding LRO(s), creating the final offering(s) from the transcode(s)
765
+ * Unless a writeToken is passed in, finalize the mez object working draft as well.
766
+ *
767
+ * If a writeToken is not passed in, then it will be retrieved from /lro_draft of most recent version of object.
768
+ * This is a "bread crumb" entry that gets created by StartABRMezzanineJobs() - unless a writeToken was passed in to StartABRMezzanineJobs() also.
756
769
  *
757
770
  * @methodGroup ABR Publishing
758
771
  * @namedParams
759
772
  * @param {string} libraryId - ID of the mezzanine library
760
773
  * @param {string} objectId - ID of the mezzanine object
761
- * @param {string} writeToken - Write token for the mezzanine object. If specified, the object will not be finalized.
774
+ * @param {string=} writeToken - Write token for the mezzanine object. If specified, the object will not be finalized.
762
775
  * @param {function=} preFinalizeFn - A function to call before finalizing changes, to allow further modifications to offering. The function will be invoked with {elvClient, nodeUrl, writeToken} to allow access to the draft and MUST NOT finalize the draft.
763
776
  * @param {boolean=} preFinalizeThrow - If set to `true` then any error thrown by preFinalizeFn will not be caught. Otherwise, any exception will be appended to the `warnings` array returned after finalization.
764
777
  *
765
778
  * @return {Promise<Object>} - The finalize response for the mezzanine object, as well as any logs, warnings and errors from the finalization
766
779
  */
767
- exports.FinalizeABRMezzanine = async function({libraryId, objectId, preFinalizeFn, preFinalizeThrow, writeToken}) {
780
+ exports.FinalizeABRMezzanine = async function({libraryId, objectId, preFinalizeFn, preFinalizeThrow, writeToken=""}) {
768
781
  ValidateParameters({libraryId, objectId});
769
782
 
770
- if(writeToken) {
771
- ValidateWriteToken(writeToken);
772
- }
783
+ const writeTokenSupplied = !!writeToken;
773
784
 
774
- const nodeUrl = await this.WriteTokenNodeUrlNetwork({writeToken});
785
+ if(writeTokenSupplied) {
786
+ ValidateWriteToken(writeToken, objectId);
787
+ objectId = objectId || this.utils.DecodeWriteToken(writeToken).objectId;
788
+ } else {
789
+ // get write token from draft info
790
+ const lroDraft = await this.LRODraftInfo({libraryId, objectId});
791
+ writeToken = lroDraft.write_token;
792
+ }
775
793
 
794
+ const fabricNodeUrl = await this.WriteTokenNodeUrlNetwork({writeToken});
776
795
  // tell http client what node to contact for this write token
777
- this.RecordWriteToken({writeToken: writeToken, fabricNodeUrl: nodeUrl});
796
+ this.RecordWriteToken({writeToken, fabricNodeUrl});
778
797
 
779
798
  const lastJobOfferingsInfo = await this.ContentObjectMetadata({
780
799
  libraryId,
@@ -786,22 +805,20 @@ exports.FinalizeABRMezzanine = async function({libraryId, objectId, preFinalizeF
786
805
  const offeringKey = MezJobMainOfferingKey(lastJobOfferingsInfo);
787
806
  const masterHash = lastJobOfferingsInfo[offeringKey].prod_master_hash;
788
807
 
789
- let authPayload = {};
808
+ let masterIdentifier = {};
790
809
  if(masterHash.startsWith("tqw__")) {
791
- authPayload["objectId"] = this.utils.DecodeWriteToken(masterHash).objectId;
810
+ masterIdentifier["objectId"] = this.utils.DecodeWriteToken(masterHash).objectId;
792
811
  } else {
793
- authPayload["versionHash"] = masterHash;
812
+ masterIdentifier["versionHash"] = masterHash;
794
813
  }
795
814
 
796
815
  // Authorization token for mezzanine and master
797
816
  let authorizationTokens = [
798
817
  await this.authClient.AuthorizationToken({libraryId, objectId, update: true}),
799
- await this.authClient.AuthorizationToken({...authPayload})
818
+ await this.authClient.AuthorizationToken({...masterIdentifier})
800
819
  ];
801
820
 
802
- const headers = {
803
- Authorization: authorizationTokens.map(token => `Bearer ${token}`).join(",")
804
- };
821
+ const headers = {Authorization: authorizationTokens.map(token => `Bearer ${token}`).join(",")};
805
822
 
806
823
  const {data, errors, warnings, logs} = await this.CallBitcodeMethod({
807
824
  objectId,
@@ -831,16 +848,15 @@ exports.FinalizeABRMezzanine = async function({libraryId, objectId, preFinalizeF
831
848
  }
832
849
  }
833
850
 
834
- const finalizeResponse = {};
835
- if(!writeToken) {
836
- finalizeResponse = await this.FinalizeContentObject({
837
- libraryId,
838
- objectId: objectId,
839
- writeToken,
840
- commitMessage: "Finalize ABR mezzanine",
841
- awaitCommitConfirmation: false
842
- });
843
- }
851
+ // only finalize if we did NOT have a writeToken passed in
852
+ const finalizeResponse = writeTokenSupplied ? {} : await this.FinalizeContentObject({
853
+ libraryId,
854
+ objectId,
855
+ writeToken,
856
+ commitMessage: "Finalize ABR mezzanine",
857
+ awaitCommitConfirmation: false
858
+ });
859
+
844
860
 
845
861
  return {
846
862
  data,