@reactionary/provider-commercetools 0.0.36 → 0.0.38

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.
@@ -1,10 +1,11 @@
1
- import { CartSchema, IdentitySchema, InventorySchema, PriceSchema, ProductSchema, SearchResultSchema } from "@reactionary/core";
1
+ import { CartSchema, IdentitySchema, InventorySchema, PriceSchema, ProductSchema, SearchResultSchema, CategorySchema } from "@reactionary/core";
2
2
  import { CommercetoolsSearchProvider } from "../providers/search.provider";
3
3
  import { CommercetoolsProductProvider } from "../providers/product.provider";
4
4
  import { CommercetoolsIdentityProvider } from "../providers/identity.provider";
5
5
  import { CommercetoolsCartProvider } from "../providers/cart.provider";
6
6
  import { CommercetoolsInventoryProvider } from "../providers/inventory.provider";
7
7
  import { CommercetoolsPriceProvider } from "../providers/price.provider";
8
+ import { CommercetoolsCategoryProvider } from "../providers/category.provider";
8
9
  function withCommercetoolsCapabilities(configuration, capabilities) {
9
10
  return (cache) => {
10
11
  const client = {};
@@ -26,6 +27,9 @@ function withCommercetoolsCapabilities(configuration, capabilities) {
26
27
  if (capabilities.price) {
27
28
  client.price = new CommercetoolsPriceProvider(configuration, PriceSchema, cache);
28
29
  }
30
+ if (capabilities.category) {
31
+ client.category = new CommercetoolsCategoryProvider(configuration, CategorySchema, cache);
32
+ }
29
33
  return client;
30
34
  };
31
35
  }
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@reactionary/provider-commercetools",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "main": "index.js",
5
5
  "types": "src/index.d.ts",
6
6
  "dependencies": {
7
- "@reactionary/core": "0.0.36",
7
+ "@reactionary/core": "0.0.38",
8
+ "@reactionary/otel": "0.0.38",
8
9
  "zod": "4.0.0-beta.20250430T185432",
9
10
  "@commercetools/ts-client": "^3.2.2",
10
11
  "@commercetools/platform-sdk": "^8.8.0"
@@ -3,11 +3,13 @@ import {
3
3
  CartProvider
4
4
  } from "@reactionary/core";
5
5
  import { CommercetoolsClient } from "../core/client";
6
+ import { traced } from "@reactionary/otel";
6
7
  class CommercetoolsCartProvider extends CartProvider {
7
8
  constructor(config, schema, cache) {
8
9
  super(schema, cache);
9
10
  this.config = config;
10
11
  }
12
+ @traced()
11
13
  async getById(payload, session) {
12
14
  if (!payload.cart.key) {
13
15
  const result = this.newModel();
@@ -19,8 +21,9 @@ class CommercetoolsCartProvider extends CartProvider {
19
21
  }
20
22
  const client = this.getClient(session);
21
23
  const remote = await client.withId({ ID: payload.cart.key }).get().execute();
22
- return this.composeCart(remote.body);
24
+ return this.parseSingle(remote.body, session);
23
25
  }
26
+ @traced()
24
27
  async add(payload, session) {
25
28
  const client = this.getClient(session);
26
29
  let id = payload.cart.key;
@@ -28,8 +31,9 @@ class CommercetoolsCartProvider extends CartProvider {
28
31
  if (!id) {
29
32
  const remoteCart = await client.post({
30
33
  body: {
31
- currency: "USD",
32
- country: "US"
34
+ currency: session.languageContext.currencyCode || "USD",
35
+ country: session.languageContext.countryCode || "US",
36
+ locale: session.languageContext.locale
33
37
  }
34
38
  }).execute();
35
39
  id = remoteCart.body.id;
@@ -50,8 +54,9 @@ class CommercetoolsCartProvider extends CartProvider {
50
54
  ]
51
55
  }
52
56
  }).execute();
53
- return this.composeCart(remoteAdd.body);
57
+ return this.parseSingle(remoteAdd.body, session);
54
58
  }
59
+ @traced()
55
60
  async remove(payload, session) {
56
61
  const client = this.getClient(session);
57
62
  const existing = await client.withId({ ID: payload.cart.key }).get().execute();
@@ -66,8 +71,9 @@ class CommercetoolsCartProvider extends CartProvider {
66
71
  ]
67
72
  }
68
73
  }).execute();
69
- return this.composeCart(remote.body);
74
+ return this.parseSingle(remote.body, session);
70
75
  }
76
+ @traced()
71
77
  async changeQuantity(payload, session) {
72
78
  const client = this.getClient(session);
73
79
  const existing = await client.withId({ ID: payload.cart.key }).get().execute();
@@ -83,8 +89,9 @@ class CommercetoolsCartProvider extends CartProvider {
83
89
  ]
84
90
  }
85
91
  }).execute();
86
- return this.composeCart(remote.body);
92
+ return this.parseSingle(remote.body, session);
87
93
  }
94
+ @traced()
88
95
  getClient(session) {
89
96
  const client = new CommercetoolsClient(this.config).getClient(
90
97
  session.identity.token
@@ -92,18 +99,76 @@ class CommercetoolsCartProvider extends CartProvider {
92
99
  const cartClient = client.withProjectKey({ projectKey: this.config.projectKey }).carts();
93
100
  return cartClient;
94
101
  }
95
- composeCart(remote) {
102
+ @traced()
103
+ parseSingle(remote, session) {
96
104
  const result = this.newModel();
97
105
  result.identifier.key = remote.id;
106
+ result.name = remote.custom?.fields["name"] || "";
107
+ result.description = remote.custom?.fields["description"] || "";
108
+ const grandTotal = remote.totalPrice.centAmount || 0;
109
+ const shippingTotal = remote.shippingInfo?.price.centAmount || 0;
110
+ const productTotal = grandTotal - shippingTotal;
111
+ const taxTotal = remote.taxedPrice?.totalTax?.centAmount || 0;
112
+ const discountTotal = remote.discountOnTotalPrice?.discountedAmount.centAmount || 0;
113
+ const surchargeTotal = 0;
114
+ const currency = remote.totalPrice.currencyCode;
115
+ result.price = {
116
+ totalTax: {
117
+ value: taxTotal / 100,
118
+ currency
119
+ },
120
+ totalDiscount: {
121
+ value: discountTotal / 100,
122
+ currency
123
+ },
124
+ totalSurcharge: {
125
+ value: surchargeTotal / 100,
126
+ currency
127
+ },
128
+ totalShipping: {
129
+ value: shippingTotal / 100,
130
+ currency: remote.shippingInfo?.price.currencyCode
131
+ },
132
+ totalProductPrice: {
133
+ value: productTotal / 100,
134
+ currency
135
+ },
136
+ grandTotal: {
137
+ value: grandTotal / 100,
138
+ currency
139
+ }
140
+ };
98
141
  for (const remoteItem of remote.lineItems) {
99
142
  const item = CartItemSchema.parse({});
100
143
  item.identifier.key = remoteItem.id;
101
144
  item.product.key = remoteItem.productId;
102
145
  item.quantity = remoteItem.quantity;
146
+ const unitPrice = remoteItem.price.value.centAmount;
147
+ const totalPrice = remoteItem.totalPrice.centAmount || 0;
148
+ const totalDiscount = remoteItem.price.discounted?.value.centAmount || 0;
149
+ const unitDiscount = totalDiscount / remoteItem.quantity;
150
+ item.price = {
151
+ unitPrice: {
152
+ value: unitPrice / 100,
153
+ currency
154
+ },
155
+ unitDiscount: {
156
+ value: unitDiscount / 100,
157
+ currency
158
+ },
159
+ totalPrice: {
160
+ value: (totalPrice || 0) / 100,
161
+ currency
162
+ },
163
+ totalDiscount: {
164
+ value: totalDiscount / 100,
165
+ currency
166
+ }
167
+ };
103
168
  result.items.push(item);
104
169
  }
105
170
  result.meta = {
106
- cache: { hit: false, key: remote.id },
171
+ cache: { hit: false, key: this.generateCacheKeySingle(result.identifier, session) },
107
172
  placeholder: false
108
173
  };
109
174
  return this.assert(result);
@@ -0,0 +1,167 @@
1
+ import { CategoryProvider, createPaginatedResponseSchema } from "@reactionary/core";
2
+ import { CommercetoolsClient } from "../core/client";
3
+ import { traced } from "@reactionary/otel";
4
+ class CommercetoolsCategoryProvider extends CategoryProvider {
5
+ constructor(config, schema, cache) {
6
+ super(schema, cache);
7
+ this.config = config;
8
+ }
9
+ getClient(session) {
10
+ const client = new CommercetoolsClient(this.config).getClient(session.identity.token).withProjectKey({ projectKey: this.config.projectKey }).categories();
11
+ return client;
12
+ }
13
+ @traced()
14
+ async getById(payload, session) {
15
+ const client = this.getClient(session);
16
+ try {
17
+ const response = await client.withKey({ key: payload.id.key }).get().execute();
18
+ return this.parseSingle(response.body, session);
19
+ } catch (error) {
20
+ console.error(`Error fetching category by ID ${payload.id.key}:`, error);
21
+ const dummyCategory = this.newModel();
22
+ dummyCategory.meta.placeholder = true;
23
+ dummyCategory.identifier = { key: payload.id.key };
24
+ return dummyCategory;
25
+ }
26
+ }
27
+ @traced()
28
+ async getBySlug(payload, session) {
29
+ const client = this.getClient(session);
30
+ try {
31
+ const response = await client.get({
32
+ queryArgs: {
33
+ where: `slug(${session.languageContext.locale}=:slug)`,
34
+ "var.slug": payload.slug,
35
+ storeProjection: session.storeIdentifier.key,
36
+ limit: 1,
37
+ withTotal: false
38
+ }
39
+ }).execute();
40
+ if (response.body.results.length === 0) {
41
+ return null;
42
+ }
43
+ return this.parseSingle(response.body.results[0], session);
44
+ } catch (error) {
45
+ console.error(`Error fetching category by slug:`, error);
46
+ return null;
47
+ }
48
+ }
49
+ @traced()
50
+ async getBreadcrumbPathToCategory(payload, session) {
51
+ const client = this.getClient(session);
52
+ const path = [];
53
+ try {
54
+ const response = await client.withKey({ key: payload.id.key }).get({
55
+ queryArgs: {
56
+ expand: "ancestors[*]"
57
+ }
58
+ }).execute();
59
+ const category = this.parseSingle(response.body, session);
60
+ for (const anc of response.body.ancestors || []) {
61
+ if (anc.obj) {
62
+ const parsedAnc = this.parseSingle(anc.obj, session);
63
+ path.push(parsedAnc);
64
+ }
65
+ }
66
+ ;
67
+ path.push(category);
68
+ } catch (error) {
69
+ console.error(`Error fetching category path for ${payload.id.key}:`, error);
70
+ }
71
+ return path;
72
+ }
73
+ @traced()
74
+ async findChildCategories(payload, session) {
75
+ const client = this.getClient(session);
76
+ try {
77
+ const parentCategory = await client.withKey({ key: payload.parentId.key }).get().execute();
78
+ const response = await client.get({
79
+ queryArgs: {
80
+ where: "parent(id = :parentId)",
81
+ "var.parentId": parentCategory.body.id,
82
+ limit: payload.paginationOptions.pageSize,
83
+ offset: (payload.paginationOptions.pageNumber - 1) * payload.paginationOptions.pageSize,
84
+ sort: "orderHint asc",
85
+ storeProjection: session.storeIdentifier.key
86
+ }
87
+ }).execute();
88
+ const result = this.parsePaginatedResult(response.body, session);
89
+ result.meta = {
90
+ cache: { hit: false, key: this.generateCacheKeyPaginatedResult("children-of-" + payload.parentId.key, result, session) },
91
+ placeholder: false
92
+ };
93
+ return result;
94
+ } catch (error) {
95
+ console.error(`Error fetching category path for ${payload.parentId.key}:`, error);
96
+ }
97
+ return createPaginatedResponseSchema(this.schema).parse({});
98
+ }
99
+ @traced()
100
+ async findTopCategories(payload, session) {
101
+ const client = this.getClient(session);
102
+ try {
103
+ const response = await client.get({
104
+ queryArgs: {
105
+ where: "parent is not defined",
106
+ limit: payload.paginationOptions.pageSize,
107
+ offset: (payload.paginationOptions.pageNumber - 1) * payload.paginationOptions.pageSize,
108
+ sort: "orderHint asc",
109
+ storeProjection: session.storeIdentifier.key
110
+ }
111
+ }).execute();
112
+ const result = this.parsePaginatedResult(response.body, session);
113
+ result.meta = {
114
+ cache: { hit: false, key: this.generateCacheKeyPaginatedResult("top", result, session) },
115
+ placeholder: false
116
+ };
117
+ return result;
118
+ } catch (error) {
119
+ console.error(`Error fetching category top categories:`, error);
120
+ }
121
+ return createPaginatedResponseSchema(this.schema).parse({});
122
+ }
123
+ @traced()
124
+ parseSingle(_body, session) {
125
+ const body = _body;
126
+ const languageContext = session.languageContext;
127
+ const model = this.newModel();
128
+ model.identifier = { key: body.key };
129
+ model.name = body.name[languageContext.locale] || "No Name";
130
+ model.slug = body.slug ? body.slug[languageContext.locale] || "" : "";
131
+ model.text = body.description ? body.description[languageContext.locale] || "" : "";
132
+ model.parentCategory = body.parent && body.parent.obj && body.parent.obj?.key ? { key: body.parent.obj.key } : void 0;
133
+ model.images = (body.assets || []).filter((asset) => asset.sources.length > 0).filter((x) => x.sources[0].contentType?.startsWith("image/")).map((asset) => {
134
+ return {
135
+ sourceUrl: asset.sources[0].uri,
136
+ altText: asset.description?.[languageContext.locale] || asset.name[languageContext.locale] || "",
137
+ height: asset.sources[0].dimensions?.h || 0,
138
+ width: asset.sources[0].dimensions?.w || 0
139
+ };
140
+ });
141
+ model.meta = {
142
+ cache: { hit: false, key: this.generateCacheKeySingle(model.identifier, session) },
143
+ placeholder: false
144
+ };
145
+ return this.assert(model);
146
+ }
147
+ @traced()
148
+ parsePaginatedResult(_body, session) {
149
+ const body = _body;
150
+ const items = body.results.map((x) => this.parseSingle(x, session));
151
+ const result = createPaginatedResponseSchema(this.schema).parse({
152
+ meta: {
153
+ cache: { hit: false, key: "unknown" },
154
+ placeholder: false
155
+ },
156
+ pageNumber: Math.floor(body.offset / body.count) + 1,
157
+ pageSize: body.count,
158
+ totalCount: body.total || 0,
159
+ totalPages: Math.ceil((body.total ?? 0) / body.count),
160
+ items
161
+ });
162
+ return result;
163
+ }
164
+ }
165
+ export {
166
+ CommercetoolsCategoryProvider
167
+ };
@@ -0,0 +1,7 @@
1
+ export * from "./cart.provider";
2
+ export * from "./category.provider";
3
+ export * from "./identity.provider";
4
+ export * from "./inventory.provider";
5
+ export * from "./price.provider";
6
+ export * from "./product.provider";
7
+ export * from "./search.provider";
@@ -13,20 +13,32 @@ class CommercetoolsInventoryProvider extends InventoryProvider {
13
13
  );
14
14
  const remote = await client.withProjectKey({ projectKey: this.config.projectKey }).inventory().get({
15
15
  queryArgs: {
16
- where: "sku=:sku",
17
- "var.sku": payload.sku
16
+ where: `sku=${payload.sku}`
18
17
  }
19
18
  }).execute();
20
- const base = this.newModel();
21
- if (remote.body.results.length > 0) {
22
- const inv = remote.body.results[0];
23
- base.quantity = inv.availableQuantity;
19
+ return this.parseSingle(remote.body, session);
20
+ }
21
+ parseSingle(_body, session) {
22
+ const body = _body;
23
+ const model = this.newModel();
24
+ model.identifier = {
25
+ sku: { key: body.sku },
26
+ channelId: {
27
+ key: body.supplyChannel?.id || "online"
28
+ }
29
+ };
30
+ model.sku = body.sku;
31
+ model.quantity = body.availableQuantity;
32
+ if (model.quantity > 0) {
33
+ model.status = "inStock";
34
+ } else {
35
+ model.status = "outOfStock";
24
36
  }
25
- base.meta = {
26
- cache: { hit: false, key: payload.sku },
37
+ model.meta = {
38
+ cache: { hit: false, key: this.generateCacheKeySingle(model.identifier, session) },
27
39
  placeholder: false
28
40
  };
29
- return this.assert(base);
41
+ return this.assert(model);
30
42
  }
31
43
  }
32
44
  export {
@@ -1,36 +1,74 @@
1
- import { PriceProvider } from "@reactionary/core";
1
+ import { PriceProvider, TieredPriceSchema } from "@reactionary/core";
2
2
  import { CommercetoolsClient } from "../core/client";
3
3
  class CommercetoolsPriceProvider extends PriceProvider {
4
4
  constructor(config, schema, cache) {
5
5
  super(schema, cache);
6
6
  this.config = config;
7
7
  }
8
+ getClient(session) {
9
+ return new CommercetoolsClient(this.config).getClient(session.identity?.token).withProjectKey({ projectKey: this.config.projectKey }).standalonePrices();
10
+ }
11
+ async getBySKUs(payload, session) {
12
+ const client = this.getClient(session);
13
+ const queryArgs = {
14
+ where: "sku in (:skus)",
15
+ "var.skus": payload.map((p) => p.sku.key)
16
+ };
17
+ const response = await client.get({
18
+ queryArgs
19
+ }).execute();
20
+ const result = [];
21
+ for (const p of payload) {
22
+ const matched = response.body.results.filter((x) => x.sku === p.sku.key && x.value.currencyCode === session.languageContext.currencyCode);
23
+ if (matched && matched.length > 0) {
24
+ result.push(this.parseSingle(matched[0], session));
25
+ } else {
26
+ result.push(this.getEmptyPriceResult(p.sku.key, session.languageContext.currencyCode));
27
+ }
28
+ }
29
+ return result;
30
+ }
8
31
  async getBySKU(payload, session) {
9
- const client = new CommercetoolsClient(this.config).getClient(
10
- session.identity?.token
11
- );
32
+ const client = this.getClient(session);
12
33
  const queryArgs = {
13
34
  where: "sku=:sku",
14
35
  "var.sku": payload.sku.key
15
36
  };
16
- const remote = await client.withProjectKey({ projectKey: this.config.projectKey }).standalonePrices().get({
37
+ const remote = await client.get({
17
38
  queryArgs
18
39
  }).execute();
40
+ const matched = remote.body.results.filter((x) => x.value.currencyCode === session.languageContext.currencyCode);
41
+ if (matched && matched.length > 0) {
42
+ return this.parseSingle(matched[0], session);
43
+ }
44
+ return this.getEmptyPriceResult(payload.sku.key, session.languageContext.currencyCode);
45
+ }
46
+ parseSingle(_body, session) {
47
+ const body = _body;
19
48
  const base = this.newModel();
20
- if (remote.body.results.length > 0) {
21
- const matched = remote.body.results[0];
22
- base.value = {
23
- cents: matched.value.centAmount,
24
- currency: "USD"
25
- };
49
+ base.unitPrice = {
50
+ value: body.value.centAmount / 100,
51
+ currency: body.value.currencyCode
52
+ };
53
+ if (body.tiers && body.tiers.length > 0) {
54
+ const p = body.tiers.map((x) => {
55
+ const tp = TieredPriceSchema.parse({});
56
+ tp.minimumQuantity = x.minimumQuantity;
57
+ tp.price = {
58
+ value: x.value.centAmount / 100,
59
+ currency: x.value.currencyCode
60
+ };
61
+ return tp;
62
+ });
63
+ base.tieredPrices = p;
26
64
  }
27
65
  base.identifier = {
28
66
  sku: {
29
- key: payload.sku.key
67
+ key: body.sku
30
68
  }
31
69
  };
32
70
  base.meta = {
33
- cache: { hit: false, key: payload.sku.key },
71
+ cache: { hit: false, key: this.generateCacheKeySingle(base.identifier, session) },
34
72
  placeholder: false
35
73
  };
36
74
  return this.assert(base);
@@ -7,14 +7,17 @@ class CommercetoolsProductProvider extends ProductProvider {
7
7
  super(schema, cache);
8
8
  this.config = config;
9
9
  }
10
- async getById(payload, _session) {
11
- const client = new CommercetoolsClient(this.config).createAnonymousClient();
12
- const remote = await client.withProjectKey({ projectKey: this.config.projectKey }).productProjections().withId({ ID: payload.id }).get().execute();
13
- return this.parse(remote.body);
10
+ getClient(session) {
11
+ return new CommercetoolsClient(this.config).getClient(session.identity?.token).withProjectKey({ projectKey: this.config.projectKey }).productProjections();
14
12
  }
15
- async getBySlug(payload, _session) {
16
- const client = new CommercetoolsClient(this.config).createAnonymousClient();
17
- const remote = await client.withProjectKey({ projectKey: this.config.projectKey }).productProjections().get({
13
+ async getById(payload, session) {
14
+ const client = this.getClient(session);
15
+ const remote = await client.withId({ ID: payload.id }).get().execute();
16
+ return this.parseSingle(remote.body, session);
17
+ }
18
+ async getBySlug(payload, session) {
19
+ const client = this.getClient(session);
20
+ const remote = await client.get({
18
21
  queryArgs: {
19
22
  where: "slug(en-US = :slug)",
20
23
  "var.slug": payload.slug
@@ -23,15 +26,16 @@ class CommercetoolsProductProvider extends ProductProvider {
23
26
  if (remote.body.results.length === 0) {
24
27
  throw new Error(`Product with slug '${payload.slug}' not found`);
25
28
  }
26
- return this.parse(remote.body.results[0]);
29
+ return this.parseSingle(remote.body.results[0], session);
27
30
  }
28
- parse(data) {
31
+ parseSingle(dataIn, session) {
32
+ const data = dataIn;
29
33
  const base = this.newModel();
30
34
  base.identifier = { key: data.id };
31
- base.name = data.name["en-US"];
32
- base.slug = data.slug["en-US"];
35
+ base.name = data.name[session.languageContext.locale];
36
+ base.slug = data.slug[session.languageContext.locale];
33
37
  if (data.description) {
34
- base.description = data.description["en-US"];
38
+ base.description = data.description[session.languageContext.locale];
35
39
  }
36
40
  if (data.masterVariant.images && data.masterVariant.images.length > 0) {
37
41
  base.image = data.masterVariant.images[0].url;
@@ -48,7 +52,7 @@ class CommercetoolsProductProvider extends ProductProvider {
48
52
  }
49
53
  }
50
54
  base.meta = {
51
- cache: { hit: false, key: data.id },
55
+ cache: { hit: false, key: this.generateCacheKeySingle(base.identifier, session) },
52
56
  placeholder: false
53
57
  };
54
58
  return this.assert(base);
@@ -7,26 +7,26 @@ class CommercetoolsSearchProvider extends SearchProvider {
7
7
  super(schema, cache);
8
8
  this.config = config;
9
9
  }
10
- async queryByTerm(payload, _session) {
10
+ async queryByTerm(payload, session) {
11
11
  const client = new CommercetoolsClient(this.config).createAnonymousClient();
12
12
  const remote = await client.withProjectKey({ projectKey: this.config.projectKey }).productProjections().search().get({
13
13
  queryArgs: {
14
14
  limit: payload.search.pageSize,
15
- offset: payload.search.pageSize * payload.search.page,
16
- ["text.en-US"]: payload.search.term
15
+ offset: (payload.search.page - 1) * payload.search.pageSize,
16
+ [`text.${session.languageContext.locale}`]: payload.search.term
17
17
  }
18
18
  }).execute();
19
- return this.parseSearchResult(remote, payload);
19
+ return this.parseSearchResult(remote, payload, session);
20
20
  }
21
- parseSearchResult(remote, payload) {
21
+ parseSearchResult(remote, payload, session) {
22
22
  const result = this.newModel();
23
23
  const remoteData = remote;
24
24
  result.identifier = payload.search;
25
25
  for (const p of remoteData.body.results) {
26
26
  const product = {
27
27
  identifier: { key: p.id },
28
- name: p.name["en-US"],
29
- slug: p.slug?.["en-US"] || p.id,
28
+ name: p.name[session.languageContext.locale] || p.id,
29
+ slug: p.slug?.[session.languageContext.locale] || p.id,
30
30
  image: p.masterVariant.images?.[0]?.url || "https://placehold.co/400"
31
31
  };
32
32
  result.products.push(product);
@@ -5,7 +5,8 @@ const CommercetoolsCapabilitiesSchema = CapabilitiesSchema.pick({
5
5
  identity: true,
6
6
  cart: true,
7
7
  inventory: true,
8
- price: true
8
+ price: true,
9
+ category: true
9
10
  }).partial();
10
11
  export {
11
12
  CommercetoolsCapabilitiesSchema
@@ -10,5 +10,5 @@ export declare class CommercetoolsCartProvider<T extends Cart = Cart> extends Ca
10
10
  remove(payload: CartMutationItemRemove, session: Session): Promise<T>;
11
11
  changeQuantity(payload: CartMutationItemQuantityChange, session: Session): Promise<T>;
12
12
  protected getClient(session: Session): import("@commercetools/platform-sdk").ByProjectKeyCartsRequestBuilder;
13
- protected composeCart(remote: CTCart): T;
13
+ protected parseSingle(remote: CTCart, session: Session): T;
14
14
  }
@@ -0,0 +1,95 @@
1
+ import { CategoryProvider, Cache, Category, Session, CategoryQueryById, CategoryQueryBySlug, CategoryQueryForBreadcrumb, CategoryQueryForChildCategories, CategoryQueryForTopCategories } from "@reactionary/core";
2
+ import z from "zod";
3
+ import { CommercetoolsConfiguration } from "../schema/configuration.schema";
4
+ import { ByProjectKeyCategoriesRequestBuilder } from "@commercetools/platform-sdk";
5
+ export declare class CommercetoolsCategoryProvider<T extends Category = Category> extends CategoryProvider<T> {
6
+ protected config: CommercetoolsConfiguration;
7
+ constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, cache: Cache);
8
+ protected getClient(session: Session): ByProjectKeyCategoriesRequestBuilder;
9
+ /**
10
+ * Look it up by the category ID (key in commercetools), and if not there, return a placeholder.
11
+ * @param id
12
+ * @param session
13
+ * @returns
14
+ */
15
+ getById(payload: CategoryQueryById, session: Session): Promise<T>;
16
+ /**
17
+ * Resolve the category by slug, in the users current locale.
18
+ * @param slug
19
+ * @param session
20
+ * @returns
21
+ */
22
+ getBySlug(payload: CategoryQueryBySlug, session: Session): Promise<T | null>;
23
+ /**
24
+ * Returns the breadcrumb path to the category, i.e. all parents up to the root.
25
+ * The returned order is from root to leaf.
26
+ * @param id
27
+ * @param session
28
+ * @returns
29
+ */
30
+ getBreadcrumbPathToCategory(payload: CategoryQueryForBreadcrumb, session: Session): Promise<T[]>;
31
+ /**
32
+ * Returns a page of child categories for the given parent category ID.
33
+ * You must provide the pagination options to control the size of the result set.
34
+ * If you expect your frontend will load many many categories, consider adding a "load more" button, or lazy load the next page.
35
+ *
36
+ * This is cached by ID + page number + page size + locale + store
37
+ * @param id
38
+ * @param paginationOptions
39
+ * @param session
40
+ * @returns
41
+ */
42
+ findChildCategories(payload: CategoryQueryForChildCategories, session: Session): Promise<{
43
+ meta: {
44
+ [x: string]: unknown;
45
+ cache: {
46
+ [x: string]: unknown;
47
+ hit: boolean;
48
+ key: string;
49
+ };
50
+ placeholder: boolean;
51
+ };
52
+ pageNumber: number;
53
+ pageSize: number;
54
+ totalCount: number;
55
+ totalPages: number;
56
+ items: T[];
57
+ }>;
58
+ findTopCategories(payload: CategoryQueryForTopCategories, session: Session): Promise<{
59
+ meta: {
60
+ [x: string]: unknown;
61
+ cache: {
62
+ [x: string]: unknown;
63
+ hit: boolean;
64
+ key: string;
65
+ };
66
+ placeholder: boolean;
67
+ };
68
+ pageNumber: number;
69
+ pageSize: number;
70
+ totalCount: number;
71
+ totalPages: number;
72
+ items: T[];
73
+ }>;
74
+ /**
75
+ * Handler for parsing a response from a remote provider and converting it
76
+ * into the typed domain model.
77
+ */
78
+ protected parseSingle(_body: unknown, session: Session): T;
79
+ protected parsePaginatedResult(_body: unknown, session: Session): {
80
+ meta: {
81
+ [x: string]: unknown;
82
+ cache: {
83
+ [x: string]: unknown;
84
+ hit: boolean;
85
+ key: string;
86
+ };
87
+ placeholder: boolean;
88
+ };
89
+ pageNumber: number;
90
+ pageSize: number;
91
+ totalCount: number;
92
+ totalPages: number;
93
+ items: T[];
94
+ };
95
+ }
@@ -0,0 +1,7 @@
1
+ export * from './cart.provider';
2
+ export * from './category.provider';
3
+ export * from './identity.provider';
4
+ export * from './inventory.provider';
5
+ export * from './price.provider';
6
+ export * from './product.provider';
7
+ export * from './search.provider';
@@ -5,4 +5,5 @@ export declare class CommercetoolsInventoryProvider<T extends Inventory = Invent
5
5
  protected config: CommercetoolsConfiguration;
6
6
  constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, cache: Cache);
7
7
  getBySKU(payload: InventoryQuery, session: Session): Promise<T>;
8
+ protected parseSingle(_body: unknown, session: Session): T;
8
9
  }
@@ -4,5 +4,8 @@ import { CommercetoolsConfiguration } from '../schema/configuration.schema';
4
4
  export declare class CommercetoolsPriceProvider<T extends Price = Price> extends PriceProvider<T> {
5
5
  protected config: CommercetoolsConfiguration;
6
6
  constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, cache: Cache);
7
+ getClient(session: Session): import("@commercetools/platform-sdk").ByProjectKeyStandalonePricesRequestBuilder;
8
+ getBySKUs(payload: PriceQueryBySku[], session: Session): Promise<T[]>;
7
9
  getBySKU(payload: PriceQueryBySku, session: Session): Promise<T>;
10
+ protected parseSingle(_body: unknown, session: Session): T;
8
11
  }
@@ -1,11 +1,11 @@
1
1
  import { ProductProvider, Product, ProductQueryById, ProductQueryBySlug, Session, Cache } from '@reactionary/core';
2
2
  import { z } from 'zod';
3
3
  import { CommercetoolsConfiguration } from '../schema/configuration.schema';
4
- import { ProductProjection } from '@commercetools/platform-sdk';
5
4
  export declare class CommercetoolsProductProvider<T extends Product = Product> extends ProductProvider<T> {
6
5
  protected config: CommercetoolsConfiguration;
7
6
  constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, cache: Cache);
8
- getById(payload: ProductQueryById, _session: Session): Promise<T>;
9
- getBySlug(payload: ProductQueryBySlug, _session: Session): Promise<T>;
10
- protected parse(data: ProductProjection): T;
7
+ getClient(session: Session): import("@commercetools/platform-sdk").ByProjectKeyProductProjectionsRequestBuilder;
8
+ getById(payload: ProductQueryById, session: Session): Promise<T>;
9
+ getBySlug(payload: ProductQueryBySlug, session: Session): Promise<T>;
10
+ protected parseSingle(dataIn: unknown, session: Session): T;
11
11
  }
@@ -4,6 +4,6 @@ import { CommercetoolsConfiguration } from '../schema/configuration.schema';
4
4
  export declare class CommercetoolsSearchProvider<T extends SearchResult = SearchResult> extends SearchProvider<T> {
5
5
  protected config: CommercetoolsConfiguration;
6
6
  constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, cache: Cache);
7
- queryByTerm(payload: SearchQueryByTerm, _session: Session): Promise<T>;
8
- protected parseSearchResult(remote: unknown, payload: SearchQueryByTerm): T;
7
+ queryByTerm(payload: SearchQueryByTerm, session: Session): Promise<T>;
8
+ protected parseSearchResult(remote: unknown, payload: SearchQueryByTerm, session: Session): T;
9
9
  }
@@ -1,13 +1,14 @@
1
1
  import { z } from 'zod';
2
2
  export declare const CommercetoolsCapabilitiesSchema: z.ZodInterface<{
3
3
  identity: z.ZodOptional<z.ZodBoolean>;
4
- search: z.ZodOptional<z.ZodBoolean>;
5
4
  product: z.ZodOptional<z.ZodBoolean>;
6
- cart: z.ZodOptional<z.ZodBoolean>;
5
+ search: z.ZodOptional<z.ZodBoolean>;
7
6
  price: z.ZodOptional<z.ZodBoolean>;
7
+ cart: z.ZodOptional<z.ZodBoolean>;
8
8
  inventory: z.ZodOptional<z.ZodBoolean>;
9
+ category: z.ZodOptional<z.ZodBoolean>;
9
10
  }, {
10
- optional: "identity" | "search" | "product" | "cart" | "price" | "inventory";
11
+ optional: "identity" | "product" | "search" | "price" | "cart" | "inventory" | "category";
11
12
  defaulted: never;
12
13
  extra: Record<string, unknown>;
13
14
  }>;