@eluvio/elv-client-js 4.2.30 → 4.2.32

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.
@@ -3,6 +3,7 @@ const Ethers = require("ethers");
3
3
  const Utils = require("./Utils");
4
4
  const UrlJoin = require("url-join");
5
5
  const {LogMessage} = require("./LogMessage");
6
+ const {ValidateObject, ValidateParameters} = require("./Validation");
6
7
 
7
8
  /*
8
9
  // -- Contract javascript files built using build/BuildContracts.js
@@ -119,6 +120,10 @@ class AuthorizationClient {
119
120
  }) {
120
121
  if(versionHash) { objectId = this.client.utils.DecodeVersionHash(versionHash).objectId; }
121
122
 
123
+ if(update && !objectId) {
124
+ throw Error("An objectId or versionHash is required when 'update' is true");
125
+ }
126
+
122
127
  const isWalletRequest =
123
128
  objectId &&
124
129
  this.client.signer &&
package/src/ElvClient.js CHANGED
@@ -131,6 +131,7 @@ class ElvClient {
131
131
  * @param {Array<string>} authServiceURIs - A list of full URIs to auth service endpoints
132
132
  * @param {Array<string>} fileServiceURIs - A list of full URIs to file service endpoints
133
133
  * @param {Array<string>=} searchURIs - A list of full URIs to search service endpoints
134
+ * @param {Array<string>=} tenantQueryURIs - A list of full URIs to tenant query service endpoints
134
135
  * @param {number=} ethereumContractTimeout=10 - Number of seconds to wait for contract calls
135
136
  * @param {string=} trustAuthorityId - (OAuth) The ID of the trust authority to use for OAuth authentication
136
137
  * @param {string=} staticToken - Static token that will be used for all authorization in place of normal auth. Also known as an anonymous token containing the space
@@ -152,6 +153,7 @@ class ElvClient {
152
153
  authServiceURIs,
153
154
  fileServiceURIs,
154
155
  searchURIs,
156
+ tenantQueryURIs,
155
157
  ethereumContractTimeout = 10,
156
158
  trustAuthorityId,
157
159
  staticToken,
@@ -179,6 +181,7 @@ class ElvClient {
179
181
  this.fileServiceURIs = fileServiceURIs || fabricURIs;
180
182
  this.ethereumURIs = ethereumURIs;
181
183
  this.searchURIs = searchURIs;
184
+ this.tenantQueryURIs = tenantQueryURIs || [];
182
185
  this.ethereumContractTimeout = ethereumContractTimeout;
183
186
 
184
187
  this.trustAuthorityId = trustAuthorityId;
@@ -262,6 +265,11 @@ class ElvClient {
262
265
 
263
266
  const searchURIs = fabricInfo.network.services.search || [];
264
267
 
268
+ let tenantQueryURIs = fabricInfo.network.services.tenant_query || [];
269
+ if(tenantQueryURIs.find(filterHTTPS)) {
270
+ tenantQueryURIs = tenantQueryURIs.filter(filterHTTPS);
271
+ }
272
+
265
273
  const fabricVersion = Math.max(...(fabricInfo.network.api_versions || [2]));
266
274
 
267
275
  return {
@@ -275,6 +283,7 @@ class ElvClient {
275
283
  fileServiceURIs,
276
284
  kmsURIs: kmsUrls,
277
285
  searchURIs,
286
+ tenantQueryURIs,
278
287
  fabricVersion
279
288
  };
280
289
  } catch(error) {
@@ -378,6 +387,7 @@ class ElvClient {
378
387
  authServiceURIs,
379
388
  fileServiceURIs,
380
389
  searchURIs,
390
+ tenantQueryURIs,
381
391
  fabricVersion
382
392
  } = await ElvClient.Configuration({
383
393
  configUrl,
@@ -395,6 +405,7 @@ class ElvClient {
395
405
  authServiceURIs,
396
406
  fileServiceURIs,
397
407
  searchURIs,
408
+ tenantQueryURIs,
398
409
  ethereumContractTimeout,
399
410
  trustAuthorityId,
400
411
  staticToken,
@@ -427,6 +438,7 @@ class ElvClient {
427
438
  this.AuthHttpClient = new HttpClient({uris: this.authServiceURIs, networkName: this.networkName, debug: this.debug});
428
439
  this.FileServiceHttpClient = new HttpClient({uris: this.fileServiceURIs, networkName: this.networkName, debug: this.debug});
429
440
  this.SearchHttpClient = new HttpClient({uris: this.searchURIs || [], networkName: this.networkName, debug: this.debug});
441
+ this.TenantQueryHttpClient = new HttpClient({uris: this.tenantQueryURIs || [], networkName: this.networkName, debug: this.debug});
430
442
  this.ethClient = new EthClient({client: this, uris: this.ethereumURIs, networkId: this.networkId, debug: this.debug, timeout: this.ethereumContractTimeout});
431
443
 
432
444
  if(!this.signer) {
@@ -483,7 +495,7 @@ class ElvClient {
483
495
  throw Error("Unable to change region: Configuration URL not set");
484
496
  }
485
497
 
486
- const {fabricURIs, ethereumURIs, authServiceURIs, fileServiceURIs, searchURIs} = await ElvClient.Configuration({
498
+ const {fabricURIs, ethereumURIs, authServiceURIs, fileServiceURIs, searchURIs, tenantQueryURIs} = await ElvClient.Configuration({
487
499
  configUrl: this.configUrl,
488
500
  region
489
501
  });
@@ -495,7 +507,8 @@ class ElvClient {
495
507
  ethereumURIs,
496
508
  authServiceURIs,
497
509
  fileServiceURIs,
498
- searchURIs
510
+ searchURIs,
511
+ tenantQueryURIs
499
512
  });
500
513
 
501
514
  return {
@@ -503,7 +516,8 @@ class ElvClient {
503
516
  ethereumURIs,
504
517
  fileServiceURIs,
505
518
  authServiceURIs,
506
- searchURIs
519
+ searchURIs,
520
+ tenantQueryURIs
507
521
  };
508
522
  }
509
523
 
@@ -558,7 +572,8 @@ class ElvClient {
558
572
  ethereumURIs: this.ethereumURIs,
559
573
  authServiceURIs: this.authServiceURIs,
560
574
  fileServiceURIs: this.fileServiceURIs,
561
- searchURIs: this.searchURIs
575
+ searchURIs: this.searchURIs,
576
+ tenantQueryURIs: this.tenantQueryURIs
562
577
  };
563
578
  }
564
579
 
@@ -571,10 +586,11 @@ class ElvClient {
571
586
  * @param {Array<string>=} authServiceURIs - A list of URLs for the auth service, in preference order
572
587
  * @param {Array<string>=} fileServiceURIs - A list of URLs for file service jobs, in preference order
573
588
  * @param {Array<string>=} searchURIs - A list of URLs for the search nodes, in preference order
589
+ * @param {Array<string>=} tenantQueryURIs - A list of URLs for the tenant query nodes, in preference order
574
590
  *
575
591
  * @methodGroup Nodes
576
592
  */
577
- SetNodes({fabricURIs, ethereumURIs, authServiceURIs, fileServiceURIs, searchURIs}) {
593
+ SetNodes({fabricURIs, ethereumURIs, authServiceURIs, fileServiceURIs, searchURIs, tenantQueryURIs}) {
578
594
  if(fabricURIs) {
579
595
  this.fabricURIs = fabricURIs;
580
596
 
@@ -609,6 +625,13 @@ class ElvClient {
609
625
  this.SearchHttpClient.uris = searchURIs;
610
626
  this.SearchHttpClient.uriIndex = 0;
611
627
  }
628
+
629
+ if(tenantQueryURIs) {
630
+ this.tenantQueryURIs = tenantQueryURIs;
631
+
632
+ this.TenantQueryHttpClient.uris = tenantQueryURIs;
633
+ this.TenantQueryHttpClient.uriIndex = 0;
634
+ }
612
635
  }
613
636
 
614
637
  /**
@@ -316,6 +316,9 @@ class FrameClient {
316
316
  "AddAccessGroupMember",
317
317
  "AddContentLibraryGroup",
318
318
  "AddContentObjectGroupPermission",
319
+ "AddContentObjectGroups",
320
+ "AddContentObjectQueryFields",
321
+ "AddContentObjectTags",
319
322
  "AddLibraryContentType",
320
323
  "AssetMetadata",
321
324
  "AuditContentObject",
@@ -341,10 +344,13 @@ class FrameClient {
341
344
  "ContentObjectAccessComplete",
342
345
  "ContentObjectGraph",
343
346
  "ContentObjectGroupPermissions",
347
+ "ContentObjectGroups",
344
348
  "ContentObjectImageUrl",
345
349
  "ContentObjectLibraryId",
346
350
  "ContentObjectMetadata",
347
351
  "ContentObjectOwner",
352
+ "ContentObjectQueryFields",
353
+ "ContentObjectTags",
348
354
  "ContentObjectTenantId",
349
355
  "ContentObjectVersions",
350
356
  "ContentObjects",
@@ -364,6 +370,7 @@ class FrameClient {
364
370
  "CreateAccessGroup",
365
371
  "CreateAndFinalizeContentObject",
366
372
  "CreateAuthorizationToken",
373
+ "CreateContentGroup",
367
374
  "CreateContentLibrary",
368
375
  "CreateContentObject",
369
376
  "CreateContentType",
@@ -451,6 +458,7 @@ class FrameClient {
451
458
  "OutputsCreate",
452
459
  "OutputsDelete",
453
460
  "OutputsDeleteBatch",
461
+ "OutputsHop",
454
462
  "OutputsList",
455
463
  "OutputsListItem",
456
464
  "OutputsModify",
@@ -474,6 +482,9 @@ class FrameClient {
474
482
  "RemoveAccessGroupMember",
475
483
  "RemoveContentObjectGroupPermission",
476
484
  "RemoveContentLibraryGroup",
485
+ "RemoveContentObjectGroups",
486
+ "RemoveContentObjectQueryFields",
487
+ "RemoveContentObjectTags",
477
488
  "RemoveLibraryContentType",
478
489
  "Rep",
479
490
  "ReplaceContractMetadata",
@@ -489,7 +500,10 @@ class FrameClient {
489
500
  "SetAuthContext",
490
501
  "SetAuthPolicy",
491
502
  "SetContentLibraryImage",
503
+ "SetContentObjectGroups",
492
504
  "SetContentObjectImage",
505
+ "SetContentObjectQueryFields",
506
+ "SetContentObjectTags",
493
507
  "SetCustomContentContract",
494
508
  "SetGroupPermission",
495
509
  "SetNodes",
@@ -525,6 +539,7 @@ class FrameClient {
525
539
  "StreamSaveConfigProfile",
526
540
  "StreamSetOfferingAndDRM",
527
541
  "StreamSiteSettings",
542
+ "StreamRestartRecording",
528
543
  "StreamStartRecording",
529
544
  "StreamStartOrStopOrReset",
530
545
  "StreamStatus",
@@ -532,6 +547,7 @@ class FrameClient {
532
547
  "StreamUnassignProfile",
533
548
  "StreamUpdateConfig",
534
549
  "SuspendNTPInstance",
550
+ "TenantContent",
535
551
  "TenantContractId",
536
552
  "TenantId",
537
553
  "UnlinkAccessGroupFromOauth",
@@ -3305,3 +3305,195 @@ exports.QParts = async function({libraryId, objectId, partHash, format="blob"})
3305
3305
  })
3306
3306
  );
3307
3307
  };
3308
+
3309
+ /* Content groups */
3310
+
3311
+ /**
3312
+ * Retrieve a content object property ("tags", "groups" or "query_fields").
3313
+ *
3314
+ * @private
3315
+ */
3316
+ const ContentObjectProperty = async function({objectId, versionHash, writeToken, property}) {
3317
+ if(writeToken && versionHash) {
3318
+ throw Error(`Cannot specify writeToken and versionHash at same time (token:${writeToken}, hash:${versionHash})`);
3319
+ }
3320
+
3321
+ if(writeToken) {
3322
+ ValidateWriteToken(writeToken);
3323
+ } else if(versionHash) {
3324
+ ValidateVersion(versionHash);
3325
+ } else {
3326
+ ValidateObject(objectId);
3327
+ }
3328
+
3329
+ if(writeToken) {
3330
+ objectId = this.utils.DecodeWriteToken(writeToken).objectId;
3331
+ } else if(versionHash) {
3332
+ objectId = this.utils.DecodeVersionHash(versionHash).objectId;
3333
+ }
3334
+
3335
+ const token = await this.GenerateStateChannelToken({
3336
+ objectId,
3337
+ versionHash
3338
+ });
3339
+
3340
+ return this.HttpClient.RequestJsonBody({
3341
+ headers: {Authorization: `Bearer ${token}`},
3342
+ method: "GET",
3343
+ path: UrlJoin("q", writeToken || versionHash || objectId, property)
3344
+ });
3345
+ };
3346
+
3347
+ /**
3348
+ * Get content tags
3349
+ *
3350
+ * @methodGroup Content Groups
3351
+ * @namedParams
3352
+ * @param {string=} objectId - ID of the object
3353
+ * @param {string=} versionHash - Hash of the object version
3354
+ * @param {string=} writeToken - Write token of the draft
3355
+ *
3356
+ * @returns {Promise<Array<string>>} - The content object tags
3357
+ */
3358
+ exports.ContentObjectTags = async function({objectId, versionHash, writeToken}) {
3359
+ this.Log(`Retrieving content tags: ${writeToken || versionHash || objectId}`);
3360
+
3361
+ return ContentObjectProperty.call(this, {objectId, versionHash, writeToken, property: "tags"});
3362
+ };
3363
+
3364
+ /**
3365
+ * Get content query fields
3366
+ *
3367
+ * @methodGroup Content Groups
3368
+ * @namedParams
3369
+ * @param {string=} objectId - ID of the object
3370
+ * @param {string=} versionHash - Hash of the object version
3371
+ * @param {string=} writeToken - Write token of the draft
3372
+ *
3373
+ * @returns {Promise<Object<string, string>>} - The content object query fields
3374
+ */
3375
+ exports.ContentObjectQueryFields = async function({objectId, versionHash, writeToken}) {
3376
+ this.Log(`Retrieving content query fields: ${writeToken || versionHash || objectId}`);
3377
+
3378
+ return ContentObjectProperty.call(this, {objectId, versionHash, writeToken, property: "query_fields"});
3379
+ };
3380
+
3381
+ /**
3382
+ * Get the groups a content object belongs to
3383
+ *
3384
+ * @methodGroup Content Groups
3385
+ * @namedParams
3386
+ * @param {string=} objectId - ID of the object
3387
+ * @param {string=} versionHash - Hash of the object version
3388
+ * @param {string=} writeToken - Write token of the draft
3389
+ *
3390
+ * @returns {Promise<Array<string>>} - The content groups the object belongs to
3391
+ */
3392
+ exports.ContentObjectGroups = async function({objectId, versionHash, writeToken}) {
3393
+ this.Log(`Retrieving content object groups: ${writeToken || versionHash || objectId}`);
3394
+
3395
+ return ContentObjectProperty.call(this, {objectId, versionHash, writeToken, property: "groups"});
3396
+ };
3397
+
3398
+ /**
3399
+ * Query tenant content.
3400
+ *
3401
+ * The response is based on the fabric index and can use groups, query_fields and tags for filtering and sorting.
3402
+ * The response may include metadata fields as specified in select/remove.
3403
+ *
3404
+ * @methodGroup Tenants
3405
+ * @namedParams
3406
+ * @param {string=} tenantId - ID of the tenant to query. Defaults to the tenant of the current user
3407
+ * @param {Array<string>=} filter - Filter expressions of the form `<field>:<comparator>:<value>`, e.g.
3408
+ * `tag:eq:elv:media:live_stream` or `qf.date:ge:2026-09-01`. Multiple expressions are combined with AND.
3409
+ *
3410
+ * Comparators: `::` or `:co:` contains, `:nc:` not contains, `:eq:` equals, `:ne:` not equals,
3411
+ * `:lt:` less than, `:le:` less than or equal, `:gt:` greater than, `:ge:` greater than or equal.
3412
+ * Comparisons are lexicographic string comparisons. `:sw:` and `:ew:` are not supported here.
3413
+ *
3414
+ * Reserved fields:
3415
+ * - `tag` - matches when *any one* of the object's tags satisfies the expression
3416
+ * - `group` - ID of a content group the object belongs to
3417
+ * - `unique_group.id`, `unique_group.type`
3418
+ * - `lib`, `library` - library ID. Supports `:eq:` only
3419
+ * - `created_block_time` - when the latest version was created. Comparison operators only
3420
+ *
3421
+ * Any other field name is treated as a query field name. Prefix with `qf.` to filter on a query field
3422
+ * whose name collides with a reserved one, e.g. `qf.tag:eq:x`. `created_block_number` is no longer
3423
+ * supported and is rejected.
3424
+ *
3425
+ * @param {string=} sortBy - Name of a query field to sort by. This is a query field name, not a metadata path,
3426
+ * and only one may be given. When omitted, results are sorted by the time the latest version was created.
3427
+ *
3428
+ * Note that sorting by a query field also restricts the results to objects that have that field
3429
+ * @param {boolean=} sortDescending=false - Sort in descending order
3430
+ * @param {(Array<string> | string)=} include - Index data to return alongside each result. Tokens:
3431
+ * `tags`, `groups`, `uniquegroups`, `fields` (query fields), `type`, `name`, `all`, `none`. May be given
3432
+ * as an array or as a comma-separated string. The Fabric defaults to query fields only; `none` suppresses
3433
+ * all index data, including those
3434
+ * @param {Array<string>=} select - Metadata subtree paths to include in each result. If omitted, no metadata
3435
+ * is retrieved at all and each result contains only its IDs and index data
3436
+ * @param {Array<string>=} remove - Metadata subtree paths to omit from each result. Only has an effect when
3437
+ * `select` is also specified
3438
+ * @param {number=} start - Index of the first object to retrieve. Defaults to the first
3439
+ * @param {number=} limit - Maximum number of objects to return. Defaults to 100, capped at 998
3440
+ * @param {string=} authorizationToken - Additional authorization token for the request
3441
+ *
3442
+ * @returns {Promise<Object>} - The query result, including `versions` and `paging` information. One entry per
3443
+ * content object - the index holds only the latest version - carrying `id`, `hash`, `qlib_id`,
3444
+ * `last_modified_at` and whatever `include` selects - by default the object's query fields
3445
+ */
3446
+ exports.TenantContent = async function({
3447
+ tenantId,
3448
+ filter=[],
3449
+ sortBy,
3450
+ sortDescending,
3451
+ include,
3452
+ select=[],
3453
+ remove=[],
3454
+ start,
3455
+ limit,
3456
+ authorizationToken
3457
+ }) {
3458
+ tenantId = tenantId || await this.userProfileClient.TenantContractId();
3459
+ ValidatePresence("tenantId", tenantId);
3460
+
3461
+ this.Log(`Querying content for tenant ${tenantId}`);
3462
+
3463
+ const path = UrlJoin("tenants", tenantId, "q", "query");
3464
+
3465
+ // Tenant query works best with plain token - it allows it to return objecte metadata fields.
3466
+ const token = authorizationToken || await this.CreatePlainToken();
3467
+
3468
+ const httpClient =
3469
+ this.TenantQueryHttpClient && this.TenantQueryHttpClient.uris && this.TenantQueryHttpClient.uris.length > 0
3470
+ ? this.TenantQueryHttpClient
3471
+ : this.HttpClient;
3472
+
3473
+ const queryParams = {
3474
+ filter,
3475
+ sort_by: sortBy,
3476
+ sort_descending: sortDescending,
3477
+ include,
3478
+ select,
3479
+ remove,
3480
+ start,
3481
+ limit
3482
+ };
3483
+
3484
+ Object.keys(queryParams).forEach(key => {
3485
+ const value = queryParams[key];
3486
+ if(value === undefined || (Array.isArray(value) && value.length === 0)) {
3487
+ delete queryParams[key];
3488
+ }
3489
+ });
3490
+
3491
+ return this.utils.ResponseToJson(
3492
+ httpClient.Request({
3493
+ headers: { "Authorization": `Bearer ${token}` },
3494
+ method: "GET",
3495
+ path: path,
3496
+ queryParams
3497
+ })
3498
+ );
3499
+ };