@attlaz/client 1.83.0 → 1.85.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/Client.d.ts CHANGED
@@ -24,6 +24,8 @@ import { PlatformLanguageEndpoint } from './Service/PlatformLanguageEndpoint.js'
24
24
  import { ProjectEndpoint } from './Service/ProjectEndpoint.js';
25
25
  import { ProjectEnvironmentEndpoint } from './Service/ProjectEnvironmentEndpoint.js';
26
26
  import { ProviderTokenEndpoint } from './Service/ProviderTokenEndpoint.js';
27
+ import { VendorProductEndpoint } from './Service/Pulse/VendorProductEndpoint.js';
28
+ import { CrawlJobEndpoint } from './Service/Pulse/CrawlJobEndpoint.js';
27
29
  import { RunnerEndpoint } from './Service/RunnerEndpoint.js';
28
30
  import { RunnerPoolEndpoint } from './Service/RunnerPoolEndpoint.js';
29
31
  import { SearchEndpoint } from './Service/SearchEndpoint.js';
@@ -109,6 +111,8 @@ export declare class Client {
109
111
  getRunnerPoolEndpoint(): RunnerPoolEndpoint;
110
112
  getProviderTokenEndpoint(): ProviderTokenEndpoint;
111
113
  getFirewallEndpoint(): FirewallEndpoint;
114
+ getVendorProductEndpoint(): VendorProductEndpoint;
115
+ getCrawlJobEndpoint(): CrawlJobEndpoint;
112
116
  private getEndpoint;
113
117
  getVersion(): string;
114
118
  }
package/dist/Client.js CHANGED
@@ -24,6 +24,8 @@ import { PlatformLanguageEndpoint } from './Service/PlatformLanguageEndpoint.js'
24
24
  import { ProjectEndpoint } from './Service/ProjectEndpoint.js';
25
25
  import { ProjectEnvironmentEndpoint } from './Service/ProjectEnvironmentEndpoint.js';
26
26
  import { ProviderTokenEndpoint } from './Service/ProviderTokenEndpoint.js';
27
+ import { VendorProductEndpoint } from './Service/Pulse/VendorProductEndpoint.js';
28
+ import { CrawlJobEndpoint } from './Service/Pulse/CrawlJobEndpoint.js';
27
29
  import { RunnerEndpoint } from './Service/RunnerEndpoint.js';
28
30
  import { RunnerPoolEndpoint } from './Service/RunnerPoolEndpoint.js';
29
31
  import { SearchEndpoint } from './Service/SearchEndpoint.js';
@@ -75,6 +77,8 @@ export class Client {
75
77
  CollectionsEndpoint,
76
78
  RunnerPoolEndpoint,
77
79
  ProviderTokenEndpoint,
80
+ VendorProductEndpoint,
81
+ CrawlJobEndpoint,
78
82
  };
79
83
  apiEndpoint = 'https://api.attlaz.com';
80
84
  constructor(token = null, config = {}) {
@@ -256,6 +260,12 @@ export class Client {
256
260
  getFirewallEndpoint() {
257
261
  return this.getEndpoint('firewall', this.Store.FirewallEndpoint);
258
262
  }
263
+ getVendorProductEndpoint() {
264
+ return this.getEndpoint('vendor-product', this.Store.VendorProductEndpoint);
265
+ }
266
+ getCrawlJobEndpoint() {
267
+ return this.getEndpoint('crawl-job', this.Store.CrawlJobEndpoint);
268
+ }
259
269
  getEndpoint(key, ClassName) {
260
270
  if (!this.endpoints.has(key)) {
261
271
  try {
@@ -311,7 +311,7 @@ export class OAuthClient {
311
311
  if (this.version !== null) {
312
312
  requestData.setHeader('Attlaz-API-Version', this.version);
313
313
  }
314
- if ((method === 'POST' || method === 'DELETE' || method === 'PUT') && parameters !== null && parameters !== undefined) {
314
+ if ((method === 'POST' || method === 'DELETE' || method === 'PUT' || method === 'PATCH') && parameters !== null && parameters !== undefined) {
315
315
  if (typeof parameters === 'object') {
316
316
  requestData.body = JsonSerializable.stringify(parameters);
317
317
  }
@@ -0,0 +1,17 @@
1
+ import { ApiRecord } from '../ApiRecord.js';
2
+ export type CrawlJobStatus = 'pending' | 'started' | 'cancelled' | 'finished';
3
+ /**
4
+ * A Market Pulse crawl job: one run of scraping a vendor. Its lifecycle (pending → started →
5
+ * finished) drives the vendor's "last scraped" timestamp (`finishedAt`) and, on finish, triggers
6
+ * post-crawl processing (price history, product matching, …).
7
+ */
8
+ export declare class CrawlJob {
9
+ id: string;
10
+ vendorId: string;
11
+ status: CrawlJobStatus;
12
+ requestedAt: Date | null;
13
+ startedAt: Date | null;
14
+ finishedAt: Date | null;
15
+ constructor(id: string, vendorId: string);
16
+ static parse(raw: ApiRecord): CrawlJob;
17
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * A Market Pulse crawl job: one run of scraping a vendor. Its lifecycle (pending → started →
3
+ * finished) drives the vendor's "last scraped" timestamp (`finishedAt`) and, on finish, triggers
4
+ * post-crawl processing (price history, product matching, …).
5
+ */
6
+ export class CrawlJob {
7
+ id;
8
+ vendorId;
9
+ status;
10
+ requestedAt;
11
+ startedAt;
12
+ finishedAt;
13
+ constructor(id, vendorId) {
14
+ this.id = id;
15
+ this.vendorId = vendorId;
16
+ this.status = 'pending';
17
+ this.requestedAt = null;
18
+ this.startedAt = null;
19
+ this.finishedAt = null;
20
+ }
21
+ static parse(raw) {
22
+ const job = new CrawlJob(raw.id, (raw.vendor ?? raw.vendor_id));
23
+ job.status = raw.status ?? 'pending';
24
+ job.requestedAt = raw.requested_at ? new Date(raw.requested_at) : null;
25
+ job.startedAt = raw.started_at ? new Date(raw.started_at) : null;
26
+ job.finishedAt = raw.finished_at ? new Date(raw.finished_at) : null;
27
+ return job;
28
+ }
29
+ }
@@ -0,0 +1,26 @@
1
+ import { ApiRecord } from '../ApiRecord.js';
2
+ /**
3
+ * A competitor's product as tracked by Market Pulse — the `/pulse/vendors/:vendorId/products`
4
+ * resource. `identifier` is the product's stable id on the competitor's site (the upsert key).
5
+ */
6
+ export declare class VendorProduct {
7
+ id: string | null;
8
+ vendorId: string;
9
+ identifier: string;
10
+ name: string | null;
11
+ image: string | null;
12
+ gtin: string | null;
13
+ brand: string | null;
14
+ url: string | null;
15
+ sku: string | null;
16
+ currency: string | null;
17
+ price: number | null;
18
+ originalPrice: number | null;
19
+ shippingCost: number | null;
20
+ isInStock: boolean | null;
21
+ properties: Record<string, unknown> | null;
22
+ createdAt: Date | null;
23
+ updatedAt: Date | null;
24
+ constructor(vendorId: string, identifier: string);
25
+ static parse(raw: ApiRecord): VendorProduct;
26
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * A competitor's product as tracked by Market Pulse — the `/pulse/vendors/:vendorId/products`
3
+ * resource. `identifier` is the product's stable id on the competitor's site (the upsert key).
4
+ */
5
+ export class VendorProduct {
6
+ id = null;
7
+ vendorId;
8
+ identifier;
9
+ name = null;
10
+ image = null;
11
+ gtin = null;
12
+ brand = null;
13
+ url = null;
14
+ sku = null;
15
+ currency = null;
16
+ price = null;
17
+ originalPrice = null;
18
+ shippingCost = null;
19
+ isInStock = null;
20
+ properties = null;
21
+ createdAt = null;
22
+ updatedAt = null;
23
+ constructor(vendorId, identifier) {
24
+ this.vendorId = vendorId;
25
+ this.identifier = identifier;
26
+ }
27
+ static parse(raw) {
28
+ const product = new VendorProduct(raw.vendor, raw.identifier);
29
+ product.id = raw.id ?? null;
30
+ product.name = raw.name ?? null;
31
+ product.image = raw.image ?? null;
32
+ product.gtin = raw.gtin ?? null;
33
+ product.brand = raw.brand ?? null;
34
+ product.url = raw.url ?? null;
35
+ product.sku = raw.sku ?? null;
36
+ product.currency = raw.currency ?? null;
37
+ product.price = toNumber(raw.price);
38
+ product.originalPrice = toNumber(raw.original_price);
39
+ product.shippingCost = toNumber(raw.shipping_cost);
40
+ product.isInStock = typeof raw.is_in_stock === 'boolean' ? raw.is_in_stock : null;
41
+ product.properties = raw.properties ?? null;
42
+ product.createdAt = raw.created_at ? new Date(raw.created_at) : null;
43
+ product.updatedAt = raw.updated_at ? new Date(raw.updated_at) : null;
44
+ return product;
45
+ }
46
+ }
47
+ function toNumber(value) {
48
+ if (value === null || value === undefined || value === '') {
49
+ return null;
50
+ }
51
+ const n = Number(value);
52
+ return Number.isFinite(n) ? n : null;
53
+ }
@@ -6,6 +6,13 @@ import { ObjectResult } from '../Model/Result/ObjectResult.js';
6
6
  export declare abstract class Endpoint {
7
7
  protected httpClient: ITransport;
8
8
  constructor(httpClient: ITransport);
9
+ /**
10
+ * Prepare a request body for sending. Normally the keys are rewritten to the API's snake_case /
11
+ * id-stripped convention (see {@link formatParameters}). A caller that has already shaped the
12
+ * body — or that carries an opaque payload whose keys must NOT be rewritten, such as a flow
13
+ * run's `arguments` — sets `_formatted: true`; then the body is sent verbatim (minus the marker).
14
+ */
15
+ private prepareParameters;
9
16
  private formatParameters;
10
17
  private formatKey;
11
18
  requestCollection<T>(action: string | QueryString, parser: (input: any) => T, parameters?: Parameters, method?: string, signWithOauthToken?: boolean): Promise<CollectionResult<T>>;
@@ -13,6 +13,19 @@ export class Endpoint {
13
13
  constructor(httpClient) {
14
14
  this.httpClient = httpClient;
15
15
  }
16
+ /**
17
+ * Prepare a request body for sending. Normally the keys are rewritten to the API's snake_case /
18
+ * id-stripped convention (see {@link formatParameters}). A caller that has already shaped the
19
+ * body — or that carries an opaque payload whose keys must NOT be rewritten, such as a flow
20
+ * run's `arguments` — sets `_formatted: true`; then the body is sent verbatim (minus the marker).
21
+ */
22
+ prepareParameters(parameters) {
23
+ if (parameters !== null && typeof parameters === 'object' && parameters._formatted === true) {
24
+ const { _formatted, ...rest } = parameters;
25
+ return rest;
26
+ }
27
+ return this.formatParameters(parameters);
28
+ }
16
29
  formatParameters(data = null) {
17
30
  if (data === null || data === undefined) {
18
31
  return null;
@@ -65,7 +78,7 @@ export class Endpoint {
65
78
  return formattedKey;
66
79
  }
67
80
  async requestCollection(action, parser, parameters = null, method = 'GET', signWithOauthToken = true) {
68
- parameters = this.formatParameters(parameters);
81
+ parameters = this.prepareParameters(parameters);
69
82
  if (action instanceof QueryString) {
70
83
  action = action.build();
71
84
  }
@@ -94,7 +107,7 @@ export class Endpoint {
94
107
  return result;
95
108
  }
96
109
  async requestObject(action, parameters = null, parser, method = 'GET', signWithOauthToken = true) {
97
- parameters = this.formatParameters(parameters);
110
+ parameters = this.prepareParameters(parameters);
98
111
  if (action instanceof QueryString) {
99
112
  action = action.build();
100
113
  }
@@ -0,0 +1,15 @@
1
+ import { CrawlJob, CrawlJobStatus } from '../../Model/Pulse/CrawlJob.js';
2
+ import { Endpoint } from '../Endpoint.js';
3
+ /**
4
+ * Market Pulse crawl jobs — `/pulse/crawl-jobs`. Mirrors the PHP client's MarketPulse\CrawlJobEndpoint.
5
+ * A crawl job's lifecycle drives the vendor's "last scraped" timestamp and, on `finished`, triggers
6
+ * post-crawl processing (price history, product matching, price-index snapshots).
7
+ */
8
+ export declare class CrawlJobEndpoint extends Endpoint {
9
+ /** Create a new (pending) crawl job for a vendor. */
10
+ create(vendorId: string): Promise<CrawlJob>;
11
+ /** Update a crawl job's status ('started', 'finished', 'cancelled'). */
12
+ updateStatus(crawlJobId: string, status: CrawlJobStatus): Promise<CrawlJob>;
13
+ /** The crawl job with this id, or null. */
14
+ getById(crawlJobId: string): Promise<CrawlJob | null>;
15
+ }
@@ -0,0 +1,32 @@
1
+ import { CrawlJob } from '../../Model/Pulse/CrawlJob.js';
2
+ import { Endpoint } from '../Endpoint.js';
3
+ /**
4
+ * Market Pulse crawl jobs — `/pulse/crawl-jobs`. Mirrors the PHP client's MarketPulse\CrawlJobEndpoint.
5
+ * A crawl job's lifecycle drives the vendor's "last scraped" timestamp and, on `finished`, triggers
6
+ * post-crawl processing (price history, product matching, price-index snapshots).
7
+ */
8
+ export class CrawlJobEndpoint extends Endpoint {
9
+ /** Create a new (pending) crawl job for a vendor. */
10
+ async create(vendorId) {
11
+ const result = await this.requestObject('/pulse/crawl-jobs', { vendor: vendorId }, CrawlJob.parse, 'POST');
12
+ const job = result.getData();
13
+ if (job === null) {
14
+ throw new Error('Unable to create crawl job: empty response');
15
+ }
16
+ return job;
17
+ }
18
+ /** Update a crawl job's status ('started', 'finished', 'cancelled'). */
19
+ async updateStatus(crawlJobId, status) {
20
+ const result = await this.requestObject('/pulse/crawl-jobs/' + crawlJobId, { status }, CrawlJob.parse, 'PATCH');
21
+ const job = result.getData();
22
+ if (job === null) {
23
+ throw new Error('Unable to update crawl job: empty response');
24
+ }
25
+ return job;
26
+ }
27
+ /** The crawl job with this id, or null. */
28
+ async getById(crawlJobId) {
29
+ const result = await this.requestObject('/pulse/crawl-jobs/' + crawlJobId, null, CrawlJob.parse, 'GET');
30
+ return result.getData();
31
+ }
32
+ }
@@ -0,0 +1,14 @@
1
+ import { VendorProduct } from '../../Model/Pulse/VendorProduct.js';
2
+ import { Endpoint } from '../Endpoint.js';
3
+ /**
4
+ * Market Pulse competitor products — `/pulse/vendors/:vendorId/products`. Mirrors the PHP client's
5
+ * MarketPulse\VendorProductEndpoint so the (Node) scraper can upsert competitor products.
6
+ */
7
+ export declare class VendorProductEndpoint extends Endpoint {
8
+ /** The product with this identifier for the vendor, or null. */
9
+ getByIdentifier(vendorId: string, identifier: string): Promise<VendorProduct | null>;
10
+ /** Create a new vendor product. */
11
+ save(product: VendorProduct): Promise<VendorProduct>;
12
+ /** Update the mutable fields of an existing vendor product (JSON-patch style, per the API). */
13
+ update(product: VendorProduct): Promise<void>;
14
+ }
@@ -0,0 +1,49 @@
1
+ import { VendorProduct } from '../../Model/Pulse/VendorProduct.js';
2
+ import { Endpoint } from '../Endpoint.js';
3
+ /**
4
+ * Market Pulse competitor products — `/pulse/vendors/:vendorId/products`. Mirrors the PHP client's
5
+ * MarketPulse\VendorProductEndpoint so the (Node) scraper can upsert competitor products.
6
+ */
7
+ export class VendorProductEndpoint extends Endpoint {
8
+ /** The product with this identifier for the vendor, or null. */
9
+ async getByIdentifier(vendorId, identifier) {
10
+ const result = await this.requestCollection('/pulse/vendors/' + vendorId + '/products?identifier=' + encodeURIComponent(identifier), VendorProduct.parse);
11
+ const products = result.getData();
12
+ return products.length > 0 ? products[0] : null;
13
+ }
14
+ /** Create a new vendor product. */
15
+ async save(product) {
16
+ // camelCase keys are snake_cased by the base Endpoint (originalPrice -> original_price, ...).
17
+ const body = {
18
+ name: product.name,
19
+ image: product.image,
20
+ identifier: product.identifier,
21
+ gtin: product.gtin,
22
+ brand: product.brand,
23
+ url: product.url,
24
+ currency: product.currency,
25
+ price: product.price,
26
+ originalPrice: product.originalPrice,
27
+ shippingCost: product.shippingCost,
28
+ isInStock: product.isInStock,
29
+ properties: product.properties,
30
+ };
31
+ const result = await this.requestObject('/pulse/vendors/' + product.vendorId + '/products', body, VendorProduct.parse, 'POST');
32
+ return result.getData() ?? product;
33
+ }
34
+ /** Update the mutable fields of an existing vendor product (JSON-patch style, per the API). */
35
+ async update(product) {
36
+ const operations = [
37
+ { op: 'add', path: 'price', value: product.price },
38
+ { op: 'add', path: 'original_price', value: product.originalPrice },
39
+ { op: 'add', path: 'brand', value: product.brand },
40
+ { op: 'add', path: 'shipping_cost', value: product.shippingCost },
41
+ { op: 'add', path: 'is_in_stock', value: product.isInStock },
42
+ { op: 'add', path: 'url', value: product.url },
43
+ { op: 'add', path: 'gtin', value: product.gtin },
44
+ { op: 'add', path: 'image', value: product.image },
45
+ { op: 'add', path: 'properties', value: product.properties },
46
+ ];
47
+ await this.requestObject('/pulse/vendors/' + product.vendorId + '/products/' + product.id, operations, (raw) => raw, 'PATCH');
48
+ }
49
+ }
@@ -4,6 +4,10 @@ import { CollectionResult } from '../Model/Result/CollectionResult.js';
4
4
  import { Endpoint } from './Endpoint.js';
5
5
  export declare class RunnerPoolEndpoint extends Endpoint {
6
6
  getByProjectEnvironment(projectEnvironmentId: string, pagination?: CursorPagination | null): Promise<CollectionResult<RunnerPool>>;
7
+ create(projectEnvironmentId: string, pool: RunnerPoolCreate): Promise<RunnerPool | null>;
7
8
  getById(queueSpecificationId: string): Promise<RunnerPool | null>;
8
9
  redeploy(runnerPoolId: string): Promise<void>;
9
10
  }
11
+ export interface RunnerPoolCreate {
12
+ name: string;
13
+ }
@@ -8,6 +8,14 @@ export class RunnerPoolEndpoint extends Endpoint {
8
8
  const result = await this.requestCollection(queryString, RunnerPool.parse);
9
9
  return result;
10
10
  }
11
+ async create(projectEnvironmentId, pool) {
12
+ const queryString = new QueryString('/runner-pools/project-environments/' + projectEnvironmentId + '');
13
+ const body = {
14
+ name: pool.name,
15
+ };
16
+ const result = await this.requestObject(queryString, body, RunnerPool.parse, 'POST');
17
+ return result.getData();
18
+ }
11
19
  async getById(queueSpecificationId) {
12
20
  const queryString = new QueryString('/runner-pools/' + queueSpecificationId);
13
21
  const result = await this.requestObject(queryString, null, RunnerPool.parse);
package/dist/index.d.ts CHANGED
@@ -79,6 +79,10 @@ export { ObjectResult } from './Model/Result/ObjectResult.js';
79
79
  export { ResultError } from './Model/Result/ResultError.js';
80
80
  export { StorageInformation } from './Model/Storage/StorageInformation.js';
81
81
  export { StorageType } from './Model/Storage/StorageType.js';
82
+ export { VendorProduct } from './Model/Pulse/VendorProduct.js';
83
+ export { VendorProductEndpoint } from './Service/Pulse/VendorProductEndpoint.js';
84
+ export { CrawlJob, CrawlJobStatus } from './Model/Pulse/CrawlJob.js';
85
+ export { CrawlJobEndpoint } from './Service/Pulse/CrawlJobEndpoint.js';
82
86
  export { StorageItem } from './Model/Storage/StorageItem.js';
83
87
  export { StorageItemInformation } from './Model/Storage/StorageItemInformation.js';
84
88
  /** Queue * */
package/dist/index.js CHANGED
@@ -67,6 +67,10 @@ export { ObjectResult } from './Model/Result/ObjectResult.js';
67
67
  export { ResultError } from './Model/Result/ResultError.js';
68
68
  export { StorageInformation } from './Model/Storage/StorageInformation.js';
69
69
  export { StorageType } from './Model/Storage/StorageType.js';
70
+ export { VendorProduct } from './Model/Pulse/VendorProduct.js';
71
+ export { VendorProductEndpoint } from './Service/Pulse/VendorProductEndpoint.js';
72
+ export { CrawlJob } from './Model/Pulse/CrawlJob.js';
73
+ export { CrawlJobEndpoint } from './Service/Pulse/CrawlJobEndpoint.js';
70
74
  export { StorageItem } from './Model/Storage/StorageItem.js';
71
75
  export { StorageItemInformation } from './Model/Storage/StorageItemInformation.js';
72
76
  /** Queue * */
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "1.82.0";
1
+ export declare const VERSION = "1.84.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "1.82.0";
1
+ export const VERSION = "1.84.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attlaz/client",
3
- "version": "1.83.0",
3
+ "version": "1.85.0",
4
4
  "description": "Javascript Client to access Attlaz API",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",