@attlaz/client 1.110.0 → 1.112.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/Http/HttpClient.js +6 -8
- package/dist/Http/HttpClientResponse.d.ts +12 -1
- package/dist/Http/HttpClientResponse.js +19 -0
- package/dist/Http/Transport/DirectTransport.d.ts +1 -0
- package/dist/Http/Transport/DirectTransport.js +4 -0
- package/dist/Http/Transport/ITransport.d.ts +2 -0
- package/dist/Http/Transport/OAuthClient.d.ts +5 -0
- package/dist/Http/Transport/OAuthClient.js +35 -3
- package/dist/MarketPulse/Model/ProductOpportunity.d.ts +7 -0
- package/dist/MarketPulse/Model/ProductOpportunity.js +9 -0
- package/dist/MarketPulse/Service/CrawlJobEndpoint.d.ts +11 -3
- package/dist/MarketPulse/Service/CrawlJobEndpoint.js +19 -5
- package/dist/MarketPulse/Service/IndexJobEndpoint.d.ts +3 -3
- package/dist/MarketPulse/Service/IndexJobEndpoint.js +7 -5
- package/dist/MarketPulse/Service/OpportunityEndpoint.d.ts +6 -1
- package/dist/MarketPulse/Service/OpportunityEndpoint.js +7 -1
- package/dist/MarketPulse/Service/ProductMatchJobEndpoint.d.ts +3 -3
- package/dist/MarketPulse/Service/ProductMatchJobEndpoint.js +7 -5
- package/dist/Service/Endpoint.d.ts +7 -0
- package/dist/Service/Endpoint.js +11 -0
- package/dist/Service/StorageEndpoint.d.ts +14 -0
- package/dist/Service/StorageEndpoint.js +27 -0
- package/dist/index.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/Http/HttpClient.js
CHANGED
|
@@ -33,7 +33,7 @@ export class HttpClient {
|
|
|
33
33
|
clientError.response = {
|
|
34
34
|
status: rawResponse.status,
|
|
35
35
|
statusText,
|
|
36
|
-
data: await rawResponse.
|
|
36
|
+
data: JSON.parse(Buffer.from(await rawResponse.arrayBuffer()).toString('utf8')),
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
catch {
|
|
@@ -45,13 +45,11 @@ export class HttpClient {
|
|
|
45
45
|
rawResponse.headers.forEach((value, header) => {
|
|
46
46
|
httpResponse.headers[header] = value;
|
|
47
47
|
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
httpResponse.body = await rawResponse.text();
|
|
54
|
-
}
|
|
48
|
+
// Bytes, never an interpretation. The caller already knows what it asked for — an
|
|
49
|
+
// endpoint supplies a parser, a blob read wants the bytes — so sniffing the content type
|
|
50
|
+
// here would be a second, weaker answer to a question already answered. It was also
|
|
51
|
+
// lossy: the old `text()` fallback UTF-8-decoded binary, turning 10 bytes into 16.
|
|
52
|
+
httpResponse.body = Buffer.from(await rawResponse.arrayBuffer());
|
|
55
53
|
return httpResponse;
|
|
56
54
|
}
|
|
57
55
|
catch (error) {
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import { Headers } from './Data/Headers.js';
|
|
2
|
+
/**
|
|
3
|
+
* A response as it came off the wire. `body` is always bytes — the transport does not interpret,
|
|
4
|
+
* because the caller already knows what it asked for. Use `getJson()` or `getBytes()` to say which.
|
|
5
|
+
*/
|
|
2
6
|
export declare class HttpClientResponse {
|
|
3
7
|
status: number;
|
|
4
8
|
statusText: string;
|
|
5
|
-
body:
|
|
9
|
+
body: Buffer | null;
|
|
6
10
|
headers: Headers;
|
|
7
11
|
constructor(status: number, statusText: string);
|
|
12
|
+
/** The raw bytes, empty when the response had no body. */
|
|
13
|
+
getBytes(): Buffer;
|
|
14
|
+
/**
|
|
15
|
+
* The body parsed as JSON, or null when there was none — a 204 and a DELETE that answers empty
|
|
16
|
+
* are normal, and `JSON.parse('')` throws.
|
|
17
|
+
*/
|
|
18
|
+
getJson<T>(): T | null;
|
|
8
19
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A response as it came off the wire. `body` is always bytes — the transport does not interpret,
|
|
3
|
+
* because the caller already knows what it asked for. Use `getJson()` or `getBytes()` to say which.
|
|
4
|
+
*/
|
|
1
5
|
export class HttpClientResponse {
|
|
2
6
|
status;
|
|
3
7
|
statusText;
|
|
@@ -7,4 +11,19 @@ export class HttpClientResponse {
|
|
|
7
11
|
this.status = status;
|
|
8
12
|
this.statusText = statusText;
|
|
9
13
|
}
|
|
14
|
+
/** The raw bytes, empty when the response had no body. */
|
|
15
|
+
getBytes() {
|
|
16
|
+
return this.body ?? Buffer.alloc(0);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The body parsed as JSON, or null when there was none — a 204 and a DELETE that answers empty
|
|
20
|
+
* are normal, and `JSON.parse('')` throws.
|
|
21
|
+
*/
|
|
22
|
+
getJson() {
|
|
23
|
+
const bytes = this.getBytes();
|
|
24
|
+
if (bytes.length === 0) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return JSON.parse(bytes.toString('utf8'));
|
|
28
|
+
}
|
|
10
29
|
}
|
|
@@ -32,6 +32,7 @@ export declare class DirectTransport implements ITransport {
|
|
|
32
32
|
private parseErrorHandler;
|
|
33
33
|
constructor(defaultBaseUrl: string, sessionPayload: string, sessionSignature: string, routes?: DirectTransportRoute[]);
|
|
34
34
|
request<T>(action: string, parameters?: Parameters, method?: string, _signWithOauthToken?: boolean): Promise<T>;
|
|
35
|
+
requestBytes(action: string, parameters?: Parameters, method?: string, _signWithOauthToken?: boolean, headers?: Record<string, string>): Promise<Buffer>;
|
|
35
36
|
private resolveClient;
|
|
36
37
|
isDebugEnabled(): boolean;
|
|
37
38
|
setParseErrorHandler(handler: ParseErrorHandler | null): void;
|
|
@@ -44,6 +44,10 @@ export class DirectTransport {
|
|
|
44
44
|
// Always call without OAuth signing — authentication is via the signed session headers
|
|
45
45
|
return await client.request(action, parameters, method, false);
|
|
46
46
|
}
|
|
47
|
+
async requestBytes(action, parameters = null, method = 'GET', _signWithOauthToken = true, headers = {}) {
|
|
48
|
+
const client = this.resolveClient(action);
|
|
49
|
+
return await client.requestBytes(action, parameters, method, false, headers);
|
|
50
|
+
}
|
|
47
51
|
resolveClient(action) {
|
|
48
52
|
for (const route of this.sortedRoutes) {
|
|
49
53
|
if (action.startsWith(route.prefix)) {
|
|
@@ -7,6 +7,8 @@ import { Parameters } from '../Data/Parameters.js';
|
|
|
7
7
|
export type ParseErrorHandler = (message: string, context: Record<string, unknown>) => void;
|
|
8
8
|
export interface ITransport {
|
|
9
9
|
request: <T>(action: string, parameters: Parameters, method: string, signWithOauthToken: boolean) => Promise<T>;
|
|
10
|
+
/** The response bytes, undecoded — for resources a JSON round trip would inflate or destroy. */
|
|
11
|
+
requestBytes: (action: string, parameters: Parameters, method: string, signWithOauthToken: boolean, headers?: Record<string, string>) => Promise<Buffer>;
|
|
10
12
|
isDebugEnabled: () => boolean;
|
|
11
13
|
reportParseError: (message: string, context: Record<string, unknown>) => void;
|
|
12
14
|
setParseErrorHandler: (handler: ParseErrorHandler | null) => void;
|
|
@@ -48,6 +48,11 @@ export declare class OAuthClient implements ITransport {
|
|
|
48
48
|
*/
|
|
49
49
|
private requestToken;
|
|
50
50
|
isTokenExpired(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Like `request`, but asks for and returns the raw bytes. For a resource that has a binary
|
|
53
|
+
* representation — a stored item — where decoding would destroy it.
|
|
54
|
+
*/
|
|
55
|
+
requestBytes(action: string, parameters?: Parameters, method?: string, signWithOauthToken?: boolean, headers?: Record<string, string>): Promise<Buffer>;
|
|
51
56
|
request<T>(action: string, parameters?: Parameters, method?: string, signWithOauthToken?: boolean): Promise<T>;
|
|
52
57
|
isAuthenticated(): boolean;
|
|
53
58
|
getToken(): OAuthClientToken | null;
|
|
@@ -219,6 +219,25 @@ export class OAuthClient {
|
|
|
219
219
|
}
|
|
220
220
|
return OAuthClientToken.isExpired(this.oauthClientToken);
|
|
221
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Like `request`, but asks for and returns the raw bytes. For a resource that has a binary
|
|
224
|
+
* representation — a stored item — where decoding would destroy it.
|
|
225
|
+
*/
|
|
226
|
+
async requestBytes(action, parameters = null, method = 'GET', signWithOauthToken = true, headers = {}) {
|
|
227
|
+
if (signWithOauthToken) {
|
|
228
|
+
await this.ensureAccessToken();
|
|
229
|
+
if (this.oauthClientToken === null) {
|
|
230
|
+
throw new ClientError('Unable to perform request, access token not provided', HttpStatus.HTTP_UNAUTHORIZED);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const requestData = this.createRequestData(action, parameters, method, signWithOauthToken);
|
|
234
|
+
requestData.setHeader('Accept', 'application/octet-stream');
|
|
235
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
236
|
+
requestData.setHeader(name, value);
|
|
237
|
+
}
|
|
238
|
+
const response = await HttpClient.request(requestData, this.options.timeoutMs);
|
|
239
|
+
return response.getBytes();
|
|
240
|
+
}
|
|
222
241
|
async request(action, parameters = null, method = 'GET', signWithOauthToken = true) {
|
|
223
242
|
if (signWithOauthToken) {
|
|
224
243
|
// Token-first, credentials-as-fallback (mirrors the PHP client): use a valid token as-is,
|
|
@@ -235,7 +254,7 @@ export class OAuthClient {
|
|
|
235
254
|
}
|
|
236
255
|
try {
|
|
237
256
|
const response = await HttpClient.request(requestData, this.options.timeoutMs);
|
|
238
|
-
return response.
|
|
257
|
+
return response.getJson();
|
|
239
258
|
}
|
|
240
259
|
catch (error) {
|
|
241
260
|
if (!(error instanceof ClientError)) {
|
|
@@ -367,20 +386,33 @@ export class OAuthClient {
|
|
|
367
386
|
const url = this.getApiEndpointUrl(action);
|
|
368
387
|
let requestData = new HttpClientRequest(url, method);
|
|
369
388
|
requestData.headers = this.getDefaultHeaders();
|
|
389
|
+
// Stated explicitly, even though JSON is what the API returns by default today. The server
|
|
390
|
+
// plans to make bytes the default once no request arrives without an Accept, and a silent
|
|
391
|
+
// client is indistinguishable from one that did not care — which is what makes that flip
|
|
392
|
+
// unsafe. An endpoint wanting another representation overrides this afterwards.
|
|
393
|
+
requestData.setHeader('Accept', 'application/json');
|
|
370
394
|
if (this.version !== null) {
|
|
371
395
|
requestData.setHeader('Attlaz-API-Version', this.version);
|
|
372
396
|
}
|
|
373
397
|
if ((method === 'POST' || method === 'DELETE' || method === 'PUT' || method === 'PATCH') && parameters !== null && parameters !== undefined) {
|
|
374
|
-
|
|
398
|
+
// A Buffer is the payload, not something to serialise. Stringifying one yields
|
|
399
|
+
// {"type":"Buffer","data":[…]} — larger than the bytes and not what any reader expects.
|
|
400
|
+
if (Buffer.isBuffer(parameters)) {
|
|
401
|
+
requestData.body = parameters;
|
|
402
|
+
requestData.setHeader('Content-Type', 'application/octet-stream');
|
|
403
|
+
}
|
|
404
|
+
else if (typeof parameters === 'object') {
|
|
375
405
|
requestData.body = JsonSerializable.stringify(parameters);
|
|
406
|
+
requestData.setJsonHeader();
|
|
376
407
|
}
|
|
377
408
|
else if (typeof parameters === 'string') {
|
|
378
409
|
requestData.body = parameters;
|
|
410
|
+
requestData.setJsonHeader();
|
|
379
411
|
}
|
|
380
412
|
else {
|
|
381
413
|
console.error('Unknown parameter type: ' + typeof parameters);
|
|
414
|
+
requestData.setJsonHeader();
|
|
382
415
|
}
|
|
383
|
-
requestData.setJsonHeader();
|
|
384
416
|
}
|
|
385
417
|
if (method === 'GET' && parameters !== null && parameters !== undefined) {
|
|
386
418
|
const params = parameters;
|
|
@@ -33,7 +33,14 @@ export declare class ProductOpportunity {
|
|
|
33
33
|
productName: string;
|
|
34
34
|
productImage: string | null;
|
|
35
35
|
productBrand: string | null;
|
|
36
|
+
productSku: string | null;
|
|
36
37
|
ownPrice: number;
|
|
38
|
+
/**
|
|
39
|
+
* What the product costs you. **Null means unknown, not free** — the API resolves a stored zero
|
|
40
|
+
* to null where the catalog's source uses zero for "never entered". The same unknown is why
|
|
41
|
+
* `marginPct` can be null.
|
|
42
|
+
*/
|
|
43
|
+
costPrice: number | null;
|
|
37
44
|
/** The product's own currency. Never assume one — see DECISIONS.md (2026-08-08). */
|
|
38
45
|
currency: string | null;
|
|
39
46
|
constructor(id: string, catalogProductId: string, type: OpportunityType);
|
|
@@ -26,7 +26,14 @@ export class ProductOpportunity {
|
|
|
26
26
|
productName = '';
|
|
27
27
|
productImage = null;
|
|
28
28
|
productBrand = null;
|
|
29
|
+
productSku = null;
|
|
29
30
|
ownPrice = 0;
|
|
31
|
+
/**
|
|
32
|
+
* What the product costs you. **Null means unknown, not free** — the API resolves a stored zero
|
|
33
|
+
* to null where the catalog's source uses zero for "never entered". The same unknown is why
|
|
34
|
+
* `marginPct` can be null.
|
|
35
|
+
*/
|
|
36
|
+
costPrice = null;
|
|
30
37
|
/** The product's own currency. Never assume one — see DECISIONS.md (2026-08-08). */
|
|
31
38
|
currency = null;
|
|
32
39
|
constructor(id, catalogProductId, type) {
|
|
@@ -44,6 +51,8 @@ export class ProductOpportunity {
|
|
|
44
51
|
opportunity.computedAt = raw.computed_at ? new Date(raw.computed_at) : null;
|
|
45
52
|
opportunity.configVersion = Number(raw.config_version ?? 0);
|
|
46
53
|
opportunity.productName = raw.product_name ?? '';
|
|
54
|
+
opportunity.productSku = raw.product_sku ?? null;
|
|
55
|
+
opportunity.costPrice = raw.cost_price === null || raw.cost_price === undefined ? null : Number(raw.cost_price);
|
|
47
56
|
opportunity.productImage = raw.product_image ?? null;
|
|
48
57
|
opportunity.productBrand = raw.product_brand ?? null;
|
|
49
58
|
opportunity.ownPrice = Number(raw.own_price ?? 0);
|
|
@@ -20,14 +20,22 @@ export declare class CrawlJobEndpoint extends Endpoint {
|
|
|
20
20
|
create(vendorId: string, isPartial?: boolean): Promise<CrawlJob>;
|
|
21
21
|
/** Update a crawl job's status ('started', 'finished', 'cancelled'). */
|
|
22
22
|
updateStatus(crawlJobId: string, status: CrawlJobStatus): Promise<CrawlJob>;
|
|
23
|
+
/**
|
|
24
|
+
* A single vendor's crawl jobs, newest first.
|
|
25
|
+
*
|
|
26
|
+
* Kept alongside the catalog list because `crawl_job` records no catalog: the scraping flow asks
|
|
27
|
+
* "is this vendor already being crawled?" knowing only a vendor. Prefer {@link getByCatalog} for
|
|
28
|
+
* anything user-facing — it is scoped to a catalog the caller can see.
|
|
29
|
+
*/
|
|
30
|
+
getByVendor(vendorId: string, pagination?: CursorPagination | null): Promise<CollectionResult<CrawlJob>>;
|
|
23
31
|
/**
|
|
24
32
|
* Crawl jobs across every competitor of a catalog (newest first), each with progress
|
|
25
|
-
* (`total` / `processed`). Pass `
|
|
33
|
+
* (`total` / `processed`). Pass `vendorIds` to narrow to one or more competitors.
|
|
26
34
|
*
|
|
27
35
|
* A crawl job belongs to a vendor, not to a catalog — the API resolves the catalog's competitors
|
|
28
|
-
* and returns the jobs of their vendors. A
|
|
36
|
+
* and returns the jobs of their vendors. A vendor outside this catalog is rejected.
|
|
29
37
|
*/
|
|
30
|
-
getByCatalog(catalogId: CatalogId | string, pagination?: CursorPagination | null,
|
|
38
|
+
getByCatalog(catalogId: CatalogId | string, pagination?: CursorPagination | null, vendorIds?: string[] | null): Promise<CollectionResult<CrawlJob>>;
|
|
31
39
|
/** The crawl job with this id, or null. */
|
|
32
40
|
getById(crawlJobId: string): Promise<CrawlJob | null>;
|
|
33
41
|
/** Register the pages this crawl will process (idempotent server-side). */
|
|
@@ -33,18 +33,32 @@ export class CrawlJobEndpoint extends Endpoint {
|
|
|
33
33
|
}
|
|
34
34
|
return job;
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* A single vendor's crawl jobs, newest first.
|
|
38
|
+
*
|
|
39
|
+
* Kept alongside the catalog list because `crawl_job` records no catalog: the scraping flow asks
|
|
40
|
+
* "is this vendor already being crawled?" knowing only a vendor. Prefer {@link getByCatalog} for
|
|
41
|
+
* anything user-facing — it is scoped to a catalog the caller can see.
|
|
42
|
+
*/
|
|
43
|
+
async getByVendor(vendorId, pagination = null) {
|
|
44
|
+
const queryString = new QueryString(path('/pulse/vendors/:vendorId/crawl-jobs', { vendorId }));
|
|
45
|
+
queryString.addPagination(pagination);
|
|
46
|
+
return await this.requestCollection(queryString, CrawlJob.parse);
|
|
47
|
+
}
|
|
36
48
|
/**
|
|
37
49
|
* Crawl jobs across every competitor of a catalog (newest first), each with progress
|
|
38
|
-
* (`total` / `processed`). Pass `
|
|
50
|
+
* (`total` / `processed`). Pass `vendorIds` to narrow to one or more competitors.
|
|
39
51
|
*
|
|
40
52
|
* A crawl job belongs to a vendor, not to a catalog — the API resolves the catalog's competitors
|
|
41
|
-
* and returns the jobs of their vendors. A
|
|
53
|
+
* and returns the jobs of their vendors. A vendor outside this catalog is rejected.
|
|
42
54
|
*/
|
|
43
|
-
async getByCatalog(catalogId, pagination = null,
|
|
55
|
+
async getByCatalog(catalogId, pagination = null, vendorIds = null) {
|
|
44
56
|
const queryString = new QueryString(path('/pulse/catalogs/:catalogId/crawl-jobs', { catalogId: catalogId.toString() }));
|
|
45
57
|
queryString.addPagination(pagination);
|
|
46
|
-
if (
|
|
47
|
-
|
|
58
|
+
if (vendorIds !== null && vendorIds.length > 0) {
|
|
59
|
+
// Repeated param (`vendor=a&vendor=b`) — QueryString expands arrays, and the API reads it
|
|
60
|
+
// with getOptionalCollection.
|
|
61
|
+
queryString.set('vendor', vendorIds);
|
|
48
62
|
}
|
|
49
63
|
return await this.requestCollection(queryString, CrawlJob.parse);
|
|
50
64
|
}
|
|
@@ -10,10 +10,10 @@ import { VendorStatus } from '../Model/VendorStatus.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export declare class IndexJobEndpoint extends Endpoint {
|
|
12
12
|
/**
|
|
13
|
-
* Index jobs across every competitor of a catalog, newest first. Pass `
|
|
14
|
-
*
|
|
13
|
+
* Index jobs across every competitor of a catalog, newest first. Pass `vendorIds` to narrow to one or more
|
|
14
|
+
* competitors; a vendor outside this catalog is rejected.
|
|
15
15
|
*/
|
|
16
|
-
getByCatalog(catalogId: CatalogId | string, pagination?: CursorPagination | null,
|
|
16
|
+
getByCatalog(catalogId: CatalogId | string, pagination?: CursorPagination | null, vendorIds?: string[] | null): Promise<CollectionResult<IndexJob>>;
|
|
17
17
|
/** Queue a re-index of a competitor's products, so later matching sees current data. */
|
|
18
18
|
requestReindex(catalogId: CatalogId | string, vendorId: string): Promise<void>;
|
|
19
19
|
/** How fresh a competitor's index and matches are, plus any match job running right now. */
|
|
@@ -9,14 +9,16 @@ import { VendorStatus } from '../Model/VendorStatus.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export class IndexJobEndpoint extends Endpoint {
|
|
11
11
|
/**
|
|
12
|
-
* Index jobs across every competitor of a catalog, newest first. Pass `
|
|
13
|
-
*
|
|
12
|
+
* Index jobs across every competitor of a catalog, newest first. Pass `vendorIds` to narrow to one or more
|
|
13
|
+
* competitors; a vendor outside this catalog is rejected.
|
|
14
14
|
*/
|
|
15
|
-
async getByCatalog(catalogId, pagination = null,
|
|
15
|
+
async getByCatalog(catalogId, pagination = null, vendorIds = null) {
|
|
16
16
|
const queryString = new QueryString(path('/pulse/catalogs/:catalogId/index-jobs', { catalogId: catalogId.toString() }));
|
|
17
17
|
queryString.addPagination(pagination);
|
|
18
|
-
if (
|
|
19
|
-
|
|
18
|
+
if (vendorIds !== null && vendorIds.length > 0) {
|
|
19
|
+
// Repeated param (`vendor=a&vendor=b`) — QueryString expands arrays, and the API reads it
|
|
20
|
+
// with getOptionalCollection.
|
|
21
|
+
queryString.set('vendor', vendorIds);
|
|
20
22
|
}
|
|
21
23
|
return await this.requestCollection(queryString, IndexJob.parse);
|
|
22
24
|
}
|
|
@@ -3,6 +3,11 @@ import { CursorPagination } from '../../Model/Pagination/CursorPagination.js';
|
|
|
3
3
|
import { CollectionResult } from '../../Model/Result/CollectionResult.js';
|
|
4
4
|
import { Endpoint } from '../../Service/Endpoint.js';
|
|
5
5
|
import { OpportunityType, ProductOpportunity } from '../Model/ProductOpportunity.js';
|
|
6
|
+
/** Narrowing of the worklist. `brand` is an exact match; `sku` matches a fragment. */
|
|
7
|
+
export type OpportunityFilters = {
|
|
8
|
+
brand?: string;
|
|
9
|
+
sku?: string;
|
|
10
|
+
};
|
|
6
11
|
/**
|
|
7
12
|
* The pricing opportunity worklist — `/pulse/catalogs/:catalogId/opportunities`.
|
|
8
13
|
*/
|
|
@@ -13,5 +18,5 @@ export declare class OpportunityEndpoint extends Endpoint {
|
|
|
13
18
|
* Pass a `type`: the score means something different per type, so an unfiltered read interleaves
|
|
14
19
|
* four scales into one order. Unfiltered is an overview of what exists, not a ranked worklist.
|
|
15
20
|
*/
|
|
16
|
-
getOpportunities(catalogId: CatalogId | string, pagination?: CursorPagination | null, type?: OpportunityType | null): Promise<CollectionResult<ProductOpportunity>>;
|
|
21
|
+
getOpportunities(catalogId: CatalogId | string, pagination?: CursorPagination | null, type?: OpportunityType | null, filters?: OpportunityFilters | null): Promise<CollectionResult<ProductOpportunity>>;
|
|
17
22
|
}
|
|
@@ -12,11 +12,17 @@ export class OpportunityEndpoint extends Endpoint {
|
|
|
12
12
|
* Pass a `type`: the score means something different per type, so an unfiltered read interleaves
|
|
13
13
|
* four scales into one order. Unfiltered is an overview of what exists, not a ranked worklist.
|
|
14
14
|
*/
|
|
15
|
-
async getOpportunities(catalogId, pagination = null, type = null) {
|
|
15
|
+
async getOpportunities(catalogId, pagination = null, type = null, filters = null) {
|
|
16
16
|
const queryString = new QueryString(path('/pulse/catalogs/:catalogId/opportunities', { catalogId: catalogId.toString() }));
|
|
17
17
|
if (type !== null) {
|
|
18
18
|
queryString.set('type', type);
|
|
19
19
|
}
|
|
20
|
+
if (filters?.brand !== undefined) {
|
|
21
|
+
queryString.set('brand', filters.brand);
|
|
22
|
+
}
|
|
23
|
+
if (filters?.sku !== undefined) {
|
|
24
|
+
queryString.set('sku', filters.sku);
|
|
25
|
+
}
|
|
20
26
|
queryString.addPagination(pagination);
|
|
21
27
|
return await this.requestCollection(queryString, ProductOpportunity.parse);
|
|
22
28
|
}
|
|
@@ -9,10 +9,10 @@ import { Endpoint } from '../../Service/Endpoint.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export declare class ProductMatchJobEndpoint extends Endpoint {
|
|
11
11
|
/**
|
|
12
|
-
* Every match job of a catalog, newest first. Pass `
|
|
13
|
-
* competitors; a vendor outside this catalog is rejected.
|
|
12
|
+
* Every match job of a catalog, newest first. Pass `vendorIds` to narrow to one or more of
|
|
13
|
+
* its competitors; a vendor outside this catalog is rejected.
|
|
14
14
|
*/
|
|
15
|
-
getByCatalog(catalogId: CatalogId | string, pagination?: CursorPagination | null,
|
|
15
|
+
getByCatalog(catalogId: CatalogId | string, pagination?: CursorPagination | null, vendorIds?: string[] | null): Promise<CollectionResult<ProductMatchJob>>;
|
|
16
16
|
/** Queue a matching run of the catalog against one competitor. */
|
|
17
17
|
requestMatching(catalogId: CatalogId | string, vendorId: string): Promise<void>;
|
|
18
18
|
/** The match job with this id, or null. */
|
|
@@ -8,14 +8,16 @@ import { Endpoint } from '../../Service/Endpoint.js';
|
|
|
8
8
|
*/
|
|
9
9
|
export class ProductMatchJobEndpoint extends Endpoint {
|
|
10
10
|
/**
|
|
11
|
-
* Every match job of a catalog, newest first. Pass `
|
|
12
|
-
* competitors; a vendor outside this catalog is rejected.
|
|
11
|
+
* Every match job of a catalog, newest first. Pass `vendorIds` to narrow to one or more of
|
|
12
|
+
* its competitors; a vendor outside this catalog is rejected.
|
|
13
13
|
*/
|
|
14
|
-
async getByCatalog(catalogId, pagination = null,
|
|
14
|
+
async getByCatalog(catalogId, pagination = null, vendorIds = null) {
|
|
15
15
|
const queryString = new QueryString(path('/pulse/catalogs/:catalogId/match-jobs', { catalogId: catalogId.toString() }));
|
|
16
16
|
queryString.addPagination(pagination);
|
|
17
|
-
if (
|
|
18
|
-
|
|
17
|
+
if (vendorIds !== null && vendorIds.length > 0) {
|
|
18
|
+
// Repeated param (`vendor=a&vendor=b`) — QueryString expands arrays, and the API reads it
|
|
19
|
+
// with getOptionalCollection.
|
|
20
|
+
queryString.set('vendor', vendorIds);
|
|
19
21
|
}
|
|
20
22
|
return await this.requestCollection(queryString, ProductMatchJob.parse);
|
|
21
23
|
}
|
|
@@ -15,6 +15,13 @@ export declare abstract class Endpoint {
|
|
|
15
15
|
private prepareParameters;
|
|
16
16
|
private formatParameters;
|
|
17
17
|
private formatKey;
|
|
18
|
+
/** Raw response bytes for this action — see `ITransport.requestBytes`. */
|
|
19
|
+
requestBytes(action: string, parameters?: Parameters, method?: string, signWithOauthToken?: boolean, headers?: Record<string, string>): Promise<Buffer>;
|
|
20
|
+
/**
|
|
21
|
+
* Send raw bytes as the request body. Metadata rides headers because the body IS the value —
|
|
22
|
+
* there is no envelope left to put it in.
|
|
23
|
+
*/
|
|
24
|
+
requestBinary(action: string, value: Buffer, headers?: Record<string, string>): Promise<void>;
|
|
18
25
|
requestCollection<T>(action: string | QueryString, parser: (input: any) => T, parameters?: Parameters, method?: string, signWithOauthToken?: boolean): Promise<CollectionResult<T>>;
|
|
19
26
|
requestObject<T>(action: string | QueryString, parameters: Parameters | undefined, parser: (input: Record<string, any>) => T, method?: string, signWithOauthToken?: boolean): Promise<ObjectResult<T>>;
|
|
20
27
|
private toApiError;
|
package/dist/Service/Endpoint.js
CHANGED
|
@@ -79,6 +79,17 @@ export class Endpoint {
|
|
|
79
79
|
}
|
|
80
80
|
return formattedKey;
|
|
81
81
|
}
|
|
82
|
+
/** Raw response bytes for this action — see `ITransport.requestBytes`. */
|
|
83
|
+
async requestBytes(action, parameters = null, method = 'GET', signWithOauthToken = true, headers = {}) {
|
|
84
|
+
return await this.httpClient.requestBytes(action, parameters, method, signWithOauthToken, headers);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Send raw bytes as the request body. Metadata rides headers because the body IS the value —
|
|
88
|
+
* there is no envelope left to put it in.
|
|
89
|
+
*/
|
|
90
|
+
async requestBinary(action, value, headers = {}) {
|
|
91
|
+
await this.httpClient.requestBytes(action, value, 'POST', true, headers);
|
|
92
|
+
}
|
|
82
93
|
async requestCollection(action, parser, parameters = null, method = 'GET', signWithOauthToken = true) {
|
|
83
94
|
parameters = this.prepareParameters(parameters);
|
|
84
95
|
if (action instanceof QueryString) {
|
|
@@ -15,6 +15,20 @@ export declare class StorageEndpoint extends Endpoint {
|
|
|
15
15
|
clearPool(projectEnvironmentId: string, storageType: StorageType, bucketKey: string): Promise<boolean>;
|
|
16
16
|
getItem(projectEnvironmentId: string, storageType: StorageType, bucketKey: string, storageItemKey: string): Promise<StorageItem | null>;
|
|
17
17
|
getBucketItem(storageBucketId: string, storageItemKey: string): Promise<StorageItem | null>;
|
|
18
|
+
/**
|
|
19
|
+
* The item's bytes, without decoding. Use for anything binary — an image, an archive — where the
|
|
20
|
+
* JSON envelope would either inflate it (base64) or destroy it (a UTF-8 decode replaces every
|
|
21
|
+
* invalid sequence, so bytes do not survive the round trip).
|
|
22
|
+
*/
|
|
23
|
+
getItemBytes(projectEnvironmentId: string, storageType: StorageType, bucketKey: string, storageItemKey: string): Promise<Buffer>;
|
|
24
|
+
/**
|
|
25
|
+
* Store raw bytes. Metadata travels as headers, since the body is the value.
|
|
26
|
+
*
|
|
27
|
+
* Pass `contentType` for anything that will be served to a browser — the CDN reads it back as the
|
|
28
|
+
* response `Content-Type`, and without it an image is delivered as `application/octet-stream`,
|
|
29
|
+
* which downloads rather than renders.
|
|
30
|
+
*/
|
|
31
|
+
setItemBytes(projectEnvironmentId: string, storageType: StorageType, bucketKey: string, storageItemKey: string, value: Buffer, expiration?: Date | null, contentType?: string | null): Promise<void>;
|
|
18
32
|
setItem(projectEnvironmentId: string, storageType: StorageType, bucketKey: string, storageItem: StorageItem): Promise<StorageItem>;
|
|
19
33
|
deleteItem(projectEnvironmentId: string, storageType: StorageType, bucketKey: string, storageItemKey: string): Promise<boolean>;
|
|
20
34
|
}
|
|
@@ -93,6 +93,33 @@ export class StorageEndpoint extends Endpoint {
|
|
|
93
93
|
throw ex;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* The item's bytes, without decoding. Use for anything binary — an image, an archive — where the
|
|
98
|
+
* JSON envelope would either inflate it (base64) or destroy it (a UTF-8 decode replaces every
|
|
99
|
+
* invalid sequence, so bytes do not survive the round trip).
|
|
100
|
+
*/
|
|
101
|
+
async getItemBytes(projectEnvironmentId, storageType, bucketKey, storageItemKey) {
|
|
102
|
+
const cmd = path('/projectenvironments/:projectEnvironmentId/storage/:storageType/:bucketKey/items/:key', { projectEnvironmentId, storageType, bucketKey, key: storageItemKey });
|
|
103
|
+
return await this.requestBytes(cmd);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Store raw bytes. Metadata travels as headers, since the body is the value.
|
|
107
|
+
*
|
|
108
|
+
* Pass `contentType` for anything that will be served to a browser — the CDN reads it back as the
|
|
109
|
+
* response `Content-Type`, and without it an image is delivered as `application/octet-stream`,
|
|
110
|
+
* which downloads rather than renders.
|
|
111
|
+
*/
|
|
112
|
+
async setItemBytes(projectEnvironmentId, storageType, bucketKey, storageItemKey, value, expiration = null, contentType = null) {
|
|
113
|
+
const cmd = path('/projectenvironments/:projectEnvironmentId/storage/:storageType/:bucketKey/items/:key', { projectEnvironmentId, storageType, bucketKey, key: storageItemKey });
|
|
114
|
+
const headers = {};
|
|
115
|
+
if (expiration !== null) {
|
|
116
|
+
headers['X-Attlaz-Expiration'] = expiration.toISOString();
|
|
117
|
+
}
|
|
118
|
+
if (contentType !== null) {
|
|
119
|
+
headers['X-Attlaz-Content-Type'] = contentType;
|
|
120
|
+
}
|
|
121
|
+
await this.requestBinary(cmd, value, headers);
|
|
122
|
+
}
|
|
96
123
|
async setItem(projectEnvironmentId, storageType, bucketKey, storageItem) {
|
|
97
124
|
const cmd = path('/projectenvironments/:projectEnvironmentId/storage/:storageType/:bucketKey/items/:key', { projectEnvironmentId, storageType, bucketKey, key: storageItem.key });
|
|
98
125
|
try {
|
package/dist/index.d.ts
CHANGED
|
@@ -123,7 +123,7 @@ export { PriceSuggestion, PriceSuggestionStatus } from './MarketPulse/Model/Pric
|
|
|
123
123
|
export { PricingRuleData } from './MarketPulse/Model/PricingRuleData.js';
|
|
124
124
|
export { PricingEndpoint } from './MarketPulse/Service/PricingEndpoint.js';
|
|
125
125
|
export { ProductOpportunity, OpportunityType } from './MarketPulse/Model/ProductOpportunity.js';
|
|
126
|
-
export { OpportunityEndpoint } from './MarketPulse/Service/OpportunityEndpoint.js';
|
|
126
|
+
export { OpportunityEndpoint, OpportunityFilters } from './MarketPulse/Service/OpportunityEndpoint.js';
|
|
127
127
|
export { ProductGroup, ProductGroupType } from './MarketPulse/Model/ProductGroup.js';
|
|
128
128
|
export { ProductGroupData } from './MarketPulse/Model/ProductGroupData.js';
|
|
129
129
|
export { ProductGroupEndpoint } from './MarketPulse/Service/ProductGroupEndpoint.js';
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.
|
|
1
|
+
export declare const VERSION = "1.111.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "1.
|
|
1
|
+
export const VERSION = "1.111.0";
|