@mediaviz/sdk 1.3.0 → 1.4.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/dist/sdk.cjs +115 -48
- package/dist/sdk.d.cts +8 -6
- package/dist/sdk.d.ts +8 -6
- package/dist/sdk.esm.d.ts +8 -6
- package/dist/sdk.esm.js +115 -48
- package/dist/sdk.umd.js +115 -48
- package/package.json +1 -1
package/dist/sdk.cjs
CHANGED
|
@@ -91,8 +91,15 @@ function requireErrors () {
|
|
|
91
91
|
* @returns {OAuthError}
|
|
92
92
|
*/
|
|
93
93
|
static fromResponse(status, body) {
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
// FastAPI wraps HTTPException payloads in a `detail` envelope, so an OAuth
|
|
95
|
+
// error arrives as { detail: { error, error_description } }. Unwrap it
|
|
96
|
+
// before falling back, or every server-side OAuth error reads as server_error.
|
|
97
|
+
const payload =
|
|
98
|
+
body && typeof body === 'object' && typeof body.error !== 'string' && body.detail
|
|
99
|
+
? body.detail
|
|
100
|
+
: body;
|
|
101
|
+
if (payload && typeof payload === 'object' && typeof payload.error === 'string') {
|
|
102
|
+
return new OAuthError(payload.error, payload.error_description ?? '', status, body);
|
|
96
103
|
}
|
|
97
104
|
return new OAuthError('server_error', 'Unexpected server response', status, body);
|
|
98
105
|
}
|
|
@@ -173,6 +180,9 @@ function requireClient () {
|
|
|
173
180
|
const { postForm, postJson } = requireHttp();
|
|
174
181
|
const { OAuthError } = requireErrors();
|
|
175
182
|
|
|
183
|
+
// In-flight dedup key for client_credentials re-mints (no refresh_token to key on).
|
|
184
|
+
const CLIENT_CREDENTIALS_KEY = Symbol('client_credentials');
|
|
185
|
+
|
|
176
186
|
class OAuthClient {
|
|
177
187
|
/**
|
|
178
188
|
* @param {import('./types').OAuthClientConfig} config
|
|
@@ -188,12 +198,22 @@ function requireClient () {
|
|
|
188
198
|
*/
|
|
189
199
|
static async registerClient(params) {
|
|
190
200
|
const baseUrl = params.baseUrl.replace(/\/+$/, '');
|
|
191
|
-
|
|
201
|
+
const body = {
|
|
192
202
|
client_name: params.clientName,
|
|
193
203
|
client_type: params.clientType,
|
|
194
204
|
redirect_uris: params.redirectUris,
|
|
195
205
|
is_first_party: params.isFirstParty,
|
|
196
|
-
}
|
|
206
|
+
};
|
|
207
|
+
// grant_types/company_id are required to provision a confidential
|
|
208
|
+
// client_credentials (machine-to-machine) client; omitted bodies keep the
|
|
209
|
+
// legacy public/authorization_code shape. The endpoint is admin-gated, so a
|
|
210
|
+
// bearer token must be supplied for the request to authenticate.
|
|
211
|
+
if (params.grantTypes !== undefined) body.grant_types = params.grantTypes;
|
|
212
|
+
if (params.companyId !== undefined) body.company_id = params.companyId;
|
|
213
|
+
const headers = params.accessToken
|
|
214
|
+
? { Authorization: `Bearer ${params.accessToken}` }
|
|
215
|
+
: {};
|
|
216
|
+
return postJson(`${baseUrl}/oauth/clients`, body, headers);
|
|
197
217
|
}
|
|
198
218
|
|
|
199
219
|
/**
|
|
@@ -311,15 +331,27 @@ function requireClient () {
|
|
|
311
331
|
return { data: json, updatedTokens: null };
|
|
312
332
|
}
|
|
313
333
|
|
|
314
|
-
// 401:
|
|
315
|
-
//
|
|
316
|
-
//
|
|
317
|
-
|
|
334
|
+
// 401: re-authenticate, then retry once.
|
|
335
|
+
// - User sessions (refresh_token present) use the refresh_token grant. The server
|
|
336
|
+
// rotates the token one-time, so concurrent callers share one in-flight call
|
|
337
|
+
// (a second rotation would get invalid_grant).
|
|
338
|
+
// - Machine-to-machine sessions (no refresh_token, client_secret configured) re-mint
|
|
339
|
+
// via client_credentials, which issues no refresh token to rotate (RFC 6749 §4.4).
|
|
340
|
+
// - With neither a refresh_token nor a client_secret there is nothing to retry with.
|
|
341
|
+
if (!refreshToken && !this._config.clientSecret) {
|
|
342
|
+
throw OAuthError.fromResponse(401, await firstResponse.json());
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const reauthKey = refreshToken || CLIENT_CREDENTIALS_KEY;
|
|
346
|
+
let pending = this._inFlightRefreshes.get(reauthKey);
|
|
318
347
|
if (!pending) {
|
|
319
|
-
|
|
320
|
-
this.
|
|
348
|
+
const reauth = refreshToken
|
|
349
|
+
? this.refreshAccessToken(refreshToken)
|
|
350
|
+
: this.getClientCredentialsToken();
|
|
351
|
+
pending = reauth.finally(() => {
|
|
352
|
+
this._inFlightRefreshes.delete(reauthKey);
|
|
321
353
|
});
|
|
322
|
-
this._inFlightRefreshes.set(
|
|
354
|
+
this._inFlightRefreshes.set(reauthKey, pending);
|
|
323
355
|
}
|
|
324
356
|
const newTokens = await pending;
|
|
325
357
|
|
|
@@ -401,6 +433,9 @@ function requireTypes () {
|
|
|
401
433
|
* @property {string} clientType - "public" or "confidential"
|
|
402
434
|
* @property {string[]} redirectUris - HTTPS redirect URIs
|
|
403
435
|
* @property {boolean} isFirstParty - Whether this is a first-party client
|
|
436
|
+
* @property {string[]} [grantTypes] - Allowed grants, e.g. ["client_credentials"]; required for machine-to-machine clients
|
|
437
|
+
* @property {number} [companyId] - Company to bind the client to; required for the client_credentials grant
|
|
438
|
+
* @property {string} [accessToken] - Admin bearer token; the registration endpoint is authorization-gated
|
|
404
439
|
*/
|
|
405
440
|
|
|
406
441
|
/**
|
|
@@ -912,6 +947,17 @@ class Keywords {
|
|
|
912
947
|
return data;
|
|
913
948
|
}
|
|
914
949
|
|
|
950
|
+
async addCompanyToKeywordFilteringList(keywordListId, { companyId } = {}) {
|
|
951
|
+
this._ctx.requireTokens();
|
|
952
|
+
let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
|
|
953
|
+
const query = new URLSearchParams();
|
|
954
|
+
if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
|
|
955
|
+
const qs = query.toString();
|
|
956
|
+
if (qs) path += '?' + qs;
|
|
957
|
+
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken);
|
|
958
|
+
return data;
|
|
959
|
+
}
|
|
960
|
+
|
|
915
961
|
async requestKeywordListExport(keywordListId) {
|
|
916
962
|
this._ctx.requireTokens();
|
|
917
963
|
const path = `/api/v1/keyword/export/${encodeURIComponent(keywordListId)}`;
|
|
@@ -944,6 +990,17 @@ class Keywords {
|
|
|
944
990
|
return data;
|
|
945
991
|
}
|
|
946
992
|
|
|
993
|
+
async removeCompanyFromKeywordFilteringList(keywordListId, { companyId } = {}) {
|
|
994
|
+
this._ctx.requireTokens();
|
|
995
|
+
let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
|
|
996
|
+
const query = new URLSearchParams();
|
|
997
|
+
if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
|
|
998
|
+
const qs = query.toString();
|
|
999
|
+
if (qs) path += '?' + qs;
|
|
1000
|
+
const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1001
|
+
return data;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
947
1004
|
async deleteKeywordFilteringListById(keywordListId) {
|
|
948
1005
|
this._ctx.requireTokens();
|
|
949
1006
|
const path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}`;
|
|
@@ -1103,6 +1160,17 @@ class Person {
|
|
|
1103
1160
|
return data;
|
|
1104
1161
|
}
|
|
1105
1162
|
|
|
1163
|
+
async getPhotoIdsByPersons(projectTableName, { personId } = {}) {
|
|
1164
|
+
this._ctx.requireTokens();
|
|
1165
|
+
let path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photos`;
|
|
1166
|
+
const query = new URLSearchParams();
|
|
1167
|
+
if (personId !== undefined) (Array.isArray(personId) ? personId : [personId]).forEach(v => query.append('person_id', v));
|
|
1168
|
+
const qs = query.toString();
|
|
1169
|
+
if (qs) path += '?' + qs;
|
|
1170
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1171
|
+
return data;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1106
1174
|
async getAllPersonsFromPhoto(projectTableName, photoId) {
|
|
1107
1175
|
this._ctx.requireTokens();
|
|
1108
1176
|
const path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photo/${encodeURIComponent(photoId)}`;
|
|
@@ -1403,13 +1471,6 @@ class Photos {
|
|
|
1403
1471
|
return data;
|
|
1404
1472
|
}
|
|
1405
1473
|
|
|
1406
|
-
async getProjectThumbnail(tableName) {
|
|
1407
|
-
this._ctx.requireTokens();
|
|
1408
|
-
const path = `/api/v1/photos_project/${encodeURIComponent(tableName)}`;
|
|
1409
|
-
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1410
|
-
return data;
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
1474
|
async updatePhotoInProject({ tableName, photoId, photoData } = {}) {
|
|
1414
1475
|
this._ctx.requireTokens();
|
|
1415
1476
|
let path = `/api/v1/photos_update`;
|
|
@@ -1472,50 +1533,56 @@ class Search {
|
|
|
1472
1533
|
return data;
|
|
1473
1534
|
}
|
|
1474
1535
|
|
|
1475
|
-
async
|
|
1536
|
+
async searchProjectPhotosParametrized(projectTableName, { andSearchText, orSearchText, notSearchText, city, country, state, albums, dateMin, dateMax, dateNullAnd, dateNullOr, ascOrDesc, bestOfSimilarSetsOnly, splitByTier, lastId, limit } = {}) {
|
|
1476
1537
|
this._ctx.requireTokens();
|
|
1477
|
-
|
|
1478
|
-
const
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1538
|
+
let path = `/api/v1/search/parametrized/${encodeURIComponent(projectTableName)}/`;
|
|
1539
|
+
const query = new URLSearchParams();
|
|
1540
|
+
if (andSearchText !== undefined) (Array.isArray(andSearchText) ? andSearchText : [andSearchText]).forEach(v => query.append('and_search_text', v));
|
|
1541
|
+
if (orSearchText !== undefined) (Array.isArray(orSearchText) ? orSearchText : [orSearchText]).forEach(v => query.append('or_search_text', v));
|
|
1542
|
+
if (notSearchText !== undefined) (Array.isArray(notSearchText) ? notSearchText : [notSearchText]).forEach(v => query.append('not_search_text', v));
|
|
1543
|
+
if (city !== undefined) (Array.isArray(city) ? city : [city]).forEach(v => query.append('city', v));
|
|
1544
|
+
if (country !== undefined) (Array.isArray(country) ? country : [country]).forEach(v => query.append('country', v));
|
|
1545
|
+
if (state !== undefined) (Array.isArray(state) ? state : [state]).forEach(v => query.append('state', v));
|
|
1546
|
+
if (albums !== undefined) (Array.isArray(albums) ? albums : [albums]).forEach(v => query.append('albums', v));
|
|
1547
|
+
if (dateMin !== undefined) (Array.isArray(dateMin) ? dateMin : [dateMin]).forEach(v => query.append('date_min', v));
|
|
1548
|
+
if (dateMax !== undefined) (Array.isArray(dateMax) ? dateMax : [dateMax]).forEach(v => query.append('date_max', v));
|
|
1549
|
+
if (dateNullAnd !== undefined) (Array.isArray(dateNullAnd) ? dateNullAnd : [dateNullAnd]).forEach(v => query.append('date_null_and', v));
|
|
1550
|
+
if (dateNullOr !== undefined) (Array.isArray(dateNullOr) ? dateNullOr : [dateNullOr]).forEach(v => query.append('date_null_or', v));
|
|
1551
|
+
if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
|
|
1552
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1553
|
+
if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
|
|
1554
|
+
if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
|
|
1555
|
+
if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
|
|
1556
|
+
const qs = query.toString();
|
|
1557
|
+
if (qs) path += '?' + qs;
|
|
1558
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1494
1559
|
return data;
|
|
1495
1560
|
}
|
|
1496
1561
|
|
|
1497
|
-
async
|
|
1562
|
+
async recallProjectPhotoIds(projectTableName, { ascOrDesc, bestOfSimilarSetsOnly, lastId, limit } = {}) {
|
|
1498
1563
|
this._ctx.requireTokens();
|
|
1499
|
-
let path = `/api/v1/search/
|
|
1564
|
+
let path = `/api/v1/search/recall/${encodeURIComponent(projectTableName)}/`;
|
|
1500
1565
|
const query = new URLSearchParams();
|
|
1501
|
-
if (
|
|
1502
|
-
if (
|
|
1566
|
+
if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
|
|
1567
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1568
|
+
if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
|
|
1569
|
+
if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
|
|
1503
1570
|
const qs = query.toString();
|
|
1504
1571
|
if (qs) path += '?' + qs;
|
|
1505
|
-
const
|
|
1506
|
-
search_text: searchText,
|
|
1507
|
-
size: size,
|
|
1508
|
-
});
|
|
1509
|
-
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
|
|
1572
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1510
1573
|
return data;
|
|
1511
1574
|
}
|
|
1512
1575
|
|
|
1513
|
-
async
|
|
1576
|
+
async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined, { minCosine, labelMinCosine, labelTopK, labelDelta, bestOfSimilarSetsOnly, splitByTier } = {}) {
|
|
1514
1577
|
this._ctx.requireTokens();
|
|
1515
|
-
let path = `/api/v1/search/
|
|
1578
|
+
let path = `/api/v1/search/natural_language/${encodeURIComponent(projectTableName)}/`;
|
|
1516
1579
|
const query = new URLSearchParams();
|
|
1517
|
-
if (blend !== undefined) (Array.isArray(blend) ? blend : [blend]).forEach(v => query.append('blend', v));
|
|
1518
1580
|
if (minCosine !== undefined) (Array.isArray(minCosine) ? minCosine : [minCosine]).forEach(v => query.append('min_cosine', v));
|
|
1581
|
+
if (labelMinCosine !== undefined) (Array.isArray(labelMinCosine) ? labelMinCosine : [labelMinCosine]).forEach(v => query.append('label_min_cosine', v));
|
|
1582
|
+
if (labelTopK !== undefined) (Array.isArray(labelTopK) ? labelTopK : [labelTopK]).forEach(v => query.append('label_top_k', v));
|
|
1583
|
+
if (labelDelta !== undefined) (Array.isArray(labelDelta) ? labelDelta : [labelDelta]).forEach(v => query.append('label_delta', v));
|
|
1584
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1585
|
+
if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
|
|
1519
1586
|
const qs = query.toString();
|
|
1520
1587
|
if (qs) path += '?' + qs;
|
|
1521
1588
|
const body = stripUndef$1({
|
package/dist/sdk.d.cts
CHANGED
|
@@ -85,10 +85,11 @@ export interface CompanyDisplay {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export interface CompanyKeywordListOverview {
|
|
88
|
+
id: number;
|
|
88
89
|
name: string;
|
|
89
90
|
active_keywords: number;
|
|
90
91
|
excluded_keywords: number;
|
|
91
|
-
|
|
92
|
+
project_ids?: string[];
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
export interface CreditBalanceOutput {
|
|
@@ -376,10 +377,12 @@ export class Keywords {
|
|
|
376
377
|
updateKeywordFilteringListLabels(keywordListId: number, listKeywordsToInclude: number[], listKeywordsToExclude: number[]): Promise<KeywordListDisplayWithList>;
|
|
377
378
|
updateKeywordFilteringListDetails(keywordListId: number, body?: { name?: string, projectList?: string[] }): Promise<KeywordListDisplayWithProjects>;
|
|
378
379
|
addProjectsToKeywordFilteringList(keywordListId: number, options?: { projectIds?: any }): Promise<Record<string, any>>;
|
|
380
|
+
addCompanyToKeywordFilteringList(keywordListId: number, options?: { companyId?: number | number[] }): Promise<Record<string, any>>;
|
|
379
381
|
requestKeywordListExport(keywordListId: number): Promise<Record<string, any>>;
|
|
380
382
|
requestKeywordListExportStatus(keywordListId: number): Promise<AnalysisStatusResponse>;
|
|
381
383
|
getKeywordsAndIds(): Promise<any>;
|
|
382
384
|
removeProjectsFromKeywordFilteringList(keywordListId: number, options?: { projectIds?: any }): Promise<Record<string, any>>;
|
|
385
|
+
removeCompanyFromKeywordFilteringList(keywordListId: number, options?: { companyId?: number | number[] }): Promise<Record<string, any>>;
|
|
383
386
|
deleteKeywordFilteringListById(keywordListId: number): Promise<Record<string, any>>;
|
|
384
387
|
}
|
|
385
388
|
|
|
@@ -408,6 +411,7 @@ export class Person {
|
|
|
408
411
|
splitPersons(projectTableName: string, id: number, options?: { newName?: string | string[], destinationPersonId?: string | string[] }): Promise<PhotoPersonLight>;
|
|
409
412
|
getAllPersonsFromProject(projectTableName: string): Promise<PhotoPersonWithS3Link[]>;
|
|
410
413
|
getAllPersonNamesFromProject(projectTableName: string): Promise<any>;
|
|
414
|
+
getPhotoIdsByPersons(projectTableName: string, options?: { personId?: string[] | string[][] }): Promise<any>;
|
|
411
415
|
getAllPersonsFromPhoto(projectTableName: string, photoId: number): Promise<PhotoPersonLight[]>;
|
|
412
416
|
}
|
|
413
417
|
|
|
@@ -422,7 +426,6 @@ export class Photos {
|
|
|
422
426
|
getProjectPhotoIdsByTableName(tableName: string, options?: { ascOrDesc?: string | string[], lastId?: number | number[], limit?: number | number[], includeAll?: boolean | boolean[], startDate?: string | string[], endDate?: string | string[], noDateTaken?: boolean | boolean[] }): Promise<any>;
|
|
423
427
|
getRankedProjectPhotoIdsByTableName(tableName: string, options?: { ascOrDesc?: string | string[], lastId?: number | number[], limit?: number | number[], startDate?: string | string[], endDate?: string | string[], noDateTaken?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
424
428
|
getProjectMonthYearsWithPhotos(tableName: string): Promise<any>;
|
|
425
|
-
getProjectThumbnail(tableName: string): Promise<any>;
|
|
426
429
|
updatePhotoInProject(options?: { tableName?: string | string[], photoId?: number | number[], photoData?: Record<string, any> | Record<string, any>[] }): Promise<PhotoDisplay>;
|
|
427
430
|
updatePhotoRanking(tableName: string, photoId: number, newCategory: string): Promise<Record<string, any>>;
|
|
428
431
|
deletePhotoFromProject(tableName: string, options?: { photoIds?: any }): Promise<Record<string, any>>;
|
|
@@ -452,10 +455,9 @@ export class Projects {
|
|
|
452
455
|
|
|
453
456
|
export class Search {
|
|
454
457
|
searchProjectPhotos(projectTableName: string, options?: { andParams?: any, andStringParams?: any, orParams?: any, orStringParams?: any, notParams?: any, notStringParams?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], dateOrder?: string | string[], customAlbumId?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], curatedAlbumId?: number | number[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
searchProjectPhotosNaturalLanguageAuto(projectTableName: string, searchText: string, size?: number, options?: { blend?: string | string[], minCosine?: number | number[] }): Promise<any>;
|
|
458
|
+
searchProjectPhotosParametrized(projectTableName: string, options?: { andSearchText?: any, orSearchText?: any, notSearchText?: any, city?: any, country?: any, state?: any, albums?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], ascOrDesc?: string | string[], bestOfSimilarSetsOnly?: boolean | boolean[], splitByTier?: boolean | boolean[], lastId?: number | number[], limit?: number | number[] }): Promise<Record<string, any>>;
|
|
459
|
+
recallProjectPhotoIds(projectTableName: string, options?: { ascOrDesc?: string | string[], bestOfSimilarSetsOnly?: boolean | boolean[], lastId?: number | number[], limit?: number | number[] }): Promise<any>;
|
|
460
|
+
searchProjectPhotosNaturalLanguage(projectTableName: string, searchText: string, size?: number, options?: { minCosine?: number | number[], labelMinCosine?: number | number[], labelTopK?: number | number[], labelDelta?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
459
461
|
getProjectSavedSearches(projectTableName: string): Promise<any>;
|
|
460
462
|
getSavedSearchById(searchId: number): Promise<SearchDisplay>;
|
|
461
463
|
saveProjectPhotosSearch(projectTableName: string, options?: { searchName?: string | string[], andParams?: any, andStringParams?: any, orParams?: any, orStringParams?: any, notParams?: any, notStringParams?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], dateOrder?: string | string[], customAlbumId?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], curatedAlbumId?: number | number[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
package/dist/sdk.d.ts
CHANGED
|
@@ -85,10 +85,11 @@ export interface CompanyDisplay {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export interface CompanyKeywordListOverview {
|
|
88
|
+
id: number;
|
|
88
89
|
name: string;
|
|
89
90
|
active_keywords: number;
|
|
90
91
|
excluded_keywords: number;
|
|
91
|
-
|
|
92
|
+
project_ids?: string[];
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
export interface CreditBalanceOutput {
|
|
@@ -376,10 +377,12 @@ export class Keywords {
|
|
|
376
377
|
updateKeywordFilteringListLabels(keywordListId: number, listKeywordsToInclude: number[], listKeywordsToExclude: number[]): Promise<KeywordListDisplayWithList>;
|
|
377
378
|
updateKeywordFilteringListDetails(keywordListId: number, body?: { name?: string, projectList?: string[] }): Promise<KeywordListDisplayWithProjects>;
|
|
378
379
|
addProjectsToKeywordFilteringList(keywordListId: number, options?: { projectIds?: any }): Promise<Record<string, any>>;
|
|
380
|
+
addCompanyToKeywordFilteringList(keywordListId: number, options?: { companyId?: number | number[] }): Promise<Record<string, any>>;
|
|
379
381
|
requestKeywordListExport(keywordListId: number): Promise<Record<string, any>>;
|
|
380
382
|
requestKeywordListExportStatus(keywordListId: number): Promise<AnalysisStatusResponse>;
|
|
381
383
|
getKeywordsAndIds(): Promise<any>;
|
|
382
384
|
removeProjectsFromKeywordFilteringList(keywordListId: number, options?: { projectIds?: any }): Promise<Record<string, any>>;
|
|
385
|
+
removeCompanyFromKeywordFilteringList(keywordListId: number, options?: { companyId?: number | number[] }): Promise<Record<string, any>>;
|
|
383
386
|
deleteKeywordFilteringListById(keywordListId: number): Promise<Record<string, any>>;
|
|
384
387
|
}
|
|
385
388
|
|
|
@@ -408,6 +411,7 @@ export class Person {
|
|
|
408
411
|
splitPersons(projectTableName: string, id: number, options?: { newName?: string | string[], destinationPersonId?: string | string[] }): Promise<PhotoPersonLight>;
|
|
409
412
|
getAllPersonsFromProject(projectTableName: string): Promise<PhotoPersonWithS3Link[]>;
|
|
410
413
|
getAllPersonNamesFromProject(projectTableName: string): Promise<any>;
|
|
414
|
+
getPhotoIdsByPersons(projectTableName: string, options?: { personId?: string[] | string[][] }): Promise<any>;
|
|
411
415
|
getAllPersonsFromPhoto(projectTableName: string, photoId: number): Promise<PhotoPersonLight[]>;
|
|
412
416
|
}
|
|
413
417
|
|
|
@@ -422,7 +426,6 @@ export class Photos {
|
|
|
422
426
|
getProjectPhotoIdsByTableName(tableName: string, options?: { ascOrDesc?: string | string[], lastId?: number | number[], limit?: number | number[], includeAll?: boolean | boolean[], startDate?: string | string[], endDate?: string | string[], noDateTaken?: boolean | boolean[] }): Promise<any>;
|
|
423
427
|
getRankedProjectPhotoIdsByTableName(tableName: string, options?: { ascOrDesc?: string | string[], lastId?: number | number[], limit?: number | number[], startDate?: string | string[], endDate?: string | string[], noDateTaken?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
424
428
|
getProjectMonthYearsWithPhotos(tableName: string): Promise<any>;
|
|
425
|
-
getProjectThumbnail(tableName: string): Promise<any>;
|
|
426
429
|
updatePhotoInProject(options?: { tableName?: string | string[], photoId?: number | number[], photoData?: Record<string, any> | Record<string, any>[] }): Promise<PhotoDisplay>;
|
|
427
430
|
updatePhotoRanking(tableName: string, photoId: number, newCategory: string): Promise<Record<string, any>>;
|
|
428
431
|
deletePhotoFromProject(tableName: string, options?: { photoIds?: any }): Promise<Record<string, any>>;
|
|
@@ -452,10 +455,9 @@ export class Projects {
|
|
|
452
455
|
|
|
453
456
|
export class Search {
|
|
454
457
|
searchProjectPhotos(projectTableName: string, options?: { andParams?: any, andStringParams?: any, orParams?: any, orStringParams?: any, notParams?: any, notStringParams?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], dateOrder?: string | string[], customAlbumId?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], curatedAlbumId?: number | number[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
searchProjectPhotosNaturalLanguageAuto(projectTableName: string, searchText: string, size?: number, options?: { blend?: string | string[], minCosine?: number | number[] }): Promise<any>;
|
|
458
|
+
searchProjectPhotosParametrized(projectTableName: string, options?: { andSearchText?: any, orSearchText?: any, notSearchText?: any, city?: any, country?: any, state?: any, albums?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], ascOrDesc?: string | string[], bestOfSimilarSetsOnly?: boolean | boolean[], splitByTier?: boolean | boolean[], lastId?: number | number[], limit?: number | number[] }): Promise<Record<string, any>>;
|
|
459
|
+
recallProjectPhotoIds(projectTableName: string, options?: { ascOrDesc?: string | string[], bestOfSimilarSetsOnly?: boolean | boolean[], lastId?: number | number[], limit?: number | number[] }): Promise<any>;
|
|
460
|
+
searchProjectPhotosNaturalLanguage(projectTableName: string, searchText: string, size?: number, options?: { minCosine?: number | number[], labelMinCosine?: number | number[], labelTopK?: number | number[], labelDelta?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
459
461
|
getProjectSavedSearches(projectTableName: string): Promise<any>;
|
|
460
462
|
getSavedSearchById(searchId: number): Promise<SearchDisplay>;
|
|
461
463
|
saveProjectPhotosSearch(projectTableName: string, options?: { searchName?: string | string[], andParams?: any, andStringParams?: any, orParams?: any, orStringParams?: any, notParams?: any, notStringParams?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], dateOrder?: string | string[], customAlbumId?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], curatedAlbumId?: number | number[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
package/dist/sdk.esm.d.ts
CHANGED
|
@@ -85,10 +85,11 @@ export interface CompanyDisplay {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export interface CompanyKeywordListOverview {
|
|
88
|
+
id: number;
|
|
88
89
|
name: string;
|
|
89
90
|
active_keywords: number;
|
|
90
91
|
excluded_keywords: number;
|
|
91
|
-
|
|
92
|
+
project_ids?: string[];
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
export interface CreditBalanceOutput {
|
|
@@ -376,10 +377,12 @@ export class Keywords {
|
|
|
376
377
|
updateKeywordFilteringListLabels(keywordListId: number, listKeywordsToInclude: number[], listKeywordsToExclude: number[]): Promise<KeywordListDisplayWithList>;
|
|
377
378
|
updateKeywordFilteringListDetails(keywordListId: number, body?: { name?: string, projectList?: string[] }): Promise<KeywordListDisplayWithProjects>;
|
|
378
379
|
addProjectsToKeywordFilteringList(keywordListId: number, options?: { projectIds?: any }): Promise<Record<string, any>>;
|
|
380
|
+
addCompanyToKeywordFilteringList(keywordListId: number, options?: { companyId?: number | number[] }): Promise<Record<string, any>>;
|
|
379
381
|
requestKeywordListExport(keywordListId: number): Promise<Record<string, any>>;
|
|
380
382
|
requestKeywordListExportStatus(keywordListId: number): Promise<AnalysisStatusResponse>;
|
|
381
383
|
getKeywordsAndIds(): Promise<any>;
|
|
382
384
|
removeProjectsFromKeywordFilteringList(keywordListId: number, options?: { projectIds?: any }): Promise<Record<string, any>>;
|
|
385
|
+
removeCompanyFromKeywordFilteringList(keywordListId: number, options?: { companyId?: number | number[] }): Promise<Record<string, any>>;
|
|
383
386
|
deleteKeywordFilteringListById(keywordListId: number): Promise<Record<string, any>>;
|
|
384
387
|
}
|
|
385
388
|
|
|
@@ -408,6 +411,7 @@ export class Person {
|
|
|
408
411
|
splitPersons(projectTableName: string, id: number, options?: { newName?: string | string[], destinationPersonId?: string | string[] }): Promise<PhotoPersonLight>;
|
|
409
412
|
getAllPersonsFromProject(projectTableName: string): Promise<PhotoPersonWithS3Link[]>;
|
|
410
413
|
getAllPersonNamesFromProject(projectTableName: string): Promise<any>;
|
|
414
|
+
getPhotoIdsByPersons(projectTableName: string, options?: { personId?: string[] | string[][] }): Promise<any>;
|
|
411
415
|
getAllPersonsFromPhoto(projectTableName: string, photoId: number): Promise<PhotoPersonLight[]>;
|
|
412
416
|
}
|
|
413
417
|
|
|
@@ -422,7 +426,6 @@ export class Photos {
|
|
|
422
426
|
getProjectPhotoIdsByTableName(tableName: string, options?: { ascOrDesc?: string | string[], lastId?: number | number[], limit?: number | number[], includeAll?: boolean | boolean[], startDate?: string | string[], endDate?: string | string[], noDateTaken?: boolean | boolean[] }): Promise<any>;
|
|
423
427
|
getRankedProjectPhotoIdsByTableName(tableName: string, options?: { ascOrDesc?: string | string[], lastId?: number | number[], limit?: number | number[], startDate?: string | string[], endDate?: string | string[], noDateTaken?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
424
428
|
getProjectMonthYearsWithPhotos(tableName: string): Promise<any>;
|
|
425
|
-
getProjectThumbnail(tableName: string): Promise<any>;
|
|
426
429
|
updatePhotoInProject(options?: { tableName?: string | string[], photoId?: number | number[], photoData?: Record<string, any> | Record<string, any>[] }): Promise<PhotoDisplay>;
|
|
427
430
|
updatePhotoRanking(tableName: string, photoId: number, newCategory: string): Promise<Record<string, any>>;
|
|
428
431
|
deletePhotoFromProject(tableName: string, options?: { photoIds?: any }): Promise<Record<string, any>>;
|
|
@@ -452,10 +455,9 @@ export class Projects {
|
|
|
452
455
|
|
|
453
456
|
export class Search {
|
|
454
457
|
searchProjectPhotos(projectTableName: string, options?: { andParams?: any, andStringParams?: any, orParams?: any, orStringParams?: any, notParams?: any, notStringParams?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], dateOrder?: string | string[], customAlbumId?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], curatedAlbumId?: number | number[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
searchProjectPhotosNaturalLanguageAuto(projectTableName: string, searchText: string, size?: number, options?: { blend?: string | string[], minCosine?: number | number[] }): Promise<any>;
|
|
458
|
+
searchProjectPhotosParametrized(projectTableName: string, options?: { andSearchText?: any, orSearchText?: any, notSearchText?: any, city?: any, country?: any, state?: any, albums?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], ascOrDesc?: string | string[], bestOfSimilarSetsOnly?: boolean | boolean[], splitByTier?: boolean | boolean[], lastId?: number | number[], limit?: number | number[] }): Promise<Record<string, any>>;
|
|
459
|
+
recallProjectPhotoIds(projectTableName: string, options?: { ascOrDesc?: string | string[], bestOfSimilarSetsOnly?: boolean | boolean[], lastId?: number | number[], limit?: number | number[] }): Promise<any>;
|
|
460
|
+
searchProjectPhotosNaturalLanguage(projectTableName: string, searchText: string, size?: number, options?: { minCosine?: number | number[], labelMinCosine?: number | number[], labelTopK?: number | number[], labelDelta?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
|
459
461
|
getProjectSavedSearches(projectTableName: string): Promise<any>;
|
|
460
462
|
getSavedSearchById(searchId: number): Promise<SearchDisplay>;
|
|
461
463
|
saveProjectPhotosSearch(projectTableName: string, options?: { searchName?: string | string[], andParams?: any, andStringParams?: any, orParams?: any, orStringParams?: any, notParams?: any, notStringParams?: any, dateMin?: string | string[], dateMax?: string | string[], dateNullAnd?: boolean | boolean[], dateNullOr?: boolean | boolean[], dateOrder?: string | string[], customAlbumId?: number | number[], bestOfSimilarSetsOnly?: boolean | boolean[], curatedAlbumId?: number | number[], splitByTier?: boolean | boolean[] }): Promise<Record<string, any>>;
|
package/dist/sdk.esm.js
CHANGED
|
@@ -89,8 +89,15 @@ function requireErrors () {
|
|
|
89
89
|
* @returns {OAuthError}
|
|
90
90
|
*/
|
|
91
91
|
static fromResponse(status, body) {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
// FastAPI wraps HTTPException payloads in a `detail` envelope, so an OAuth
|
|
93
|
+
// error arrives as { detail: { error, error_description } }. Unwrap it
|
|
94
|
+
// before falling back, or every server-side OAuth error reads as server_error.
|
|
95
|
+
const payload =
|
|
96
|
+
body && typeof body === 'object' && typeof body.error !== 'string' && body.detail
|
|
97
|
+
? body.detail
|
|
98
|
+
: body;
|
|
99
|
+
if (payload && typeof payload === 'object' && typeof payload.error === 'string') {
|
|
100
|
+
return new OAuthError(payload.error, payload.error_description ?? '', status, body);
|
|
94
101
|
}
|
|
95
102
|
return new OAuthError('server_error', 'Unexpected server response', status, body);
|
|
96
103
|
}
|
|
@@ -171,6 +178,9 @@ function requireClient () {
|
|
|
171
178
|
const { postForm, postJson } = requireHttp();
|
|
172
179
|
const { OAuthError } = requireErrors();
|
|
173
180
|
|
|
181
|
+
// In-flight dedup key for client_credentials re-mints (no refresh_token to key on).
|
|
182
|
+
const CLIENT_CREDENTIALS_KEY = Symbol('client_credentials');
|
|
183
|
+
|
|
174
184
|
class OAuthClient {
|
|
175
185
|
/**
|
|
176
186
|
* @param {import('./types').OAuthClientConfig} config
|
|
@@ -186,12 +196,22 @@ function requireClient () {
|
|
|
186
196
|
*/
|
|
187
197
|
static async registerClient(params) {
|
|
188
198
|
const baseUrl = params.baseUrl.replace(/\/+$/, '');
|
|
189
|
-
|
|
199
|
+
const body = {
|
|
190
200
|
client_name: params.clientName,
|
|
191
201
|
client_type: params.clientType,
|
|
192
202
|
redirect_uris: params.redirectUris,
|
|
193
203
|
is_first_party: params.isFirstParty,
|
|
194
|
-
}
|
|
204
|
+
};
|
|
205
|
+
// grant_types/company_id are required to provision a confidential
|
|
206
|
+
// client_credentials (machine-to-machine) client; omitted bodies keep the
|
|
207
|
+
// legacy public/authorization_code shape. The endpoint is admin-gated, so a
|
|
208
|
+
// bearer token must be supplied for the request to authenticate.
|
|
209
|
+
if (params.grantTypes !== undefined) body.grant_types = params.grantTypes;
|
|
210
|
+
if (params.companyId !== undefined) body.company_id = params.companyId;
|
|
211
|
+
const headers = params.accessToken
|
|
212
|
+
? { Authorization: `Bearer ${params.accessToken}` }
|
|
213
|
+
: {};
|
|
214
|
+
return postJson(`${baseUrl}/oauth/clients`, body, headers);
|
|
195
215
|
}
|
|
196
216
|
|
|
197
217
|
/**
|
|
@@ -309,15 +329,27 @@ function requireClient () {
|
|
|
309
329
|
return { data: json, updatedTokens: null };
|
|
310
330
|
}
|
|
311
331
|
|
|
312
|
-
// 401:
|
|
313
|
-
//
|
|
314
|
-
//
|
|
315
|
-
|
|
332
|
+
// 401: re-authenticate, then retry once.
|
|
333
|
+
// - User sessions (refresh_token present) use the refresh_token grant. The server
|
|
334
|
+
// rotates the token one-time, so concurrent callers share one in-flight call
|
|
335
|
+
// (a second rotation would get invalid_grant).
|
|
336
|
+
// - Machine-to-machine sessions (no refresh_token, client_secret configured) re-mint
|
|
337
|
+
// via client_credentials, which issues no refresh token to rotate (RFC 6749 §4.4).
|
|
338
|
+
// - With neither a refresh_token nor a client_secret there is nothing to retry with.
|
|
339
|
+
if (!refreshToken && !this._config.clientSecret) {
|
|
340
|
+
throw OAuthError.fromResponse(401, await firstResponse.json());
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const reauthKey = refreshToken || CLIENT_CREDENTIALS_KEY;
|
|
344
|
+
let pending = this._inFlightRefreshes.get(reauthKey);
|
|
316
345
|
if (!pending) {
|
|
317
|
-
|
|
318
|
-
this.
|
|
346
|
+
const reauth = refreshToken
|
|
347
|
+
? this.refreshAccessToken(refreshToken)
|
|
348
|
+
: this.getClientCredentialsToken();
|
|
349
|
+
pending = reauth.finally(() => {
|
|
350
|
+
this._inFlightRefreshes.delete(reauthKey);
|
|
319
351
|
});
|
|
320
|
-
this._inFlightRefreshes.set(
|
|
352
|
+
this._inFlightRefreshes.set(reauthKey, pending);
|
|
321
353
|
}
|
|
322
354
|
const newTokens = await pending;
|
|
323
355
|
|
|
@@ -399,6 +431,9 @@ function requireTypes () {
|
|
|
399
431
|
* @property {string} clientType - "public" or "confidential"
|
|
400
432
|
* @property {string[]} redirectUris - HTTPS redirect URIs
|
|
401
433
|
* @property {boolean} isFirstParty - Whether this is a first-party client
|
|
434
|
+
* @property {string[]} [grantTypes] - Allowed grants, e.g. ["client_credentials"]; required for machine-to-machine clients
|
|
435
|
+
* @property {number} [companyId] - Company to bind the client to; required for the client_credentials grant
|
|
436
|
+
* @property {string} [accessToken] - Admin bearer token; the registration endpoint is authorization-gated
|
|
402
437
|
*/
|
|
403
438
|
|
|
404
439
|
/**
|
|
@@ -910,6 +945,17 @@ class Keywords {
|
|
|
910
945
|
return data;
|
|
911
946
|
}
|
|
912
947
|
|
|
948
|
+
async addCompanyToKeywordFilteringList(keywordListId, { companyId } = {}) {
|
|
949
|
+
this._ctx.requireTokens();
|
|
950
|
+
let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
|
|
951
|
+
const query = new URLSearchParams();
|
|
952
|
+
if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
|
|
953
|
+
const qs = query.toString();
|
|
954
|
+
if (qs) path += '?' + qs;
|
|
955
|
+
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken);
|
|
956
|
+
return data;
|
|
957
|
+
}
|
|
958
|
+
|
|
913
959
|
async requestKeywordListExport(keywordListId) {
|
|
914
960
|
this._ctx.requireTokens();
|
|
915
961
|
const path = `/api/v1/keyword/export/${encodeURIComponent(keywordListId)}`;
|
|
@@ -942,6 +988,17 @@ class Keywords {
|
|
|
942
988
|
return data;
|
|
943
989
|
}
|
|
944
990
|
|
|
991
|
+
async removeCompanyFromKeywordFilteringList(keywordListId, { companyId } = {}) {
|
|
992
|
+
this._ctx.requireTokens();
|
|
993
|
+
let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
|
|
994
|
+
const query = new URLSearchParams();
|
|
995
|
+
if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
|
|
996
|
+
const qs = query.toString();
|
|
997
|
+
if (qs) path += '?' + qs;
|
|
998
|
+
const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
|
|
999
|
+
return data;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
945
1002
|
async deleteKeywordFilteringListById(keywordListId) {
|
|
946
1003
|
this._ctx.requireTokens();
|
|
947
1004
|
const path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}`;
|
|
@@ -1101,6 +1158,17 @@ class Person {
|
|
|
1101
1158
|
return data;
|
|
1102
1159
|
}
|
|
1103
1160
|
|
|
1161
|
+
async getPhotoIdsByPersons(projectTableName, { personId } = {}) {
|
|
1162
|
+
this._ctx.requireTokens();
|
|
1163
|
+
let path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photos`;
|
|
1164
|
+
const query = new URLSearchParams();
|
|
1165
|
+
if (personId !== undefined) (Array.isArray(personId) ? personId : [personId]).forEach(v => query.append('person_id', v));
|
|
1166
|
+
const qs = query.toString();
|
|
1167
|
+
if (qs) path += '?' + qs;
|
|
1168
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1169
|
+
return data;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1104
1172
|
async getAllPersonsFromPhoto(projectTableName, photoId) {
|
|
1105
1173
|
this._ctx.requireTokens();
|
|
1106
1174
|
const path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photo/${encodeURIComponent(photoId)}`;
|
|
@@ -1401,13 +1469,6 @@ class Photos {
|
|
|
1401
1469
|
return data;
|
|
1402
1470
|
}
|
|
1403
1471
|
|
|
1404
|
-
async getProjectThumbnail(tableName) {
|
|
1405
|
-
this._ctx.requireTokens();
|
|
1406
|
-
const path = `/api/v1/photos_project/${encodeURIComponent(tableName)}`;
|
|
1407
|
-
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1408
|
-
return data;
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
1472
|
async updatePhotoInProject({ tableName, photoId, photoData } = {}) {
|
|
1412
1473
|
this._ctx.requireTokens();
|
|
1413
1474
|
let path = `/api/v1/photos_update`;
|
|
@@ -1470,50 +1531,56 @@ class Search {
|
|
|
1470
1531
|
return data;
|
|
1471
1532
|
}
|
|
1472
1533
|
|
|
1473
|
-
async
|
|
1534
|
+
async searchProjectPhotosParametrized(projectTableName, { andSearchText, orSearchText, notSearchText, city, country, state, albums, dateMin, dateMax, dateNullAnd, dateNullOr, ascOrDesc, bestOfSimilarSetsOnly, splitByTier, lastId, limit } = {}) {
|
|
1474
1535
|
this._ctx.requireTokens();
|
|
1475
|
-
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1536
|
+
let path = `/api/v1/search/parametrized/${encodeURIComponent(projectTableName)}/`;
|
|
1537
|
+
const query = new URLSearchParams();
|
|
1538
|
+
if (andSearchText !== undefined) (Array.isArray(andSearchText) ? andSearchText : [andSearchText]).forEach(v => query.append('and_search_text', v));
|
|
1539
|
+
if (orSearchText !== undefined) (Array.isArray(orSearchText) ? orSearchText : [orSearchText]).forEach(v => query.append('or_search_text', v));
|
|
1540
|
+
if (notSearchText !== undefined) (Array.isArray(notSearchText) ? notSearchText : [notSearchText]).forEach(v => query.append('not_search_text', v));
|
|
1541
|
+
if (city !== undefined) (Array.isArray(city) ? city : [city]).forEach(v => query.append('city', v));
|
|
1542
|
+
if (country !== undefined) (Array.isArray(country) ? country : [country]).forEach(v => query.append('country', v));
|
|
1543
|
+
if (state !== undefined) (Array.isArray(state) ? state : [state]).forEach(v => query.append('state', v));
|
|
1544
|
+
if (albums !== undefined) (Array.isArray(albums) ? albums : [albums]).forEach(v => query.append('albums', v));
|
|
1545
|
+
if (dateMin !== undefined) (Array.isArray(dateMin) ? dateMin : [dateMin]).forEach(v => query.append('date_min', v));
|
|
1546
|
+
if (dateMax !== undefined) (Array.isArray(dateMax) ? dateMax : [dateMax]).forEach(v => query.append('date_max', v));
|
|
1547
|
+
if (dateNullAnd !== undefined) (Array.isArray(dateNullAnd) ? dateNullAnd : [dateNullAnd]).forEach(v => query.append('date_null_and', v));
|
|
1548
|
+
if (dateNullOr !== undefined) (Array.isArray(dateNullOr) ? dateNullOr : [dateNullOr]).forEach(v => query.append('date_null_or', v));
|
|
1549
|
+
if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
|
|
1550
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1551
|
+
if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
|
|
1552
|
+
if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
|
|
1553
|
+
if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
|
|
1554
|
+
const qs = query.toString();
|
|
1555
|
+
if (qs) path += '?' + qs;
|
|
1556
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1492
1557
|
return data;
|
|
1493
1558
|
}
|
|
1494
1559
|
|
|
1495
|
-
async
|
|
1560
|
+
async recallProjectPhotoIds(projectTableName, { ascOrDesc, bestOfSimilarSetsOnly, lastId, limit } = {}) {
|
|
1496
1561
|
this._ctx.requireTokens();
|
|
1497
|
-
let path = `/api/v1/search/
|
|
1562
|
+
let path = `/api/v1/search/recall/${encodeURIComponent(projectTableName)}/`;
|
|
1498
1563
|
const query = new URLSearchParams();
|
|
1499
|
-
if (
|
|
1500
|
-
if (
|
|
1564
|
+
if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
|
|
1565
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1566
|
+
if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
|
|
1567
|
+
if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
|
|
1501
1568
|
const qs = query.toString();
|
|
1502
1569
|
if (qs) path += '?' + qs;
|
|
1503
|
-
const
|
|
1504
|
-
search_text: searchText,
|
|
1505
|
-
size: size,
|
|
1506
|
-
});
|
|
1507
|
-
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
|
|
1570
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1508
1571
|
return data;
|
|
1509
1572
|
}
|
|
1510
1573
|
|
|
1511
|
-
async
|
|
1574
|
+
async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined, { minCosine, labelMinCosine, labelTopK, labelDelta, bestOfSimilarSetsOnly, splitByTier } = {}) {
|
|
1512
1575
|
this._ctx.requireTokens();
|
|
1513
|
-
let path = `/api/v1/search/
|
|
1576
|
+
let path = `/api/v1/search/natural_language/${encodeURIComponent(projectTableName)}/`;
|
|
1514
1577
|
const query = new URLSearchParams();
|
|
1515
|
-
if (blend !== undefined) (Array.isArray(blend) ? blend : [blend]).forEach(v => query.append('blend', v));
|
|
1516
1578
|
if (minCosine !== undefined) (Array.isArray(minCosine) ? minCosine : [minCosine]).forEach(v => query.append('min_cosine', v));
|
|
1579
|
+
if (labelMinCosine !== undefined) (Array.isArray(labelMinCosine) ? labelMinCosine : [labelMinCosine]).forEach(v => query.append('label_min_cosine', v));
|
|
1580
|
+
if (labelTopK !== undefined) (Array.isArray(labelTopK) ? labelTopK : [labelTopK]).forEach(v => query.append('label_top_k', v));
|
|
1581
|
+
if (labelDelta !== undefined) (Array.isArray(labelDelta) ? labelDelta : [labelDelta]).forEach(v => query.append('label_delta', v));
|
|
1582
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1583
|
+
if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
|
|
1517
1584
|
const qs = query.toString();
|
|
1518
1585
|
if (qs) path += '?' + qs;
|
|
1519
1586
|
const body = stripUndef$1({
|
package/dist/sdk.umd.js
CHANGED
|
@@ -95,8 +95,15 @@
|
|
|
95
95
|
* @returns {OAuthError}
|
|
96
96
|
*/
|
|
97
97
|
static fromResponse(status, body) {
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
// FastAPI wraps HTTPException payloads in a `detail` envelope, so an OAuth
|
|
99
|
+
// error arrives as { detail: { error, error_description } }. Unwrap it
|
|
100
|
+
// before falling back, or every server-side OAuth error reads as server_error.
|
|
101
|
+
const payload =
|
|
102
|
+
body && typeof body === 'object' && typeof body.error !== 'string' && body.detail
|
|
103
|
+
? body.detail
|
|
104
|
+
: body;
|
|
105
|
+
if (payload && typeof payload === 'object' && typeof payload.error === 'string') {
|
|
106
|
+
return new OAuthError(payload.error, payload.error_description ?? '', status, body);
|
|
100
107
|
}
|
|
101
108
|
return new OAuthError('server_error', 'Unexpected server response', status, body);
|
|
102
109
|
}
|
|
@@ -177,6 +184,9 @@
|
|
|
177
184
|
const { postForm, postJson } = requireHttp();
|
|
178
185
|
const { OAuthError } = requireErrors();
|
|
179
186
|
|
|
187
|
+
// In-flight dedup key for client_credentials re-mints (no refresh_token to key on).
|
|
188
|
+
const CLIENT_CREDENTIALS_KEY = Symbol('client_credentials');
|
|
189
|
+
|
|
180
190
|
class OAuthClient {
|
|
181
191
|
/**
|
|
182
192
|
* @param {import('./types').OAuthClientConfig} config
|
|
@@ -192,12 +202,22 @@
|
|
|
192
202
|
*/
|
|
193
203
|
static async registerClient(params) {
|
|
194
204
|
const baseUrl = params.baseUrl.replace(/\/+$/, '');
|
|
195
|
-
|
|
205
|
+
const body = {
|
|
196
206
|
client_name: params.clientName,
|
|
197
207
|
client_type: params.clientType,
|
|
198
208
|
redirect_uris: params.redirectUris,
|
|
199
209
|
is_first_party: params.isFirstParty,
|
|
200
|
-
}
|
|
210
|
+
};
|
|
211
|
+
// grant_types/company_id are required to provision a confidential
|
|
212
|
+
// client_credentials (machine-to-machine) client; omitted bodies keep the
|
|
213
|
+
// legacy public/authorization_code shape. The endpoint is admin-gated, so a
|
|
214
|
+
// bearer token must be supplied for the request to authenticate.
|
|
215
|
+
if (params.grantTypes !== undefined) body.grant_types = params.grantTypes;
|
|
216
|
+
if (params.companyId !== undefined) body.company_id = params.companyId;
|
|
217
|
+
const headers = params.accessToken
|
|
218
|
+
? { Authorization: `Bearer ${params.accessToken}` }
|
|
219
|
+
: {};
|
|
220
|
+
return postJson(`${baseUrl}/oauth/clients`, body, headers);
|
|
201
221
|
}
|
|
202
222
|
|
|
203
223
|
/**
|
|
@@ -315,15 +335,27 @@
|
|
|
315
335
|
return { data: json, updatedTokens: null };
|
|
316
336
|
}
|
|
317
337
|
|
|
318
|
-
// 401:
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
|
|
338
|
+
// 401: re-authenticate, then retry once.
|
|
339
|
+
// - User sessions (refresh_token present) use the refresh_token grant. The server
|
|
340
|
+
// rotates the token one-time, so concurrent callers share one in-flight call
|
|
341
|
+
// (a second rotation would get invalid_grant).
|
|
342
|
+
// - Machine-to-machine sessions (no refresh_token, client_secret configured) re-mint
|
|
343
|
+
// via client_credentials, which issues no refresh token to rotate (RFC 6749 §4.4).
|
|
344
|
+
// - With neither a refresh_token nor a client_secret there is nothing to retry with.
|
|
345
|
+
if (!refreshToken && !this._config.clientSecret) {
|
|
346
|
+
throw OAuthError.fromResponse(401, await firstResponse.json());
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const reauthKey = refreshToken || CLIENT_CREDENTIALS_KEY;
|
|
350
|
+
let pending = this._inFlightRefreshes.get(reauthKey);
|
|
322
351
|
if (!pending) {
|
|
323
|
-
|
|
324
|
-
this.
|
|
352
|
+
const reauth = refreshToken
|
|
353
|
+
? this.refreshAccessToken(refreshToken)
|
|
354
|
+
: this.getClientCredentialsToken();
|
|
355
|
+
pending = reauth.finally(() => {
|
|
356
|
+
this._inFlightRefreshes.delete(reauthKey);
|
|
325
357
|
});
|
|
326
|
-
this._inFlightRefreshes.set(
|
|
358
|
+
this._inFlightRefreshes.set(reauthKey, pending);
|
|
327
359
|
}
|
|
328
360
|
const newTokens = await pending;
|
|
329
361
|
|
|
@@ -405,6 +437,9 @@
|
|
|
405
437
|
* @property {string} clientType - "public" or "confidential"
|
|
406
438
|
* @property {string[]} redirectUris - HTTPS redirect URIs
|
|
407
439
|
* @property {boolean} isFirstParty - Whether this is a first-party client
|
|
440
|
+
* @property {string[]} [grantTypes] - Allowed grants, e.g. ["client_credentials"]; required for machine-to-machine clients
|
|
441
|
+
* @property {number} [companyId] - Company to bind the client to; required for the client_credentials grant
|
|
442
|
+
* @property {string} [accessToken] - Admin bearer token; the registration endpoint is authorization-gated
|
|
408
443
|
*/
|
|
409
444
|
|
|
410
445
|
/**
|
|
@@ -916,6 +951,17 @@
|
|
|
916
951
|
return data;
|
|
917
952
|
}
|
|
918
953
|
|
|
954
|
+
async addCompanyToKeywordFilteringList(keywordListId, { companyId } = {}) {
|
|
955
|
+
this._ctx.requireTokens();
|
|
956
|
+
let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
|
|
957
|
+
const query = new URLSearchParams();
|
|
958
|
+
if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
|
|
959
|
+
const qs = query.toString();
|
|
960
|
+
if (qs) path += '?' + qs;
|
|
961
|
+
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken);
|
|
962
|
+
return data;
|
|
963
|
+
}
|
|
964
|
+
|
|
919
965
|
async requestKeywordListExport(keywordListId) {
|
|
920
966
|
this._ctx.requireTokens();
|
|
921
967
|
const path = `/api/v1/keyword/export/${encodeURIComponent(keywordListId)}`;
|
|
@@ -948,6 +994,17 @@
|
|
|
948
994
|
return data;
|
|
949
995
|
}
|
|
950
996
|
|
|
997
|
+
async removeCompanyFromKeywordFilteringList(keywordListId, { companyId } = {}) {
|
|
998
|
+
this._ctx.requireTokens();
|
|
999
|
+
let path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}/company`;
|
|
1000
|
+
const query = new URLSearchParams();
|
|
1001
|
+
if (companyId !== undefined) (Array.isArray(companyId) ? companyId : [companyId]).forEach(v => query.append('company_id', v));
|
|
1002
|
+
const qs = query.toString();
|
|
1003
|
+
if (qs) path += '?' + qs;
|
|
1004
|
+
const { data } = await this._ctx.client.request(path, 'DELETE', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1005
|
+
return data;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
951
1008
|
async deleteKeywordFilteringListById(keywordListId) {
|
|
952
1009
|
this._ctx.requireTokens();
|
|
953
1010
|
const path = `/api/v1/keyword/${encodeURIComponent(keywordListId)}`;
|
|
@@ -1107,6 +1164,17 @@
|
|
|
1107
1164
|
return data;
|
|
1108
1165
|
}
|
|
1109
1166
|
|
|
1167
|
+
async getPhotoIdsByPersons(projectTableName, { personId } = {}) {
|
|
1168
|
+
this._ctx.requireTokens();
|
|
1169
|
+
let path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photos`;
|
|
1170
|
+
const query = new URLSearchParams();
|
|
1171
|
+
if (personId !== undefined) (Array.isArray(personId) ? personId : [personId]).forEach(v => query.append('person_id', v));
|
|
1172
|
+
const qs = query.toString();
|
|
1173
|
+
if (qs) path += '?' + qs;
|
|
1174
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1175
|
+
return data;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1110
1178
|
async getAllPersonsFromPhoto(projectTableName, photoId) {
|
|
1111
1179
|
this._ctx.requireTokens();
|
|
1112
1180
|
const path = `/api/v1/person/${encodeURIComponent(projectTableName)}/photo/${encodeURIComponent(photoId)}`;
|
|
@@ -1407,13 +1475,6 @@
|
|
|
1407
1475
|
return data;
|
|
1408
1476
|
}
|
|
1409
1477
|
|
|
1410
|
-
async getProjectThumbnail(tableName) {
|
|
1411
|
-
this._ctx.requireTokens();
|
|
1412
|
-
const path = `/api/v1/photos_project/${encodeURIComponent(tableName)}`;
|
|
1413
|
-
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1414
|
-
return data;
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
1478
|
async updatePhotoInProject({ tableName, photoId, photoData } = {}) {
|
|
1418
1479
|
this._ctx.requireTokens();
|
|
1419
1480
|
let path = `/api/v1/photos_update`;
|
|
@@ -1476,50 +1537,56 @@
|
|
|
1476
1537
|
return data;
|
|
1477
1538
|
}
|
|
1478
1539
|
|
|
1479
|
-
async
|
|
1540
|
+
async searchProjectPhotosParametrized(projectTableName, { andSearchText, orSearchText, notSearchText, city, country, state, albums, dateMin, dateMax, dateNullAnd, dateNullOr, ascOrDesc, bestOfSimilarSetsOnly, splitByTier, lastId, limit } = {}) {
|
|
1480
1541
|
this._ctx.requireTokens();
|
|
1481
|
-
|
|
1482
|
-
const
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1542
|
+
let path = `/api/v1/search/parametrized/${encodeURIComponent(projectTableName)}/`;
|
|
1543
|
+
const query = new URLSearchParams();
|
|
1544
|
+
if (andSearchText !== undefined) (Array.isArray(andSearchText) ? andSearchText : [andSearchText]).forEach(v => query.append('and_search_text', v));
|
|
1545
|
+
if (orSearchText !== undefined) (Array.isArray(orSearchText) ? orSearchText : [orSearchText]).forEach(v => query.append('or_search_text', v));
|
|
1546
|
+
if (notSearchText !== undefined) (Array.isArray(notSearchText) ? notSearchText : [notSearchText]).forEach(v => query.append('not_search_text', v));
|
|
1547
|
+
if (city !== undefined) (Array.isArray(city) ? city : [city]).forEach(v => query.append('city', v));
|
|
1548
|
+
if (country !== undefined) (Array.isArray(country) ? country : [country]).forEach(v => query.append('country', v));
|
|
1549
|
+
if (state !== undefined) (Array.isArray(state) ? state : [state]).forEach(v => query.append('state', v));
|
|
1550
|
+
if (albums !== undefined) (Array.isArray(albums) ? albums : [albums]).forEach(v => query.append('albums', v));
|
|
1551
|
+
if (dateMin !== undefined) (Array.isArray(dateMin) ? dateMin : [dateMin]).forEach(v => query.append('date_min', v));
|
|
1552
|
+
if (dateMax !== undefined) (Array.isArray(dateMax) ? dateMax : [dateMax]).forEach(v => query.append('date_max', v));
|
|
1553
|
+
if (dateNullAnd !== undefined) (Array.isArray(dateNullAnd) ? dateNullAnd : [dateNullAnd]).forEach(v => query.append('date_null_and', v));
|
|
1554
|
+
if (dateNullOr !== undefined) (Array.isArray(dateNullOr) ? dateNullOr : [dateNullOr]).forEach(v => query.append('date_null_or', v));
|
|
1555
|
+
if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
|
|
1556
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1557
|
+
if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
|
|
1558
|
+
if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
|
|
1559
|
+
if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
|
|
1560
|
+
const qs = query.toString();
|
|
1561
|
+
if (qs) path += '?' + qs;
|
|
1562
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1498
1563
|
return data;
|
|
1499
1564
|
}
|
|
1500
1565
|
|
|
1501
|
-
async
|
|
1566
|
+
async recallProjectPhotoIds(projectTableName, { ascOrDesc, bestOfSimilarSetsOnly, lastId, limit } = {}) {
|
|
1502
1567
|
this._ctx.requireTokens();
|
|
1503
|
-
let path = `/api/v1/search/
|
|
1568
|
+
let path = `/api/v1/search/recall/${encodeURIComponent(projectTableName)}/`;
|
|
1504
1569
|
const query = new URLSearchParams();
|
|
1505
|
-
if (
|
|
1506
|
-
if (
|
|
1570
|
+
if (ascOrDesc !== undefined) (Array.isArray(ascOrDesc) ? ascOrDesc : [ascOrDesc]).forEach(v => query.append('asc_or_desc', v));
|
|
1571
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1572
|
+
if (lastId !== undefined) (Array.isArray(lastId) ? lastId : [lastId]).forEach(v => query.append('last_id', v));
|
|
1573
|
+
if (limit !== undefined) (Array.isArray(limit) ? limit : [limit]).forEach(v => query.append('limit', v));
|
|
1507
1574
|
const qs = query.toString();
|
|
1508
1575
|
if (qs) path += '?' + qs;
|
|
1509
|
-
const
|
|
1510
|
-
search_text: searchText,
|
|
1511
|
-
size: size,
|
|
1512
|
-
});
|
|
1513
|
-
const { data } = await this._ctx.client.request(path, 'POST', this._ctx.accessToken, this._ctx.refreshToken, body);
|
|
1576
|
+
const { data } = await this._ctx.client.request(path, 'GET', this._ctx.accessToken, this._ctx.refreshToken);
|
|
1514
1577
|
return data;
|
|
1515
1578
|
}
|
|
1516
1579
|
|
|
1517
|
-
async
|
|
1580
|
+
async searchProjectPhotosNaturalLanguage(projectTableName, searchText, size = undefined, { minCosine, labelMinCosine, labelTopK, labelDelta, bestOfSimilarSetsOnly, splitByTier } = {}) {
|
|
1518
1581
|
this._ctx.requireTokens();
|
|
1519
|
-
let path = `/api/v1/search/
|
|
1582
|
+
let path = `/api/v1/search/natural_language/${encodeURIComponent(projectTableName)}/`;
|
|
1520
1583
|
const query = new URLSearchParams();
|
|
1521
|
-
if (blend !== undefined) (Array.isArray(blend) ? blend : [blend]).forEach(v => query.append('blend', v));
|
|
1522
1584
|
if (minCosine !== undefined) (Array.isArray(minCosine) ? minCosine : [minCosine]).forEach(v => query.append('min_cosine', v));
|
|
1585
|
+
if (labelMinCosine !== undefined) (Array.isArray(labelMinCosine) ? labelMinCosine : [labelMinCosine]).forEach(v => query.append('label_min_cosine', v));
|
|
1586
|
+
if (labelTopK !== undefined) (Array.isArray(labelTopK) ? labelTopK : [labelTopK]).forEach(v => query.append('label_top_k', v));
|
|
1587
|
+
if (labelDelta !== undefined) (Array.isArray(labelDelta) ? labelDelta : [labelDelta]).forEach(v => query.append('label_delta', v));
|
|
1588
|
+
if (bestOfSimilarSetsOnly !== undefined) (Array.isArray(bestOfSimilarSetsOnly) ? bestOfSimilarSetsOnly : [bestOfSimilarSetsOnly]).forEach(v => query.append('best_of_similar_sets_only', v));
|
|
1589
|
+
if (splitByTier !== undefined) (Array.isArray(splitByTier) ? splitByTier : [splitByTier]).forEach(v => query.append('split_by_tier', v));
|
|
1523
1590
|
const qs = query.toString();
|
|
1524
1591
|
if (qs) path += '?' + qs;
|
|
1525
1592
|
const body = stripUndef$1({
|