@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,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Identity,
|
|
3
|
+
type IdentityMutationLogin,
|
|
4
|
+
type IdentityMutationLogout,
|
|
5
|
+
type IdentityMutationRegister,
|
|
6
|
+
type IdentityQuerySelf,
|
|
7
|
+
type RequestContext,
|
|
8
|
+
type Cache,
|
|
9
|
+
IdentityProvider,
|
|
10
|
+
Reactionary,
|
|
11
|
+
IdentityQuerySelfSchema,
|
|
12
|
+
IdentitySchema,
|
|
13
|
+
IdentityMutationRegisterSchema,
|
|
14
|
+
IdentityMutationLoginSchema,
|
|
15
|
+
IdentityMutationLogoutSchema,
|
|
16
|
+
type AnonymousIdentity,
|
|
17
|
+
type RegisteredIdentity,
|
|
18
|
+
type Result,
|
|
19
|
+
success,
|
|
20
|
+
} from '@reactionary/core';
|
|
21
|
+
import type { MedusaConfiguration } from '../schema/configuration.schema.js';
|
|
22
|
+
import type z from 'zod';
|
|
23
|
+
import type { MedusaAPI } from '../core/client.js';
|
|
24
|
+
import createDebug from 'debug';
|
|
25
|
+
|
|
26
|
+
const debug = createDebug('reactionary:medusa:identity');
|
|
27
|
+
|
|
28
|
+
export class MedusaIdentityProvider extends IdentityProvider {
|
|
29
|
+
protected config: MedusaConfiguration;
|
|
30
|
+
protected medusaApi: MedusaAPI;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
config: MedusaConfiguration,
|
|
34
|
+
cache: Cache,
|
|
35
|
+
context: RequestContext,
|
|
36
|
+
medusaApi: MedusaAPI
|
|
37
|
+
) {
|
|
38
|
+
super(cache, context);
|
|
39
|
+
|
|
40
|
+
this.config = config;
|
|
41
|
+
this.medusaApi = medusaApi;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected createAnonymousIdentity(): AnonymousIdentity {
|
|
45
|
+
return {
|
|
46
|
+
type: 'Anonymous',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Reactionary({
|
|
51
|
+
inputSchema: IdentityQuerySelfSchema,
|
|
52
|
+
outputSchema: IdentitySchema,
|
|
53
|
+
})
|
|
54
|
+
public override async getSelf(
|
|
55
|
+
_payload: IdentityQuerySelf
|
|
56
|
+
): Promise<Result<Identity>> {
|
|
57
|
+
try {
|
|
58
|
+
const medusaClient = await this.medusaApi.getClient();
|
|
59
|
+
const token = await medusaClient.client.getToken();
|
|
60
|
+
|
|
61
|
+
if (!token) {
|
|
62
|
+
debug('No active session token found, returning anonymous identity');
|
|
63
|
+
return success(this.createAnonymousIdentity());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Try to fetch customer details to verify authentication
|
|
67
|
+
const customerResponse = await medusaClient.store.customer.retrieve();
|
|
68
|
+
|
|
69
|
+
if (customerResponse.customer) {
|
|
70
|
+
debug('Customer authenticated:', customerResponse.customer.email);
|
|
71
|
+
return success({
|
|
72
|
+
id: {
|
|
73
|
+
userId: customerResponse.customer.id,
|
|
74
|
+
},
|
|
75
|
+
type: 'Registered',
|
|
76
|
+
} satisfies RegisteredIdentity);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return success(this.createAnonymousIdentity());
|
|
80
|
+
} catch (error) {
|
|
81
|
+
debug('getSelf failed, returning anonymous identity:', error);
|
|
82
|
+
return success(this.createAnonymousIdentity());
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Reactionary({
|
|
87
|
+
inputSchema: IdentityMutationLoginSchema,
|
|
88
|
+
outputSchema: IdentitySchema,
|
|
89
|
+
})
|
|
90
|
+
public override async login(payload: IdentityMutationLogin): Promise<Result<Identity>> {
|
|
91
|
+
debug('Attempting login for user:', payload.username);
|
|
92
|
+
const identity = await this.medusaApi.login(
|
|
93
|
+
payload.username,
|
|
94
|
+
payload.password,
|
|
95
|
+
this.context
|
|
96
|
+
) satisfies Identity;
|
|
97
|
+
|
|
98
|
+
return success(identity);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@Reactionary({
|
|
102
|
+
inputSchema: IdentityMutationLogoutSchema,
|
|
103
|
+
outputSchema: IdentitySchema,
|
|
104
|
+
})
|
|
105
|
+
public override async logout(_payload: IdentityMutationLogout): Promise<Result<Identity>> {
|
|
106
|
+
debug('Logging out user');
|
|
107
|
+
const identity = await this.medusaApi.logout(this.context);
|
|
108
|
+
|
|
109
|
+
return success(identity);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@Reactionary({
|
|
113
|
+
inputSchema: IdentityMutationRegisterSchema,
|
|
114
|
+
outputSchema: IdentitySchema,
|
|
115
|
+
})
|
|
116
|
+
public override async register(
|
|
117
|
+
payload: IdentityMutationRegister
|
|
118
|
+
): Promise<Result<Identity>> {
|
|
119
|
+
debug('Registering new user:', payload.username);
|
|
120
|
+
|
|
121
|
+
// Extract first and last name from username or use defaults
|
|
122
|
+
// In a real implementation, you might want to add firstName/lastName to the schema
|
|
123
|
+
const nameParts = payload.username.split('.');
|
|
124
|
+
const firstName = nameParts[0] || 'User';
|
|
125
|
+
const lastName = nameParts.slice(1).join(' ') || 'Account';
|
|
126
|
+
|
|
127
|
+
const identity = await this.medusaApi.register(
|
|
128
|
+
payload.username, // Using username as email
|
|
129
|
+
payload.password,
|
|
130
|
+
firstName,
|
|
131
|
+
lastName,
|
|
132
|
+
this.context
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return success(identity);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Inventory,
|
|
3
|
+
type InventoryQueryBySKU,
|
|
4
|
+
type RequestContext,
|
|
5
|
+
type Cache,
|
|
6
|
+
InventoryProvider,
|
|
7
|
+
InventorySchema,
|
|
8
|
+
InventoryQueryBySKUSchema,
|
|
9
|
+
Reactionary,
|
|
10
|
+
type InventoryIdentifier,
|
|
11
|
+
type InventoryStatus,
|
|
12
|
+
type NotFoundError,
|
|
13
|
+
type Result,
|
|
14
|
+
success,
|
|
15
|
+
} from '@reactionary/core';
|
|
16
|
+
import type z from 'zod';
|
|
17
|
+
import type { MedusaConfiguration } from '../schema/configuration.schema.js';
|
|
18
|
+
import { MedusaAdminAPI, type MedusaAPI } from '../core/client.js';
|
|
19
|
+
import createDebug from 'debug';
|
|
20
|
+
|
|
21
|
+
const debug = createDebug('reactionary:medusa:inventory');
|
|
22
|
+
|
|
23
|
+
export class MedusaInventoryProvider extends InventoryProvider {
|
|
24
|
+
protected config: MedusaConfiguration;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
config: MedusaConfiguration,
|
|
28
|
+
cache: Cache,
|
|
29
|
+
context: RequestContext,
|
|
30
|
+
public medusaApi: MedusaAPI
|
|
31
|
+
) {
|
|
32
|
+
super(cache, context);
|
|
33
|
+
this.config = config;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Reactionary({
|
|
37
|
+
inputSchema: InventoryQueryBySKUSchema,
|
|
38
|
+
outputSchema: InventorySchema,
|
|
39
|
+
})
|
|
40
|
+
public override async getBySKU(payload: InventoryQueryBySKU): Promise<Result<Inventory, NotFoundError>> {
|
|
41
|
+
const sku = payload.variant.sku;
|
|
42
|
+
const fulfillmentCenterKey = payload.fulfilmentCenter.key;
|
|
43
|
+
|
|
44
|
+
if (debug.enabled) {
|
|
45
|
+
debug(`Fetching inventory for SKU: ${sku}, fulfillment center: ${fulfillmentCenterKey}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const adminClient = await (new MedusaAdminAPI(this.config, this.context).getClient());
|
|
50
|
+
|
|
51
|
+
// Get inventory items for this variant
|
|
52
|
+
const inventoryResponse = await adminClient.admin.inventoryItem.list({
|
|
53
|
+
sku: [sku],
|
|
54
|
+
limit: 1,
|
|
55
|
+
offset: 0
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
if (!inventoryResponse.inventory_items || inventoryResponse.inventory_items.length === 0) {
|
|
59
|
+
if (debug.enabled) {
|
|
60
|
+
debug(`No inventory items found for SKU: ${sku}`);
|
|
61
|
+
}
|
|
62
|
+
return success(this.createEmptyInventoryResult(sku, fulfillmentCenterKey));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const inventoryItem = inventoryResponse.inventory_items[0];
|
|
66
|
+
|
|
67
|
+
// Get inventory levels for this item and location
|
|
68
|
+
// In Medusa, we need to find the stock location that matches our fulfillment center key
|
|
69
|
+
const locationsResponse = await adminClient.admin.stockLocation.list({
|
|
70
|
+
name: fulfillmentCenterKey,
|
|
71
|
+
limit: 1,
|
|
72
|
+
offset: 0
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
let quantity = 0;
|
|
76
|
+
let locationFound = false;
|
|
77
|
+
|
|
78
|
+
if (locationsResponse.stock_locations && locationsResponse.stock_locations.length > 0) {
|
|
79
|
+
const location = locationsResponse.stock_locations[0];
|
|
80
|
+
locationFound = true;
|
|
81
|
+
|
|
82
|
+
// Get inventory level for this item at this location
|
|
83
|
+
const levelsResponse = await adminClient.admin.inventoryItem.listLevels(inventoryItem.id, {
|
|
84
|
+
location_id: [location.id],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (levelsResponse.inventory_levels && levelsResponse.inventory_levels.length > 0) {
|
|
88
|
+
const level = levelsResponse.inventory_levels[0];
|
|
89
|
+
quantity = level.stocked_quantity - level.reserved_quantity;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!locationFound) {
|
|
94
|
+
if (debug.enabled) {
|
|
95
|
+
debug(`No stock location found with name: ${fulfillmentCenterKey}`);
|
|
96
|
+
}
|
|
97
|
+
return success(this.createEmptyInventoryResult(sku, fulfillmentCenterKey));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return success(this.parseSingle({
|
|
101
|
+
sku: payload.variant.sku,
|
|
102
|
+
fulfillmentCenterKey,
|
|
103
|
+
quantity,
|
|
104
|
+
inventoryItemId: inventoryItem.id,
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (debug.enabled) {
|
|
109
|
+
debug(`Error fetching inventory for SKU: ${sku}`, error);
|
|
110
|
+
}
|
|
111
|
+
return success(this.createEmptyInventoryResult(sku, fulfillmentCenterKey));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected parseSingle(_body: unknown): Inventory {
|
|
116
|
+
const { sku, fulfillmentCenterKey, quantity } = _body as {
|
|
117
|
+
sku: string;
|
|
118
|
+
fulfillmentCenterKey: string;
|
|
119
|
+
quantity: number;
|
|
120
|
+
inventoryItemId: string;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const identifier = {
|
|
124
|
+
variant: {
|
|
125
|
+
sku,
|
|
126
|
+
},
|
|
127
|
+
fulfillmentCenter: {
|
|
128
|
+
key: fulfillmentCenterKey,
|
|
129
|
+
},
|
|
130
|
+
} satisfies InventoryIdentifier;
|
|
131
|
+
|
|
132
|
+
let status: InventoryStatus = 'outOfStock';
|
|
133
|
+
if (quantity > 0) {
|
|
134
|
+
status = 'inStock';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const result = {
|
|
138
|
+
identifier,
|
|
139
|
+
quantity,
|
|
140
|
+
status
|
|
141
|
+
} satisfies Inventory;
|
|
142
|
+
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Utility function to create an empty inventory result.
|
|
148
|
+
* This is used when no inventory is found for a given SKU + fulfillment center combination.
|
|
149
|
+
* @param sku
|
|
150
|
+
* @param fulfillmentCenterKey
|
|
151
|
+
* @returns
|
|
152
|
+
*/
|
|
153
|
+
protected createEmptyInventoryResult(sku: string, fulfillmentCenterKey: string): Inventory {
|
|
154
|
+
const identifier = {
|
|
155
|
+
variant: { sku },
|
|
156
|
+
fulfillmentCenter: { key: fulfillmentCenterKey },
|
|
157
|
+
} satisfies InventoryIdentifier;
|
|
158
|
+
|
|
159
|
+
const quantity = 0;
|
|
160
|
+
const status = 'outOfStock';
|
|
161
|
+
const result = {
|
|
162
|
+
identifier,
|
|
163
|
+
quantity,
|
|
164
|
+
status
|
|
165
|
+
} satisfies Inventory;
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
protected override getResourceName(): string {
|
|
171
|
+
return 'inventory';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RequestContext,
|
|
3
|
+
Cache,
|
|
4
|
+
OrderSearchQueryByTerm,
|
|
5
|
+
OrderSearchResult,
|
|
6
|
+
Result,
|
|
7
|
+
OrderStatus,
|
|
8
|
+
Address,
|
|
9
|
+
IdentityIdentifier,
|
|
10
|
+
MonetaryAmount,
|
|
11
|
+
Currency,
|
|
12
|
+
OrderSearchResultItem,
|
|
13
|
+
OrderSearchIdentifier,
|
|
14
|
+
AddressIdentifier,
|
|
15
|
+
OrderInventoryStatus,
|
|
16
|
+
} from '@reactionary/core';
|
|
17
|
+
import {
|
|
18
|
+
AddressIdentifierSchema,
|
|
19
|
+
OrderSearchProvider,
|
|
20
|
+
OrderSearchQueryByTermSchema,
|
|
21
|
+
OrderSearchResultSchema,
|
|
22
|
+
Reactionary,
|
|
23
|
+
success,
|
|
24
|
+
} from '@reactionary/core';
|
|
25
|
+
import type { MedusaConfiguration } from '../schema/configuration.schema.js';
|
|
26
|
+
import type { MedusaAPI } from '../core/client.js';
|
|
27
|
+
import createDebug from 'debug';
|
|
28
|
+
import type { OrderStatus as MedusaOrderStatus, StoreOrder, StoreOrderAddress, StoreOrderListResponse } from '@medusajs/types';
|
|
29
|
+
|
|
30
|
+
const debug = createDebug('reactionary:medusa:order-search');
|
|
31
|
+
|
|
32
|
+
export class MedusaOrderSearchProvider extends OrderSearchProvider {
|
|
33
|
+
protected config: MedusaConfiguration;
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
config: MedusaConfiguration,
|
|
37
|
+
cache: Cache,
|
|
38
|
+
context: RequestContext,
|
|
39
|
+
public medusaApi: MedusaAPI
|
|
40
|
+
) {
|
|
41
|
+
super(cache, context);
|
|
42
|
+
|
|
43
|
+
this.config = config;
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Reactionary({
|
|
48
|
+
inputSchema: OrderSearchQueryByTermSchema,
|
|
49
|
+
outputSchema: OrderSearchResultSchema,
|
|
50
|
+
})
|
|
51
|
+
public async queryByTerm(payload: OrderSearchQueryByTerm): Promise<Result<OrderSearchResult>> {
|
|
52
|
+
debug('queryByTerm', payload);
|
|
53
|
+
|
|
54
|
+
const medusa = await this.medusaApi.getClient();
|
|
55
|
+
|
|
56
|
+
if (payload.search.term) {
|
|
57
|
+
debug('Searching orders by term is not supported in Medusa');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (payload.search.partNumber) {
|
|
61
|
+
debug('Searching orders by part number is not supported in Medusa');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (payload.search.startDate) {
|
|
65
|
+
debug('Searching orders by start date is not supported in Medusa');
|
|
66
|
+
}
|
|
67
|
+
if (payload.search.endDate) {
|
|
68
|
+
debug('Searching orders by end date is not supported in Medusa');
|
|
69
|
+
}
|
|
70
|
+
/*
|
|
71
|
+
if (payload.search.user && payload.search.user.userId) {
|
|
72
|
+
debug('Searching orders by customer ID is not supported in Medusa');
|
|
73
|
+
} */
|
|
74
|
+
const statusFilter: MedusaOrderStatus[] = (payload.search.orderStatus ?? []).map((status) => {
|
|
75
|
+
let retStatus: MedusaOrderStatus = 'draft';
|
|
76
|
+
if (status === 'AwaitingPayment') {
|
|
77
|
+
retStatus = 'draft';
|
|
78
|
+
}
|
|
79
|
+
if (status === 'ReleasedToFulfillment') {
|
|
80
|
+
retStatus = 'pending';
|
|
81
|
+
}
|
|
82
|
+
if (status === 'Shipped') {
|
|
83
|
+
retStatus = 'completed';
|
|
84
|
+
}
|
|
85
|
+
if (status === 'Cancelled') {
|
|
86
|
+
retStatus = 'canceled';
|
|
87
|
+
}
|
|
88
|
+
return retStatus;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
const response = await medusa.store.order.list({
|
|
93
|
+
status: statusFilter,
|
|
94
|
+
limit: payload.search.paginationOptions.pageSize,
|
|
95
|
+
offset:
|
|
96
|
+
(payload.search.paginationOptions.pageNumber - 1) *
|
|
97
|
+
payload.search.paginationOptions.pageSize,
|
|
98
|
+
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const result = this.parsePaginatedResult(response, payload) as OrderSearchResult;
|
|
102
|
+
if (debug.enabled) {
|
|
103
|
+
debug(
|
|
104
|
+
`Search for term "${payload.search.term}" returned ${response.orders.length} orders (page ${payload.search.paginationOptions.pageNumber} of ${result.totalPages})`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return success(result);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected composeAddressFromStoreAddress(storeAddress: StoreOrderAddress): Address {
|
|
112
|
+
return {
|
|
113
|
+
identifier: AddressIdentifierSchema.parse({
|
|
114
|
+
nickName: storeAddress.id,
|
|
115
|
+
} satisfies AddressIdentifier),
|
|
116
|
+
firstName: storeAddress.first_name || '',
|
|
117
|
+
lastName: storeAddress.last_name || '',
|
|
118
|
+
streetAddress: storeAddress.address_1 || '',
|
|
119
|
+
streetNumber: storeAddress.address_2 || '',
|
|
120
|
+
city: storeAddress.city || '',
|
|
121
|
+
postalCode: storeAddress.postal_code || '',
|
|
122
|
+
countryCode: storeAddress.country_code || '',
|
|
123
|
+
region: '',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
protected parseSingle(body: StoreOrder) {
|
|
130
|
+
const identifier = { key: body.id };
|
|
131
|
+
const userId: IdentityIdentifier = {
|
|
132
|
+
userId: body.customer_id || '',
|
|
133
|
+
};
|
|
134
|
+
const customerName = `${body.billing_address?.first_name} ${body.billing_address?.last_name}`;
|
|
135
|
+
const shippingAddress = this.composeAddressFromStoreAddress(body.shipping_address!);
|
|
136
|
+
const orderDate = new Date(body.created_at).toISOString();
|
|
137
|
+
|
|
138
|
+
let orderStatus: OrderStatus = 'AwaitingPayment'
|
|
139
|
+
if (body.status === 'draft') {
|
|
140
|
+
orderStatus = 'AwaitingPayment';
|
|
141
|
+
}
|
|
142
|
+
if (body.status === 'pending') {
|
|
143
|
+
orderStatus = 'ReleasedToFulfillment';
|
|
144
|
+
}
|
|
145
|
+
if (body.status === 'completed') {
|
|
146
|
+
orderStatus = 'Shipped';
|
|
147
|
+
}
|
|
148
|
+
if (body.status === 'canceled') {
|
|
149
|
+
orderStatus = 'Cancelled';
|
|
150
|
+
}
|
|
151
|
+
let inventoryStatus: OrderInventoryStatus = 'NotAllocated'
|
|
152
|
+
// Medusa does not have direct mapping for inventory status on orders
|
|
153
|
+
// This is a placeholder logic and may need to be adjusted based on actual requirements
|
|
154
|
+
if(body.fulfillment_status === "fulfilled") {
|
|
155
|
+
inventoryStatus = 'Allocated';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const totalAmount: MonetaryAmount = {
|
|
159
|
+
currency: body.currency_code.toUpperCase() as Currency,
|
|
160
|
+
value: body.total ? body.total : 0
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const order = {
|
|
164
|
+
identifier,
|
|
165
|
+
userId,
|
|
166
|
+
customerName,
|
|
167
|
+
shippingAddress,
|
|
168
|
+
orderDate,
|
|
169
|
+
orderStatus,
|
|
170
|
+
inventoryStatus,
|
|
171
|
+
totalAmount
|
|
172
|
+
} satisfies OrderSearchResultItem;
|
|
173
|
+
|
|
174
|
+
return order;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
protected parsePaginatedResult(
|
|
178
|
+
body: StoreOrderListResponse,
|
|
179
|
+
query: OrderSearchQueryByTerm
|
|
180
|
+
) {
|
|
181
|
+
const identifier = {
|
|
182
|
+
...query.search,
|
|
183
|
+
} satisfies OrderSearchIdentifier;
|
|
184
|
+
|
|
185
|
+
const orders: OrderSearchResultItem[] = body.orders.map((o) => {
|
|
186
|
+
return this.parseSingle(o);
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const result = {
|
|
190
|
+
identifier,
|
|
191
|
+
pageNumber: (Math.ceil(body.offset / body.limit) || 0) + 1,
|
|
192
|
+
pageSize: body.limit,
|
|
193
|
+
totalCount: body.count,
|
|
194
|
+
totalPages: Math.ceil(body.count / body.limit || 0) + 1,
|
|
195
|
+
items: orders,
|
|
196
|
+
} satisfies OrderSearchResult;
|
|
197
|
+
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
}
|