@reactionary/magento 0.0.1
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/README.md +11 -0
- package/capabilities/cart.capability.js +293 -0
- package/capabilities/category.capability.js +254 -0
- package/capabilities/identity.capability.js +107 -0
- package/capabilities/inventory.capability.js +84 -0
- package/capabilities/price.capability.js +72 -0
- package/capabilities/product-search.capability.js +113 -0
- package/capabilities/product.capability.js +146 -0
- package/core/client.js +321 -0
- package/core/initialize.js +176 -0
- package/core/initialize.types.js +13 -0
- package/factories/cart/cart.factory.js +135 -0
- package/factories/category/category.factory.js +48 -0
- package/factories/identity/identity.factory.js +12 -0
- package/factories/index.js +7 -0
- package/factories/inventory/inventory.factory.js +24 -0
- package/factories/price/price.factory.js +43 -0
- package/factories/product/product.factory.js +115 -0
- package/factories/product-search/product-search.factory.js +88 -0
- package/index.js +14 -0
- package/package.json +15 -0
- package/schema/capabilities.schema.js +31 -0
- package/schema/configuration.schema.js +13 -0
- package/schema/magento.schema.js +5 -0
- package/schema/magento.types.js +0 -0
- package/src/capabilities/cart.capability.d.ts +24 -0
- package/src/capabilities/category.capability.d.ts +22 -0
- package/src/capabilities/identity.capability.d.ts +13 -0
- package/src/capabilities/inventory.capability.d.ts +11 -0
- package/src/capabilities/price.capability.d.ts +12 -0
- package/src/capabilities/product-search.capability.d.ts +15 -0
- package/src/capabilities/product.capability.d.ts +14 -0
- package/src/core/client.d.ts +93 -0
- package/src/core/initialize.d.ts +5 -0
- package/src/core/initialize.types.d.ts +80 -0
- package/src/factories/cart/cart.factory.d.ts +20 -0
- package/src/factories/category/category.factory.d.ts +14 -0
- package/src/factories/identity/identity.factory.d.ts +7 -0
- package/src/factories/index.d.ts +7 -0
- package/src/factories/inventory/inventory.factory.d.ts +13 -0
- package/src/factories/price/price.factory.d.ts +15 -0
- package/src/factories/product/product.factory.d.ts +13 -0
- package/src/factories/product-search/product-search.factory.d.ts +12 -0
- package/src/index.d.ts +14 -0
- package/src/schema/capabilities.schema.d.ts +74 -0
- package/src/schema/configuration.schema.d.ts +11 -0
- package/src/schema/magento.schema.d.ts +5 -0
- package/src/schema/magento.types.d.ts +200 -0
package/core/client.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import createDebug from "debug";
|
|
2
|
+
const debug = createDebug("reactionary:magento");
|
|
3
|
+
const SESSION_KEY = "MAGENTO_PROVIDER";
|
|
4
|
+
class RequestContextTokenStore {
|
|
5
|
+
constructor(context, keyPrefix = "__x") {
|
|
6
|
+
this.context = context;
|
|
7
|
+
this.keyPrefix = keyPrefix;
|
|
8
|
+
}
|
|
9
|
+
ensureNamespace() {
|
|
10
|
+
if (this.context.session[SESSION_KEY] === void 0) {
|
|
11
|
+
this.context.session[SESSION_KEY] = {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async getItem(key) {
|
|
15
|
+
this.ensureNamespace();
|
|
16
|
+
const session = this.context.session[SESSION_KEY];
|
|
17
|
+
return session[`${this.keyPrefix}_${key}`] ?? null;
|
|
18
|
+
}
|
|
19
|
+
async setItem(key, value) {
|
|
20
|
+
this.ensureNamespace();
|
|
21
|
+
const session = this.context.session[SESSION_KEY];
|
|
22
|
+
session[`${this.keyPrefix}_${key}`] = value;
|
|
23
|
+
}
|
|
24
|
+
async removeItem(key) {
|
|
25
|
+
this.ensureNamespace();
|
|
26
|
+
const session = this.context.session[SESSION_KEY];
|
|
27
|
+
delete session[`${this.keyPrefix}_${key}`];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
class MagentoRest {
|
|
31
|
+
constructor(baseUrl, storeCode, getAuthHeader) {
|
|
32
|
+
this.baseUrl = baseUrl;
|
|
33
|
+
this.storeCode = storeCode;
|
|
34
|
+
this.getAuthHeader = getAuthHeader;
|
|
35
|
+
this.apiUrl = `${this.baseUrl}/rest/${this.storeCode}`;
|
|
36
|
+
}
|
|
37
|
+
apiUrl;
|
|
38
|
+
normalizeUrl(path) {
|
|
39
|
+
const base = this.apiUrl.replace(/\/+$/, "");
|
|
40
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
41
|
+
return `${base}${p}`;
|
|
42
|
+
}
|
|
43
|
+
async request(method, path, body) {
|
|
44
|
+
const url = this.normalizeUrl(path);
|
|
45
|
+
const headers = {
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
...await this.getAuthHeader()
|
|
48
|
+
};
|
|
49
|
+
const res = await fetch(url, {
|
|
50
|
+
method,
|
|
51
|
+
headers,
|
|
52
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const text = await res.text().catch(() => "");
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Magento request failed: ${method} ${path} \u2192 ${res.status}
|
|
58
|
+
${text}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
62
|
+
if (contentType.includes("application/json")) {
|
|
63
|
+
return await res.json();
|
|
64
|
+
}
|
|
65
|
+
return await res.text();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
class Magento {
|
|
69
|
+
constructor(rest, tokenStore) {
|
|
70
|
+
this.rest = rest;
|
|
71
|
+
this.tokenStore = tokenStore;
|
|
72
|
+
}
|
|
73
|
+
auth = {
|
|
74
|
+
login: async (email, password) => {
|
|
75
|
+
const token = await this.rest.request(
|
|
76
|
+
"POST",
|
|
77
|
+
"/V1/integration/customer/token",
|
|
78
|
+
{ username: email, password }
|
|
79
|
+
);
|
|
80
|
+
const normalized = typeof token === "string" ? token.replace(/^"|"$/g, "") : String(token);
|
|
81
|
+
await this.tokenStore.setItem("customerToken", normalized);
|
|
82
|
+
return normalized;
|
|
83
|
+
},
|
|
84
|
+
logout: async () => {
|
|
85
|
+
await this.tokenStore.removeItem("customerToken");
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
store = {
|
|
89
|
+
customer: {
|
|
90
|
+
register: async (customer, password) => {
|
|
91
|
+
return this.rest.request("POST", "/V1/customers", { customer, password });
|
|
92
|
+
},
|
|
93
|
+
me: async () => {
|
|
94
|
+
return this.rest.request("GET", "/V1/customers/me");
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
product: {
|
|
98
|
+
getBySKU: async (sku) => {
|
|
99
|
+
return this.rest.request(
|
|
100
|
+
"GET",
|
|
101
|
+
`/V1/products/${encodeURIComponent(sku)}`
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
search: async (params) => {
|
|
105
|
+
return this.rest.request(
|
|
106
|
+
"GET",
|
|
107
|
+
`/V1/products?${params.toString()}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
category: {
|
|
112
|
+
getById: async (categoryId) => {
|
|
113
|
+
return this.rest.request("GET", `/V1/categories/${encodeURIComponent(categoryId)}`);
|
|
114
|
+
},
|
|
115
|
+
getByExternalId: async (externalId) => {
|
|
116
|
+
const searchParams = new URLSearchParams({
|
|
117
|
+
"searchCriteria[filterGroups][0][filters][0][field]": "external_id",
|
|
118
|
+
"searchCriteria[filterGroups][0][filters][0][value]": externalId,
|
|
119
|
+
"searchCriteria[filterGroups][0][filters][0][condition_type]": "eq"
|
|
120
|
+
});
|
|
121
|
+
const response = await this.rest.request("GET", `/V1/categories?${searchParams.toString()}`);
|
|
122
|
+
return response.items?.[0] || null;
|
|
123
|
+
},
|
|
124
|
+
list: async (params) => {
|
|
125
|
+
return this.rest.request("GET", `/V1/categories/list?${params.toString()}`);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
inventory: {
|
|
129
|
+
getStockStatus: async (sku) => {
|
|
130
|
+
return this.rest.request("GET", `/V1/stockStatuses/${encodeURIComponent(sku)}`);
|
|
131
|
+
},
|
|
132
|
+
getSourceItems: async (params) => {
|
|
133
|
+
return this.rest.request("GET", `/V1/inventory/source-items?${params.toString()}`);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
cart: {
|
|
137
|
+
create: async (customerToken) => {
|
|
138
|
+
if (customerToken) {
|
|
139
|
+
const res2 = await this.rest.request("POST", "/V1/carts/mine");
|
|
140
|
+
return typeof res2 === "string" ? res2.replace(/^"|"$/g, "") : String(res2);
|
|
141
|
+
}
|
|
142
|
+
const res = await this.rest.request("POST", "/V1/guest-carts");
|
|
143
|
+
return res.replace(/^"|"$/g, "");
|
|
144
|
+
},
|
|
145
|
+
get: async (cartId, customerToken) => {
|
|
146
|
+
if (customerToken) {
|
|
147
|
+
return this.rest.request("GET", "/V1/carts/mine");
|
|
148
|
+
}
|
|
149
|
+
return this.rest.request("GET", `/V1/guest-carts/${cartId}`);
|
|
150
|
+
},
|
|
151
|
+
getTotals: async (cartId, customerToken) => {
|
|
152
|
+
if (customerToken) {
|
|
153
|
+
return this.rest.request("GET", "/V1/carts/mine/totals");
|
|
154
|
+
}
|
|
155
|
+
return this.rest.request("GET", `/V1/guest-carts/${cartId}/totals`);
|
|
156
|
+
},
|
|
157
|
+
addItem: async (cartId, item, customerToken) => {
|
|
158
|
+
const id = cartId && !isNaN(Number(cartId)) ? Number(cartId) : cartId;
|
|
159
|
+
if (customerToken) {
|
|
160
|
+
return this.rest.request("POST", "/V1/carts/mine/items", {
|
|
161
|
+
cartItem: { ...item, quote_id: id }
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return this.rest.request("POST", `/V1/guest-carts/${cartId}/items`, {
|
|
165
|
+
cartItem: { ...item, quote_id: id, quoteId: id }
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
updateItem: async (cartId, itemId, item, customerToken) => {
|
|
169
|
+
const id = cartId && !isNaN(Number(cartId)) ? Number(cartId) : cartId;
|
|
170
|
+
if (customerToken) {
|
|
171
|
+
return this.rest.request("PUT", `/V1/carts/mine/items/${itemId}`, {
|
|
172
|
+
cartItem: { ...item, item_id: itemId, quote_id: id }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return this.rest.request("PUT", `/V1/guest-carts/${cartId}/items/${itemId}`, {
|
|
176
|
+
cartItem: { ...item, item_id: itemId, quote_id: id, quoteId: id }
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
removeItem: async (cartId, itemId, customerToken) => {
|
|
180
|
+
if (customerToken) {
|
|
181
|
+
return this.rest.request("DELETE", `/V1/carts/mine/items/${itemId}`);
|
|
182
|
+
}
|
|
183
|
+
return this.rest.request("DELETE", `/V1/guest-carts/${cartId}/items/${itemId}`);
|
|
184
|
+
},
|
|
185
|
+
applyCoupon: async (cartId, couponCode, customerToken) => {
|
|
186
|
+
if (customerToken) {
|
|
187
|
+
return this.rest.request("PUT", `/V1/carts/mine/coupons/${encodeURIComponent(couponCode)}`);
|
|
188
|
+
}
|
|
189
|
+
return this.rest.request("PUT", `/V1/guest-carts/${cartId}/coupons/${encodeURIComponent(couponCode)}`);
|
|
190
|
+
},
|
|
191
|
+
removeCoupon: async (cartId, customerToken) => {
|
|
192
|
+
if (customerToken) {
|
|
193
|
+
return this.rest.request("DELETE", "/V1/carts/mine/coupons");
|
|
194
|
+
}
|
|
195
|
+
return this.rest.request("DELETE", `/V1/guest-carts/${cartId}/coupons`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
class MagentoAdminClient {
|
|
201
|
+
rest;
|
|
202
|
+
client;
|
|
203
|
+
constructor(config, context) {
|
|
204
|
+
this.rest = new MagentoRest(config.baseUrl, config.storeCode, async () => {
|
|
205
|
+
const headers = {};
|
|
206
|
+
const token = config.adminApiKey ?? "";
|
|
207
|
+
if (token)
|
|
208
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
209
|
+
return headers;
|
|
210
|
+
});
|
|
211
|
+
this.client = new Magento(this.rest, new RequestContextTokenStore(context));
|
|
212
|
+
if (debug.enabled)
|
|
213
|
+
debug("MagentoAdminClient created");
|
|
214
|
+
}
|
|
215
|
+
async getClient() {
|
|
216
|
+
return this.client;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
class MagentoClient {
|
|
220
|
+
constructor(config, context) {
|
|
221
|
+
this.config = config;
|
|
222
|
+
this.tokenStore = new RequestContextTokenStore(context);
|
|
223
|
+
this.client = void 0;
|
|
224
|
+
this.rest = new MagentoRest(this.config.baseUrl, this.config.storeCode, async () => {
|
|
225
|
+
const headers = {};
|
|
226
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
227
|
+
if (customerToken) {
|
|
228
|
+
headers["Authorization"] = `Bearer ${customerToken}`;
|
|
229
|
+
return headers;
|
|
230
|
+
}
|
|
231
|
+
const adminToken = this.config.adminApiKey;
|
|
232
|
+
if (adminToken) {
|
|
233
|
+
headers["Authorization"] = `Bearer ${adminToken}`;
|
|
234
|
+
}
|
|
235
|
+
return headers;
|
|
236
|
+
});
|
|
237
|
+
if (debug.enabled)
|
|
238
|
+
debug("MagentoClient created");
|
|
239
|
+
}
|
|
240
|
+
tokenStore;
|
|
241
|
+
rest;
|
|
242
|
+
client;
|
|
243
|
+
async getClient() {
|
|
244
|
+
if (!this.client) {
|
|
245
|
+
this.client = Promise.resolve(
|
|
246
|
+
new Magento(this.rest, this.tokenStore)
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
return this.client;
|
|
250
|
+
}
|
|
251
|
+
async login(email, password) {
|
|
252
|
+
const client = await this.getClient();
|
|
253
|
+
return client.auth.login(email, password);
|
|
254
|
+
}
|
|
255
|
+
async logout() {
|
|
256
|
+
const client = await this.getClient();
|
|
257
|
+
return client.auth.logout();
|
|
258
|
+
}
|
|
259
|
+
async getMe() {
|
|
260
|
+
const client = await this.getClient();
|
|
261
|
+
return client.store.customer.me();
|
|
262
|
+
}
|
|
263
|
+
async register(customer, password) {
|
|
264
|
+
const client = await this.getClient();
|
|
265
|
+
return client.store.customer.register(customer, password);
|
|
266
|
+
}
|
|
267
|
+
async getProductBySKU(sku) {
|
|
268
|
+
const client = await this.getClient();
|
|
269
|
+
return client.store.product.getBySKU(sku);
|
|
270
|
+
}
|
|
271
|
+
async resolveProductForSKU(sku) {
|
|
272
|
+
return this.getProductBySKU(sku);
|
|
273
|
+
}
|
|
274
|
+
async createCart() {
|
|
275
|
+
const client = await this.getClient();
|
|
276
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
277
|
+
return client.store.cart.create(customerToken);
|
|
278
|
+
}
|
|
279
|
+
async getCart(cartId) {
|
|
280
|
+
const client = await this.getClient();
|
|
281
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
282
|
+
return client.store.cart.get(cartId, customerToken);
|
|
283
|
+
}
|
|
284
|
+
async getCartTotals(cartId) {
|
|
285
|
+
const client = await this.getClient();
|
|
286
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
287
|
+
return client.store.cart.getTotals(cartId, customerToken);
|
|
288
|
+
}
|
|
289
|
+
async addItemToCart(cartId, item) {
|
|
290
|
+
const client = await this.getClient();
|
|
291
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
292
|
+
return client.store.cart.addItem(cartId, item, customerToken);
|
|
293
|
+
}
|
|
294
|
+
async updateCartItem(cartId, itemId, item) {
|
|
295
|
+
const client = await this.getClient();
|
|
296
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
297
|
+
return client.store.cart.updateItem(cartId, itemId, item, customerToken);
|
|
298
|
+
}
|
|
299
|
+
async removeCartItem(cartId, itemId) {
|
|
300
|
+
const client = await this.getClient();
|
|
301
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
302
|
+
return client.store.cart.removeItem(cartId, itemId, customerToken);
|
|
303
|
+
}
|
|
304
|
+
async applyCoupon(cartId, couponCode) {
|
|
305
|
+
const client = await this.getClient();
|
|
306
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
307
|
+
return client.store.cart.applyCoupon(cartId, couponCode, customerToken);
|
|
308
|
+
}
|
|
309
|
+
async removeCoupon(cartId) {
|
|
310
|
+
const client = await this.getClient();
|
|
311
|
+
const customerToken = await this.tokenStore.getItem("customerToken");
|
|
312
|
+
return client.store.cart.removeCoupon(cartId, customerToken);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
export {
|
|
316
|
+
Magento,
|
|
317
|
+
MagentoAdminClient,
|
|
318
|
+
MagentoClient,
|
|
319
|
+
RequestContextTokenStore,
|
|
320
|
+
SESSION_KEY
|
|
321
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CartIdentifierSchema,
|
|
3
|
+
CartPaginatedSearchResultSchema,
|
|
4
|
+
CartSchema,
|
|
5
|
+
CategoryPaginatedResultSchema,
|
|
6
|
+
CategorySchema,
|
|
7
|
+
InventorySchema,
|
|
8
|
+
PriceSchema,
|
|
9
|
+
ProductSchema,
|
|
10
|
+
ProductSearchResultSchema
|
|
11
|
+
} from "@reactionary/core";
|
|
12
|
+
import { MagentoCartCapability } from "../capabilities/cart.capability.js";
|
|
13
|
+
import { MagentoCategoryCapability } from "../capabilities/category.capability.js";
|
|
14
|
+
import { MagentoIdentityCapability } from "../capabilities/identity.capability.js";
|
|
15
|
+
import { MagentoInventoryCapability } from "../capabilities/inventory.capability.js";
|
|
16
|
+
import { MagentoPriceCapability } from "../capabilities/price.capability.js";
|
|
17
|
+
import { MagentoProductSearchCapability } from "../capabilities/product-search.capability.js";
|
|
18
|
+
import { MagentoProductCapability } from "../capabilities/product.capability.js";
|
|
19
|
+
import {
|
|
20
|
+
MagentoCapabilitiesSchema
|
|
21
|
+
} from "../schema/capabilities.schema.js";
|
|
22
|
+
import {
|
|
23
|
+
MagentoConfigurationSchema
|
|
24
|
+
} from "../schema/configuration.schema.js";
|
|
25
|
+
import { MagentoClient } from "./client.js";
|
|
26
|
+
import {
|
|
27
|
+
MagentoCartFactory,
|
|
28
|
+
MagentoCategoryFactory,
|
|
29
|
+
MagentoInventoryFactory,
|
|
30
|
+
MagentoPriceFactory,
|
|
31
|
+
MagentoProductFactory,
|
|
32
|
+
MagentoProductSearchFactory
|
|
33
|
+
} from "../factories/index.js";
|
|
34
|
+
import {
|
|
35
|
+
resolveCapabilityWithFactory,
|
|
36
|
+
resolveDirectCapability
|
|
37
|
+
} from "./initialize.types.js";
|
|
38
|
+
function withMagentoCapabilities(configuration, capabilities) {
|
|
39
|
+
return (cache, context) => {
|
|
40
|
+
const client = {};
|
|
41
|
+
const config = MagentoConfigurationSchema.parse(configuration);
|
|
42
|
+
const caps = MagentoCapabilitiesSchema.parse(capabilities);
|
|
43
|
+
const magentoApi = new MagentoClient(config, context);
|
|
44
|
+
const buildCapabilityArgs = (factory) => ({
|
|
45
|
+
cache,
|
|
46
|
+
context,
|
|
47
|
+
config,
|
|
48
|
+
magentoApi,
|
|
49
|
+
factory
|
|
50
|
+
});
|
|
51
|
+
if (caps.product?.enabled) {
|
|
52
|
+
client.product = resolveCapabilityWithFactory(
|
|
53
|
+
capabilities.product,
|
|
54
|
+
{
|
|
55
|
+
factory: new MagentoProductFactory(ProductSchema, config),
|
|
56
|
+
capability: (args) => new MagentoProductCapability(
|
|
57
|
+
args.config,
|
|
58
|
+
args.cache,
|
|
59
|
+
args.context,
|
|
60
|
+
args.magentoApi,
|
|
61
|
+
args.factory
|
|
62
|
+
)
|
|
63
|
+
},
|
|
64
|
+
buildCapabilityArgs
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (caps.productSearch?.enabled) {
|
|
68
|
+
client.productSearch = resolveCapabilityWithFactory(
|
|
69
|
+
capabilities.productSearch,
|
|
70
|
+
{
|
|
71
|
+
factory: new MagentoProductSearchFactory(ProductSearchResultSchema, config),
|
|
72
|
+
capability: (args) => new MagentoProductSearchCapability(
|
|
73
|
+
args.config,
|
|
74
|
+
args.cache,
|
|
75
|
+
args.context,
|
|
76
|
+
args.magentoApi,
|
|
77
|
+
args.factory
|
|
78
|
+
)
|
|
79
|
+
},
|
|
80
|
+
buildCapabilityArgs
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (caps.category?.enabled) {
|
|
84
|
+
client.category = resolveCapabilityWithFactory(
|
|
85
|
+
capabilities.category,
|
|
86
|
+
{
|
|
87
|
+
factory: new MagentoCategoryFactory(
|
|
88
|
+
CategorySchema,
|
|
89
|
+
CategoryPaginatedResultSchema
|
|
90
|
+
),
|
|
91
|
+
capability: (args) => new MagentoCategoryCapability(
|
|
92
|
+
args.config,
|
|
93
|
+
args.cache,
|
|
94
|
+
args.context,
|
|
95
|
+
args.magentoApi,
|
|
96
|
+
args.factory
|
|
97
|
+
)
|
|
98
|
+
},
|
|
99
|
+
buildCapabilityArgs
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (caps.cart?.enabled) {
|
|
103
|
+
client.cart = resolveCapabilityWithFactory(
|
|
104
|
+
capabilities.cart,
|
|
105
|
+
{
|
|
106
|
+
factory: new MagentoCartFactory(
|
|
107
|
+
CartSchema,
|
|
108
|
+
CartIdentifierSchema,
|
|
109
|
+
CartPaginatedSearchResultSchema
|
|
110
|
+
),
|
|
111
|
+
capability: (args) => new MagentoCartCapability(
|
|
112
|
+
args.config,
|
|
113
|
+
args.cache,
|
|
114
|
+
args.context,
|
|
115
|
+
args.magentoApi,
|
|
116
|
+
args.factory
|
|
117
|
+
)
|
|
118
|
+
},
|
|
119
|
+
buildCapabilityArgs
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
if (caps.price?.enabled) {
|
|
123
|
+
client.price = resolveCapabilityWithFactory(
|
|
124
|
+
capabilities.price,
|
|
125
|
+
{
|
|
126
|
+
factory: new MagentoPriceFactory(PriceSchema),
|
|
127
|
+
capability: (args) => new MagentoPriceCapability(
|
|
128
|
+
args.config,
|
|
129
|
+
args.cache,
|
|
130
|
+
args.context,
|
|
131
|
+
args.magentoApi,
|
|
132
|
+
args.factory
|
|
133
|
+
)
|
|
134
|
+
},
|
|
135
|
+
buildCapabilityArgs
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
if (caps.inventory?.enabled) {
|
|
139
|
+
client.inventory = resolveCapabilityWithFactory(
|
|
140
|
+
capabilities.inventory,
|
|
141
|
+
{
|
|
142
|
+
factory: new MagentoInventoryFactory(InventorySchema),
|
|
143
|
+
capability: (args) => new MagentoInventoryCapability(
|
|
144
|
+
args.config,
|
|
145
|
+
args.cache,
|
|
146
|
+
args.context,
|
|
147
|
+
args.magentoApi,
|
|
148
|
+
args.factory
|
|
149
|
+
)
|
|
150
|
+
},
|
|
151
|
+
buildCapabilityArgs
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
if (caps.identity?.enabled) {
|
|
155
|
+
client.identity = resolveDirectCapability(
|
|
156
|
+
capabilities.identity,
|
|
157
|
+
(args) => new MagentoIdentityCapability(
|
|
158
|
+
args.config,
|
|
159
|
+
args.cache,
|
|
160
|
+
args.context,
|
|
161
|
+
args.magentoApi
|
|
162
|
+
),
|
|
163
|
+
{
|
|
164
|
+
cache,
|
|
165
|
+
context,
|
|
166
|
+
config,
|
|
167
|
+
magentoApi
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return client;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
withMagentoCapabilities
|
|
176
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function resolveCapabilityWithFactory(capability, defaults, buildCapabilityArgs) {
|
|
2
|
+
const factory = capability?.factory ?? defaults.factory;
|
|
3
|
+
const capabilityFactory = capability?.capability ?? defaults.capability;
|
|
4
|
+
return capabilityFactory(buildCapabilityArgs(factory));
|
|
5
|
+
}
|
|
6
|
+
function resolveDirectCapability(capability, defaultCapability, args) {
|
|
7
|
+
const capabilityFactory = capability?.capability ?? defaultCapability;
|
|
8
|
+
return capabilityFactory(args);
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
resolveCapabilityWithFactory,
|
|
12
|
+
resolveDirectCapability
|
|
13
|
+
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProductVariantIdentifierSchema
|
|
3
|
+
} from "@reactionary/core";
|
|
4
|
+
import { MagentoCartIdentifierSchema } from "../../schema/magento.schema.js";
|
|
5
|
+
class MagentoCartFactory {
|
|
6
|
+
cartSchema;
|
|
7
|
+
cartIdentifierSchema;
|
|
8
|
+
cartPaginatedSearchResultSchema;
|
|
9
|
+
constructor(cartSchema, cartIdentifierSchema, cartPaginatedSearchResultSchema) {
|
|
10
|
+
this.cartSchema = cartSchema;
|
|
11
|
+
this.cartIdentifierSchema = cartIdentifierSchema;
|
|
12
|
+
this.cartPaginatedSearchResultSchema = cartPaginatedSearchResultSchema;
|
|
13
|
+
}
|
|
14
|
+
parseCart(_context, data) {
|
|
15
|
+
const requestedId = data._requestedId;
|
|
16
|
+
const currency = data.quote_currency_code || "USD";
|
|
17
|
+
const cost = this.parseCostBreakdown(data, currency);
|
|
18
|
+
const items = (data.items || []).map((item) => this.parseCartItem(item, currency));
|
|
19
|
+
let identifierKey = data.masked_id;
|
|
20
|
+
if (!identifierKey && requestedId && isNaN(Number(requestedId))) {
|
|
21
|
+
identifierKey = requestedId;
|
|
22
|
+
}
|
|
23
|
+
if (!identifierKey) {
|
|
24
|
+
identifierKey = String(data.id || "");
|
|
25
|
+
}
|
|
26
|
+
const result = {
|
|
27
|
+
identifier: {
|
|
28
|
+
key: String(identifierKey || "")
|
|
29
|
+
},
|
|
30
|
+
user: {
|
|
31
|
+
userId: String(data.customer?.id || "")
|
|
32
|
+
},
|
|
33
|
+
items,
|
|
34
|
+
price: cost,
|
|
35
|
+
appliedPromotions: [],
|
|
36
|
+
name: data.name || "",
|
|
37
|
+
description: data.description || ""
|
|
38
|
+
};
|
|
39
|
+
return this.cartSchema.parse(result);
|
|
40
|
+
}
|
|
41
|
+
parseCartIdentifier(_context, data) {
|
|
42
|
+
const result = MagentoCartIdentifierSchema.parse({
|
|
43
|
+
key: String(data.id || data.masked_id || "")
|
|
44
|
+
});
|
|
45
|
+
return this.cartIdentifierSchema.parse(result);
|
|
46
|
+
}
|
|
47
|
+
parseCartPaginatedSearchResult(_context, data, query) {
|
|
48
|
+
const result = {
|
|
49
|
+
identifier: query.search,
|
|
50
|
+
items: data.items.map((item) => this.parseCartSearchResultItem(_context, item)),
|
|
51
|
+
totalCount: data.totalCount,
|
|
52
|
+
pageNumber: query.search.paginationOptions.pageNumber,
|
|
53
|
+
pageSize: query.search.paginationOptions.pageSize,
|
|
54
|
+
totalPages: Math.ceil(data.totalCount / query.search.paginationOptions.pageSize)
|
|
55
|
+
};
|
|
56
|
+
return this.cartPaginatedSearchResultSchema.parse(result);
|
|
57
|
+
}
|
|
58
|
+
parseCartSearchResultItem(context, data) {
|
|
59
|
+
const cart = this.parseCart(context, data);
|
|
60
|
+
return {
|
|
61
|
+
identifier: cart.identifier,
|
|
62
|
+
user: cart.user,
|
|
63
|
+
numItems: cart.items.length,
|
|
64
|
+
name: cart.name,
|
|
65
|
+
lastModifiedDate: data.updated_at ?? ""
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
parseCostBreakdown(cart, currency) {
|
|
69
|
+
return {
|
|
70
|
+
totalProductPrice: {
|
|
71
|
+
value: cart.subtotal ?? cart.base_subtotal ?? 0,
|
|
72
|
+
currency
|
|
73
|
+
},
|
|
74
|
+
grandTotal: {
|
|
75
|
+
value: cart.grand_total ?? cart.base_grand_total ?? 0,
|
|
76
|
+
currency
|
|
77
|
+
},
|
|
78
|
+
totalTax: {
|
|
79
|
+
value: cart.tax_amount ?? cart.base_tax_amount ?? 0,
|
|
80
|
+
currency
|
|
81
|
+
},
|
|
82
|
+
totalShipping: {
|
|
83
|
+
value: cart.shipping_amount ?? cart.base_shipping_amount ?? 0,
|
|
84
|
+
currency
|
|
85
|
+
},
|
|
86
|
+
totalDiscount: {
|
|
87
|
+
value: Math.abs(cart.discount_amount ?? cart.base_discount_amount ?? 0),
|
|
88
|
+
currency
|
|
89
|
+
},
|
|
90
|
+
totalSurcharge: {
|
|
91
|
+
value: 0,
|
|
92
|
+
currency
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
parseItemPrice(item, currency) {
|
|
97
|
+
const qty = item.qty ?? 0;
|
|
98
|
+
return {
|
|
99
|
+
unitPrice: {
|
|
100
|
+
value: item.price ?? 0,
|
|
101
|
+
currency
|
|
102
|
+
},
|
|
103
|
+
unitDiscount: {
|
|
104
|
+
value: item.discount_amount ? Math.abs(item.discount_amount / (qty || 1)) : 0,
|
|
105
|
+
currency
|
|
106
|
+
},
|
|
107
|
+
totalPrice: {
|
|
108
|
+
value: item.row_total !== void 0 ? item.row_total : (item.price || 0) * (qty || 0),
|
|
109
|
+
currency
|
|
110
|
+
},
|
|
111
|
+
totalDiscount: {
|
|
112
|
+
value: Math.abs(item.discount_amount || 0),
|
|
113
|
+
currency
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
parseCartItem(item, currency) {
|
|
118
|
+
return {
|
|
119
|
+
identifier: {
|
|
120
|
+
key: String(item.item_id)
|
|
121
|
+
},
|
|
122
|
+
product: {
|
|
123
|
+
key: item.sku
|
|
124
|
+
},
|
|
125
|
+
variant: ProductVariantIdentifierSchema.parse({
|
|
126
|
+
sku: item.sku
|
|
127
|
+
}),
|
|
128
|
+
quantity: item.qty,
|
|
129
|
+
price: this.parseItemPrice(item, currency)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
MagentoCartFactory
|
|
135
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CategoryIdentifierSchema
|
|
3
|
+
} from "@reactionary/core";
|
|
4
|
+
class MagentoCategoryFactory {
|
|
5
|
+
categorySchema;
|
|
6
|
+
categoryPaginatedResultSchema;
|
|
7
|
+
constructor(categorySchema, categoryPaginatedResultSchema) {
|
|
8
|
+
this.categorySchema = categorySchema;
|
|
9
|
+
this.categoryPaginatedResultSchema = categoryPaginatedResultSchema;
|
|
10
|
+
}
|
|
11
|
+
parseCategory(_context, data) {
|
|
12
|
+
const identifier = CategoryIdentifierSchema.parse({
|
|
13
|
+
key: String(data.custom_attributes?.find((a) => a.attribute_code === "external_id")?.value || data.id)
|
|
14
|
+
});
|
|
15
|
+
const name = data.name || "";
|
|
16
|
+
const urlKeyAttr = data.custom_attributes?.find((a) => a.attribute_code === "url_key");
|
|
17
|
+
const slug = urlKeyAttr ? String(urlKeyAttr.value) : "";
|
|
18
|
+
const textAttr = data.custom_attributes?.find((a) => a.attribute_code === "description");
|
|
19
|
+
const text = textAttr ? String(textAttr.value) : name;
|
|
20
|
+
const parentCategoryStr = String(data.parent_id || "");
|
|
21
|
+
const parentCategory = parentCategoryStr && parentCategoryStr !== "0" ? { key: parentCategoryStr } : void 0;
|
|
22
|
+
const result = {
|
|
23
|
+
identifier,
|
|
24
|
+
name,
|
|
25
|
+
slug,
|
|
26
|
+
text,
|
|
27
|
+
parentCategory,
|
|
28
|
+
images: []
|
|
29
|
+
};
|
|
30
|
+
return this.categorySchema.parse(result);
|
|
31
|
+
}
|
|
32
|
+
parseCategoryPaginatedResult(context, data) {
|
|
33
|
+
const items = (data.items || []).map((c) => this.parseCategory(context, c));
|
|
34
|
+
const totalCount = data.total_count ?? 0;
|
|
35
|
+
const totalPages = Math.max(1, Math.ceil(totalCount / data.pageSize));
|
|
36
|
+
const result = {
|
|
37
|
+
pageNumber: data.currentPage,
|
|
38
|
+
pageSize: data.pageSize,
|
|
39
|
+
totalCount,
|
|
40
|
+
totalPages,
|
|
41
|
+
items
|
|
42
|
+
};
|
|
43
|
+
return this.categoryPaginatedResultSchema.parse(result);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
MagentoCategoryFactory
|
|
48
|
+
};
|