@reactionary/source 0.0.52 → 0.2.16
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/.env-template +19 -0
- package/.github/workflows/pull-request.yml +3 -1
- package/.github/workflows/release.yml +9 -0
- package/.vscode/extensions.json +0 -2
- package/LICENSE +21 -0
- package/README.md +175 -23
- package/core/package.json +6 -3
- package/core/src/cache/cache.interface.ts +1 -0
- package/core/src/cache/index.ts +4 -0
- package/core/src/cache/memory-cache.ts +30 -2
- package/core/src/cache/noop-cache.ts +15 -1
- package/core/src/cache/redis-cache.ts +20 -0
- package/core/src/client/client-builder.ts +71 -54
- package/core/src/client/client.ts +9 -47
- package/core/src/client/index.ts +2 -0
- package/core/src/decorators/index.ts +1 -0
- package/core/src/decorators/reactionary.decorator.ts +203 -34
- package/core/src/index.ts +6 -19
- package/core/src/initialization.ts +1 -18
- package/core/src/metrics/metrics.ts +67 -0
- package/core/src/providers/analytics.provider.ts +1 -6
- package/core/src/providers/base.provider.ts +5 -69
- package/core/src/providers/cart.provider.ts +15 -55
- package/core/src/providers/category.provider.ts +7 -11
- package/core/src/providers/checkout.provider.ts +17 -15
- package/core/src/providers/identity.provider.ts +6 -8
- package/core/src/providers/index.ts +2 -1
- package/core/src/providers/inventory.provider.ts +15 -5
- package/core/src/providers/order-search.provider.ts +29 -0
- package/core/src/providers/order.provider.ts +47 -15
- package/core/src/providers/price.provider.ts +30 -36
- package/core/src/providers/product-search.provider.ts +61 -0
- package/core/src/providers/product.provider.ts +71 -12
- package/core/src/providers/profile.provider.ts +74 -14
- package/core/src/providers/store.provider.ts +3 -5
- package/core/src/schemas/capabilities.schema.ts +10 -3
- package/core/src/schemas/errors/generic.error.ts +9 -0
- package/core/src/schemas/errors/index.ts +4 -0
- package/core/src/schemas/errors/invalid-input.error.ts +9 -0
- package/core/src/schemas/errors/invalid-output.error.ts +9 -0
- package/core/src/schemas/errors/not-found.error.ts +9 -0
- package/core/src/schemas/index.ts +7 -0
- package/core/src/schemas/models/analytics.model.ts +2 -1
- package/core/src/schemas/models/base.model.ts +6 -24
- package/core/src/schemas/models/cart.model.ts +5 -8
- package/core/src/schemas/models/category.model.ts +4 -9
- package/core/src/schemas/models/checkout.model.ts +6 -7
- package/core/src/schemas/models/cost.model.ts +4 -3
- package/core/src/schemas/models/currency.model.ts +2 -1
- package/core/src/schemas/models/identifiers.model.ts +106 -62
- package/core/src/schemas/models/identity.model.ts +10 -19
- package/core/src/schemas/models/index.ts +2 -1
- package/core/src/schemas/models/inventory.model.ts +8 -5
- package/core/src/schemas/models/order-search.model.ts +28 -0
- package/core/src/schemas/models/order.model.ts +20 -26
- package/core/src/schemas/models/payment.model.ts +14 -17
- package/core/src/schemas/models/price.model.ts +11 -11
- package/core/src/schemas/models/product-search.model.ts +42 -0
- package/core/src/schemas/models/product.model.ts +64 -22
- package/core/src/schemas/models/profile.model.ts +19 -22
- package/core/src/schemas/models/shipping-method.model.ts +24 -29
- package/core/src/schemas/models/store.model.ts +9 -5
- package/core/src/schemas/mutations/analytics.mutation.ts +8 -7
- package/core/src/schemas/mutations/base.mutation.ts +2 -1
- package/core/src/schemas/mutations/cart.mutation.ts +33 -33
- package/core/src/schemas/mutations/checkout.mutation.ts +23 -30
- package/core/src/schemas/mutations/identity.mutation.ts +4 -3
- package/core/src/schemas/mutations/profile.mutation.ts +38 -3
- package/core/src/schemas/queries/base.query.ts +2 -1
- package/core/src/schemas/queries/cart.query.ts +3 -3
- package/core/src/schemas/queries/category.query.ts +18 -18
- package/core/src/schemas/queries/checkout.query.ts +7 -9
- package/core/src/schemas/queries/identity.query.ts +2 -1
- package/core/src/schemas/queries/index.ts +2 -1
- package/core/src/schemas/queries/inventory.query.ts +5 -5
- package/core/src/schemas/queries/order-search.query.ts +10 -0
- package/core/src/schemas/queries/order.query.ts +3 -2
- package/core/src/schemas/queries/price.query.ts +10 -4
- package/core/src/schemas/queries/product-search.query.ts +16 -0
- package/core/src/schemas/queries/product.query.ts +13 -6
- package/core/src/schemas/queries/profile.query.ts +5 -2
- package/core/src/schemas/queries/store.query.ts +6 -5
- package/core/src/schemas/result.ts +107 -0
- package/core/src/schemas/session.schema.ts +4 -4
- package/core/src/test/reactionary.decorator.spec.ts +249 -0
- package/core/src/zod-utils.ts +19 -0
- package/core/tsconfig.json +1 -1
- package/core/tsconfig.spec.json +2 -26
- package/core/vitest.config.ts +14 -0
- package/documentation/1-purpose.md +114 -0
- package/documentation/2-getting-started.md +229 -0
- package/documentation/3-querying-and-changing-data.md +74 -0
- package/documentation/4-product-data.md +107 -0
- package/documentation/5-cart-and-checkout.md +211 -0
- package/documentation/6-product-search.md +143 -0
- package/documentation/7-marketing.md +3 -0
- package/eslint.config.mjs +1 -0
- package/examples/node/eslint.config.mjs +1 -4
- package/examples/node/package.json +10 -3
- package/examples/node/project.json +4 -1
- package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +22 -23
- package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +15 -11
- package/examples/node/src/basic/basic-node-setup.spec.ts +44 -28
- package/examples/node/src/basic/client-creation.spec.ts +53 -0
- package/examples/node/src/capabilities/cart.spec.ts +255 -0
- package/examples/node/src/capabilities/category.spec.ts +193 -0
- package/examples/node/src/capabilities/checkout.spec.ts +341 -0
- package/examples/node/src/capabilities/identity.spec.ts +93 -0
- package/examples/node/src/capabilities/inventory.spec.ts +66 -0
- package/examples/node/src/capabilities/order-search.spec.ts +265 -0
- package/examples/node/src/capabilities/order.spec.ts +91 -0
- package/examples/node/src/capabilities/price.spec.ts +51 -0
- package/examples/node/src/capabilities/product-search.spec.ts +293 -0
- package/examples/node/src/capabilities/product.spec.ts +122 -0
- package/examples/node/src/capabilities/profile.spec.ts +316 -0
- package/examples/node/src/capabilities/store.spec.ts +26 -0
- package/examples/node/src/utils.ts +147 -0
- package/examples/node/tsconfig.json +9 -12
- package/examples/node/tsconfig.lib.json +1 -2
- package/examples/node/tsconfig.spec.json +2 -14
- package/examples/node/vitest.config.ts +14 -0
- package/migrations.json +22 -5
- package/nx.json +8 -47
- package/package.json +24 -96
- package/providers/algolia/README.md +39 -2
- package/providers/algolia/package.json +2 -1
- package/providers/algolia/src/core/initialize.ts +7 -14
- package/providers/algolia/src/index.ts +2 -4
- package/providers/algolia/src/providers/index.ts +1 -0
- package/providers/algolia/src/providers/product-search.provider.ts +241 -0
- package/providers/algolia/src/schema/capabilities.schema.ts +2 -3
- package/providers/algolia/src/schema/index.ts +3 -0
- package/providers/algolia/src/schema/search.schema.ts +8 -8
- package/providers/algolia/tsconfig.json +1 -1
- package/providers/algolia/tsconfig.lib.json +1 -1
- package/providers/algolia/tsconfig.spec.json +2 -14
- package/providers/algolia/vitest.config.ts +14 -0
- package/providers/commercetools/README.md +30 -3
- package/providers/commercetools/package.json +2 -1
- package/providers/commercetools/src/core/client.ts +178 -99
- package/providers/commercetools/src/core/initialize.ts +130 -74
- package/providers/commercetools/src/core/token-cache.ts +45 -0
- package/providers/commercetools/src/index.ts +3 -2
- package/providers/commercetools/src/providers/cart.provider.ts +281 -341
- package/providers/commercetools/src/providers/category.provider.ts +223 -138
- package/providers/commercetools/src/providers/checkout.provider.ts +631 -449
- package/providers/commercetools/src/providers/identity.provider.ts +50 -29
- package/providers/commercetools/src/providers/index.ts +2 -2
- package/providers/commercetools/src/providers/inventory.provider.ts +76 -74
- package/providers/commercetools/src/providers/order-search.provider.ts +220 -0
- package/providers/commercetools/src/providers/order.provider.ts +96 -61
- package/providers/commercetools/src/providers/price.provider.ts +147 -117
- package/providers/commercetools/src/providers/product-search.provider.ts +528 -0
- package/providers/commercetools/src/providers/product.provider.ts +249 -74
- package/providers/commercetools/src/providers/profile.provider.ts +445 -28
- package/providers/commercetools/src/providers/store.provider.ts +54 -40
- package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
- package/providers/commercetools/src/schema/commercetools.schema.ts +17 -3
- package/providers/commercetools/src/schema/configuration.schema.ts +1 -0
- package/providers/commercetools/src/schema/session.schema.ts +7 -0
- package/providers/commercetools/src/test/caching.spec.ts +82 -0
- package/providers/commercetools/src/test/identity.spec.ts +109 -0
- package/providers/commercetools/src/test/test-utils.ts +21 -19
- package/providers/commercetools/tsconfig.json +1 -1
- package/providers/commercetools/tsconfig.lib.json +1 -1
- package/providers/commercetools/tsconfig.spec.json +2 -14
- package/providers/commercetools/vitest.config.ts +15 -0
- package/providers/fake/README.md +20 -4
- package/providers/fake/package.json +2 -1
- package/providers/fake/src/core/initialize.ts +47 -49
- package/providers/fake/src/providers/analytics.provider.ts +5 -7
- package/providers/fake/src/providers/cart.provider.ts +163 -92
- package/providers/fake/src/providers/category.provider.ts +78 -50
- package/providers/fake/src/providers/checkout.provider.ts +254 -0
- package/providers/fake/src/providers/identity.provider.ts +57 -65
- package/providers/fake/src/providers/index.ts +6 -2
- package/providers/fake/src/providers/inventory.provider.ts +40 -36
- package/providers/fake/src/providers/order-search.provider.ts +78 -0
- package/providers/fake/src/providers/order.provider.ts +106 -0
- package/providers/fake/src/providers/price.provider.ts +93 -41
- package/providers/fake/src/providers/product-search.provider.ts +206 -0
- package/providers/fake/src/providers/product.provider.ts +56 -41
- package/providers/fake/src/providers/profile.provider.ts +147 -0
- package/providers/fake/src/providers/store.provider.ts +30 -20
- package/providers/fake/src/schema/capabilities.schema.ts +5 -1
- package/providers/fake/src/test/cart.provider.spec.ts +59 -80
- package/providers/fake/src/test/category.provider.spec.ts +145 -87
- package/providers/fake/src/test/checkout.provider.spec.ts +222 -0
- package/providers/fake/src/test/order-search.provider.spec.ts +50 -0
- package/providers/fake/src/test/order.provider.spec.ts +44 -0
- package/providers/fake/src/test/price.provider.spec.ts +50 -45
- package/providers/fake/src/test/product.provider.spec.ts +15 -7
- package/providers/fake/src/test/profile.provider.spec.ts +167 -0
- package/providers/fake/tsconfig.json +1 -1
- package/providers/fake/tsconfig.lib.json +1 -1
- package/providers/fake/tsconfig.spec.json +2 -12
- package/providers/fake/vitest.config.ts +14 -0
- package/providers/medusa/README.md +30 -0
- package/providers/medusa/TESTING.md +98 -0
- package/providers/medusa/eslint.config.mjs +19 -0
- package/providers/medusa/package.json +22 -0
- package/providers/medusa/project.json +34 -0
- package/providers/medusa/src/core/client.ts +370 -0
- package/providers/medusa/src/core/initialize.ts +78 -0
- package/providers/medusa/src/index.ts +13 -0
- package/providers/medusa/src/providers/cart.provider.ts +575 -0
- package/providers/medusa/src/providers/category.provider.ts +247 -0
- package/providers/medusa/src/providers/checkout.provider.ts +636 -0
- package/providers/medusa/src/providers/identity.provider.ts +137 -0
- package/providers/medusa/src/providers/inventory.provider.ts +173 -0
- package/providers/medusa/src/providers/order-search.provider.ts +202 -0
- package/providers/medusa/src/providers/order.provider.ts +226 -0
- package/providers/medusa/src/providers/price.provider.ts +140 -0
- package/providers/medusa/src/providers/product-search.provider.ts +243 -0
- package/providers/medusa/src/providers/product.provider.ts +261 -0
- package/providers/medusa/src/providers/profile.provider.ts +392 -0
- package/providers/medusa/src/schema/capabilities.schema.ts +18 -0
- package/providers/medusa/src/schema/configuration.schema.ts +11 -0
- package/providers/medusa/src/schema/medusa.schema.ts +31 -0
- package/providers/medusa/src/test/cart.provider.spec.ts +240 -0
- package/providers/medusa/src/test/category.provider.spec.ts +231 -0
- package/providers/medusa/src/test/checkout.spec.ts +349 -0
- package/providers/medusa/src/test/identity.provider.spec.ts +122 -0
- package/providers/medusa/src/test/inventory.provider.spec.ts +88 -0
- package/providers/medusa/src/test/large-cart.provider.spec.ts +103 -0
- package/providers/medusa/src/test/price.provider.spec.ts +104 -0
- package/providers/medusa/src/test/product.provider.spec.ts +146 -0
- package/providers/medusa/src/test/search.provider.spec.ts +203 -0
- package/providers/medusa/src/test/test-utils.ts +13 -0
- package/providers/medusa/src/utils/medusa-helpers.ts +89 -0
- package/providers/medusa/tsconfig.json +21 -0
- package/providers/medusa/tsconfig.lib.json +9 -0
- package/providers/medusa/tsconfig.spec.json +4 -0
- package/providers/medusa/vitest.config.ts +15 -0
- package/providers/meilisearch/README.md +48 -0
- package/providers/meilisearch/eslint.config.mjs +22 -0
- package/providers/meilisearch/package.json +13 -0
- package/providers/meilisearch/project.json +34 -0
- package/providers/meilisearch/src/core/initialize.ts +21 -0
- package/providers/meilisearch/src/index.ts +6 -0
- package/providers/meilisearch/src/providers/index.ts +1 -0
- package/providers/meilisearch/src/providers/order-search.provider.ts +222 -0
- package/providers/meilisearch/src/providers/product-search.provider.ts +251 -0
- package/providers/meilisearch/src/schema/capabilities.schema.ts +10 -0
- package/providers/meilisearch/src/schema/configuration.schema.ts +11 -0
- package/providers/meilisearch/src/schema/index.ts +3 -0
- package/providers/meilisearch/src/schema/search.schema.ts +14 -0
- package/providers/meilisearch/tsconfig.json +24 -0
- package/providers/meilisearch/tsconfig.lib.json +10 -0
- package/providers/meilisearch/tsconfig.spec.json +4 -0
- package/providers/meilisearch/vitest.config.ts +14 -0
- package/providers/posthog/package.json +2 -1
- package/providers/posthog/tsconfig.json +1 -1
- package/tsconfig.base.json +5 -0
- package/vitest.config.ts +10 -0
- package/core/src/providers/search.provider.ts +0 -18
- package/core/src/schemas/models/search.model.ts +0 -36
- package/core/src/schemas/queries/search.query.ts +0 -9
- package/examples/next/.swcrc +0 -30
- package/examples/next/eslint.config.mjs +0 -21
- package/examples/next/index.d.ts +0 -6
- package/examples/next/next-env.d.ts +0 -5
- package/examples/next/next.config.js +0 -31
- package/examples/next/project.json +0 -9
- package/examples/next/public/.gitkeep +0 -0
- package/examples/next/public/favicon.ico +0 -0
- package/examples/next/src/app/global.css +0 -0
- package/examples/next/src/app/layout.tsx +0 -18
- package/examples/next/src/app/page.module.scss +0 -2
- package/examples/next/src/app/page.tsx +0 -47
- package/examples/next/src/instrumentation.ts +0 -9
- package/examples/next/tsconfig.json +0 -44
- package/examples/node/jest.config.ts +0 -10
- package/jest.config.ts +0 -6
- package/jest.preset.js +0 -3
- package/providers/algolia/jest.config.ts +0 -10
- package/providers/algolia/src/providers/product.provider.ts +0 -66
- package/providers/algolia/src/providers/search.provider.ts +0 -106
- package/providers/algolia/src/test/search.provider.spec.ts +0 -91
- package/providers/commercetools/jest.config.cjs +0 -10
- package/providers/commercetools/src/providers/search.provider.ts +0 -96
- package/providers/commercetools/src/test/cart.provider.spec.ts +0 -199
- package/providers/commercetools/src/test/category.provider.spec.ts +0 -168
- package/providers/commercetools/src/test/checkout.provider.spec.ts +0 -312
- package/providers/commercetools/src/test/identity.provider.spec.ts +0 -88
- package/providers/commercetools/src/test/inventory.provider.spec.ts +0 -41
- package/providers/commercetools/src/test/price.provider.spec.ts +0 -81
- package/providers/commercetools/src/test/product.provider.spec.ts +0 -80
- package/providers/commercetools/src/test/profile.provider.spec.ts +0 -49
- package/providers/commercetools/src/test/search.provider.spec.ts +0 -61
- package/providers/commercetools/src/test/store.provider.spec.ts +0 -37
- package/providers/fake/jest.config.cjs +0 -10
- package/providers/fake/src/providers/search.provider.ts +0 -132
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Cache,
|
|
3
|
+
Order,
|
|
4
|
+
OrderQueryById,
|
|
5
|
+
RequestContext,
|
|
6
|
+
Result,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
IdentityIdentifier,
|
|
9
|
+
CostBreakDown,
|
|
10
|
+
Currency,
|
|
11
|
+
OrderItem,
|
|
12
|
+
CartItem,
|
|
13
|
+
ProductVariantIdentifier,
|
|
14
|
+
ItemCostBreakdown,
|
|
15
|
+
OrderStatus,
|
|
16
|
+
InventoryStatus,
|
|
17
|
+
OrderInventoryStatus,
|
|
18
|
+
PaymentMethodIdentifier,
|
|
19
|
+
PaymentInstruction,
|
|
20
|
+
} from '@reactionary/core';
|
|
21
|
+
import {
|
|
22
|
+
OrderProvider,
|
|
23
|
+
OrderQueryByIdSchema,
|
|
24
|
+
OrderSchema,
|
|
25
|
+
Reactionary,
|
|
26
|
+
success,
|
|
27
|
+
error,
|
|
28
|
+
ProductVariantIdentifierSchema,
|
|
29
|
+
} from '@reactionary/core';
|
|
30
|
+
import createDebug from 'debug';
|
|
31
|
+
import type { MedusaAPI } from '../core/client.js';
|
|
32
|
+
import type { MedusaConfiguration } from '../schema/configuration.schema.js';
|
|
33
|
+
import { handleProviderError } from '../utils/medusa-helpers.js';
|
|
34
|
+
import type { StoreOrder, StoreOrderLineItem, StorePaymentCollection } from '@medusajs/types';
|
|
35
|
+
import { parseMedusaItemPrice, parseMedusaCostBreakdown } from '../utils/medusa-helpers.js'
|
|
36
|
+
const debug = createDebug('reactionary:medusa:order');
|
|
37
|
+
|
|
38
|
+
export class MedusaOrderProvider extends OrderProvider {
|
|
39
|
+
protected config: MedusaConfiguration;
|
|
40
|
+
|
|
41
|
+
constructor(
|
|
42
|
+
config: MedusaConfiguration,
|
|
43
|
+
cache: Cache,
|
|
44
|
+
context: RequestContext,
|
|
45
|
+
public medusaApi: MedusaAPI
|
|
46
|
+
) {
|
|
47
|
+
super(cache, context);
|
|
48
|
+
|
|
49
|
+
this.config = config;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Reactionary({
|
|
53
|
+
inputSchema: OrderQueryByIdSchema,
|
|
54
|
+
outputSchema: OrderSchema,
|
|
55
|
+
})
|
|
56
|
+
public async getById(payload: OrderQueryById): Promise<Result<Order, NotFoundError>> {
|
|
57
|
+
debug('getById', payload);
|
|
58
|
+
|
|
59
|
+
const medusa = await this.medusaApi.getClient();
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
// TODO: Implement actual order retrieval logic
|
|
63
|
+
// const response = await medusa.store.order.retrieve(payload.order.key);
|
|
64
|
+
const response = await medusa.store.order.retrieve(payload.order.key)
|
|
65
|
+
|
|
66
|
+
const order = this.parseSingle(response.order);
|
|
67
|
+
|
|
68
|
+
return success(order);
|
|
69
|
+
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return handleProviderError('order', err);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Extension point to control the parsing of a single cart item price
|
|
78
|
+
* @param remoteItem
|
|
79
|
+
* @param currency
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
protected parseItemPrice(
|
|
83
|
+
remoteItem: StoreOrderLineItem,
|
|
84
|
+
currency: Currency
|
|
85
|
+
): ItemCostBreakdown {
|
|
86
|
+
return parseMedusaItemPrice(remoteItem, currency);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Extension point to control the parsing of the cost breakdown of a cart
|
|
91
|
+
* @param remote
|
|
92
|
+
* @returns
|
|
93
|
+
*/
|
|
94
|
+
protected parseCostBreakdown(remote: StoreOrder): CostBreakDown {
|
|
95
|
+
return parseMedusaCostBreakdown(remote);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Extension point to control the parsing of a single cart item
|
|
100
|
+
* @param remoteItem
|
|
101
|
+
* @param currency
|
|
102
|
+
* @returns
|
|
103
|
+
*/
|
|
104
|
+
protected parseOrderItem(
|
|
105
|
+
remoteItem: StoreOrderLineItem,
|
|
106
|
+
currency: Currency
|
|
107
|
+
): OrderItem {
|
|
108
|
+
const item: OrderItem = {
|
|
109
|
+
identifier: {
|
|
110
|
+
key: remoteItem.id,
|
|
111
|
+
},
|
|
112
|
+
variant: ProductVariantIdentifierSchema.parse({
|
|
113
|
+
sku: remoteItem.variant_sku || '',
|
|
114
|
+
} satisfies ProductVariantIdentifier),
|
|
115
|
+
quantity: remoteItem.quantity || 1,
|
|
116
|
+
price: this.parseItemPrice(remoteItem, currency),
|
|
117
|
+
inventoryStatus: 'Allocated'
|
|
118
|
+
};
|
|
119
|
+
return item;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
protected parsePaymentInstruction(remotePayment: StorePaymentCollection, order: StoreOrder) {
|
|
123
|
+
const paymentMethodIdentifier: PaymentMethodIdentifier = {
|
|
124
|
+
method: remotePayment.payment_providers?.[0]?.id || 'unknown',
|
|
125
|
+
name: remotePayment.payment_providers?.[0]?.id || 'unknown',
|
|
126
|
+
paymentProcessor: remotePayment.payment_providers?.[0]?.id || 'unknown',
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
let status: PaymentInstruction['status'] = 'pending';
|
|
130
|
+
switch (remotePayment.status) {
|
|
131
|
+
case 'not_paid':
|
|
132
|
+
status = 'pending';
|
|
133
|
+
break;
|
|
134
|
+
case 'awaiting':
|
|
135
|
+
status = 'pending';
|
|
136
|
+
break;
|
|
137
|
+
case 'authorized':
|
|
138
|
+
status = 'authorized';
|
|
139
|
+
break;
|
|
140
|
+
case 'partially_authorized':
|
|
141
|
+
status = 'pending';
|
|
142
|
+
break;
|
|
143
|
+
case 'canceled':
|
|
144
|
+
status = 'canceled';
|
|
145
|
+
break;
|
|
146
|
+
case 'failed':
|
|
147
|
+
status = 'canceled';
|
|
148
|
+
break;
|
|
149
|
+
case 'completed':
|
|
150
|
+
status = 'capture';
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const paymentData = remotePayment.payments?.[0].data || {};
|
|
155
|
+
const pi = {
|
|
156
|
+
identifier: {
|
|
157
|
+
key: remotePayment.id,
|
|
158
|
+
},
|
|
159
|
+
amount: {
|
|
160
|
+
value: remotePayment.amount,
|
|
161
|
+
currency: remotePayment.currency_code?.toUpperCase() as Currency,
|
|
162
|
+
},
|
|
163
|
+
paymentMethod: paymentMethodIdentifier,
|
|
164
|
+
protocolData: paymentData
|
|
165
|
+
? Object.entries(paymentData).map(([key, value]) => ({
|
|
166
|
+
key,
|
|
167
|
+
value: String(value),
|
|
168
|
+
}))
|
|
169
|
+
: [],
|
|
170
|
+
status,
|
|
171
|
+
} satisfies PaymentInstruction;
|
|
172
|
+
return pi;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
protected parseSingle(body: StoreOrder): Order {
|
|
179
|
+
|
|
180
|
+
const identifier = { key: body.id };
|
|
181
|
+
const userId: IdentityIdentifier = {
|
|
182
|
+
userId: body.customer_id || '',
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const items = (body.items || []).map((item) => {
|
|
186
|
+
return this.parseOrderItem(item, body.currency_code.toUpperCase() as Currency);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const price = this.parseCostBreakdown(body);
|
|
190
|
+
|
|
191
|
+
let orderStatus: OrderStatus = 'AwaitingPayment'
|
|
192
|
+
if (body.status === 'draft') {
|
|
193
|
+
orderStatus = 'AwaitingPayment';
|
|
194
|
+
}
|
|
195
|
+
if (body.status === 'pending') {
|
|
196
|
+
orderStatus = 'ReleasedToFulfillment';
|
|
197
|
+
}
|
|
198
|
+
if (body.status === 'completed') {
|
|
199
|
+
orderStatus = 'Shipped';
|
|
200
|
+
}
|
|
201
|
+
if (body.status === 'canceled') {
|
|
202
|
+
orderStatus = 'Cancelled';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let inventoryStatus: OrderInventoryStatus = 'NotAllocated'
|
|
206
|
+
// Medusa does not have direct mapping for inventory status on orders
|
|
207
|
+
// This is a placeholder logic and may need to be adjusted based on actual requirements
|
|
208
|
+
if(body.fulfillment_status === "fulfilled") {
|
|
209
|
+
inventoryStatus = 'Allocated';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const paymentInstructions: PaymentInstruction[] = body.payment_collections?.map( (pc) => {
|
|
213
|
+
return this.parsePaymentInstruction(pc, body);
|
|
214
|
+
}) || [];
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
identifier,
|
|
218
|
+
userId,
|
|
219
|
+
items,
|
|
220
|
+
price,
|
|
221
|
+
orderStatus,
|
|
222
|
+
inventoryStatus,
|
|
223
|
+
paymentInstructions
|
|
224
|
+
} satisfies Order;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { StoreProductVariant } from '@medusajs/types';
|
|
2
|
+
import {
|
|
3
|
+
PriceProvider,
|
|
4
|
+
PriceSchema,
|
|
5
|
+
CustomerPriceQuerySchema,
|
|
6
|
+
ListPriceQuerySchema,
|
|
7
|
+
Reactionary,
|
|
8
|
+
success,
|
|
9
|
+
type Cache,
|
|
10
|
+
type Currency,
|
|
11
|
+
type CustomerPriceQuery,
|
|
12
|
+
type ListPriceQuery,
|
|
13
|
+
type Price,
|
|
14
|
+
type RequestContext,
|
|
15
|
+
type PriceIdentifier,
|
|
16
|
+
type MonetaryAmount,
|
|
17
|
+
type Result
|
|
18
|
+
} from '@reactionary/core';
|
|
19
|
+
import createDebug from 'debug';
|
|
20
|
+
import type z from 'zod';
|
|
21
|
+
import type { MedusaAPI } from '../core/client.js';
|
|
22
|
+
import type { MedusaConfiguration } from '../schema/configuration.schema.js';
|
|
23
|
+
|
|
24
|
+
const debug = createDebug('reactionary:medusa:price');
|
|
25
|
+
|
|
26
|
+
export class MedusaPriceProvider extends PriceProvider {
|
|
27
|
+
protected config: MedusaConfiguration;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
config: MedusaConfiguration,
|
|
31
|
+
cache: Cache,
|
|
32
|
+
context: RequestContext,
|
|
33
|
+
public medusaApi: MedusaAPI
|
|
34
|
+
) {
|
|
35
|
+
super(cache, context);
|
|
36
|
+
this.config = config;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Reactionary({
|
|
40
|
+
inputSchema: ListPriceQuerySchema,
|
|
41
|
+
outputSchema: PriceSchema,
|
|
42
|
+
})
|
|
43
|
+
public override async getListPrice(payload: ListPriceQuery): Promise<Result<Price>> {
|
|
44
|
+
const result = await this.getBySKU(payload);
|
|
45
|
+
|
|
46
|
+
return success(result);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Reactionary({
|
|
50
|
+
inputSchema: CustomerPriceQuerySchema,
|
|
51
|
+
outputSchema: PriceSchema,
|
|
52
|
+
})
|
|
53
|
+
public override async getCustomerPrice(payload: CustomerPriceQuery): Promise<Result<Price>> {
|
|
54
|
+
const result = await this.getBySKU(payload);
|
|
55
|
+
|
|
56
|
+
return success(result);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
protected async getBySKU(payload: ListPriceQuery | CustomerPriceQuery ): Promise<Price> {
|
|
61
|
+
const sku = payload.variant.sku;
|
|
62
|
+
|
|
63
|
+
if (debug.enabled) {
|
|
64
|
+
debug(`Fetching price for SKU: ${sku}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const productForSKU = await this.medusaApi.resolveProductForSKU(payload.variant.sku);
|
|
69
|
+
|
|
70
|
+
const client = await this.medusaApi.getClient();
|
|
71
|
+
const product = (
|
|
72
|
+
await client.store.product.retrieve(
|
|
73
|
+
productForSKU.id || '',
|
|
74
|
+
{ region_id: (await this.medusaApi.getActiveRegion()).id }
|
|
75
|
+
)
|
|
76
|
+
).product;
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
const variant = product.variants?.find((v) => v.sku === sku);
|
|
80
|
+
if (!variant) {
|
|
81
|
+
if (debug.enabled) {
|
|
82
|
+
debug(
|
|
83
|
+
`Variant with SKU ${sku} not found in product ${product.id}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return this.createEmptyPriceResult(sku);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// For simplicity, return the first matched product
|
|
90
|
+
return this.parseSingle(variant);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (debug.enabled) {
|
|
93
|
+
debug(
|
|
94
|
+
`Error fetching price for SKU ${sku}: ${(error as Error).message}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return this.createEmptyPriceResult(sku);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
protected parseSingle(variant: StoreProductVariant): Price {
|
|
102
|
+
const identifier = {
|
|
103
|
+
variant: {
|
|
104
|
+
sku: variant.sku || '',
|
|
105
|
+
},
|
|
106
|
+
} satisfies PriceIdentifier;
|
|
107
|
+
|
|
108
|
+
// In Medusa v2, calculated_price contains the final price for the variant
|
|
109
|
+
// based on the region, currency, and any applicable price lists
|
|
110
|
+
const calculatedPrice = variant.calculated_price;
|
|
111
|
+
|
|
112
|
+
let unitPrice;
|
|
113
|
+
if (calculatedPrice) {
|
|
114
|
+
unitPrice = {
|
|
115
|
+
value: calculatedPrice.calculated_amount || 0,
|
|
116
|
+
currency: (calculatedPrice.currency_code?.toUpperCase() ||
|
|
117
|
+
this.context.languageContext.currencyCode) as Currency,
|
|
118
|
+
} satisfies MonetaryAmount;
|
|
119
|
+
} else {
|
|
120
|
+
// Fallback to empty price if no calculated price available
|
|
121
|
+
unitPrice = {
|
|
122
|
+
value: -1,
|
|
123
|
+
currency: this.context.languageContext.currencyCode as Currency,
|
|
124
|
+
} satisfies MonetaryAmount;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = {
|
|
128
|
+
identifier,
|
|
129
|
+
tieredPrices: [],
|
|
130
|
+
unitPrice
|
|
131
|
+
} satisfies Price;
|
|
132
|
+
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
protected override getResourceName(): string {
|
|
138
|
+
return 'price';
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProductSearchProvider,
|
|
3
|
+
ProductSearchQueryByTermSchema,
|
|
4
|
+
type Cache,
|
|
5
|
+
type RequestContext,
|
|
6
|
+
type ProductSearchQueryByTerm,
|
|
7
|
+
type ProductSearchResult,
|
|
8
|
+
type ProductSearchResultItem,
|
|
9
|
+
ImageSchema,
|
|
10
|
+
ProductVariantIdentifierSchema,
|
|
11
|
+
type ProductVariantIdentifier,
|
|
12
|
+
ProductVariantOptionSchema,
|
|
13
|
+
type ProductVariantOption,
|
|
14
|
+
ProductOptionIdentifierSchema,
|
|
15
|
+
type ProductOptionIdentifier,
|
|
16
|
+
ProductSearchResultItemVariantSchema,
|
|
17
|
+
type ProductSearchResultItemVariant,
|
|
18
|
+
type FacetIdentifier,
|
|
19
|
+
type FacetValueIdentifier,
|
|
20
|
+
type ProductSearchResultFacet,
|
|
21
|
+
type ProductSearchResultFacetValue,
|
|
22
|
+
Reactionary,
|
|
23
|
+
ProductSearchResultSchema,
|
|
24
|
+
type ProductSearchQueryCreateNavigationFilter,
|
|
25
|
+
FacetValueIdentifierSchema,
|
|
26
|
+
FacetIdentifierSchema,
|
|
27
|
+
type Result,
|
|
28
|
+
success,
|
|
29
|
+
} from '@reactionary/core';
|
|
30
|
+
import createDebug from 'debug';
|
|
31
|
+
import type z from 'zod';
|
|
32
|
+
import type { MedusaConfiguration } from '../schema/configuration.schema.js';
|
|
33
|
+
import type { MedusaAPI } from '../core/client.js';
|
|
34
|
+
import type {
|
|
35
|
+
StoreProduct,
|
|
36
|
+
StoreProductCategory,
|
|
37
|
+
StoreProductListResponse,
|
|
38
|
+
StoreProductVariant,
|
|
39
|
+
} from '@medusajs/types';
|
|
40
|
+
|
|
41
|
+
const debug = createDebug('reactionary:medusa:search');
|
|
42
|
+
|
|
43
|
+
export class MedusaSearchProvider extends ProductSearchProvider {
|
|
44
|
+
protected config: MedusaConfiguration;
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
config: MedusaConfiguration,
|
|
48
|
+
cache: Cache,
|
|
49
|
+
context: RequestContext,
|
|
50
|
+
public medusaApi: MedusaAPI
|
|
51
|
+
) {
|
|
52
|
+
super(cache, context);
|
|
53
|
+
this.config = config;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected async resolveCategoryIdByExternalId(
|
|
57
|
+
externalId: string
|
|
58
|
+
): Promise<StoreProductCategory | null> {
|
|
59
|
+
const sdk = await this.medusaApi.getClient();
|
|
60
|
+
let offset = 0;
|
|
61
|
+
const limit = 50;
|
|
62
|
+
let candidate: StoreProductCategory | undefined = undefined;
|
|
63
|
+
while (true) {
|
|
64
|
+
try {
|
|
65
|
+
const categoryResult = await sdk.store.category.list({
|
|
66
|
+
offset,
|
|
67
|
+
limit,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (categoryResult.product_categories.length === 0) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
candidate = categoryResult.product_categories.find(
|
|
75
|
+
(cat) => cat.metadata?.['external_id'] === externalId
|
|
76
|
+
);
|
|
77
|
+
if (candidate) {
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
offset += limit;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
'Category not found ' + externalId + ' due to error: ' + error
|
|
84
|
+
);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return candidate || null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Reactionary({
|
|
92
|
+
inputSchema: ProductSearchQueryByTermSchema,
|
|
93
|
+
outputSchema: ProductSearchResultSchema,
|
|
94
|
+
cache: true,
|
|
95
|
+
cacheTimeToLiveInSeconds: 300,
|
|
96
|
+
currencyDependentCaching: false,
|
|
97
|
+
localeDependentCaching: true
|
|
98
|
+
})
|
|
99
|
+
public override async queryByTerm(
|
|
100
|
+
payload: ProductSearchQueryByTerm
|
|
101
|
+
): Promise<Result<ProductSearchResult>> {
|
|
102
|
+
const client = await this.medusaApi.getClient();
|
|
103
|
+
|
|
104
|
+
let categoryIdToFind: string | null = null;
|
|
105
|
+
if (payload.search.categoryFilter?.key) {
|
|
106
|
+
debug(
|
|
107
|
+
`Resolving category filter for key: ${payload.search.categoryFilter.key}`
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const category = await this.resolveCategoryIdByExternalId(
|
|
111
|
+
payload.search.categoryFilter.key
|
|
112
|
+
);
|
|
113
|
+
if (category) {
|
|
114
|
+
categoryIdToFind = category.id;
|
|
115
|
+
debug(
|
|
116
|
+
`Resolved category filter key ${payload.search.categoryFilter.key} to id: ${categoryIdToFind}`
|
|
117
|
+
);
|
|
118
|
+
} else {
|
|
119
|
+
debug(
|
|
120
|
+
`Could not resolve category filter for key: ${payload.search.categoryFilter.key}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const finalSearch = (payload.search.term || '').trim().replace('*', '');
|
|
125
|
+
const response = await client.store.product.list({
|
|
126
|
+
q: finalSearch,
|
|
127
|
+
...(categoryIdToFind ? { category_id: categoryIdToFind } : {}),
|
|
128
|
+
limit: payload.search.paginationOptions.pageSize,
|
|
129
|
+
offset:
|
|
130
|
+
(payload.search.paginationOptions.pageNumber - 1) *
|
|
131
|
+
payload.search.paginationOptions.pageSize,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const result = this.parsePaginatedResult(response) as ProductSearchResult;
|
|
135
|
+
if (debug.enabled) {
|
|
136
|
+
debug(
|
|
137
|
+
`Search for term "${payload.search.term}" returned ${response.products.length} products (page ${payload.search.paginationOptions.pageNumber} of ${result.totalPages})`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Set result metadata
|
|
142
|
+
result.identifier = {
|
|
143
|
+
...payload.search,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return success(result);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected parsePaginatedResult(remote: StoreProductListResponse) {
|
|
150
|
+
// Parse facets
|
|
151
|
+
// no facets available from Medusa at the moment
|
|
152
|
+
|
|
153
|
+
const products: ProductSearchResultItem[] = remote.products.map((p) =>
|
|
154
|
+
this.parseSingle(p)
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const result = {
|
|
158
|
+
identifier: {
|
|
159
|
+
facets: [],
|
|
160
|
+
filters: [],
|
|
161
|
+
paginationOptions: {
|
|
162
|
+
pageNumber: 1,
|
|
163
|
+
pageSize: 0,
|
|
164
|
+
},
|
|
165
|
+
term: '',
|
|
166
|
+
},
|
|
167
|
+
pageNumber: (Math.ceil(remote.offset / remote.limit) || 0) + 1,
|
|
168
|
+
pageSize: remote.limit,
|
|
169
|
+
totalCount: remote.count,
|
|
170
|
+
totalPages: Math.ceil(remote.count / remote.limit || 0) + 1,
|
|
171
|
+
items: products,
|
|
172
|
+
facets: [],
|
|
173
|
+
} satisfies ProductSearchResult;
|
|
174
|
+
|
|
175
|
+
(result as ProductSearchResult).facets = [];
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
protected parseSingle(_body: StoreProduct): ProductSearchResultItem {
|
|
180
|
+
const heroVariant = _body.variants?.[0];
|
|
181
|
+
const identifier = { key: _body.id };
|
|
182
|
+
const slug = _body.handle;
|
|
183
|
+
const name = heroVariant?.title || _body.title;
|
|
184
|
+
const variants = [];
|
|
185
|
+
if (heroVariant) {
|
|
186
|
+
variants.push(this.parseVariant(heroVariant, _body));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const result = {
|
|
190
|
+
identifier,
|
|
191
|
+
name,
|
|
192
|
+
slug,
|
|
193
|
+
variants,
|
|
194
|
+
} satisfies ProductSearchResultItem;
|
|
195
|
+
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
protected parseVariant(
|
|
200
|
+
variant: StoreProductVariant,
|
|
201
|
+
product: StoreProduct
|
|
202
|
+
): ProductSearchResultItemVariant {
|
|
203
|
+
const img = ImageSchema.parse({
|
|
204
|
+
sourceUrl: product.images?.[0].url ?? '',
|
|
205
|
+
altText: product.title || undefined,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return ProductSearchResultItemVariantSchema.parse({
|
|
209
|
+
variant: ProductVariantIdentifierSchema.parse({
|
|
210
|
+
sku: variant.sku || '',
|
|
211
|
+
} satisfies ProductVariantIdentifier),
|
|
212
|
+
image: img,
|
|
213
|
+
} satisfies Partial<ProductSearchResultItemVariant>);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
public override async createCategoryNavigationFilter(
|
|
217
|
+
payload: ProductSearchQueryCreateNavigationFilter
|
|
218
|
+
): Promise<Result<FacetValueIdentifier>> {
|
|
219
|
+
const facetIdentifier = FacetIdentifierSchema.parse({
|
|
220
|
+
key: 'categories',
|
|
221
|
+
});
|
|
222
|
+
const facetValueIdentifier = FacetValueIdentifierSchema.parse({
|
|
223
|
+
facet: facetIdentifier,
|
|
224
|
+
key: payload.categoryPath[payload.categoryPath.length - 1].identifier.key,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return success(facetValueIdentifier);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
protected override parseFacetValue(
|
|
231
|
+
facetValueIdentifier: FacetValueIdentifier,
|
|
232
|
+
label: string,
|
|
233
|
+
count: number
|
|
234
|
+
): ProductSearchResultFacetValue {
|
|
235
|
+
throw new Error('Method not implemented.');
|
|
236
|
+
}
|
|
237
|
+
protected override parseFacet(
|
|
238
|
+
facetIdentifier: FacetIdentifier,
|
|
239
|
+
facetValue: unknown
|
|
240
|
+
): ProductSearchResultFacet {
|
|
241
|
+
throw new Error('Method not implemented.');
|
|
242
|
+
}
|
|
243
|
+
}
|