@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.
- package/core/initialize.js +5 -1
- package/package.json +3 -2
- package/providers/cart.provider.js +73 -8
- package/providers/category.provider.js +167 -0
- package/providers/index.js +7 -0
- package/providers/inventory.provider.js +21 -9
- package/providers/price.provider.js +51 -13
- package/providers/product.provider.js +17 -13
- package/providers/search.provider.js +7 -7
- package/schema/capabilities.schema.js +2 -1
- package/src/providers/cart.provider.d.ts +1 -1
- package/src/providers/category.provider.d.ts +95 -0
- package/src/providers/index.d.ts +7 -0
- package/src/providers/inventory.provider.d.ts +1 -0
- package/src/providers/price.provider.d.ts +3 -0
- package/src/providers/product.provider.d.ts +4 -4
- package/src/providers/search.provider.d.ts +2 -2
- package/src/schema/capabilities.schema.d.ts +4 -3
package/core/initialize.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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
|
+
};
|
|
@@ -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:
|
|
17
|
-
"var.sku": payload.sku
|
|
16
|
+
where: `sku=${payload.sku}`
|
|
18
17
|
}
|
|
19
18
|
}).execute();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
cache: { hit: false, key:
|
|
37
|
+
model.meta = {
|
|
38
|
+
cache: { hit: false, key: this.generateCacheKeySingle(model.identifier, session) },
|
|
27
39
|
placeholder: false
|
|
28
40
|
};
|
|
29
|
-
return this.assert(
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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:
|
|
67
|
+
key: body.sku
|
|
30
68
|
}
|
|
31
69
|
};
|
|
32
70
|
base.meta = {
|
|
33
|
-
cache: { hit: false, 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
|
-
|
|
11
|
-
|
|
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
|
|
16
|
-
const client =
|
|
17
|
-
const remote = await client.
|
|
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.
|
|
29
|
+
return this.parseSingle(remote.body.results[0], session);
|
|
27
30
|
}
|
|
28
|
-
|
|
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[
|
|
32
|
-
base.slug = data.slug[
|
|
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[
|
|
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:
|
|
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,
|
|
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.
|
|
16
|
-
[
|
|
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[
|
|
29
|
-
slug: p.slug?.[
|
|
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);
|
|
@@ -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
|
|
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
|
+
}
|
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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,
|
|
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
|
-
|
|
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" | "
|
|
11
|
+
optional: "identity" | "product" | "search" | "price" | "cart" | "inventory" | "category";
|
|
11
12
|
defaulted: never;
|
|
12
13
|
extra: Record<string, unknown>;
|
|
13
14
|
}>;
|