@thorprovider/adapters 3.0.2
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/.turbo/turbo-build.log +18 -0
- package/.turbo/turbo-test.log +1 -0
- package/.turbo/turbo-type-check.log +0 -0
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +271 -0
- package/README.md +634 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.mts +2524 -0
- package/dist/index.d.ts +2524 -0
- package/dist/index.js +8136 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8045 -0
- package/dist/index.mjs.map +1 -0
- package/docs/RETRY_SOLUTION_SUMMARY.md +55 -0
- package/docs/authentication.md +165 -0
- package/docs/capabilities-system.md +143 -0
- package/docs/category-customization.md +309 -0
- package/docs/fetch-reliability-solution.md +399 -0
- package/docs/getting-started.md +154 -0
- package/docs/multi-tenant-channel-scoping.md +225 -0
- package/docs/profile-management.md +147 -0
- package/docs/providers-overview.md +85 -0
- package/docs/services-overview.md +135 -0
- package/docs/update-documentation.md +113 -0
- package/package.json +43 -0
- package/publish.log +93 -0
- package/quick-reference.md +169 -0
- package/src/admin/AdminAuthService.ts +88 -0
- package/src/admin/AdminSessionService.ts +91 -0
- package/src/admin/MetricsService.ts +145 -0
- package/src/admin/index.ts +13 -0
- package/src/backend/BackendMetadataAdapter.ts +88 -0
- package/src/backend/factory.ts +107 -0
- package/src/backend/index.ts +12 -0
- package/src/backend/medusa/MedusaMetadataAdapter.ts +139 -0
- package/src/core/errors.js +161 -0
- package/src/core/errors.ts +132 -0
- package/src/core/fetch-utils.js +240 -0
- package/src/core/fetch-utils.ts +227 -0
- package/src/core/index.ts +8 -0
- package/src/core/logger.js +63 -0
- package/src/core/logger.ts +37 -0
- package/src/factory/index.ts +102 -0
- package/src/index.ts +205 -0
- package/src/lib/money-transform.ts +80 -0
- package/src/metadata/channel-scope-validation.js +136 -0
- package/src/metadata/channel-scope-validation.ts +151 -0
- package/src/metadata/channel-scope.js +174 -0
- package/src/metadata/channel-scope.ts +215 -0
- package/src/metadata/index.js +31 -0
- package/src/metadata/index.ts +37 -0
- package/src/providers/bigcommerce/bigcommerce.ts +6 -0
- package/src/providers/bigcommerce/index.ts +315 -0
- package/src/providers/bigcommerce/transforms.ts +227 -0
- package/src/providers/index.ts +22 -0
- package/src/providers/magento/index.ts +387 -0
- package/src/providers/magento/magento.ts +5 -0
- package/src/providers/magento/queries.ts +267 -0
- package/src/providers/magento/transforms.ts +249 -0
- package/src/providers/medusa/auth.js +601 -0
- package/src/providers/medusa/auth.ts +496 -0
- package/src/providers/medusa/fulfillment-planner.ts +225 -0
- package/src/providers/medusa/index.js +2485 -0
- package/src/providers/medusa/index.ts +4239 -0
- package/src/providers/medusa/medusa.ts +8 -0
- package/src/providers/medusa/payment.js +265 -0
- package/src/providers/medusa/payment.ts +269 -0
- package/src/providers/medusa/transforms.js +505 -0
- package/src/providers/medusa/transforms.ts +625 -0
- package/src/providers/shopify/index.ts +444 -0
- package/src/providers/shopify/mutations.ts +102 -0
- package/src/providers/shopify/queries.ts +324 -0
- package/src/providers/shopify/shopify.ts +7 -0
- package/src/providers/shopify/transforms.ts +270 -0
- package/src/providers/spree/index.ts +344 -0
- package/src/providers/spree/spree.ts +5 -0
- package/src/providers/spree/transforms.ts +254 -0
- package/src/providers/woocommerce/index.ts +310 -0
- package/src/providers/woocommerce/transforms.ts +254 -0
- package/src/providers/woocommerce/woocommerce.ts +5 -0
- package/src/resilience/README.md +299 -0
- package/src/resilience/circuit-breaker.ts +222 -0
- package/src/resilience/index.ts +10 -0
- package/src/types/capabilities.types.js +141 -0
- package/src/types/capabilities.types.ts +366 -0
- package/src/types/index.ts +24 -0
- package/src/types/provider.js +9 -0
- package/src/types/provider.ts +9 -0
- package/src/types/ui-responses.ts +261 -0
- package/src/utils/city-coordinates.ts +40 -0
- package/src/utils/geolocation.ts +52 -0
- package/src/utils/payment-utils.js +152 -0
- package/src/utils/payment-utils.ts +203 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +14 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @thorprovider/adapters — Magento (Adobe Commerce) Provider
|
|
3
|
+
* Implements CommerceProvider for Magento 2 GraphQL + REST APIs
|
|
4
|
+
*
|
|
5
|
+
* Uses GraphQL Storefront for read operations (products, categories, cart).
|
|
6
|
+
* REST API for customer/order operations requiring auth tokens.
|
|
7
|
+
* PaaS-first approach (not Adobe IMS/SaaS).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
Product,
|
|
12
|
+
GetProductsOptions,
|
|
13
|
+
Cart,
|
|
14
|
+
CartLineInput,
|
|
15
|
+
CartLineUpdate,
|
|
16
|
+
Collection,
|
|
17
|
+
CollectionProductsOptions,
|
|
18
|
+
GetCollectionsOptions,
|
|
19
|
+
ProductCategory,
|
|
20
|
+
GetCategoriesOptions,
|
|
21
|
+
Order,
|
|
22
|
+
GetOrdersOptions,
|
|
23
|
+
Customer,
|
|
24
|
+
Address,
|
|
25
|
+
PaymentMethod,
|
|
26
|
+
PaymentMethodsOptions,
|
|
27
|
+
ShippingMethod,
|
|
28
|
+
StockLocation,
|
|
29
|
+
InventoryLevel,
|
|
30
|
+
StockValidation,
|
|
31
|
+
FulfillmentOption,
|
|
32
|
+
StorefrontContext,
|
|
33
|
+
BackendCapabilities,
|
|
34
|
+
} from '@thorprovider/types';
|
|
35
|
+
import type { CommerceProvider, ProviderConfig } from '../../types/provider';
|
|
36
|
+
import { MAGENTO_CAPABILITIES } from '../../types/capabilities.types';
|
|
37
|
+
import { createLogger } from '../../core/logger';
|
|
38
|
+
import { ProviderAPIError } from '../../core/errors';
|
|
39
|
+
import { transformProduct, transformCart, transformCollection, transformOrder, transformCustomer } from './transforms';
|
|
40
|
+
import {
|
|
41
|
+
GET_PRODUCTS,
|
|
42
|
+
GET_PRODUCT_BY_URL_KEY,
|
|
43
|
+
GET_CATEGORIES,
|
|
44
|
+
GET_CATEGORY_BY_URL_KEY,
|
|
45
|
+
GET_CATEGORY_PRODUCTS,
|
|
46
|
+
GET_CART,
|
|
47
|
+
CREATE_EMPTY_CART,
|
|
48
|
+
ADD_TO_CART,
|
|
49
|
+
REMOVE_FROM_CART,
|
|
50
|
+
UPDATE_CART_ITEMS,
|
|
51
|
+
APPLY_COUPON,
|
|
52
|
+
REMOVE_COUPON,
|
|
53
|
+
GET_CUSTOMER,
|
|
54
|
+
GET_CUSTOMER_ORDERS,
|
|
55
|
+
} from './queries';
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Magento provider configuration
|
|
59
|
+
*/
|
|
60
|
+
export interface MagentoProviderConfig extends ProviderConfig {
|
|
61
|
+
/** Magento base URL (e.g., 'https://mymagento.com') */
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
/** Store code for multi-tenant (default: 'default') */
|
|
64
|
+
storeCode?: string;
|
|
65
|
+
/** Customer bearer token (for authenticated operations) */
|
|
66
|
+
customerToken?: string;
|
|
67
|
+
/** Integration access token (for admin-level operations) */
|
|
68
|
+
accessToken?: string;
|
|
69
|
+
/** Default currency code (default: 'USD') */
|
|
70
|
+
currencyCode?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class MagentoProvider implements CommerceProvider {
|
|
74
|
+
readonly name = 'magento';
|
|
75
|
+
readonly version = '1.0.0';
|
|
76
|
+
readonly capabilities: BackendCapabilities = MAGENTO_CAPABILITIES;
|
|
77
|
+
|
|
78
|
+
private logger: ReturnType<typeof createLogger>;
|
|
79
|
+
private baseUrl: string;
|
|
80
|
+
private storeCode: string;
|
|
81
|
+
private currencyCode: string;
|
|
82
|
+
private graphqlEndpoint: string;
|
|
83
|
+
private customerToken?: string;
|
|
84
|
+
private accessToken?: string;
|
|
85
|
+
|
|
86
|
+
constructor(config: MagentoProviderConfig) {
|
|
87
|
+
this.logger = createLogger('MagentoProvider', config.debug);
|
|
88
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
89
|
+
this.storeCode = config.storeCode ?? 'default';
|
|
90
|
+
this.currencyCode = config.currencyCode ?? 'USD';
|
|
91
|
+
this.graphqlEndpoint = `${this.baseUrl}/graphql`;
|
|
92
|
+
this.customerToken = config.customerToken;
|
|
93
|
+
this.accessToken = config.accessToken;
|
|
94
|
+
|
|
95
|
+
this.logger.info('Initializing Magento Provider', {
|
|
96
|
+
baseUrl: this.baseUrl,
|
|
97
|
+
storeCode: this.storeCode,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private async graphql<T = any>(query: string, variables?: Record<string, any>): Promise<T> {
|
|
102
|
+
const headers: Record<string, string> = {
|
|
103
|
+
'Content-Type': 'application/json',
|
|
104
|
+
'Store': this.storeCode,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
if (this.customerToken) {
|
|
108
|
+
headers['Authorization'] = `Bearer ${this.customerToken}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const response = await fetch(this.graphqlEndpoint, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers,
|
|
114
|
+
body: JSON.stringify({ query, variables }),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new ProviderAPIError(
|
|
119
|
+
`Magento GraphQL error: ${response.status}`,
|
|
120
|
+
'MAGENTO_API_ERROR',
|
|
121
|
+
response.status,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const json = await response.json() as { data?: T; errors?: Array<{ message: string }> };
|
|
126
|
+
|
|
127
|
+
if (json.errors?.length) {
|
|
128
|
+
const message = json.errors.map((e) => e.message).join('; ');
|
|
129
|
+
throw new ProviderAPIError(`Magento GraphQL error: ${message}`, 'MAGENTO_GRAPHQL_ERROR');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return json.data as T;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================
|
|
136
|
+
// Product Operations (GraphQL)
|
|
137
|
+
// ============================================
|
|
138
|
+
|
|
139
|
+
async getProduct(handle: string): Promise<Product | undefined> {
|
|
140
|
+
try {
|
|
141
|
+
const data = await this.graphql<{ products: { items: any[] } }>(GET_PRODUCT_BY_URL_KEY, { urlKey: handle });
|
|
142
|
+
return data.products.items[0] ? transformProduct(data.products.items[0], this.currencyCode) : undefined;
|
|
143
|
+
} catch {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async getProducts(options?: GetProductsOptions): Promise<Product[]> {
|
|
149
|
+
const variables: Record<string, any> = {
|
|
150
|
+
pageSize: options?.first ?? 20,
|
|
151
|
+
currentPage: 1,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
if (options?.query) variables.search = options.query;
|
|
155
|
+
if (options?.sortKey) {
|
|
156
|
+
const sortMap: Record<string, Record<string, string>> = {
|
|
157
|
+
'price-asc': { price: 'ASC' },
|
|
158
|
+
'price-desc': { price: 'DESC' },
|
|
159
|
+
'title-asc': { name: 'ASC' },
|
|
160
|
+
'title-desc': { name: 'DESC' },
|
|
161
|
+
'created-desc': { created_at: 'DESC' },
|
|
162
|
+
'relevance': { relevance: 'DESC' },
|
|
163
|
+
};
|
|
164
|
+
variables.sort = sortMap[options.sortKey] ?? { relevance: 'DESC' };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const data = await this.graphql<{ products: { items: any[] } }>(GET_PRODUCTS, variables);
|
|
168
|
+
return (data.products?.items ?? []).map((p) => transformProduct(p, this.currencyCode));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async getProductRecommendations(_productId: string): Promise<Product[]> {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ============================================
|
|
176
|
+
// Collection/Category Operations (GraphQL)
|
|
177
|
+
// ============================================
|
|
178
|
+
|
|
179
|
+
async getCollection(handle: string): Promise<Collection | undefined> {
|
|
180
|
+
try {
|
|
181
|
+
const data = await this.graphql<{ categories: { items: any[] } }>(GET_CATEGORY_BY_URL_KEY, { urlKey: handle });
|
|
182
|
+
return data.categories.items[0] ? transformCollection(data.categories.items[0]) : undefined;
|
|
183
|
+
} catch {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async getCollections(options?: GetCollectionsOptions): Promise<Collection[]> {
|
|
189
|
+
const data = await this.graphql<{ categories: { items: any[] } }>(GET_CATEGORIES, {
|
|
190
|
+
pageSize: options?.limit ?? 50,
|
|
191
|
+
});
|
|
192
|
+
return (data.categories?.items ?? []).map(transformCollection);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async getCollectionProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
|
|
196
|
+
const data = await this.graphql<{ products: { items: any[] } }>(GET_CATEGORY_PRODUCTS, {
|
|
197
|
+
urlKey: handle,
|
|
198
|
+
pageSize: options?.first ?? 20,
|
|
199
|
+
});
|
|
200
|
+
return (data.products?.items ?? []).map((p) => transformProduct(p, this.currencyCode));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async getCategory(handle: string): Promise<ProductCategory | undefined> {
|
|
204
|
+
return this.getCollection(handle) as any;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async getCategories(options?: GetCategoriesOptions): Promise<ProductCategory[]> {
|
|
208
|
+
return this.getCollections(options) as any;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async getCategoryProducts(handle: string, options?: CollectionProductsOptions): Promise<Product[]> {
|
|
212
|
+
return this.getCollectionProducts(handle, options);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ============================================
|
|
216
|
+
// Cart Operations (GraphQL)
|
|
217
|
+
// ============================================
|
|
218
|
+
|
|
219
|
+
async createCart(): Promise<Cart> {
|
|
220
|
+
const data = await this.graphql<{ createEmptyCart: string }>(CREATE_EMPTY_CART);
|
|
221
|
+
const cartId = data.createEmptyCart;
|
|
222
|
+
const cartData = await this.graphql<{ cart: any }>(GET_CART, { cartId });
|
|
223
|
+
return transformCart(cartData.cart ?? { id: cartId, items: [], prices: {} }, this.currencyCode);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async getCart(cartId: string): Promise<Cart | undefined> {
|
|
227
|
+
try {
|
|
228
|
+
const data = await this.graphql<{ cart: any }>(GET_CART, { cartId });
|
|
229
|
+
return data.cart ? transformCart(data.cart, this.currencyCode) : undefined;
|
|
230
|
+
} catch {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart> {
|
|
236
|
+
const cartItems = lines.map((l) => ({
|
|
237
|
+
sku: l.merchandiseId,
|
|
238
|
+
quantity: l.quantity,
|
|
239
|
+
}));
|
|
240
|
+
|
|
241
|
+
const data = await this.graphql<{ addProductsToCart: { cart: any; user_errors: any[] } }>(ADD_TO_CART, {
|
|
242
|
+
cartId,
|
|
243
|
+
cartItems,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
if (data.addProductsToCart.user_errors?.length) {
|
|
247
|
+
throw new ProviderAPIError(
|
|
248
|
+
`Add to cart failed: ${data.addProductsToCart.user_errors.map((e: any) => e.message).join('; ')}`,
|
|
249
|
+
'MAGENTO_CART_ERROR',
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return transformCart(data.addProductsToCart.cart, this.currencyCode);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async removeFromCart(cartId: string, lineIds: string[]): Promise<Cart> {
|
|
257
|
+
let cart: any;
|
|
258
|
+
for (const itemUid of lineIds) {
|
|
259
|
+
const data = await this.graphql<{ removeItemFromCart: { cart: any } }>(REMOVE_FROM_CART, {
|
|
260
|
+
cartId,
|
|
261
|
+
cartItemUid: itemUid,
|
|
262
|
+
});
|
|
263
|
+
cart = data.removeItemFromCart.cart;
|
|
264
|
+
}
|
|
265
|
+
return transformCart(cart ?? { id: cartId, items: [], prices: {} }, this.currencyCode);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async updateCart(cartId: string, lines: CartLineUpdate[]): Promise<Cart> {
|
|
269
|
+
const cartItems = lines.map((l) => ({
|
|
270
|
+
cart_item_uid: l.id,
|
|
271
|
+
quantity: l.quantity,
|
|
272
|
+
}));
|
|
273
|
+
|
|
274
|
+
const data = await this.graphql<{ updateCartItems: { cart: any } }>(UPDATE_CART_ITEMS, {
|
|
275
|
+
cartId,
|
|
276
|
+
cartItems,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
return transformCart(data.updateCartItems.cart, this.currencyCode);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ============================================
|
|
283
|
+
// Checkout Operations
|
|
284
|
+
// ============================================
|
|
285
|
+
|
|
286
|
+
async completeCheckout(_cartId: string): Promise<Order> {
|
|
287
|
+
throw new ProviderAPIError(
|
|
288
|
+
'Magento checkout requires multi-step process (set addresses, shipping, payment, place order). Use the Magento checkout flow.',
|
|
289
|
+
'MAGENTO_CHECKOUT_MULTI_STEP',
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async getShippingMethods(_cartId: string): Promise<ShippingMethod[]> {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async getRegionCountries(): Promise<import('@thorprovider/types').Country[]> {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async addShippingMethod(_cartId: string, _methodId: string): Promise<Cart> {
|
|
302
|
+
throw new ProviderAPIError(
|
|
303
|
+
'Magento shipping selection requires set-shipping-methods mutation.',
|
|
304
|
+
'MAGENTO_CHECKOUT_MULTI_STEP',
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async initiatePaymentSession(_cartId: string, _providerId: string): Promise<void> {
|
|
309
|
+
// Magento handles payment via set-payment-method mutation
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async applyDiscountCode(cartId: string, code: string): Promise<Cart> {
|
|
313
|
+
const data = await this.graphql<{ applyCouponToCart: { cart: any } }>(APPLY_COUPON, {
|
|
314
|
+
cartId,
|
|
315
|
+
couponCode: code,
|
|
316
|
+
});
|
|
317
|
+
return transformCart(data.applyCouponToCart.cart, this.currencyCode);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async removeDiscountCode(cartId: string, _code: string): Promise<Cart> {
|
|
321
|
+
const data = await this.graphql<{ removeCouponFromCart: { cart: any } }>(REMOVE_COUPON, {
|
|
322
|
+
cartId,
|
|
323
|
+
});
|
|
324
|
+
return transformCart(data.removeCouponFromCart.cart, this.currencyCode);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ============================================
|
|
328
|
+
// Customer & Orders (GraphQL — requires customerToken)
|
|
329
|
+
// ============================================
|
|
330
|
+
|
|
331
|
+
async getOrders(_options: GetOrdersOptions): Promise<Order[]> {
|
|
332
|
+
if (!this.customerToken) return [];
|
|
333
|
+
try {
|
|
334
|
+
const data = await this.graphql<{ customer: { orders: { items: any[] } } }>(GET_CUSTOMER_ORDERS, {
|
|
335
|
+
pageSize: 20,
|
|
336
|
+
currentPage: 1,
|
|
337
|
+
});
|
|
338
|
+
return (data.customer?.orders?.items ?? []).map((o) => transformOrder(o, this.currencyCode));
|
|
339
|
+
} catch {
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async getPaymentMethods(_options: PaymentMethodsOptions): Promise<PaymentMethod[]> {
|
|
345
|
+
return [];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ============================================
|
|
349
|
+
// Inventory
|
|
350
|
+
// ============================================
|
|
351
|
+
|
|
352
|
+
async getStockLocations(): Promise<StockLocation[]> { return []; }
|
|
353
|
+
async checkInventoryLevels(): Promise<InventoryLevel[]> { return []; }
|
|
354
|
+
async validateCartStock(): Promise<StockValidation> { return { allAvailable: true, unavailableItems: [] }; }
|
|
355
|
+
async getFulfillmentOptions(): Promise<FulfillmentOption[]> { return []; }
|
|
356
|
+
|
|
357
|
+
// ============================================
|
|
358
|
+
// Admin Operations
|
|
359
|
+
// ============================================
|
|
360
|
+
|
|
361
|
+
async cancelOrder(_orderId: string): Promise<Order> {
|
|
362
|
+
throw new ProviderAPIError('Order cancellation requires Magento Admin API.', 'MAGENTO_ADMIN_REQUIRED');
|
|
363
|
+
}
|
|
364
|
+
async createFulfillment(_orderId: string): Promise<void> {
|
|
365
|
+
throw new ProviderAPIError('Fulfillment requires Magento Admin API.', 'MAGENTO_ADMIN_REQUIRED');
|
|
366
|
+
}
|
|
367
|
+
async createRefund(_orderId: string): Promise<void> {
|
|
368
|
+
throw new ProviderAPIError('Refunds require Magento Admin API.', 'MAGENTO_ADMIN_REQUIRED');
|
|
369
|
+
}
|
|
370
|
+
async getAdminOrders(): Promise<{ orders: Order[]; count: number }> { return { orders: [], count: 0 }; }
|
|
371
|
+
|
|
372
|
+
// ============================================
|
|
373
|
+
// Storefront / Multi-Tenant
|
|
374
|
+
// ============================================
|
|
375
|
+
|
|
376
|
+
async getStorefrontContext(): Promise<StorefrontContext> {
|
|
377
|
+
return {
|
|
378
|
+
id: this.storeCode,
|
|
379
|
+
name: `Magento ${this.storeCode}`,
|
|
380
|
+
platformType: 'magento',
|
|
381
|
+
requiresProductScoping: true,
|
|
382
|
+
currencyCode: this.currencyCode,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
isStorefrontRequired(): boolean { return true; }
|
|
387
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Magento GraphQL Queries for Storefront
|
|
3
|
+
* Used by MagentoProvider to query products, categories, cart, and customer data.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const PRODUCT_FIELDS = `
|
|
7
|
+
id
|
|
8
|
+
uid
|
|
9
|
+
sku
|
|
10
|
+
name
|
|
11
|
+
url_key
|
|
12
|
+
stock_status
|
|
13
|
+
short_description { html }
|
|
14
|
+
description { html }
|
|
15
|
+
meta_title
|
|
16
|
+
meta_description
|
|
17
|
+
created_at
|
|
18
|
+
updated_at
|
|
19
|
+
price_range {
|
|
20
|
+
minimum_price {
|
|
21
|
+
regular_price { value currency }
|
|
22
|
+
final_price { value currency }
|
|
23
|
+
}
|
|
24
|
+
maximum_price {
|
|
25
|
+
regular_price { value currency }
|
|
26
|
+
final_price { value currency }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
small_image { url label }
|
|
30
|
+
image { url label }
|
|
31
|
+
media_gallery {
|
|
32
|
+
url
|
|
33
|
+
label
|
|
34
|
+
}
|
|
35
|
+
... on ConfigurableProduct {
|
|
36
|
+
configurable_options {
|
|
37
|
+
attribute_code
|
|
38
|
+
attribute_id
|
|
39
|
+
label
|
|
40
|
+
values {
|
|
41
|
+
label
|
|
42
|
+
value_index
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
variants {
|
|
46
|
+
product {
|
|
47
|
+
id uid sku name
|
|
48
|
+
stock_status
|
|
49
|
+
price_range {
|
|
50
|
+
minimum_price {
|
|
51
|
+
final_price { value currency }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
small_image { url label }
|
|
55
|
+
}
|
|
56
|
+
attributes {
|
|
57
|
+
label
|
|
58
|
+
value_index
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
export const GET_PRODUCTS = `
|
|
65
|
+
query GetProducts($search: String, $pageSize: Int, $currentPage: Int, $sort: ProductAttributeSortInput) {
|
|
66
|
+
products(search: $search, pageSize: $pageSize, currentPage: $currentPage, sort: $sort) {
|
|
67
|
+
items { ${PRODUCT_FIELDS} }
|
|
68
|
+
total_count
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
export const GET_PRODUCT_BY_URL_KEY = `
|
|
74
|
+
query GetProductByUrlKey($urlKey: String!) {
|
|
75
|
+
products(filter: { url_key: { eq: $urlKey } }, pageSize: 1) {
|
|
76
|
+
items { ${PRODUCT_FIELDS} }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
export const GET_CATEGORIES = `
|
|
82
|
+
query GetCategories($pageSize: Int) {
|
|
83
|
+
categories(pageSize: $pageSize) {
|
|
84
|
+
items {
|
|
85
|
+
id uid name url_key url_path description meta_title meta_description
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
|
|
91
|
+
export const GET_CATEGORY_BY_URL_KEY = `
|
|
92
|
+
query GetCategoryByUrlKey($urlKey: String!) {
|
|
93
|
+
categories(filters: { url_key: { eq: $urlKey } }, pageSize: 1) {
|
|
94
|
+
items {
|
|
95
|
+
id uid name url_key url_path description meta_title meta_description
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
export const GET_CATEGORY_PRODUCTS = `
|
|
102
|
+
query GetCategoryProducts($urlKey: String!, $pageSize: Int) {
|
|
103
|
+
products(filter: { category_url_key: { eq: $urlKey } }, pageSize: $pageSize) {
|
|
104
|
+
items { ${PRODUCT_FIELDS} }
|
|
105
|
+
total_count
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
109
|
+
|
|
110
|
+
export const GET_CART = `
|
|
111
|
+
query GetCart($cartId: String!) {
|
|
112
|
+
cart(cart_id: $cartId) {
|
|
113
|
+
id
|
|
114
|
+
items {
|
|
115
|
+
id uid quantity
|
|
116
|
+
product {
|
|
117
|
+
id uid sku name url_key
|
|
118
|
+
small_image { url }
|
|
119
|
+
thumbnail { url }
|
|
120
|
+
price_range {
|
|
121
|
+
minimum_price {
|
|
122
|
+
final_price { value currency }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
prices {
|
|
127
|
+
price { value currency }
|
|
128
|
+
row_total { value currency }
|
|
129
|
+
}
|
|
130
|
+
... on ConfigurableCartItem {
|
|
131
|
+
configurable_options {
|
|
132
|
+
option_label
|
|
133
|
+
value_label
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
prices {
|
|
138
|
+
grand_total { value currency }
|
|
139
|
+
subtotal_excluding_tax { value currency }
|
|
140
|
+
subtotal_including_tax { value currency }
|
|
141
|
+
applied_taxes {
|
|
142
|
+
amount { value currency }
|
|
143
|
+
label
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
export const CREATE_EMPTY_CART = `
|
|
151
|
+
mutation CreateEmptyCart {
|
|
152
|
+
createEmptyCart
|
|
153
|
+
}
|
|
154
|
+
`;
|
|
155
|
+
|
|
156
|
+
export const ADD_TO_CART = `
|
|
157
|
+
mutation AddToCart($cartId: String!, $cartItems: [CartItemInput!]!) {
|
|
158
|
+
addProductsToCart(cartId: $cartId, cartItems: $cartItems) {
|
|
159
|
+
cart {
|
|
160
|
+
id
|
|
161
|
+
items { id uid quantity product { sku name } prices { price { value currency } row_total { value currency } } }
|
|
162
|
+
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
163
|
+
}
|
|
164
|
+
user_errors { code message }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
export const REMOVE_FROM_CART = `
|
|
170
|
+
mutation RemoveFromCart($cartId: String!, $cartItemUid: ID!) {
|
|
171
|
+
removeItemFromCart(input: { cart_id: $cartId, cart_item_uid: $cartItemUid }) {
|
|
172
|
+
cart {
|
|
173
|
+
id
|
|
174
|
+
items { id uid quantity product { sku name } prices { price { value currency } row_total { value currency } } }
|
|
175
|
+
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
export const UPDATE_CART_ITEMS = `
|
|
182
|
+
mutation UpdateCartItems($cartId: String!, $cartItems: [CartItemUpdateInput!]!) {
|
|
183
|
+
updateCartItems(input: { cart_id: $cartId, cart_items: $cartItems }) {
|
|
184
|
+
cart {
|
|
185
|
+
id
|
|
186
|
+
items { id uid quantity product { sku name } prices { price { value currency } row_total { value currency } } }
|
|
187
|
+
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
`;
|
|
192
|
+
|
|
193
|
+
export const APPLY_COUPON = `
|
|
194
|
+
mutation ApplyCoupon($cartId: String!, $couponCode: String!) {
|
|
195
|
+
applyCouponToCart(input: { cart_id: $cartId, coupon_code: $couponCode }) {
|
|
196
|
+
cart {
|
|
197
|
+
id
|
|
198
|
+
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
`;
|
|
203
|
+
|
|
204
|
+
export const REMOVE_COUPON = `
|
|
205
|
+
mutation RemoveCoupon($cartId: String!) {
|
|
206
|
+
removeCouponFromCart(input: { cart_id: $cartId }) {
|
|
207
|
+
cart {
|
|
208
|
+
id
|
|
209
|
+
prices { grand_total { value currency } subtotal_excluding_tax { value currency } }
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
`;
|
|
214
|
+
|
|
215
|
+
export const GET_CUSTOMER = `
|
|
216
|
+
query GetCustomer {
|
|
217
|
+
customer {
|
|
218
|
+
id
|
|
219
|
+
email
|
|
220
|
+
firstname
|
|
221
|
+
lastname
|
|
222
|
+
addresses {
|
|
223
|
+
id
|
|
224
|
+
firstname lastname company
|
|
225
|
+
street city
|
|
226
|
+
region { region region_code }
|
|
227
|
+
postcode
|
|
228
|
+
country_code
|
|
229
|
+
telephone
|
|
230
|
+
default_shipping
|
|
231
|
+
default_billing
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
|
|
237
|
+
export const GET_CUSTOMER_ORDERS = `
|
|
238
|
+
query GetCustomerOrders($pageSize: Int, $currentPage: Int) {
|
|
239
|
+
customer {
|
|
240
|
+
orders(pageSize: $pageSize, currentPage: $currentPage) {
|
|
241
|
+
items {
|
|
242
|
+
id order_number order_date status
|
|
243
|
+
total {
|
|
244
|
+
grand_total { value currency }
|
|
245
|
+
subtotal { value currency }
|
|
246
|
+
total_tax { value currency }
|
|
247
|
+
total_shipping { value currency }
|
|
248
|
+
}
|
|
249
|
+
items {
|
|
250
|
+
id product_name product_sku
|
|
251
|
+
quantity_ordered
|
|
252
|
+
product_sale_price { value currency }
|
|
253
|
+
}
|
|
254
|
+
shipping_address {
|
|
255
|
+
firstname lastname street city
|
|
256
|
+
region postcode country_code telephone
|
|
257
|
+
}
|
|
258
|
+
billing_address {
|
|
259
|
+
firstname lastname street city
|
|
260
|
+
region postcode country_code telephone
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
total_count
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
`;
|