@eluvio/elv-client-js 4.0.12 → 4.0.13
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/dist/ElvClient-min.js +8 -8
- package/dist/ElvClient-node-min.js +8 -8
- package/dist/ElvFrameClient-min.js +2 -2
- package/dist/ElvWalletClient-min.js +6 -6
- package/dist/ElvWalletClient-node-min.js +7 -7
- package/dist/src/FrameClient.js +1 -1
- package/dist/src/HttpClient.js +30 -27
- package/dist/src/client/ABRPublishing.js +187 -147
- package/dist/src/client/ContentAccess.js +1 -0
- package/dist/src/client/ContentManagement.js +4 -2
- package/dist/src/walletClient/Configuration.js +1 -1
- package/package.json +1 -1
- package/src/FrameClient.js +1 -0
- package/src/HttpClient.js +4 -2
- package/src/client/ABRPublishing.js +126 -105
- package/src/client/ContentAccess.js +1 -0
- package/src/client/ContentManagement.js +4 -2
- package/src/walletClient/Configuration.js +3 -2
- package/utilities/ListAccessGroups.js +0 -34
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
const R = require("ramda");
|
|
10
10
|
const UrlJoin = require("url-join");
|
|
11
|
-
const HttpClient = require("../HttpClient");
|
|
12
11
|
|
|
13
12
|
const {
|
|
14
13
|
ValidateLibrary,
|
|
@@ -16,6 +15,19 @@ const {
|
|
|
16
15
|
ValidateParameters
|
|
17
16
|
} = require("../Validation");
|
|
18
17
|
|
|
18
|
+
// When `/abr_mezzanine/offerings` contains more than one entry, only 1 is the 'real' offering, the others are
|
|
19
|
+
// additional copies to be modified upon finalization due to addlOfferingSpecs having been specified in call to
|
|
20
|
+
// `CreateABRMezzanine()`. The 'real' offering will have an object stored in `mez_prep_specs`, the copies will not.
|
|
21
|
+
// This function accepts the metadata retrieved from `/abr_mezzanine/offerings` and returns the offering key for the
|
|
22
|
+
// 'real' offering that actually has transcode LROs.
|
|
23
|
+
// If no suitable offering is found, throws an error.
|
|
24
|
+
const MezJobMainOfferingKey = function(abrMezOfferings) {
|
|
25
|
+
if(!abrMezOfferings) throw Error("No mezzanine preparation job info found at /abr_mezzanine");
|
|
26
|
+
const mainOfferingKey = Object.keys(abrMezOfferings).find(offKey => abrMezOfferings[offKey].mez_prep_specs);
|
|
27
|
+
if(!mainOfferingKey) throw Error("Could not determine offering key for last submitted job");
|
|
28
|
+
return mainOfferingKey;
|
|
29
|
+
};
|
|
30
|
+
|
|
19
31
|
/**
|
|
20
32
|
* Create a master media content object with the given files.
|
|
21
33
|
*
|
|
@@ -438,13 +450,12 @@ exports.CreateABRMezzanine = async function({
|
|
|
438
450
|
};
|
|
439
451
|
|
|
440
452
|
/**
|
|
441
|
-
* Start
|
|
453
|
+
* Start transcoding jobs previously set up by CreateABRMezzanine() on the specified mezzanine
|
|
442
454
|
*
|
|
443
455
|
* @methodGroup ABR Publishing
|
|
444
456
|
* @namedParams
|
|
445
457
|
* @param {string} libraryId - ID of the mezzanine library
|
|
446
458
|
* @param {string} objectId - ID of the mezzanine object
|
|
447
|
-
* @param {string=} offeringKey=default - The offering to process
|
|
448
459
|
* @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)
|
|
449
460
|
* - Format: {region, bucket, accessKey, secret}
|
|
450
461
|
* @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
|
|
@@ -454,19 +465,20 @@ exports.CreateABRMezzanine = async function({
|
|
|
454
465
|
exports.StartABRMezzanineJobs = async function({
|
|
455
466
|
libraryId,
|
|
456
467
|
objectId,
|
|
457
|
-
offeringKey="default",
|
|
458
468
|
access=[],
|
|
459
469
|
jobIndexes = null
|
|
460
470
|
}) {
|
|
461
471
|
ValidateParameters({libraryId, objectId});
|
|
462
472
|
|
|
463
|
-
const
|
|
473
|
+
const lastJobOfferingsInfo = await this.ContentObjectMetadata({
|
|
464
474
|
libraryId,
|
|
465
475
|
objectId,
|
|
466
476
|
metadataSubtree: UrlJoin("abr_mezzanine", "offerings")
|
|
467
477
|
});
|
|
478
|
+
const offeringKey = MezJobMainOfferingKey(lastJobOfferingsInfo);
|
|
468
479
|
|
|
469
|
-
const prepSpecs =
|
|
480
|
+
const prepSpecs = lastJobOfferingsInfo[offeringKey].mez_prep_specs;
|
|
481
|
+
if(!prepSpecs) throw Error("No stream preparation specs found");
|
|
470
482
|
|
|
471
483
|
// Retrieve all masters associated with this offering
|
|
472
484
|
let masterVersionHashes = Object.keys(prepSpecs).map(spec =>
|
|
@@ -497,7 +509,7 @@ exports.StartABRMezzanineJobs = async function({
|
|
|
497
509
|
|
|
498
510
|
const lroInfo = {
|
|
499
511
|
write_token: processingDraft.write_token,
|
|
500
|
-
node:
|
|
512
|
+
node: processingDraft.nodeUrl,
|
|
501
513
|
offering: offeringKey
|
|
502
514
|
};
|
|
503
515
|
|
|
@@ -507,7 +519,7 @@ exports.StartABRMezzanineJobs = async function({
|
|
|
507
519
|
libraryId,
|
|
508
520
|
objectId,
|
|
509
521
|
writeToken: statusDraft.write_token,
|
|
510
|
-
metadataSubtree:
|
|
522
|
+
metadataSubtree: "lro_draft",
|
|
511
523
|
metadata: lroInfo
|
|
512
524
|
});
|
|
513
525
|
|
|
@@ -536,6 +548,7 @@ exports.StartABRMezzanineJobs = async function({
|
|
|
536
548
|
hash: finalizeResponse.hash,
|
|
537
549
|
lro_draft: lroInfo,
|
|
538
550
|
writeToken: processingDraft.write_token,
|
|
551
|
+
nodeUrl: processingDraft.nodeUrl,
|
|
539
552
|
data,
|
|
540
553
|
logs: logs || [],
|
|
541
554
|
warnings: warnings || [],
|
|
@@ -544,45 +557,71 @@ exports.StartABRMezzanineJobs = async function({
|
|
|
544
557
|
};
|
|
545
558
|
|
|
546
559
|
/**
|
|
547
|
-
* Retrieve
|
|
560
|
+
* Retrieve node and write token for a mezzanine's current offering preparation job (if any).
|
|
561
|
+
* Also returns the offering key.
|
|
548
562
|
*
|
|
549
563
|
* @methodGroup ABR Publishing
|
|
550
564
|
* @namedParams
|
|
551
565
|
* @param {string} libraryId - ID of the library
|
|
552
566
|
* @param {string} objectId - ID of the object
|
|
553
|
-
* @param {string=} offeringKey=default - Offering key of the mezzanine
|
|
554
567
|
*
|
|
555
568
|
* @return {Promise<Object>} - LRO status
|
|
556
569
|
*/
|
|
557
|
-
exports.
|
|
558
|
-
|
|
570
|
+
exports.LRODraftInfo = async function({libraryId, objectId}) {
|
|
571
|
+
const standardPathContents = await this.ContentObjectMetadata({
|
|
572
|
+
libraryId,
|
|
573
|
+
objectId,
|
|
574
|
+
metadataSubtree: "lro_draft"
|
|
575
|
+
});
|
|
559
576
|
|
|
560
|
-
|
|
561
|
-
await this.ContentObjectMetadata({
|
|
562
|
-
libraryId,
|
|
563
|
-
objectId,
|
|
564
|
-
metadataSubtree: `lro_draft_${offeringKey}`
|
|
565
|
-
}) ||
|
|
566
|
-
await this.ContentObjectMetadata({
|
|
567
|
-
libraryId,
|
|
568
|
-
objectId,
|
|
569
|
-
metadataSubtree: "lro_draft"
|
|
570
|
-
});
|
|
577
|
+
if(standardPathContents) return standardPathContents;
|
|
571
578
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
+
// get last job info, under /abr_mezzanine/offerings/
|
|
580
|
+
const lastJobOfferingsInfo = await this.ContentObjectMetadata({
|
|
581
|
+
libraryId,
|
|
582
|
+
objectId,
|
|
583
|
+
metadataSubtree: UrlJoin("abr_mezzanine", "offerings")
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
if(!lastJobOfferingsInfo) throw Error("No metadata for mezzanine preparation job found at /abr_mezzanine");
|
|
587
|
+
|
|
588
|
+
const mainOfferingKey = MezJobMainOfferingKey(lastJobOfferingsInfo);
|
|
589
|
+
if(!mainOfferingKey) throw Error("Could not determine offering key for last submitted job");
|
|
579
590
|
|
|
591
|
+
// see if offering from last job was finalized
|
|
592
|
+
const ready = lastJobOfferingsInfo[mainOfferingKey].ready;
|
|
593
|
+
|
|
594
|
+
// old location for LRO draft info
|
|
595
|
+
const oldPathContents = await this.ContentObjectMetadata({
|
|
596
|
+
libraryId,
|
|
597
|
+
objectId,
|
|
598
|
+
metadataSubtree: `lro_draft_${mainOfferingKey}`
|
|
599
|
+
});
|
|
600
|
+
if(oldPathContents) {
|
|
601
|
+
return oldPathContents;
|
|
602
|
+
} else {
|
|
580
603
|
if(ready) {
|
|
581
|
-
throw Error(
|
|
604
|
+
throw Error("No LRO draft found for this mezzanine - looks like last mez prep job was already finalized.");
|
|
582
605
|
} else {
|
|
583
|
-
throw Error("No LRO draft found for this mezzanine");
|
|
606
|
+
throw Error("No LRO draft found for this mezzanine - looks like last mez prep job was either cancelled or discarded.");
|
|
584
607
|
}
|
|
585
608
|
}
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Retrieve status information for mezzanine transcoding jobs, aka long running operations (LROs) on the given object.
|
|
613
|
+
*
|
|
614
|
+
* @methodGroup ABR Publishing
|
|
615
|
+
* @namedParams
|
|
616
|
+
* @param {string} libraryId - ID of the library
|
|
617
|
+
* @param {string} objectId - ID of the object
|
|
618
|
+
*
|
|
619
|
+
* @return {Promise<Object>} - LRO status
|
|
620
|
+
*/
|
|
621
|
+
exports.LROStatus = async function({libraryId, objectId}) {
|
|
622
|
+
ValidateParameters({libraryId, objectId});
|
|
623
|
+
|
|
624
|
+
const lroDraft = await this.LRODraftInfo({libraryId, objectId});
|
|
586
625
|
|
|
587
626
|
this.HttpClient.RecordWriteToken(lroDraft.write_token, lroDraft.node);
|
|
588
627
|
|
|
@@ -602,98 +641,80 @@ exports.LROStatus = async function({libraryId, objectId, offeringKey="default"})
|
|
|
602
641
|
* @param {string} libraryId - ID of the mezzanine library
|
|
603
642
|
* @param {string} objectId - ID of the mezzanine object
|
|
604
643
|
* @param {string} writeToken - Write token for the mezzanine object
|
|
605
|
-
* @param {string=} offeringKey=default - The offering to process
|
|
606
644
|
* @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.
|
|
607
645
|
* @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.
|
|
608
646
|
*
|
|
609
647
|
* @return {Promise<Object>} - The finalize response for the mezzanine object, as well as any logs, warnings and errors from the finalization
|
|
610
648
|
*/
|
|
611
|
-
exports.FinalizeABRMezzanine = async function({libraryId, objectId,
|
|
649
|
+
exports.FinalizeABRMezzanine = async function({libraryId, objectId, preFinalizeFn, preFinalizeThrow}) {
|
|
612
650
|
ValidateParameters({libraryId, objectId});
|
|
613
651
|
|
|
614
|
-
const lroDraft = await this.
|
|
652
|
+
const lroDraft = await this.LRODraftInfo({libraryId, objectId});
|
|
653
|
+
|
|
654
|
+
// tell http client what node to contact for this write token
|
|
655
|
+
this.HttpClient.RecordWriteToken(lroDraft.write_token, lroDraft.node);
|
|
656
|
+
|
|
657
|
+
const lastJobOfferingsInfo = await this.ContentObjectMetadata({
|
|
615
658
|
libraryId,
|
|
616
659
|
objectId,
|
|
617
|
-
|
|
660
|
+
writeToken: lroDraft.write_token,
|
|
661
|
+
metadataSubtree: UrlJoin("abr_mezzanine", "offerings")
|
|
618
662
|
});
|
|
619
663
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const httpClient = this.HttpClient;
|
|
625
|
-
let error, result;
|
|
626
|
-
try {
|
|
627
|
-
// Point directly to the node containing the draft
|
|
628
|
-
this.HttpClient = new HttpClient({uris: [lroDraft.node], debug: httpClient.debug});
|
|
664
|
+
const offeringKey = MezJobMainOfferingKey(lastJobOfferingsInfo);
|
|
665
|
+
const masterHash = lastJobOfferingsInfo[offeringKey].prod_master_hash;
|
|
629
666
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
});
|
|
667
|
+
// Authorization token for mezzanine and master
|
|
668
|
+
let authorizationTokens = [
|
|
669
|
+
await this.authClient.AuthorizationToken({libraryId, objectId, update: true}),
|
|
670
|
+
await this.authClient.AuthorizationToken({versionHash: masterHash})
|
|
671
|
+
];
|
|
636
672
|
|
|
637
|
-
|
|
673
|
+
const headers = {
|
|
674
|
+
Authorization: authorizationTokens.map(token => `Bearer ${token}`).join(",")
|
|
675
|
+
};
|
|
638
676
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
677
|
+
const {data, errors, warnings, logs} = await this.CallBitcodeMethod({
|
|
678
|
+
objectId,
|
|
679
|
+
libraryId,
|
|
680
|
+
writeToken: lroDraft.write_token,
|
|
681
|
+
method: UrlJoin("media", "abr_mezzanine", "offerings", offeringKey, "finalize"),
|
|
682
|
+
headers,
|
|
683
|
+
constant: false
|
|
684
|
+
});
|
|
644
685
|
|
|
645
|
-
|
|
646
|
-
|
|
686
|
+
let preFinalizeWarnings = [];
|
|
687
|
+
if(preFinalizeFn) {
|
|
688
|
+
const params = {
|
|
689
|
+
elvClient: this,
|
|
690
|
+
nodeUrl: lroDraft.node,
|
|
691
|
+
writeToken: lroDraft.write_token
|
|
647
692
|
};
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
let preFinalizeWarnings = [];
|
|
659
|
-
if(preFinalizeFn) {
|
|
660
|
-
const params = {
|
|
661
|
-
elvClient: this,
|
|
662
|
-
nodeUrl: lroDraft.node,
|
|
663
|
-
writeToken: lroDraft.write_token
|
|
664
|
-
};
|
|
665
|
-
if(preFinalizeThrow){
|
|
666
|
-
await preFinalizeFn(params);
|
|
667
|
-
} else try {
|
|
668
|
-
await preFinalizeFn(params);
|
|
669
|
-
} catch(e) {
|
|
670
|
-
preFinalizeWarnings = `Error trying to set video stream codec descriptors: ${e}`;
|
|
693
|
+
try {
|
|
694
|
+
await preFinalizeFn(params);
|
|
695
|
+
} catch(err) {
|
|
696
|
+
if(preFinalizeThrow) {
|
|
697
|
+
// noinspection ExceptionCaughtLocallyJS
|
|
698
|
+
throw new Error("Error running preFinalize function", {cause: err});
|
|
699
|
+
} else {
|
|
700
|
+
preFinalizeWarnings = `Error running preFinalize function: ${err}`;
|
|
671
701
|
}
|
|
672
702
|
}
|
|
673
|
-
|
|
674
|
-
const finalizeResponse = await this.FinalizeContentObject({
|
|
675
|
-
libraryId,
|
|
676
|
-
objectId: objectId,
|
|
677
|
-
writeToken: lroDraft.write_token,
|
|
678
|
-
commitMessage: "Finalize ABR mezzanine",
|
|
679
|
-
awaitCommitConfirmation: false
|
|
680
|
-
});
|
|
681
|
-
|
|
682
|
-
result = {
|
|
683
|
-
data,
|
|
684
|
-
logs: logs || [],
|
|
685
|
-
warnings: (warnings || []).concat(preFinalizeWarnings),
|
|
686
|
-
errors: errors || [],
|
|
687
|
-
...finalizeResponse
|
|
688
|
-
};
|
|
689
|
-
} catch(err) {
|
|
690
|
-
error = err;
|
|
691
|
-
} finally {
|
|
692
|
-
// Ensure original http client is restored
|
|
693
|
-
this.HttpClient = httpClient;
|
|
694
703
|
}
|
|
695
704
|
|
|
696
|
-
|
|
705
|
+
const finalizeResponse = await this.FinalizeContentObject({
|
|
706
|
+
libraryId,
|
|
707
|
+
objectId: objectId,
|
|
708
|
+
writeToken: lroDraft.write_token,
|
|
709
|
+
commitMessage: "Finalize ABR mezzanine",
|
|
710
|
+
awaitCommitConfirmation: false
|
|
711
|
+
});
|
|
697
712
|
|
|
698
|
-
return
|
|
713
|
+
return {
|
|
714
|
+
data,
|
|
715
|
+
logs: logs || [],
|
|
716
|
+
warnings: (warnings || []).concat(preFinalizeWarnings),
|
|
717
|
+
errors: errors || [],
|
|
718
|
+
...finalizeResponse
|
|
719
|
+
};
|
|
699
720
|
};
|
|
@@ -104,6 +104,7 @@ exports.Visibility = async function({id, clearCache}) {
|
|
|
104
104
|
*
|
|
105
105
|
* @methodGroup Content Objects
|
|
106
106
|
* @param {string} objectId - The ID of the object
|
|
107
|
+
* @param {boolean=} clearCache - Clear any Visibility info cached by client for this object (forces new Ethereum calls)
|
|
107
108
|
*
|
|
108
109
|
* @return {string} - Key for the permission of the object - Use this to retrieve more details from client.permissionLevels
|
|
109
110
|
*/
|
|
@@ -183,6 +183,7 @@ exports.CreateContentType = async function({name, metadata={}, bitcode}) {
|
|
|
183
183
|
method: "POST",
|
|
184
184
|
path: path
|
|
185
185
|
});
|
|
186
|
+
// extract the url for the node that handled the request
|
|
186
187
|
const nodeUrl = (new URL(rawCreateResponse.url)).origin;
|
|
187
188
|
const createResponse = await this.utils.ResponseToJson(rawCreateResponse);
|
|
188
189
|
|
|
@@ -748,8 +749,8 @@ exports.CreateNonOwnerCap = async function({objectId, libraryId, publicKey, writ
|
|
|
748
749
|
* @param {string} libraryId - ID of the library
|
|
749
750
|
* @param {string} objectId - ID of the object
|
|
750
751
|
* @param {object=} options -
|
|
751
|
-
* meta
|
|
752
|
-
* type
|
|
752
|
+
* @param {object=} options.meta - New metadata for the object - will be merged into existing metadata if specified
|
|
753
|
+
* @param {string=} options.type - New type for the object - Object ID, version hash or name of type
|
|
753
754
|
*
|
|
754
755
|
* @returns {Promise<object>} - Response containing the object ID and write token of the draft, as well as URL of node handling the draft
|
|
755
756
|
*/
|
|
@@ -781,6 +782,7 @@ exports.EditContentObject = async function({libraryId, objectId, options={}}) {
|
|
|
781
782
|
path: path,
|
|
782
783
|
body: options
|
|
783
784
|
});
|
|
785
|
+
// extract the url for the node that handled the request
|
|
784
786
|
const nodeUrl = (new URL(rawEditResponse.url)).origin;
|
|
785
787
|
const editResponse = await this.utils.ResponseToJson(rawEditResponse);
|
|
786
788
|
|
|
@@ -24,9 +24,9 @@ let WalletConfiguration = {
|
|
|
24
24
|
},
|
|
25
25
|
__MARKETPLACE_ORDER: [
|
|
26
26
|
"PREVIEW",
|
|
27
|
+
"masked-singer-brazil-marketplace",
|
|
27
28
|
"de228e92-ed45-4fe0-8e52-658cf366e962",
|
|
28
29
|
"wwe-marketplace-main",
|
|
29
|
-
"realcannonballrun-marketplace",
|
|
30
30
|
"maskverse-marketplace",
|
|
31
31
|
"dolly-marketplace",
|
|
32
32
|
"eluvio-live-marketplace-sonark",
|
|
@@ -36,7 +36,8 @@ let WalletConfiguration = {
|
|
|
36
36
|
"emp-marketplace",
|
|
37
37
|
"microsoft",
|
|
38
38
|
"indieflix-marketplace",
|
|
39
|
-
"angels-airwaves-marketplace"
|
|
39
|
+
"angels-airwaves-marketplace",
|
|
40
|
+
"realcannonballrun-marketplace"
|
|
40
41
|
]
|
|
41
42
|
};
|
|
42
43
|
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// List all access gropus visible to the current private key
|
|
2
|
-
|
|
3
|
-
const Utility = require("./lib/Utility");
|
|
4
|
-
|
|
5
|
-
const Client = require("./lib/concerns/Client");
|
|
6
|
-
const Logger = require("./lib/concerns/Logger");
|
|
7
|
-
|
|
8
|
-
class ListAccessGroups extends Utility {
|
|
9
|
-
blueprint() {
|
|
10
|
-
return {
|
|
11
|
-
concerns: [Logger, Client]
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async body() {
|
|
16
|
-
const logger = this.logger;
|
|
17
|
-
const libList = await this.concerns.Library.list();
|
|
18
|
-
logger.data("libraries", libList);
|
|
19
|
-
|
|
20
|
-
logger.logList(libList);
|
|
21
|
-
if(libList.length === 0) logger.warn("No visible libraries found using supplied private key.");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
header() {
|
|
25
|
-
return "Get list of access groups";
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if(require.main === module) {
|
|
31
|
-
Utility.cmdLineInvoke(ListAccessGroups);
|
|
32
|
-
} else {
|
|
33
|
-
module.exports = ListAccessGroups;
|
|
34
|
-
}
|