@reactionary/source 0.0.41 → 0.0.48
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/.claude/settings.local.json +28 -0
- package/.env-template +8 -5
- package/.vscode/settings.json +5 -0
- package/README.md +41 -0
- package/core/package.json +3 -1
- package/core/src/cache/cache.interface.ts +14 -18
- package/core/src/cache/memory-cache.ts +56 -0
- package/core/src/cache/noop-cache.ts +5 -23
- package/core/src/cache/redis-cache.ts +28 -38
- package/core/src/client/client-builder.ts +3 -3
- package/core/src/client/client.ts +11 -9
- package/core/src/decorators/reactionary.decorator.ts +80 -8
- package/core/src/index.ts +5 -29
- package/core/src/initialization.ts +43 -0
- package/core/src/providers/analytics.provider.ts +1 -1
- package/core/src/providers/base.provider.ts +61 -25
- package/core/src/providers/cart-payment.provider.ts +57 -0
- package/core/src/providers/cart.provider.ts +131 -8
- package/core/src/providers/category.provider.ts +9 -9
- package/core/src/providers/identity.provider.ts +8 -7
- package/core/src/providers/index.ts +12 -0
- package/core/src/providers/inventory.provider.ts +4 -4
- package/core/src/providers/price.provider.ts +7 -7
- package/core/src/providers/product.provider.ts +17 -5
- package/core/src/providers/profile.provider.ts +22 -0
- package/core/src/providers/search.provider.ts +4 -4
- package/core/src/providers/store.provider.ts +14 -0
- package/core/src/schemas/capabilities.schema.ts +3 -1
- package/core/src/schemas/models/analytics.model.ts +1 -1
- package/core/src/schemas/models/cart.model.ts +16 -3
- package/core/src/schemas/models/identifiers.model.ts +90 -22
- package/core/src/schemas/models/identity.model.ts +23 -7
- package/core/src/schemas/models/index.ts +15 -0
- package/core/src/schemas/models/payment.model.ts +41 -0
- package/core/src/schemas/models/profile.model.ts +35 -0
- package/core/src/schemas/models/shipping-method.model.ts +14 -0
- package/core/src/schemas/models/store.model.ts +11 -0
- package/core/src/schemas/mutations/cart-payment.mutation.ts +21 -0
- package/core/src/schemas/mutations/cart.mutation.ts +62 -3
- package/core/src/schemas/mutations/identity.mutation.ts +8 -1
- package/core/src/schemas/mutations/index.ts +10 -0
- package/core/src/schemas/mutations/profile.mutation.ts +9 -0
- package/core/src/schemas/queries/cart-payment.query.ts +12 -0
- package/core/src/schemas/queries/cart.query.ts +1 -1
- package/core/src/schemas/queries/identity.query.ts +1 -1
- package/core/src/schemas/queries/index.ts +3 -0
- package/core/src/schemas/queries/inventory.query.ts +4 -12
- package/core/src/schemas/queries/price.query.ts +1 -1
- package/core/src/schemas/queries/profile.query.ts +7 -0
- package/core/src/schemas/queries/search.query.ts +1 -1
- package/core/src/schemas/queries/store.query.ts +11 -0
- package/core/src/schemas/session.schema.ts +31 -6
- package/eslint.config.mjs +7 -0
- package/examples/next/src/app/page.tsx +4 -12
- package/examples/node/package.json +1 -3
- package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +9 -8
- package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +4 -3
- package/examples/node/src/basic/basic-node-setup.spec.ts +4 -5
- package/nx.json +1 -0
- package/otel/src/metrics.ts +2 -1
- package/otel/src/provider-instrumentation.ts +2 -1
- package/otel/src/tracer.ts +7 -6
- package/otel/src/trpc-middleware.ts +3 -2
- package/package.json +2 -1
- package/providers/algolia/src/core/initialize.ts +4 -3
- package/providers/algolia/src/providers/product.provider.ts +15 -13
- package/providers/algolia/src/providers/search.provider.ts +9 -9
- package/providers/algolia/src/schema/capabilities.schema.ts +1 -1
- package/providers/algolia/src/test/search.provider.spec.ts +10 -10
- package/providers/algolia/src/test/test-utils.ts +9 -4
- package/providers/commercetools/README.md +27 -0
- package/providers/commercetools/src/core/client.ts +164 -117
- package/providers/commercetools/src/core/initialize.ts +24 -14
- package/providers/commercetools/src/providers/cart-payment.provider.ts +193 -0
- package/providers/commercetools/src/providers/cart.provider.ts +402 -125
- package/providers/commercetools/src/providers/category.provider.ts +35 -35
- package/providers/commercetools/src/providers/identity.provider.ts +23 -75
- package/providers/commercetools/src/providers/index.ts +2 -0
- package/providers/commercetools/src/providers/inventory.provider.ts +69 -40
- package/providers/commercetools/src/providers/price.provider.ts +79 -47
- package/providers/commercetools/src/providers/product.provider.ts +36 -30
- package/providers/commercetools/src/providers/profile.provider.ts +61 -0
- package/providers/commercetools/src/providers/search.provider.ts +16 -12
- package/providers/commercetools/src/providers/store.provider.ts +78 -0
- package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
- package/providers/commercetools/src/schema/commercetools.schema.ts +18 -0
- package/providers/commercetools/src/schema/configuration.schema.ts +2 -1
- package/providers/commercetools/src/test/cart-payment.provider.spec.ts +145 -0
- package/providers/commercetools/src/test/cart.provider.spec.ts +82 -22
- package/providers/commercetools/src/test/category.provider.spec.ts +18 -17
- package/providers/commercetools/src/test/identity.provider.spec.ts +88 -0
- package/providers/commercetools/src/test/inventory.provider.spec.ts +41 -0
- package/providers/commercetools/src/test/price.provider.spec.ts +9 -8
- package/providers/commercetools/src/test/product.provider.spec.ts +33 -5
- package/providers/commercetools/src/test/profile.provider.spec.ts +49 -0
- package/providers/commercetools/src/test/search.provider.spec.ts +8 -7
- package/providers/commercetools/src/test/store.provider.spec.ts +37 -0
- package/providers/commercetools/src/test/test-utils.ts +7 -31
- package/providers/fake/src/core/initialize.ts +96 -38
- package/providers/fake/src/providers/analytics.provider.ts +6 -5
- package/providers/fake/src/providers/cart.provider.ts +66 -19
- package/providers/fake/src/providers/category.provider.ts +12 -12
- package/providers/fake/src/providers/identity.provider.ts +22 -14
- package/providers/fake/src/providers/index.ts +1 -0
- package/providers/fake/src/providers/inventory.provider.ts +13 -13
- package/providers/fake/src/providers/price.provider.ts +13 -13
- package/providers/fake/src/providers/product.provider.ts +13 -10
- package/providers/fake/src/providers/search.provider.ts +7 -5
- package/providers/fake/src/providers/store.provider.ts +47 -0
- package/providers/fake/src/schema/capabilities.schema.ts +4 -1
- package/providers/fake/src/test/cart.provider.spec.ts +18 -18
- package/providers/fake/src/test/category.provider.spec.ts +55 -37
- package/providers/fake/src/test/price.provider.spec.ts +9 -14
- package/providers/fake/src/test/product.provider.spec.ts +27 -0
- package/providers/fake/src/test/test-utils.ts +2 -28
- package/providers/posthog/src/core/initialize.ts +3 -3
- package/providers/posthog/src/schema/capabilities.schema.ts +1 -1
- package/trpc/src/client.ts +42 -41
- package/trpc/src/index.ts +4 -3
- package/trpc/src/integration.spec.ts +11 -11
- package/trpc/src/server.ts +26 -24
- package/trpc/src/test-utils.ts +9 -4
- package/trpc/src/types.ts +24 -22
- package/core/src/cache/cache-evaluation.interface.ts +0 -19
- package/examples/node/src/test-utils.ts +0 -26
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type {
|
|
3
|
+
BaseModel} from '../schemas/models/base.model';
|
|
4
|
+
import {
|
|
5
|
+
createPaginatedResponseSchema,
|
|
6
|
+
} from '../schemas/models/base.model';
|
|
7
|
+
import type { Cache } from '../cache/cache.interface';
|
|
8
|
+
import type { RequestContext, Session } from '../schemas/session.schema';
|
|
9
|
+
import type { IdentifierType } from '../schemas/models/identifiers.model';
|
|
10
|
+
import { hasher } from "node-object-hash";
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* Base capability provider, responsible for mutations (changes) and queries (fetches)
|
|
9
14
|
* for a given business object domain.
|
|
10
15
|
*/
|
|
11
|
-
export abstract class BaseProvider<
|
|
12
|
-
T extends BaseModel = BaseModel
|
|
13
|
-
> {
|
|
16
|
+
export abstract class BaseProvider<T extends BaseModel = BaseModel> {
|
|
14
17
|
protected cache: Cache;
|
|
15
18
|
|
|
16
|
-
constructor(
|
|
17
|
-
public readonly schema: z.ZodType<T>,
|
|
18
|
-
cache: Cache
|
|
19
|
-
) {
|
|
19
|
+
constructor(public readonly schema: z.ZodType<T>, cache: Cache) {
|
|
20
20
|
this.cache = cache;
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -39,37 +39,73 @@ export abstract class BaseProvider<
|
|
|
39
39
|
* Handler for parsing a response from a remote provider and converting it
|
|
40
40
|
* into the typed domain model.
|
|
41
41
|
*/
|
|
42
|
-
protected parseSingle(_body: unknown,
|
|
42
|
+
protected parseSingle(_body: unknown, reqCtx: RequestContext): T {
|
|
43
43
|
const model = this.newModel();
|
|
44
44
|
|
|
45
45
|
return this.assert(model);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
protected parsePaginatedResult(_body: unknown,
|
|
49
|
+
protected parsePaginatedResult(_body: unknown, reqCtx: RequestContext): z.infer<ReturnType<typeof createPaginatedResponseSchema<typeof this.schema>>> {
|
|
50
50
|
return createPaginatedResponseSchema(this.schema).parse({});
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
public generateDependencyIdsForModel(model: unknown): Array<string> {
|
|
54
|
+
// TODO: Messy because we can't guarantee that a model has an identifier (type-wise)
|
|
55
|
+
const identifier = (model as any)?.identifier;
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
if (!identifier) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const h = hasher({ sort: true, coerce: false });
|
|
62
|
+
const hash = h.hash(identifier);
|
|
63
|
+
|
|
64
|
+
return [hash];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
protected generateCacheKeyForQuery(scope: string, query: object): string {
|
|
68
|
+
const h = hasher({ sort: true, coerce: false });
|
|
69
|
+
|
|
70
|
+
const queryHash = h.hash(query);
|
|
71
|
+
|
|
72
|
+
// TODO: This really should include the internationalization parts as well (locale, currency, etc), or at least provide the option
|
|
73
|
+
// for specifying in the decorator whether they do (eg categories don't really seem to depend on currency...)
|
|
74
|
+
|
|
75
|
+
return `${scope}:${queryHash}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected generateCacheKeyPaginatedResult(
|
|
79
|
+
resultSetName: string,
|
|
80
|
+
res: ReturnType<typeof this.parsePaginatedResult>,
|
|
81
|
+
reqCtx: RequestContext
|
|
82
|
+
): string {
|
|
54
83
|
const type = this.getResourceName();
|
|
55
|
-
const langPart =
|
|
56
|
-
const currencyPart =
|
|
57
|
-
const storePart =
|
|
84
|
+
const langPart = reqCtx.languageContext.locale;
|
|
85
|
+
const currencyPart = reqCtx.languageContext.currencyCode || 'default';
|
|
86
|
+
const storePart = reqCtx.storeIdentifier?.key || 'default';
|
|
58
87
|
return `${type}-${resultSetName}-paginated|pageNumber:${res.pageNumber}|pageSize:${res.pageSize}|store:${storePart}|lang:${langPart}|currency:${currencyPart}`;
|
|
59
88
|
}
|
|
60
89
|
|
|
61
|
-
|
|
62
|
-
|
|
90
|
+
protected generateCacheKeySingle(
|
|
91
|
+
identifier: IdentifierType,
|
|
92
|
+
reqCtx: RequestContext
|
|
93
|
+
): string {
|
|
63
94
|
const type = this.getResourceName();
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
95
|
+
|
|
96
|
+
const idPart = Object.entries(identifier)
|
|
97
|
+
.map(([k, v]) => `${k}:${v}`)
|
|
98
|
+
.join('#');
|
|
99
|
+
|
|
100
|
+
const langPart = reqCtx.languageContext.locale;
|
|
101
|
+
const currencyPart = reqCtx.languageContext.currencyCode || 'default';
|
|
102
|
+
const storePart = reqCtx.storeIdentifier?.key || 'default';
|
|
103
|
+
|
|
68
104
|
return `${type}-${idPart}|store:${storePart}|lang:${langPart}|currency:${currencyPart}`;
|
|
69
105
|
}
|
|
70
106
|
|
|
71
107
|
/**
|
|
72
108
|
* Returns the abstract resource name provided by the remote system.
|
|
73
109
|
*/
|
|
74
|
-
protected abstract getResourceName(): string
|
|
110
|
+
protected abstract getResourceName(): string;
|
|
75
111
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CartPaymentInstruction } from '../schemas/models/payment.model';
|
|
2
|
+
import type { CartPaymentMutationAddPayment, CartPaymentMutationCancelPayment } from '../schemas/mutations/cart-payment.mutation';
|
|
3
|
+
import type { CartPaymentQueryByCart } from '../schemas/queries/cart-payment.query';
|
|
4
|
+
import type { RequestContext} from '../schemas/session.schema';
|
|
5
|
+
import { Session } from '../schemas/session.schema';
|
|
6
|
+
import { BaseProvider } from './base.provider';
|
|
7
|
+
|
|
8
|
+
export abstract class CartPaymentProvider<
|
|
9
|
+
T extends CartPaymentInstruction = CartPaymentInstruction
|
|
10
|
+
> extends BaseProvider<T> {
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Returns all payment instructions associated with a given cart, optionally filtered by status
|
|
16
|
+
*
|
|
17
|
+
* Usecase: Fetch all registered payment instructions to show on checkout page, in case you support multiple payments in your storefront,
|
|
18
|
+
* and need to show how far the user has come in the payment process. Also useful if user reloads page, or goes back to site or otherwise breaks the flow.
|
|
19
|
+
*
|
|
20
|
+
* Only returns payment instructions in status 'pending' or 'authorized'
|
|
21
|
+
* @param cartIdentifier
|
|
22
|
+
* @param session
|
|
23
|
+
*/
|
|
24
|
+
public abstract getByCartIdentifier(payload: CartPaymentQueryByCart, reqCtx: RequestContext): Promise<T[]>;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Calls payment provider to set up a new payment intent. Returns whatever is needed to continue the payment process, e.g. a client secret for Stripe, or a redirect URL for PayPal.
|
|
29
|
+
*
|
|
30
|
+
* Usecase: User has filled out checkout form, and is ready to pay. You call this to get the payment process started.
|
|
31
|
+
*
|
|
32
|
+
* Note: The payment provider MAY change the cart during the payment process, so be sure to reload the cart object after this call.
|
|
33
|
+
*
|
|
34
|
+
* @param payload
|
|
35
|
+
* @param session
|
|
36
|
+
*/
|
|
37
|
+
public abstract initiatePaymentForCart(payload: CartPaymentMutationAddPayment, reqCtx: RequestContext): Promise<T>;
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Cancel a payment instruction. This will typically void the payment intent in the payment provider, and set the status of the payment instruction to 'canceled'.
|
|
43
|
+
*
|
|
44
|
+
* Usecase: User has decided to cancel the payment, e.g. by going back in the checkout process, or by closing the browser window. You call this to clean up the payment instruction.
|
|
45
|
+
*
|
|
46
|
+
* Note: The payment provider MAY change the cart during the cancellation process, so be sure to reload the cart object after this call.
|
|
47
|
+
*
|
|
48
|
+
* @param payload
|
|
49
|
+
* @param session
|
|
50
|
+
* @returns
|
|
51
|
+
*/
|
|
52
|
+
public abstract cancelPaymentInstruction(payload: CartPaymentMutationCancelPayment, reqCtx: RequestContext): Promise<T>;
|
|
53
|
+
|
|
54
|
+
protected override getResourceName(): string {
|
|
55
|
+
return 'cart-payment-instruction';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,17 +1,140 @@
|
|
|
1
1
|
import { BaseProvider } from "./base.provider";
|
|
2
|
-
import { Cart } from "../schemas/models/cart.model";
|
|
3
|
-
import { CartQueryById } from "../schemas/queries/cart.query";
|
|
4
|
-
import {
|
|
5
|
-
import { CartMutationItemAdd, CartMutationItemQuantityChange, CartMutationItemRemove } from "../schemas/mutations/cart.mutation";
|
|
2
|
+
import type { Cart } from "../schemas/models/cart.model";
|
|
3
|
+
import type { CartQueryById } from "../schemas/queries/cart.query";
|
|
4
|
+
import type { RequestContext } from "../schemas/session.schema";
|
|
5
|
+
import type { CartMutationApplyCoupon, CartMutationChangeCurrency, CartMutationCheckout, CartMutationDeleteCart, CartMutationItemAdd, CartMutationItemQuantityChange, CartMutationItemRemove, CartMutationRemoveCoupon, CartMutationSetBillingAddress, CartMutationSetShippingInfo } from "../schemas/mutations/cart.mutation";
|
|
6
|
+
import type { CartIdentifier, OrderIdentifier } from "../schemas/models/identifiers.model";
|
|
6
7
|
|
|
7
8
|
export abstract class CartProvider<
|
|
8
9
|
T extends Cart = Cart
|
|
9
10
|
> extends BaseProvider<T> {
|
|
10
|
-
public abstract getById(payload: CartQueryById, session: Session): Promise<T>;
|
|
11
|
-
public abstract add(payload: CartMutationItemAdd, session: Session): Promise<T>;
|
|
12
|
-
public abstract remove(payload: CartMutationItemRemove, session: Session): Promise<T>;
|
|
13
|
-
public abstract changeQuantity(payload: CartMutationItemQuantityChange, session: Session): Promise<T>;
|
|
14
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Get cart by ID.
|
|
14
|
+
*
|
|
15
|
+
* Usecase: Unclear, until we support multiple carts per user.
|
|
16
|
+
* @param payload
|
|
17
|
+
* @param session
|
|
18
|
+
*/
|
|
19
|
+
public abstract getById(payload: CartQueryById, reqCtx: RequestContext): Promise<T>;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the active cart id for the user.
|
|
24
|
+
*
|
|
25
|
+
* Usecase: Most common usecase during site load, or after login. You want to get the active cart for the user, so you can display it in the minicart.
|
|
26
|
+
* @param session
|
|
27
|
+
*/
|
|
28
|
+
public abstract getActiveCartId(reqCtx: RequestContext): Promise<CartIdentifier>;
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Add item to cart. If no cart exists, create a new one. Returns the updated and recalculated cart.
|
|
33
|
+
* Does not automatically consolidate items, so if you want to have second add of same item to increase quantity,
|
|
34
|
+
* you need to handle that in your logic or on the server.
|
|
35
|
+
*
|
|
36
|
+
* Usecase: Add item to cart, create cart if none exists.
|
|
37
|
+
* @param payload
|
|
38
|
+
* @param session
|
|
39
|
+
*/
|
|
40
|
+
public abstract add(payload: CartMutationItemAdd, reqCtx: RequestContext): Promise<T>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Remove item from cart. If the cart is empty after removal, delete the cart. Returns the updated and recalculated cart.
|
|
44
|
+
*
|
|
45
|
+
* Usecase: Remove item from cart, delete cart if empty.
|
|
46
|
+
* @param payload
|
|
47
|
+
* @param session
|
|
48
|
+
*/
|
|
49
|
+
public abstract remove(payload: CartMutationItemRemove, reqCtx: RequestContext): Promise<T>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Change quantity of item in cart. If the cart is empty after change, delete the cart. Returns the updated and recalculated cart.
|
|
53
|
+
* Changing quantity to 0 is not allowed. Use the remove call instead. This is done to avoid accidental removal of item.
|
|
54
|
+
* Calls with quantity 0 will just be ignored.
|
|
55
|
+
*
|
|
56
|
+
* Usecase: Change quantity of item in cart, like in a minicart, or in the full cart view.
|
|
57
|
+
* @param payload
|
|
58
|
+
* @param session
|
|
59
|
+
*/
|
|
60
|
+
public abstract changeQuantity(payload: CartMutationItemQuantityChange, reqCtx: RequestContext): Promise<T>;
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Deletes the entire cart.
|
|
65
|
+
*
|
|
66
|
+
* Usecase: User wants to empty the cart or something is wrong with the current cart, and you want to clear it out and start fresh.
|
|
67
|
+
* @param payload
|
|
68
|
+
* @param session
|
|
69
|
+
*/
|
|
70
|
+
public abstract deleteCart(payload: CartMutationDeleteCart, reqCtx: RequestContext): Promise<T>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Sets shipping method and address on the cart. Returns the updated and recalculated cart.
|
|
74
|
+
*
|
|
75
|
+
* Usecase: User selects shipping method during checkout.
|
|
76
|
+
* @param payload
|
|
77
|
+
* @param session
|
|
78
|
+
*/
|
|
79
|
+
public abstract setShippingInfo(payload: CartMutationSetShippingInfo, reqCtx: RequestContext): Promise<T>;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Sets billing address on the cart. Returns the updated and recalculated cart.
|
|
83
|
+
*
|
|
84
|
+
* Usecase: User enters billing address during checkout.
|
|
85
|
+
*
|
|
86
|
+
* @param payload
|
|
87
|
+
* @param session
|
|
88
|
+
*/
|
|
89
|
+
public abstract setBillingAddress(payload: CartMutationSetBillingAddress, reqCtx: RequestContext): Promise<T>;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Applies a coupon code to the cart. Returns the updated and recalculated cart.
|
|
93
|
+
*
|
|
94
|
+
* Usecase: User applies a coupon code during checkout.
|
|
95
|
+
* @param payload
|
|
96
|
+
* @param session
|
|
97
|
+
*/
|
|
98
|
+
public abstract applyCouponCode(payload: CartMutationApplyCoupon, reqCtx: RequestContext): Promise<T>;
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Removes a coupon code from the cart. Returns the updated and recalculated cart.
|
|
103
|
+
*
|
|
104
|
+
* Usecase: User removes a coupon code during checkout.
|
|
105
|
+
* @param payload
|
|
106
|
+
* @param session
|
|
107
|
+
*/
|
|
108
|
+
public abstract removeCouponCode(payload: CartMutationRemoveCoupon, reqCtx: RequestContext): Promise<T>;
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Checks out the cart. Returns the order identifier of the newly created order.
|
|
113
|
+
*
|
|
114
|
+
* Usecase: User proceeds to checkout.
|
|
115
|
+
*
|
|
116
|
+
* @param payload
|
|
117
|
+
* @param session
|
|
118
|
+
*/
|
|
119
|
+
public abstract checkout(payload: CartMutationCheckout, reqCtx: RequestContext): Promise<OrderIdentifier>;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Changes the currency of the cart.
|
|
123
|
+
*
|
|
124
|
+
* Usecase: User wants to change the currency for his session. This will change the currency of the cart, and recalculate prices.
|
|
125
|
+
* @param newCurrency
|
|
126
|
+
* @param session
|
|
127
|
+
*/
|
|
128
|
+
public abstract changeCurrency(payload: CartMutationChangeCurrency, reqCtx: RequestContext): Promise<T>;
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
protected createEmptyCart(): T {
|
|
133
|
+
const cart = this.newModel();
|
|
134
|
+
cart.meta = { placeholder: true, cache: { hit: true, key: 'empty-cart' } };
|
|
135
|
+
cart.identifier = { key: '' };
|
|
136
|
+
return cart;
|
|
137
|
+
}
|
|
15
138
|
|
|
16
139
|
protected override getResourceName(): string {
|
|
17
140
|
return 'cart';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
import { PaginationOptions } from "../schemas/models/base.model";
|
|
4
|
-
import { Category } from "../schemas/models/category.model";
|
|
4
|
+
import type { Category } from "../schemas/models/category.model";
|
|
5
5
|
import { CategoryIdentifier } from "../schemas/models/identifiers.model";
|
|
6
|
-
import { CategoryQueryById, CategoryQueryBySlug, CategoryQueryForBreadcrumb, CategoryQueryForChildCategories, CategoryQueryForTopCategories } from "../schemas/queries/category.query";
|
|
7
|
-
|
|
6
|
+
import type { CategoryQueryById, CategoryQueryBySlug, CategoryQueryForBreadcrumb, CategoryQueryForChildCategories, CategoryQueryForTopCategories } from "../schemas/queries/category.query";
|
|
7
|
+
import type { RequestContext} from "../schemas/session.schema";
|
|
8
8
|
import { Session } from "../schemas/session.schema";
|
|
9
9
|
import { BaseProvider } from "./base.provider";
|
|
10
10
|
|
|
@@ -32,11 +32,11 @@ export abstract class CategoryProvider<
|
|
|
32
32
|
* For now, the result will be en empty category, but we should probably throw an error instead.
|
|
33
33
|
*
|
|
34
34
|
* Use case: You have received a list of category ids from a recommendation engine, and you need to show a tile of this.
|
|
35
|
-
* Future optimization: getByIds(ids: CategoryIdentifier[],
|
|
35
|
+
* Future optimization: getByIds(ids: CategoryIdentifier[], reqCtx: RequestContext): Promise<T[]>
|
|
36
36
|
* @param id
|
|
37
37
|
* @param session
|
|
38
38
|
*/
|
|
39
|
-
public abstract getById(payload: CategoryQueryById,
|
|
39
|
+
public abstract getById(payload: CategoryQueryById, reqCtx: RequestContext): Promise<T>;
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Gets a single category by its seo slug
|
|
@@ -45,7 +45,7 @@ export abstract class CategoryProvider<
|
|
|
45
45
|
* @param slug the slug
|
|
46
46
|
* @param session
|
|
47
47
|
*/
|
|
48
|
-
public abstract getBySlug(payload: CategoryQueryBySlug,
|
|
48
|
+
public abstract getBySlug(payload: CategoryQueryBySlug, reqCtx: RequestContext): Promise<T | null>;
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -56,7 +56,7 @@ export abstract class CategoryProvider<
|
|
|
56
56
|
* @param id
|
|
57
57
|
* @param session
|
|
58
58
|
*/
|
|
59
|
-
public abstract getBreadcrumbPathToCategory(payload: CategoryQueryForBreadcrumb,
|
|
59
|
+
public abstract getBreadcrumbPathToCategory(payload: CategoryQueryForBreadcrumb, reqCtx: RequestContext): Promise<T[]>;
|
|
60
60
|
|
|
61
61
|
// hmm, this is not really good enough.... We need a type we can pass in that will allow us to specify the precise return type, but otoh we also need
|
|
62
62
|
// to be able to verify and assert the output type. FIXME
|
|
@@ -71,7 +71,7 @@ export abstract class CategoryProvider<
|
|
|
71
71
|
* @param id The ID of the parent category.
|
|
72
72
|
* @param session The session information.
|
|
73
73
|
*/
|
|
74
|
-
public abstract findChildCategories(payload: CategoryQueryForChildCategories,
|
|
74
|
+
public abstract findChildCategories(payload: CategoryQueryForChildCategories, reqCtx: RequestContext): Promise< ReturnType<typeof this.parsePaginatedResult>>;
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Returns all top categories, i.e. categories without a parent.
|
|
@@ -80,7 +80,7 @@ export abstract class CategoryProvider<
|
|
|
80
80
|
* @param paginationOptions
|
|
81
81
|
* @param session
|
|
82
82
|
*/
|
|
83
|
-
public abstract findTopCategories( payload: CategoryQueryForTopCategories,
|
|
83
|
+
public abstract findTopCategories( payload: CategoryQueryForTopCategories, reqCtx: RequestContext): Promise<ReturnType<typeof this.parsePaginatedResult>>;
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
protected override getResourceName(): string {
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { Identity } from "../schemas/models/identity.model";
|
|
2
|
-
import { IdentityMutationLogin, IdentityMutationLogout } from "../schemas/mutations/identity.mutation";
|
|
3
|
-
import { IdentityQuerySelf } from "../schemas/queries/identity.query";
|
|
4
|
-
import {
|
|
1
|
+
import type { Identity } from "../schemas/models/identity.model";
|
|
2
|
+
import type { IdentityMutationLogin, IdentityMutationLogout, IdentityMutationRegister } from "../schemas/mutations/identity.mutation";
|
|
3
|
+
import type { IdentityQuerySelf } from "../schemas/queries/identity.query";
|
|
4
|
+
import type { RequestContext} from "../schemas/session.schema";
|
|
5
5
|
import { BaseProvider } from "./base.provider";
|
|
6
6
|
|
|
7
7
|
export abstract class IdentityProvider<
|
|
8
8
|
T extends Identity = Identity,
|
|
9
9
|
> extends BaseProvider<T> {
|
|
10
|
-
public abstract getSelf(payload: IdentityQuerySelf,
|
|
11
|
-
public abstract login(payload: IdentityMutationLogin,
|
|
12
|
-
public abstract logout(payload: IdentityMutationLogout,
|
|
10
|
+
public abstract getSelf(payload: IdentityQuerySelf, reqCtx: RequestContext): Promise<T>;
|
|
11
|
+
public abstract login(payload: IdentityMutationLogin, reqCtx: RequestContext): Promise<T>;
|
|
12
|
+
public abstract logout(payload: IdentityMutationLogout, reqCtx: RequestContext): Promise<T>;
|
|
13
|
+
public abstract register(payload: IdentityMutationRegister, reqCtx: RequestContext): Promise<T>;
|
|
13
14
|
|
|
14
15
|
protected override getResourceName(): string {
|
|
15
16
|
return 'identity';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './analytics.provider';
|
|
2
|
+
export * from './base.provider';
|
|
3
|
+
export * from './cart-payment.provider';
|
|
4
|
+
export * from './cart.provider';
|
|
5
|
+
export * from './category.provider';
|
|
6
|
+
export * from './identity.provider';
|
|
7
|
+
export * from './inventory.provider';
|
|
8
|
+
export * from './price.provider';
|
|
9
|
+
export * from './product.provider';
|
|
10
|
+
export * from './profile.provider';
|
|
11
|
+
export * from './search.provider';
|
|
12
|
+
export * from './store.provider';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Inventory } from '../schemas/models/inventory.model';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { Inventory } from '../schemas/models/inventory.model';
|
|
2
|
+
import type { InventoryQueryBySKU } from '../schemas/queries/inventory.query';
|
|
3
|
+
import type { RequestContext } from '../schemas/session.schema';
|
|
4
4
|
import { BaseProvider } from './base.provider';
|
|
5
5
|
|
|
6
6
|
export abstract class InventoryProvider<
|
|
7
7
|
T extends Inventory = Inventory
|
|
8
8
|
> extends BaseProvider<T> {
|
|
9
|
-
public abstract getBySKU(payload:
|
|
9
|
+
public abstract getBySKU(payload: InventoryQueryBySKU, reqCtx: RequestContext): Promise<T>;
|
|
10
10
|
|
|
11
11
|
protected override getResourceName(): string {
|
|
12
12
|
return 'inventory';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Currency } from '../schemas/models/currency.model';
|
|
2
|
-
import { Price } from '../schemas/models/price.model';
|
|
3
|
-
import { PriceQueryBySku } from '../schemas/queries/price.query';
|
|
4
|
-
import {
|
|
1
|
+
import type { Currency } from '../schemas/models/currency.model';
|
|
2
|
+
import type { Price } from '../schemas/models/price.model';
|
|
3
|
+
import type { PriceQueryBySku } from '../schemas/queries/price.query';
|
|
4
|
+
import type { RequestContext } from '../schemas/session.schema';
|
|
5
5
|
import { BaseProvider } from './base.provider';
|
|
6
6
|
|
|
7
7
|
export abstract class PriceProvider<
|
|
@@ -19,7 +19,7 @@ export abstract class PriceProvider<
|
|
|
19
19
|
* @param payload The SKU to query
|
|
20
20
|
* @param session The session information
|
|
21
21
|
*/
|
|
22
|
-
public abstract getBySKU(payload: PriceQueryBySku,
|
|
22
|
+
public abstract getBySKU(payload: PriceQueryBySku, reqCtx: RequestContext): Promise<T>;
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -29,7 +29,7 @@ export abstract class PriceProvider<
|
|
|
29
29
|
* @param payload The SKUs to query
|
|
30
30
|
* @param session The session information
|
|
31
31
|
*/
|
|
32
|
-
public abstract getBySKUs(payload: PriceQueryBySku[],
|
|
32
|
+
public abstract getBySKUs(payload: PriceQueryBySku[], reqCtx: RequestContext): Promise<T[]>;
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
/**
|
|
@@ -40,7 +40,7 @@ export abstract class PriceProvider<
|
|
|
40
40
|
* @param currency
|
|
41
41
|
* @returns
|
|
42
42
|
*/
|
|
43
|
-
protected
|
|
43
|
+
protected createEmptyPriceResult(sku: string, currency: Currency): T {
|
|
44
44
|
const base = this.newModel();
|
|
45
45
|
base.identifier = {
|
|
46
46
|
sku: { key: sku }
|
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { Product } from '../schemas/models/product.model';
|
|
1
|
+
import type { Product } from '../schemas/models/product.model';
|
|
2
2
|
import { BaseProvider } from './base.provider';
|
|
3
|
-
import {
|
|
4
|
-
import { ProductQueryById, ProductQueryBySlug } from '../schemas/queries/product.query';
|
|
3
|
+
import type { RequestContext } from '../schemas/session.schema';
|
|
4
|
+
import type { ProductQueryById, ProductQueryBySlug } from '../schemas/queries/product.query';
|
|
5
5
|
|
|
6
6
|
export abstract class ProductProvider<
|
|
7
7
|
T extends Product = Product
|
|
8
8
|
> extends BaseProvider<T> {
|
|
9
|
-
public abstract getById(payload: ProductQueryById,
|
|
10
|
-
public abstract getBySlug(payload: ProductQueryBySlug,
|
|
9
|
+
public abstract getById(payload: ProductQueryById, reqCtx: RequestContext): Promise<T>;
|
|
10
|
+
public abstract getBySlug(payload: ProductQueryBySlug, reqCtx: RequestContext): Promise<T | null>;
|
|
11
11
|
|
|
12
|
+
|
|
13
|
+
protected createEmptyProduct(id: string): T {
|
|
14
|
+
const product = this.newModel();
|
|
15
|
+
product.identifier = { key: id };
|
|
16
|
+
product.meta.placeholder = true;
|
|
17
|
+
return product;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The resource name, used for caching and logging.
|
|
22
|
+
* @returns
|
|
23
|
+
*/
|
|
12
24
|
protected override getResourceName(): string {
|
|
13
25
|
return 'product';
|
|
14
26
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Profile } from '../schemas/models';
|
|
2
|
+
import type { ProfileMutationUpdate } from '../schemas/mutations';
|
|
3
|
+
import type { ProfileQuerySelf } from '../schemas/queries';
|
|
4
|
+
import type { RequestContext } from '../schemas/session.schema';
|
|
5
|
+
import { BaseProvider } from './base.provider';
|
|
6
|
+
|
|
7
|
+
export abstract class ProfileProvider<
|
|
8
|
+
T extends Profile = Profile
|
|
9
|
+
> extends BaseProvider<T> {
|
|
10
|
+
public abstract getSelf(
|
|
11
|
+
payload: ProfileQuerySelf,
|
|
12
|
+
reqCtx: RequestContext
|
|
13
|
+
): Promise<T>;
|
|
14
|
+
public abstract update(
|
|
15
|
+
payload: ProfileMutationUpdate,
|
|
16
|
+
reqCtx: RequestContext
|
|
17
|
+
): Promise<T>;
|
|
18
|
+
|
|
19
|
+
protected override getResourceName(): string {
|
|
20
|
+
return 'profile';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { SearchResult } from '../schemas/models/search.model';
|
|
2
|
-
import { SearchQueryByTerm } from '../schemas/queries/search.query';
|
|
3
|
-
import {
|
|
1
|
+
import type { SearchResult } from '../schemas/models/search.model';
|
|
2
|
+
import type { SearchQueryByTerm } from '../schemas/queries/search.query';
|
|
3
|
+
import type { RequestContext } from '../schemas/session.schema';
|
|
4
4
|
import { BaseProvider } from './base.provider';
|
|
5
5
|
|
|
6
6
|
export abstract class SearchProvider<
|
|
7
7
|
T extends SearchResult = SearchResult
|
|
8
8
|
> extends BaseProvider<T> {
|
|
9
|
-
public abstract queryByTerm(payload: SearchQueryByTerm,
|
|
9
|
+
public abstract queryByTerm(payload: SearchQueryByTerm, reqCtx: RequestContext): Promise<SearchResult>;
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
protected override getResourceName(): string {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Store } from '../schemas/models/store.model';
|
|
2
|
+
import type { StoreQueryByProximity } from '../schemas/queries';
|
|
3
|
+
import type { RequestContext } from '../schemas/session.schema';
|
|
4
|
+
import { BaseProvider } from './base.provider';
|
|
5
|
+
|
|
6
|
+
export abstract class StoreProvider<
|
|
7
|
+
T extends Store = Store
|
|
8
|
+
> extends BaseProvider<T> {
|
|
9
|
+
public abstract queryByProximity(payload: StoreQueryByProximity, reqCtx: RequestContext): Promise<Array<T>>;
|
|
10
|
+
|
|
11
|
+
protected override getResourceName(): string {
|
|
12
|
+
return 'store';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -6,9 +6,11 @@ export const CapabilitiesSchema = z.looseObject({
|
|
|
6
6
|
analytics: z.boolean(),
|
|
7
7
|
identity: z.boolean(),
|
|
8
8
|
cart: z.boolean(),
|
|
9
|
+
cartPayment: z.boolean(),
|
|
9
10
|
inventory: z.boolean(),
|
|
10
11
|
price: z.boolean(),
|
|
11
|
-
category: z.boolean()
|
|
12
|
+
category: z.boolean(),
|
|
13
|
+
store: z.boolean()
|
|
12
14
|
});
|
|
13
15
|
|
|
14
16
|
export type Capabilities = z.infer<typeof CapabilitiesSchema>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { CartIdentifierSchema, CartItemIdentifierSchema, ProductIdentifierSchema } from '../models/identifiers.model';
|
|
2
|
+
import { CartIdentifierSchema, CartItemIdentifierSchema, IdentityIdentifierSchema, ProductIdentifierSchema, SKUIdentifierSchema } from '../models/identifiers.model';
|
|
3
3
|
import { BaseModelSchema } from './base.model';
|
|
4
4
|
import { MonetaryAmountSchema } from './price.model';
|
|
5
|
+
import { AddressSchema } from './profile.model';
|
|
6
|
+
import { ShippingMethodSchema } from './shipping-method.model';
|
|
5
7
|
|
|
6
8
|
export const CostBreakDownSchema = z.looseObject({
|
|
7
9
|
totalTax: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The amount of tax paid on the cart. This may include VAT, GST, sales tax, etc.'),
|
|
@@ -11,7 +13,6 @@ export const CostBreakDownSchema = z.looseObject({
|
|
|
11
13
|
totalProductPrice: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The total price of products in the cart.'),
|
|
12
14
|
grandTotal: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The total price for the cart including all taxes, discounts, and shipping.'),
|
|
13
15
|
});
|
|
14
|
-
export type CostBreakDown = z.infer<typeof CostBreakDownSchema>;
|
|
15
16
|
|
|
16
17
|
export const ItemCostBreakdownSchema = z.looseObject({
|
|
17
18
|
unitPrice: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The price per single unit of the item.'),
|
|
@@ -20,22 +21,34 @@ export const ItemCostBreakdownSchema = z.looseObject({
|
|
|
20
21
|
totalDiscount: MonetaryAmountSchema.default(() => MonetaryAmountSchema.parse({})).describe('The total discount applied to all units of the item.'),
|
|
21
22
|
});
|
|
22
23
|
|
|
23
|
-
export type ItemCostBreakdown = z.infer<typeof ItemCostBreakdownSchema>;
|
|
24
24
|
|
|
25
25
|
export const CartItemSchema = z.looseObject({
|
|
26
26
|
identifier: CartItemIdentifierSchema.default(() => CartItemIdentifierSchema.parse({})),
|
|
27
27
|
product: ProductIdentifierSchema.default(() => ProductIdentifierSchema.parse({})),
|
|
28
|
+
sku: SKUIdentifierSchema.default(() => SKUIdentifierSchema.parse({})),
|
|
28
29
|
quantity: z.number().default(0),
|
|
29
30
|
price: ItemCostBreakdownSchema.default(() => ItemCostBreakdownSchema.parse({})),
|
|
30
31
|
});
|
|
31
32
|
|
|
32
33
|
export const CartSchema = BaseModelSchema.extend({
|
|
33
34
|
identifier: CartIdentifierSchema.default(() => CartIdentifierSchema.parse({})),
|
|
35
|
+
|
|
36
|
+
userId: IdentityIdentifierSchema.default(() => IdentityIdentifierSchema.parse({})),
|
|
37
|
+
|
|
34
38
|
items: z.array(CartItemSchema).default(() => []),
|
|
35
39
|
price: CostBreakDownSchema.default(() => CostBreakDownSchema.parse({})),
|
|
36
40
|
name: z.string().default(''),
|
|
37
41
|
description: z.string().default(''),
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
shippingAddress: AddressSchema.optional(),
|
|
45
|
+
billingAddress: AddressSchema.optional(),
|
|
46
|
+
shippingMethod: ShippingMethodSchema.optional(),
|
|
38
47
|
});
|
|
39
48
|
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export type CostBreakDown = z.infer<typeof CostBreakDownSchema>;
|
|
52
|
+
export type ItemCostBreakdown = z.infer<typeof ItemCostBreakdownSchema>;
|
|
40
53
|
export type CartItem = z.infer<typeof CartItemSchema>;
|
|
41
54
|
export type Cart = z.infer<typeof CartSchema>;
|