@reactionary/magento 0.0.1 → 0.10.19
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/LICENSE +21 -0
- package/capabilities/cart.capability.js +22 -22
- package/capabilities/category.capability.js +10 -9
- package/capabilities/checkout.capability.js +308 -0
- package/capabilities/identity.capability.js +31 -16
- package/capabilities/inventory.capability.js +2 -2
- package/capabilities/order-search.capability.js +150 -0
- package/capabilities/order.capability.js +51 -0
- package/capabilities/price.capability.js +2 -2
- package/capabilities/product-associations.capability.js +118 -0
- package/capabilities/product-recommendations.capability.js +148 -0
- package/capabilities/product-reviews.capability.js +182 -0
- package/capabilities/product-search.capability.js +2 -2
- package/capabilities/product.capability.js +6 -5
- package/capabilities/profile.capability.js +283 -0
- package/core/client.js +324 -24
- package/core/initialize.js +150 -2
- package/core/product-reviews.graphql.js +72 -0
- package/factories/category/category.factory.js +1 -1
- package/factories/checkout/checkout.factory.js +203 -0
- package/factories/index.js +6 -0
- package/factories/order/order.factory.js +115 -0
- package/factories/order-search/order-search.factory.js +79 -0
- package/factories/product/product.factory.js +23 -18
- package/factories/product-associations/product-associations.factory.js +25 -0
- package/factories/product-reviews/product-reviews.factory.js +65 -0
- package/factories/product-search/product-search.factory.js +3 -4
- package/factories/profile/profile.factory.js +61 -0
- package/index.js +9 -0
- package/package.json +11 -6
- package/schema/capabilities.schema.js +16 -2
- package/schema/configuration.schema.js +5 -1
- package/src/capabilities/cart.capability.d.ts +5 -0
- package/src/capabilities/checkout.capability.d.ts +30 -0
- package/src/capabilities/order-search.capability.d.ts +16 -0
- package/src/capabilities/order.capability.d.ts +12 -0
- package/src/capabilities/product-associations.capability.d.ts +23 -0
- package/src/capabilities/product-recommendations.capability.d.ts +24 -0
- package/src/capabilities/product-reviews.capability.d.ts +35 -0
- package/src/capabilities/profile.capability.d.ts +24 -0
- package/src/core/client.d.ts +77 -2
- package/src/core/initialize.types.d.ts +40 -7
- package/src/core/product-reviews.graphql.d.ts +10 -0
- package/src/factories/category/category.factory.d.ts +2 -2
- package/src/factories/checkout/checkout.factory.d.ts +32 -0
- package/src/factories/index.d.ts +6 -0
- package/src/factories/order/order.factory.d.ts +77 -0
- package/src/factories/order-search/order-search.factory.d.ts +43 -0
- package/src/factories/product/product.factory.d.ts +2 -1
- package/src/factories/product-associations/product-associations.factory.d.ts +15 -0
- package/src/factories/product-reviews/product-reviews.factory.d.ts +40 -0
- package/src/factories/profile/profile.factory.d.ts +43 -0
- package/src/index.d.ts +9 -0
- package/src/schema/capabilities.schema.d.ts +50 -2
- package/src/schema/configuration.schema.d.ts +7 -0
- package/src/schema/magento.types.d.ts +219 -0
- package/src/utils/magento-product-lookup.d.ts +15 -0
- package/src/utils/magento-product.d.ts +36 -0
- package/src/utils/magento-review.d.ts +20 -0
- package/utils/magento-product-lookup.js +76 -0
- package/utils/magento-product.js +110 -0
- package/utils/magento-review.js +34 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const REVIEW_FIELDS = `
|
|
2
|
+
average_rating
|
|
3
|
+
created_at
|
|
4
|
+
nickname
|
|
5
|
+
summary
|
|
6
|
+
text
|
|
7
|
+
ratings_breakdown {
|
|
8
|
+
name
|
|
9
|
+
value
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
const PRODUCT_RATING_SUMMARY_QUERY = `
|
|
13
|
+
query ReactionaryProductRatingSummary($sku: String!) {
|
|
14
|
+
products(filter: { sku: { eq: $sku } }, pageSize: 1, currentPage: 1) {
|
|
15
|
+
items {
|
|
16
|
+
sku
|
|
17
|
+
rating_summary
|
|
18
|
+
review_count
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
const PRODUCT_REVIEWS_QUERY = `
|
|
24
|
+
query ReactionaryProductReviews($sku: String!, $pageSize: Int!, $currentPage: Int!) {
|
|
25
|
+
products(filter: { sku: { eq: $sku } }, pageSize: 1, currentPage: 1) {
|
|
26
|
+
items {
|
|
27
|
+
sku
|
|
28
|
+
rating_summary
|
|
29
|
+
review_count
|
|
30
|
+
reviews(pageSize: $pageSize, currentPage: $currentPage) {
|
|
31
|
+
items {
|
|
32
|
+
${REVIEW_FIELDS}
|
|
33
|
+
}
|
|
34
|
+
page_info {
|
|
35
|
+
page_size
|
|
36
|
+
current_page
|
|
37
|
+
total_pages
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
const PRODUCT_REVIEW_RATINGS_METADATA_QUERY = `
|
|
45
|
+
query ReactionaryProductReviewRatingsMetadata {
|
|
46
|
+
productReviewRatingsMetadata {
|
|
47
|
+
items {
|
|
48
|
+
id
|
|
49
|
+
name
|
|
50
|
+
values {
|
|
51
|
+
value_id
|
|
52
|
+
value
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
const CREATE_PRODUCT_REVIEW_MUTATION = `
|
|
59
|
+
mutation ReactionaryCreateProductReview($input: CreateProductReviewInput!) {
|
|
60
|
+
createProductReview(input: $input) {
|
|
61
|
+
review {
|
|
62
|
+
${REVIEW_FIELDS}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
export {
|
|
68
|
+
CREATE_PRODUCT_REVIEW_MUTATION,
|
|
69
|
+
PRODUCT_RATING_SUMMARY_QUERY,
|
|
70
|
+
PRODUCT_REVIEWS_QUERY,
|
|
71
|
+
PRODUCT_REVIEW_RATINGS_METADATA_QUERY
|
|
72
|
+
};
|
|
@@ -29,7 +29,7 @@ class MagentoCategoryFactory {
|
|
|
29
29
|
};
|
|
30
30
|
return this.categorySchema.parse(result);
|
|
31
31
|
}
|
|
32
|
-
parseCategoryPaginatedResult(context, data) {
|
|
32
|
+
parseCategoryPaginatedResult(context, data, _query) {
|
|
33
33
|
const items = (data.items || []).map((c) => this.parseCategory(context, c));
|
|
34
34
|
const totalCount = data.total_count ?? 0;
|
|
35
35
|
const totalPages = Math.max(1, Math.ceil(totalCount / data.pageSize));
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddressIdentifierSchema,
|
|
3
|
+
MonetaryAmountSchema,
|
|
4
|
+
ShippingMethodIdentifierSchema
|
|
5
|
+
} from "@reactionary/core";
|
|
6
|
+
function encodeShippingMethodKey(carrierCode, methodCode) {
|
|
7
|
+
return `${carrierCode}_${methodCode}`;
|
|
8
|
+
}
|
|
9
|
+
class MagentoCheckoutFactory {
|
|
10
|
+
checkoutSchema;
|
|
11
|
+
shippingMethodSchema;
|
|
12
|
+
paymentMethodSchema;
|
|
13
|
+
config;
|
|
14
|
+
constructor(checkoutSchema, shippingMethodSchema, paymentMethodSchema, config) {
|
|
15
|
+
this.checkoutSchema = checkoutSchema;
|
|
16
|
+
this.shippingMethodSchema = shippingMethodSchema;
|
|
17
|
+
this.paymentMethodSchema = paymentMethodSchema;
|
|
18
|
+
this.config = config;
|
|
19
|
+
}
|
|
20
|
+
resolveCurrency(data) {
|
|
21
|
+
const code = data.totals?.quote_currency_code || data.cart.quote_currency_code || this.config.defaultCurrency || "USD";
|
|
22
|
+
return code;
|
|
23
|
+
}
|
|
24
|
+
parseAddress(address) {
|
|
25
|
+
const street = address.street ?? [];
|
|
26
|
+
return {
|
|
27
|
+
identifier: AddressIdentifierSchema.parse({
|
|
28
|
+
nickName: address.company || ""
|
|
29
|
+
}),
|
|
30
|
+
firstName: address.firstname || "",
|
|
31
|
+
lastName: address.lastname || "",
|
|
32
|
+
streetAddress: street[0] || "",
|
|
33
|
+
streetNumber: street[1] || "",
|
|
34
|
+
city: address.city || "",
|
|
35
|
+
region: address.region || "",
|
|
36
|
+
postalCode: address.postcode || "",
|
|
37
|
+
countryCode: address.country_id || ""
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
parseCostBreakdown(data, currency) {
|
|
41
|
+
const source = data.totals ?? data.cart;
|
|
42
|
+
return {
|
|
43
|
+
totalProductPrice: {
|
|
44
|
+
value: source.subtotal ?? source.base_subtotal ?? 0,
|
|
45
|
+
currency
|
|
46
|
+
},
|
|
47
|
+
grandTotal: {
|
|
48
|
+
value: source.grand_total ?? source.base_grand_total ?? 0,
|
|
49
|
+
currency
|
|
50
|
+
},
|
|
51
|
+
totalTax: {
|
|
52
|
+
value: source.tax_amount ?? source.base_tax_amount ?? 0,
|
|
53
|
+
currency
|
|
54
|
+
},
|
|
55
|
+
totalShipping: {
|
|
56
|
+
value: source.shipping_amount ?? source.base_shipping_amount ?? 0,
|
|
57
|
+
currency
|
|
58
|
+
},
|
|
59
|
+
totalDiscount: {
|
|
60
|
+
value: Math.abs(source.discount_amount ?? source.base_discount_amount ?? 0),
|
|
61
|
+
currency
|
|
62
|
+
},
|
|
63
|
+
totalSurcharge: {
|
|
64
|
+
value: 0,
|
|
65
|
+
currency
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
parseItemPrice(item, currency) {
|
|
70
|
+
const qty = item.qty ?? 0;
|
|
71
|
+
return {
|
|
72
|
+
unitPrice: {
|
|
73
|
+
value: item.price ?? 0,
|
|
74
|
+
currency
|
|
75
|
+
},
|
|
76
|
+
unitDiscount: {
|
|
77
|
+
value: item.discount_amount ? Math.abs(item.discount_amount / (qty || 1)) : 0,
|
|
78
|
+
currency
|
|
79
|
+
},
|
|
80
|
+
totalPrice: {
|
|
81
|
+
value: item.row_total !== void 0 ? item.row_total : (item.price || 0) * (qty || 0),
|
|
82
|
+
currency
|
|
83
|
+
},
|
|
84
|
+
totalDiscount: {
|
|
85
|
+
value: Math.abs(item.discount_amount || 0),
|
|
86
|
+
currency
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
parseCheckoutItem(item, currency) {
|
|
91
|
+
return {
|
|
92
|
+
identifier: {
|
|
93
|
+
key: String(item.item_id)
|
|
94
|
+
},
|
|
95
|
+
variant: {
|
|
96
|
+
sku: item.sku
|
|
97
|
+
},
|
|
98
|
+
quantity: item.qty ?? 0,
|
|
99
|
+
price: this.parseItemPrice(item, currency)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
parsePaymentInstruction(stored) {
|
|
103
|
+
return {
|
|
104
|
+
identifier: { key: stored.key },
|
|
105
|
+
amount: {
|
|
106
|
+
value: stored.amountValue,
|
|
107
|
+
currency: stored.amountCurrency
|
|
108
|
+
},
|
|
109
|
+
paymentMethod: {
|
|
110
|
+
method: stored.method,
|
|
111
|
+
name: stored.name,
|
|
112
|
+
paymentProcessor: stored.paymentProcessor
|
|
113
|
+
},
|
|
114
|
+
protocolData: stored.protocolData,
|
|
115
|
+
status: "pending"
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
parseShippingInstruction(state) {
|
|
119
|
+
if (!state.shippingInstruction) {
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
shippingMethod: { key: state.shippingInstruction.shippingMethodKey },
|
|
124
|
+
pickupPoint: state.shippingInstruction.pickupPoint,
|
|
125
|
+
instructions: state.shippingInstruction.instructions,
|
|
126
|
+
consentForUnattendedDelivery: state.shippingInstruction.consentForUnattendedDelivery
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
parseCheckout(_context, data) {
|
|
130
|
+
const checkoutData = data;
|
|
131
|
+
const cart = checkoutData.cart;
|
|
132
|
+
const state = checkoutData.state ?? {};
|
|
133
|
+
const currency = this.resolveCurrency(checkoutData);
|
|
134
|
+
const key = checkoutData.requestedKey || cart.masked_id || String(cart.id || "");
|
|
135
|
+
const items = (cart.items || []).map(
|
|
136
|
+
(item) => this.parseCheckoutItem(item, currency)
|
|
137
|
+
);
|
|
138
|
+
const billingAddress = state.billingAddress ? this.parseAddress(state.billingAddress) : void 0;
|
|
139
|
+
const shippingAddress = state.shippingAddress ? this.parseAddress(state.shippingAddress) : void 0;
|
|
140
|
+
const paymentInstructions = (state.paymentInstructions ?? []).map(
|
|
141
|
+
(pi) => this.parsePaymentInstruction(pi)
|
|
142
|
+
);
|
|
143
|
+
const readyForFinalization = Boolean(
|
|
144
|
+
state.shippingInstruction && paymentInstructions.length > 0 && (billingAddress || shippingAddress)
|
|
145
|
+
);
|
|
146
|
+
const pointOfContact = {
|
|
147
|
+
email: state.email || cart.customer?.email || "",
|
|
148
|
+
phone: state.phone
|
|
149
|
+
};
|
|
150
|
+
const result = {
|
|
151
|
+
identifier: { key },
|
|
152
|
+
originalCartReference: { key },
|
|
153
|
+
resultingOrder: state.orderId ? { key: state.orderId } : void 0,
|
|
154
|
+
items,
|
|
155
|
+
price: this.parseCostBreakdown(checkoutData, currency),
|
|
156
|
+
name: cart.name || "",
|
|
157
|
+
description: cart.description || "",
|
|
158
|
+
pointOfContact,
|
|
159
|
+
billingAddress,
|
|
160
|
+
shippingAddress,
|
|
161
|
+
shippingInstruction: this.parseShippingInstruction(state),
|
|
162
|
+
paymentInstructions,
|
|
163
|
+
readyForFinalization
|
|
164
|
+
};
|
|
165
|
+
return this.checkoutSchema.parse(result);
|
|
166
|
+
}
|
|
167
|
+
parseShippingMethod(_context, data) {
|
|
168
|
+
const method = data;
|
|
169
|
+
const currency = this.config.defaultCurrency || "USD";
|
|
170
|
+
const sm = {
|
|
171
|
+
identifier: ShippingMethodIdentifierSchema.parse({
|
|
172
|
+
key: encodeShippingMethodKey(method.carrier_code, method.method_code)
|
|
173
|
+
}),
|
|
174
|
+
name: method.method_title || method.method_code,
|
|
175
|
+
description: method.carrier_title || "",
|
|
176
|
+
price: MonetaryAmountSchema.parse({
|
|
177
|
+
value: method.amount ?? 0,
|
|
178
|
+
currency
|
|
179
|
+
}),
|
|
180
|
+
deliveryTime: "",
|
|
181
|
+
carrier: method.carrier_code
|
|
182
|
+
};
|
|
183
|
+
return this.shippingMethodSchema.parse(sm);
|
|
184
|
+
}
|
|
185
|
+
parsePaymentMethod(_context, data) {
|
|
186
|
+
const method = data;
|
|
187
|
+
const pm = {
|
|
188
|
+
identifier: {
|
|
189
|
+
method: method.code,
|
|
190
|
+
name: method.title,
|
|
191
|
+
paymentProcessor: method.code
|
|
192
|
+
},
|
|
193
|
+
logo: void 0,
|
|
194
|
+
description: method.title,
|
|
195
|
+
isPunchOut: false
|
|
196
|
+
};
|
|
197
|
+
return this.paymentMethodSchema.parse(pm);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
export {
|
|
201
|
+
MagentoCheckoutFactory,
|
|
202
|
+
encodeShippingMethodKey
|
|
203
|
+
};
|
package/factories/index.js
CHANGED
|
@@ -5,3 +5,9 @@ export * from "./inventory/inventory.factory.js";
|
|
|
5
5
|
export * from "./price/price.factory.js";
|
|
6
6
|
export * from "./product/product.factory.js";
|
|
7
7
|
export * from "./product-search/product-search.factory.js";
|
|
8
|
+
export * from "./profile/profile.factory.js";
|
|
9
|
+
export * from "./order-search/order-search.factory.js";
|
|
10
|
+
export * from "./order/order.factory.js";
|
|
11
|
+
export * from "./checkout/checkout.factory.js";
|
|
12
|
+
export * from "./product-associations/product-associations.factory.js";
|
|
13
|
+
export * from "./product-reviews/product-reviews.factory.js";
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddressIdentifierSchema
|
|
3
|
+
} from "@reactionary/core";
|
|
4
|
+
import { mapMagentoOrderStatus } from "../order-search/order-search.factory.js";
|
|
5
|
+
function mapMagentoPaymentStatus(state) {
|
|
6
|
+
switch (state) {
|
|
7
|
+
case "complete":
|
|
8
|
+
case "closed":
|
|
9
|
+
return "captured";
|
|
10
|
+
case "processing":
|
|
11
|
+
return "authorized";
|
|
12
|
+
case "canceled":
|
|
13
|
+
return "canceled";
|
|
14
|
+
default:
|
|
15
|
+
return "pending";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class MagentoOrderFactory {
|
|
19
|
+
orderSchema;
|
|
20
|
+
constructor(orderSchema) {
|
|
21
|
+
this.orderSchema = orderSchema;
|
|
22
|
+
}
|
|
23
|
+
parseAddress(address) {
|
|
24
|
+
if (!address) return void 0;
|
|
25
|
+
const street = address.street ?? [];
|
|
26
|
+
return {
|
|
27
|
+
identifier: AddressIdentifierSchema.parse({
|
|
28
|
+
nickName: address.entity_id ? String(address.entity_id) : "address"
|
|
29
|
+
}),
|
|
30
|
+
firstName: address.firstname || "",
|
|
31
|
+
lastName: address.lastname || "",
|
|
32
|
+
streetAddress: street[0] || "",
|
|
33
|
+
streetNumber: street[1] || "",
|
|
34
|
+
city: address.city || "",
|
|
35
|
+
region: address.region || "",
|
|
36
|
+
postalCode: address.postcode || "",
|
|
37
|
+
countryCode: address.country_id || ""
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
parseOrderItem(order, item, currency) {
|
|
41
|
+
const unitPrice = item.price ?? 0;
|
|
42
|
+
const totalPrice = item.row_total ?? unitPrice * (item.qty_ordered ?? 1);
|
|
43
|
+
const price = {
|
|
44
|
+
unitPrice: { value: unitPrice, currency },
|
|
45
|
+
totalPrice: { value: totalPrice, currency },
|
|
46
|
+
unitDiscount: { value: 0, currency },
|
|
47
|
+
totalDiscount: { value: 0, currency }
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
identifier: { key: String(item.item_id) },
|
|
51
|
+
variant: { sku: item.sku },
|
|
52
|
+
quantity: item.qty_ordered ?? 0,
|
|
53
|
+
price,
|
|
54
|
+
inventoryStatus: order.state === "complete" ? "Allocated" : "NotAllocated"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
parseCostBreakdown(order, currency) {
|
|
58
|
+
return {
|
|
59
|
+
grandTotal: { value: order.grand_total ?? 0, currency },
|
|
60
|
+
totalProductPrice: { value: order.subtotal ?? 0, currency },
|
|
61
|
+
totalShipping: { value: order.shipping_amount ?? 0, currency },
|
|
62
|
+
totalTax: { value: order.tax_amount ?? 0, currency },
|
|
63
|
+
totalDiscount: { value: Math.abs(order.discount_amount ?? 0), currency },
|
|
64
|
+
totalSurcharge: { value: 0, currency }
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
parsePaymentInstruction(order, currency) {
|
|
68
|
+
const method = order.payment?.method ?? "unknown";
|
|
69
|
+
return {
|
|
70
|
+
identifier: { key: String(order.entity_id) },
|
|
71
|
+
amount: { value: order.payment?.amount_ordered ?? order.grand_total ?? 0, currency },
|
|
72
|
+
paymentMethod: { method, name: method, paymentProcessor: method },
|
|
73
|
+
protocolData: [],
|
|
74
|
+
status: mapMagentoPaymentStatus(order.state)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
parseShippingMethod(order, method, currency) {
|
|
78
|
+
if (!method && !order.shipping_description) return void 0;
|
|
79
|
+
return {
|
|
80
|
+
identifier: { key: method ?? "default" },
|
|
81
|
+
name: order.shipping_description ?? method ?? "",
|
|
82
|
+
description: order.shipping_description ?? "",
|
|
83
|
+
price: { value: order.shipping_amount ?? 0, currency },
|
|
84
|
+
deliveryTime: ""
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
parseOrder(_context, data) {
|
|
88
|
+
const order = data;
|
|
89
|
+
const currency = (order.order_currency_code || "USD").toUpperCase();
|
|
90
|
+
const orderStatus = mapMagentoOrderStatus(order.status);
|
|
91
|
+
const inventoryStatus = order.state === "complete" ? "Allocated" : "NotAllocated";
|
|
92
|
+
const userId = { userId: order.customer_id ? String(order.customer_id) : "" };
|
|
93
|
+
const shippingAssignment = order.extension_attributes?.shipping_assignments?.[0]?.shipping;
|
|
94
|
+
const result = {
|
|
95
|
+
identifier: { key: String(order.entity_id) },
|
|
96
|
+
userId,
|
|
97
|
+
items: (order.items ?? []).map((item) => this.parseOrderItem(order, item, currency)),
|
|
98
|
+
price: this.parseCostBreakdown(order, currency),
|
|
99
|
+
orderStatus,
|
|
100
|
+
inventoryStatus,
|
|
101
|
+
paymentInstructions: order.payment ? [this.parsePaymentInstruction(order, currency)] : [],
|
|
102
|
+
shippingAddress: this.parseAddress(shippingAssignment?.address ?? order.billing_address),
|
|
103
|
+
billingAddress: this.parseAddress(order.billing_address),
|
|
104
|
+
shippingMethod: this.parseShippingMethod(order, shippingAssignment?.method, currency),
|
|
105
|
+
orderDate: new Date(order.created_at || Date.now()).toISOString(),
|
|
106
|
+
incrementId: order.increment_id,
|
|
107
|
+
customerEmail: order.customer_email || order.billing_address?.email,
|
|
108
|
+
customerPhone: order.billing_address?.telephone
|
|
109
|
+
};
|
|
110
|
+
return this.orderSchema.parse(result);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export {
|
|
114
|
+
MagentoOrderFactory
|
|
115
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AddressIdentifierSchema
|
|
3
|
+
} from "@reactionary/core";
|
|
4
|
+
function mapMagentoOrderStatus(status) {
|
|
5
|
+
switch (status) {
|
|
6
|
+
case "processing":
|
|
7
|
+
return "ReleasedToFulfillment";
|
|
8
|
+
case "complete":
|
|
9
|
+
case "closed":
|
|
10
|
+
return "Shipped";
|
|
11
|
+
case "canceled":
|
|
12
|
+
return "Cancelled";
|
|
13
|
+
default:
|
|
14
|
+
return "AwaitingPayment";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
class MagentoOrderSearchFactory {
|
|
18
|
+
orderSearchResultSchema;
|
|
19
|
+
constructor(orderSearchResultSchema) {
|
|
20
|
+
this.orderSearchResultSchema = orderSearchResultSchema;
|
|
21
|
+
}
|
|
22
|
+
parseAddress(address) {
|
|
23
|
+
const street = address.street ?? [];
|
|
24
|
+
return {
|
|
25
|
+
identifier: AddressIdentifierSchema.parse({
|
|
26
|
+
nickName: address.entity_id ? String(address.entity_id) : "billing"
|
|
27
|
+
}),
|
|
28
|
+
firstName: address.firstname || "",
|
|
29
|
+
lastName: address.lastname || "",
|
|
30
|
+
streetAddress: street[0] || "",
|
|
31
|
+
streetNumber: street[1] || "",
|
|
32
|
+
city: address.city || "",
|
|
33
|
+
region: address.region || "",
|
|
34
|
+
postalCode: address.postcode || "",
|
|
35
|
+
countryCode: address.country_id || ""
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
parseOrderSearchResultItem(_context, order) {
|
|
39
|
+
const totalAmount = {
|
|
40
|
+
currency: (order.order_currency_code || "USD").toUpperCase(),
|
|
41
|
+
value: order.grand_total ?? 0
|
|
42
|
+
};
|
|
43
|
+
const inventoryStatus = order.state === "complete" ? "Allocated" : "NotAllocated";
|
|
44
|
+
const userId = {
|
|
45
|
+
userId: order.customer_id ? String(order.customer_id) : ""
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
identifier: { key: String(order.entity_id) },
|
|
49
|
+
userId,
|
|
50
|
+
customerName: `${order.customer_firstname ?? ""} ${order.customer_lastname ?? ""}`.trim(),
|
|
51
|
+
shippingAddress: order.billing_address ? this.parseAddress(order.billing_address) : void 0,
|
|
52
|
+
orderDate: new Date(order.created_at || Date.now()).toISOString(),
|
|
53
|
+
orderStatus: mapMagentoOrderStatus(order.status),
|
|
54
|
+
inventoryStatus,
|
|
55
|
+
totalAmount
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
parseOrderSearchResult(context, data, query) {
|
|
59
|
+
const response = data;
|
|
60
|
+
const items = response.items ?? [];
|
|
61
|
+
const pageSize = response.search_criteria?.page_size || query.search.paginationOptions.pageSize;
|
|
62
|
+
const pageNumber = response.search_criteria?.current_page || query.search.paginationOptions.pageNumber;
|
|
63
|
+
const totalCount = response.total_count ?? items.length;
|
|
64
|
+
const identifier = { ...query.search };
|
|
65
|
+
const result = {
|
|
66
|
+
identifier,
|
|
67
|
+
pageNumber,
|
|
68
|
+
pageSize,
|
|
69
|
+
totalCount,
|
|
70
|
+
totalPages: pageSize > 0 ? Math.ceil(totalCount / pageSize) : 0,
|
|
71
|
+
items: items.map((o) => this.parseOrderSearchResultItem(context, o))
|
|
72
|
+
};
|
|
73
|
+
return this.orderSearchResultSchema.parse(result);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
MagentoOrderSearchFactory,
|
|
78
|
+
mapMagentoOrderStatus
|
|
79
|
+
};
|
|
@@ -2,12 +2,11 @@ import {
|
|
|
2
2
|
CategoryIdentifierSchema,
|
|
3
3
|
ProductIdentifierSchema
|
|
4
4
|
} from "@reactionary/core";
|
|
5
|
+
import { getProductKey, parseDamImages } from "../../utils/magento-product.js";
|
|
5
6
|
function getCustomAttribute(product, code) {
|
|
6
|
-
if (!product.custom_attributes)
|
|
7
|
-
return void 0;
|
|
7
|
+
if (!product.custom_attributes) return void 0;
|
|
8
8
|
const found = product.custom_attributes.find((a) => a.attribute_code === code);
|
|
9
|
-
if (found?.value === null || found?.value === void 0)
|
|
10
|
-
return void 0;
|
|
9
|
+
if (found?.value === null || found?.value === void 0) return void 0;
|
|
11
10
|
return String(found.value);
|
|
12
11
|
}
|
|
13
12
|
function normalizeBaseUrl(baseUrl) {
|
|
@@ -26,11 +25,11 @@ class MagentoProductFactory {
|
|
|
26
25
|
this.config = config;
|
|
27
26
|
this.productSchema = productSchema;
|
|
28
27
|
}
|
|
28
|
+
config;
|
|
29
29
|
productSchema;
|
|
30
30
|
parseProduct(_context, data) {
|
|
31
31
|
const sku = data.sku;
|
|
32
|
-
const
|
|
33
|
-
const identifier = ProductIdentifierSchema.parse({ key: idKey });
|
|
32
|
+
const identifier = ProductIdentifierSchema.parse({ key: getProductKey(data) });
|
|
34
33
|
const name = data.name || sku;
|
|
35
34
|
const slug = getCustomAttribute(data, "url_key") ?? getCustomAttribute(data, "url_path") ?? "";
|
|
36
35
|
const description = getCustomAttribute(data, "description") ?? getCustomAttribute(data, "short_description") ?? "";
|
|
@@ -59,10 +58,7 @@ class MagentoProductFactory {
|
|
|
59
58
|
const sku = product.sku;
|
|
60
59
|
const ean = getCustomAttribute(product, "ean") ?? getCustomAttribute(product, "gtin") ?? getCustomAttribute(product, "barcode") ?? "";
|
|
61
60
|
const upc = getCustomAttribute(product, "upc") ?? "";
|
|
62
|
-
const images = (product
|
|
63
|
-
sourceUrl: buildMagentoImageUrl(this.config, m.file),
|
|
64
|
-
altText: product.name || sku
|
|
65
|
-
}));
|
|
61
|
+
const images = this.parseImages(product);
|
|
66
62
|
const options = [];
|
|
67
63
|
return {
|
|
68
64
|
identifier: { sku },
|
|
@@ -75,6 +71,19 @@ class MagentoProductFactory {
|
|
|
75
71
|
barcode: ean
|
|
76
72
|
};
|
|
77
73
|
}
|
|
74
|
+
parseImages(product) {
|
|
75
|
+
const altText = product.name || product.sku;
|
|
76
|
+
if (this.config.mediaSource === "EXTERNAL") {
|
|
77
|
+
return parseDamImages(
|
|
78
|
+
getCustomAttribute(product, "original_dam_reference"),
|
|
79
|
+
altText
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return (product.media_gallery_entries ?? []).filter((m) => m.file.length > 0).map((m) => ({
|
|
83
|
+
sourceUrl: buildMagentoImageUrl(this.config, m.file),
|
|
84
|
+
altText
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
78
87
|
createSynthAttribute(key, name, value) {
|
|
79
88
|
const attributeIdentifier = { key };
|
|
80
89
|
const valueIdentifier = { key: `${key}-${value}` };
|
|
@@ -93,17 +102,13 @@ class MagentoProductFactory {
|
|
|
93
102
|
}
|
|
94
103
|
parseAttributes(product) {
|
|
95
104
|
const sharedAttributes = [];
|
|
96
|
-
if (!product.custom_attributes)
|
|
97
|
-
return sharedAttributes;
|
|
105
|
+
if (!product.custom_attributes) return sharedAttributes;
|
|
98
106
|
for (const entry of product.custom_attributes) {
|
|
99
107
|
const key = entry.attribute_code;
|
|
100
108
|
const value = entry.value;
|
|
101
|
-
if (!key)
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
continue;
|
|
105
|
-
if (value === null || value === void 0)
|
|
106
|
-
continue;
|
|
109
|
+
if (!key) continue;
|
|
110
|
+
if (key === "description" || key === "short_description") continue;
|
|
111
|
+
if (value === null || value === void 0) continue;
|
|
107
112
|
const stringValue = typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : JSON.stringify(value);
|
|
108
113
|
sharedAttributes.push(this.createSynthAttribute(key, key, stringValue));
|
|
109
114
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
} from "@reactionary/core";
|
|
3
|
+
import { buildProductSearchResultItem } from "../../utils/magento-product.js";
|
|
4
|
+
class MagentoProductAssociationsFactory {
|
|
5
|
+
productAssociationSchema;
|
|
6
|
+
config;
|
|
7
|
+
constructor(productAssociationSchema, config) {
|
|
8
|
+
this.productAssociationSchema = productAssociationSchema;
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
parseProduct(_context, product) {
|
|
12
|
+
return buildProductSearchResultItem(this.config, product);
|
|
13
|
+
}
|
|
14
|
+
parseAssociation(context, data) {
|
|
15
|
+
const result = {
|
|
16
|
+
associationIdentifier: data.identifier,
|
|
17
|
+
associationReturnType: "productSearchResultItem",
|
|
18
|
+
product: this.parseProduct(context, data.product)
|
|
19
|
+
};
|
|
20
|
+
return this.productAssociationSchema.parse(result);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export {
|
|
24
|
+
MagentoProductAssociationsFactory
|
|
25
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildProductReviewKey,
|
|
3
|
+
ratingPercentToStars,
|
|
4
|
+
toIsoTimestamp
|
|
5
|
+
} from "../../utils/magento-review.js";
|
|
6
|
+
class MagentoProductReviewsFactory {
|
|
7
|
+
ratingSummarySchema;
|
|
8
|
+
reviewSchema;
|
|
9
|
+
reviewPaginatedSchema;
|
|
10
|
+
constructor(ratingSummarySchema, reviewSchema, reviewPaginatedSchema) {
|
|
11
|
+
this.ratingSummarySchema = ratingSummarySchema;
|
|
12
|
+
this.reviewSchema = reviewSchema;
|
|
13
|
+
this.reviewPaginatedSchema = reviewPaginatedSchema;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Magento's GraphQL schema offers no per-star breakdown, so the distribution
|
|
17
|
+
* is left undefined rather than reconstructed from the current page.
|
|
18
|
+
*/
|
|
19
|
+
parseRatingSummary(_context, data) {
|
|
20
|
+
return this.ratingSummarySchema.parse({
|
|
21
|
+
identifier: {
|
|
22
|
+
product: data.product
|
|
23
|
+
},
|
|
24
|
+
averageRating: ratingPercentToStars(data.ratingSummary),
|
|
25
|
+
totalRatings: data.reviewCount ?? void 0,
|
|
26
|
+
ratingDistribution: void 0
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Magento does not expose whether a review came from a confirmed purchase, so
|
|
31
|
+
* `verified` is always false.
|
|
32
|
+
*/
|
|
33
|
+
parseReview(_context, data) {
|
|
34
|
+
return this.reviewSchema.parse({
|
|
35
|
+
identifier: {
|
|
36
|
+
key: buildProductReviewKey(data.sku, data.review)
|
|
37
|
+
},
|
|
38
|
+
product: data.product,
|
|
39
|
+
authorName: data.review.nickname || "Anonymous",
|
|
40
|
+
rating: ratingPercentToStars(data.review.average_rating),
|
|
41
|
+
title: data.review.summary ?? "",
|
|
42
|
+
content: data.review.text ?? "",
|
|
43
|
+
createdAt: toIsoTimestamp(data.review.created_at),
|
|
44
|
+
verified: false
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
parseReviewPaginatedResult(context, data) {
|
|
48
|
+
return this.reviewPaginatedSchema.parse({
|
|
49
|
+
items: data.reviews.map(
|
|
50
|
+
(review) => this.parseReview(context, {
|
|
51
|
+
product: data.product,
|
|
52
|
+
sku: data.sku,
|
|
53
|
+
review
|
|
54
|
+
})
|
|
55
|
+
),
|
|
56
|
+
totalCount: data.totalCount,
|
|
57
|
+
pageSize: data.pageSize,
|
|
58
|
+
pageNumber: data.pageNumber,
|
|
59
|
+
totalPages: Math.ceil(data.totalCount / Math.max(data.pageSize, 1))
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
MagentoProductReviewsFactory
|
|
65
|
+
};
|
|
@@ -4,11 +4,9 @@ import {
|
|
|
4
4
|
ProductVariantIdentifierSchema
|
|
5
5
|
} from "@reactionary/core";
|
|
6
6
|
function getCustomAttribute(product, code) {
|
|
7
|
-
if (!product.custom_attributes)
|
|
8
|
-
return void 0;
|
|
7
|
+
if (!product.custom_attributes) return void 0;
|
|
9
8
|
const found = product.custom_attributes.find((a) => a.attribute_code === code);
|
|
10
|
-
if (found?.value === null || found?.value === void 0)
|
|
11
|
-
return void 0;
|
|
9
|
+
if (found?.value === null || found?.value === void 0) return void 0;
|
|
12
10
|
return String(found.value);
|
|
13
11
|
}
|
|
14
12
|
function normalizeBaseUrl(baseUrl) {
|
|
@@ -27,6 +25,7 @@ class MagentoProductSearchFactory {
|
|
|
27
25
|
this.config = config;
|
|
28
26
|
this.productSearchResultSchema = productSearchResultSchema;
|
|
29
27
|
}
|
|
28
|
+
config;
|
|
30
29
|
productSearchResultSchema;
|
|
31
30
|
parseSearchResult(context, data, query) {
|
|
32
31
|
const pageSize = query.search.paginationOptions.pageSize;
|