@reactionary/source 0.3.18 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -14
- package/core/src/client/client-builder.ts +54 -6
- package/core/src/factories/cart.factory.ts +40 -0
- package/core/src/factories/category.factory.ts +40 -0
- package/core/src/factories/checkout.factory.ts +59 -0
- package/core/src/factories/identity.factory.ts +26 -0
- package/core/src/factories/index.ts +15 -0
- package/core/src/factories/inventory.factory.ts +26 -0
- package/core/src/factories/order-search.factory.ts +33 -0
- package/core/src/factories/order.factory.ts +21 -0
- package/core/src/factories/price.factory.ts +29 -0
- package/core/src/factories/product-associations.factory.ts +30 -0
- package/core/src/factories/product-list.factory.ts +82 -0
- package/core/src/factories/product-reviews.factory.ts +58 -0
- package/core/src/factories/product-search.factory.ts +31 -0
- package/core/src/factories/product.factory.ts +21 -0
- package/core/src/factories/profile.factory.ts +23 -0
- package/core/src/factories/store.factory.ts +21 -0
- package/core/src/index.ts +3 -1
- package/core/src/providers/cart.provider.ts +12 -10
- package/core/src/providers/category.provider.ts +9 -7
- package/core/src/providers/checkout.provider.ts +14 -10
- package/core/src/providers/identity.provider.ts +7 -7
- package/core/src/providers/inventory.provider.ts +4 -4
- package/core/src/providers/order-search.provider.ts +4 -2
- package/core/src/providers/order.provider.ts +4 -4
- package/core/src/providers/price.provider.ts +5 -5
- package/core/src/providers/product-associations.provider.ts +6 -4
- package/core/src/providers/product-list.provider.ts +13 -8
- package/core/src/providers/product-reviews.provider.ts +8 -4
- package/core/src/providers/product-search.provider.ts +15 -28
- package/core/src/providers/product.provider.ts +6 -6
- package/core/src/providers/profile.provider.ts +8 -8
- package/core/src/providers/store.provider.ts +2 -2
- package/core/src/test/client-builder.spec.ts +81 -0
- package/examples/node/package.json +7 -7
- package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +10 -4
- package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +8 -2
- package/examples/node/src/basic/basic-node-setup.spec.ts +5 -1
- package/examples/node/src/basic/client-creation.spec.ts +3 -3
- package/examples/node/src/utils.ts +41 -41
- package/examples/node/tsconfig.lib.json +2 -1
- package/package.json +1 -1
- package/providers/algolia/src/core/initialize.ts +76 -21
- package/providers/algolia/src/core/initialize.types.ts +107 -0
- package/providers/algolia/src/factories/index.ts +1 -0
- package/providers/algolia/src/factories/product-search/product-search.factory.ts +182 -0
- package/providers/algolia/src/index.ts +2 -2
- package/providers/algolia/src/providers/analytics.provider.ts +4 -4
- package/providers/algolia/src/providers/product-search.provider.ts +67 -184
- package/providers/algolia/src/schema/capabilities.schema.ts +81 -7
- package/providers/algolia/src/test/analytics.spec.ts +9 -1
- package/providers/algolia/src/test/client-builder-product-search-extension.example.ts +85 -0
- package/providers/commercetools/src/core/capability-descriptors.ts +324 -0
- package/providers/commercetools/src/core/initialize.ts +35 -151
- package/providers/commercetools/src/core/initialize.types.ts +174 -0
- package/providers/commercetools/src/factories/cart/cart.factory.ts +142 -0
- package/providers/commercetools/src/factories/category/category.factory.ts +77 -0
- package/providers/commercetools/src/factories/checkout/checkout-initializer-overrides.example.ts +94 -0
- package/providers/commercetools/src/factories/checkout/checkout.factory.ts +338 -0
- package/providers/commercetools/src/factories/identity/identity.factory.ts +26 -0
- package/providers/commercetools/src/factories/inventory/inventory.factory.ts +49 -0
- package/providers/commercetools/src/factories/order/order.factory.ts +149 -0
- package/providers/commercetools/src/factories/order-search/order-search.factory.ts +108 -0
- package/providers/commercetools/src/factories/price/price.factory.ts +76 -0
- package/providers/commercetools/src/factories/product/product-factory-baseline.example.ts +14 -0
- package/providers/commercetools/src/factories/product/product-factory-schema-and-parse-extension.example.ts +35 -0
- package/providers/commercetools/src/factories/product/product-factory-schema-extension.example.ts +23 -0
- package/providers/commercetools/src/factories/product/product-initializer-factory-extension.example.ts +41 -0
- package/providers/commercetools/src/factories/product/product-provider-custom-method-only.example.ts +47 -0
- package/providers/commercetools/src/factories/product/product-provider-schema-signature-extension.example.ts +61 -0
- package/providers/commercetools/src/factories/product/product.factory.ts +220 -0
- package/providers/commercetools/src/factories/product/utils.example.ts +9 -0
- package/providers/commercetools/src/factories/product-associations/product-associations.factory.ts +103 -0
- package/providers/commercetools/src/factories/product-list/product-list.factory.ts +122 -0
- package/providers/commercetools/src/factories/product-reviews/product-reviews.factory.ts +81 -0
- package/providers/commercetools/src/factories/product-search/product-search.factory.ts +182 -0
- package/providers/commercetools/src/factories/profile/profile.factory.ts +94 -0
- package/providers/commercetools/src/factories/store/store.factory.ts +49 -0
- package/providers/commercetools/src/index.ts +15 -0
- package/providers/commercetools/src/providers/cart.provider.ts +67 -193
- package/providers/commercetools/src/providers/category.provider.ts +24 -64
- package/providers/commercetools/src/providers/checkout.provider.ts +50 -322
- package/providers/commercetools/src/providers/identity.provider.ts +35 -15
- package/providers/commercetools/src/providers/inventory.provider.ts +13 -31
- package/providers/commercetools/src/providers/order-search.provider.ts +16 -110
- package/providers/commercetools/src/providers/order.provider.ts +13 -144
- package/providers/commercetools/src/providers/price.provider.ts +37 -51
- package/providers/commercetools/src/providers/product-associations.provider.ts +39 -104
- package/providers/commercetools/src/providers/product-list.provider.ts +38 -23
- package/providers/commercetools/src/providers/product-reviews.provider.ts +34 -14
- package/providers/commercetools/src/providers/product-search.provider.ts +17 -170
- package/providers/commercetools/src/providers/product.provider.ts +20 -199
- package/providers/commercetools/src/providers/profile.provider.ts +27 -73
- package/providers/commercetools/src/providers/store.provider.ts +13 -31
- package/providers/commercetools/src/schema/capabilities.schema.ts +258 -20
- package/providers/commercetools/src/test/caching.spec.ts +18 -2
- package/providers/commercetools/src/test/client-builder-merge-extensions.example.ts +125 -0
- package/providers/fake/src/core/initialize.ts +213 -44
- package/providers/fake/src/core/initialize.types.ts +164 -0
- package/providers/fake/src/factories/cart/cart.factory.ts +34 -0
- package/providers/fake/src/factories/category/category.factory.ts +40 -0
- package/providers/fake/src/factories/checkout/checkout.factory.ts +53 -0
- package/providers/fake/src/factories/identity/identity.factory.ts +25 -0
- package/providers/fake/src/factories/index.ts +14 -0
- package/providers/fake/src/factories/inventory/inventory.factory.ts +25 -0
- package/providers/fake/src/factories/order/order.factory.ts +22 -0
- package/providers/fake/src/factories/order-search/order-search.factory.ts +27 -0
- package/providers/fake/src/factories/price/price.factory.ts +26 -0
- package/providers/fake/src/factories/product/product.factory.ts +22 -0
- package/providers/fake/src/factories/product-associations/product-associations.factory.ts +25 -0
- package/providers/fake/src/factories/product-reviews/product-reviews.factory.ts +53 -0
- package/providers/fake/src/factories/product-search/product-search.factory.ts +27 -0
- package/providers/fake/src/factories/profile/profile.factory.ts +22 -0
- package/providers/fake/src/factories/store/store.factory.ts +22 -0
- package/providers/fake/src/index.ts +2 -0
- package/providers/fake/src/providers/cart.provider.ts +23 -14
- package/providers/fake/src/providers/category.provider.ts +120 -105
- package/providers/fake/src/providers/checkout.provider.ts +39 -20
- package/providers/fake/src/providers/identity.provider.ts +40 -34
- package/providers/fake/src/providers/inventory.provider.ts +26 -24
- package/providers/fake/src/providers/order-search.provider.ts +38 -30
- package/providers/fake/src/providers/order.provider.ts +21 -37
- package/providers/fake/src/providers/price.provider.ts +42 -34
- package/providers/fake/src/providers/product-associations.provider.ts +23 -10
- package/providers/fake/src/providers/product-reviews.provider.ts +71 -69
- package/providers/fake/src/providers/product-search.provider.ts +43 -70
- package/providers/fake/src/providers/product.provider.ts +34 -32
- package/providers/fake/src/providers/profile.provider.ts +62 -55
- package/providers/fake/src/providers/store.provider.ts +38 -22
- package/providers/fake/src/schema/capabilities.schema.ts +175 -18
- package/providers/fake/src/test/cart.provider.spec.ts +20 -3
- package/providers/fake/src/test/category.provider.spec.ts +4 -1
- package/providers/fake/src/test/checkout.provider.spec.ts +12 -2
- package/providers/fake/src/test/client-builder-product-extension.example.ts +75 -0
- package/providers/fake/src/test/order-search.provider.spec.ts +4 -7
- package/providers/fake/src/test/order.provider.spec.ts +4 -6
- package/providers/fake/src/test/price.provider.spec.ts +3 -1
- package/providers/fake/src/test/product.provider.spec.ts +8 -2
- package/providers/fake/src/test/profile.provider.spec.ts +4 -2
- package/providers/google-analytics/src/core/initialize.ts +37 -12
- package/providers/google-analytics/src/core/initialize.types.ts +47 -0
- package/providers/google-analytics/src/index.ts +1 -0
- package/providers/google-analytics/src/schema/capabilities.schema.ts +31 -5
- package/providers/medusa/src/core/initialize.ts +324 -81
- package/providers/medusa/src/core/initialize.types.ts +184 -0
- package/providers/medusa/src/factories/cart/cart.factory.ts +34 -0
- package/providers/medusa/src/factories/category/category.factory.ts +37 -0
- package/providers/medusa/src/factories/checkout/checkout.factory.ts +50 -0
- package/providers/medusa/src/factories/identity/identity.factory.ts +22 -0
- package/providers/medusa/src/factories/index.ts +12 -0
- package/providers/medusa/src/factories/inventory/inventory.factory.ts +25 -0
- package/providers/medusa/src/factories/order/order.factory.ts +22 -0
- package/providers/medusa/src/factories/order-search/order-search.factory.ts +27 -0
- package/providers/medusa/src/factories/price/price.factory.ts +26 -0
- package/providers/medusa/src/factories/product/product.factory.ts +22 -0
- package/providers/medusa/src/factories/product-associations/product-associations.factory.ts +25 -0
- package/providers/medusa/src/factories/product-search/product-search.factory.ts +27 -0
- package/providers/medusa/src/factories/profile/profile.factory.ts +22 -0
- package/providers/medusa/src/index.ts +2 -0
- package/providers/medusa/src/providers/cart.provider.ts +33 -20
- package/providers/medusa/src/providers/category.provider.ts +30 -12
- package/providers/medusa/src/providers/checkout.provider.ts +42 -17
- package/providers/medusa/src/providers/identity.provider.ts +1 -1
- package/providers/medusa/src/providers/inventory.provider.ts +21 -7
- package/providers/medusa/src/providers/order-search.provider.ts +16 -5
- package/providers/medusa/src/providers/order.provider.ts +17 -5
- package/providers/medusa/src/providers/price.provider.ts +26 -7
- package/providers/medusa/src/providers/product-associations.provider.ts +19 -8
- package/providers/medusa/src/providers/product-search.provider.ts +19 -31
- package/providers/medusa/src/providers/product.provider.ts +47 -11
- package/providers/medusa/src/providers/profile.provider.ts +35 -11
- package/providers/medusa/src/schema/capabilities.schema.ts +229 -18
- package/providers/medusa/src/test/cart.provider.spec.ts +18 -2
- package/providers/medusa/src/test/category.provider.spec.ts +4 -1
- package/providers/medusa/src/test/checkout.spec.ts +9 -9
- package/providers/medusa/src/test/inventory.provider.spec.ts +3 -1
- package/providers/medusa/src/test/large-cart.provider.spec.ts +8 -2
- package/providers/medusa/src/test/price.provider.spec.ts +8 -1
- package/providers/medusa/src/test/product.provider.spec.ts +3 -1
- package/providers/medusa/src/test/search.provider.spec.ts +16 -3
- package/providers/meilisearch/src/core/initialize.ts +88 -21
- package/providers/meilisearch/src/core/initialize.types.ts +119 -0
- package/providers/meilisearch/src/factories/index.ts +2 -0
- package/providers/meilisearch/src/factories/order-search/order-search.factory.ts +27 -0
- package/providers/meilisearch/src/factories/product-search/product-search.factory.ts +27 -0
- package/providers/meilisearch/src/index.ts +2 -0
- package/providers/meilisearch/src/providers/index.ts +1 -0
- package/providers/meilisearch/src/providers/order-search.provider.ts +21 -6
- package/providers/meilisearch/src/providers/product-search.provider.ts +24 -8
- package/providers/meilisearch/src/schema/capabilities.schema.ts +95 -8
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Price as CTPrice, ProductVariant as CTProductVariant } from '@commercetools/platform-sdk';
|
|
2
|
+
import type {
|
|
3
|
+
PriceSchema} from '@reactionary/core';
|
|
4
|
+
import {
|
|
5
|
+
type AnyPriceSchema,
|
|
6
|
+
type Currency,
|
|
7
|
+
type MonetaryAmount,
|
|
8
|
+
type Price,
|
|
9
|
+
type PriceFactory,
|
|
10
|
+
type PriceIdentifier,
|
|
11
|
+
type RequestContext,
|
|
12
|
+
} from '@reactionary/core';
|
|
13
|
+
import type * as z from 'zod';
|
|
14
|
+
|
|
15
|
+
export class CommercetoolsPriceFactory<
|
|
16
|
+
TPriceSchema extends AnyPriceSchema = typeof PriceSchema,
|
|
17
|
+
> implements PriceFactory<TPriceSchema>
|
|
18
|
+
{
|
|
19
|
+
public readonly priceSchema: TPriceSchema;
|
|
20
|
+
|
|
21
|
+
constructor(priceSchema: TPriceSchema) {
|
|
22
|
+
this.priceSchema = priceSchema;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public parsePrice(
|
|
26
|
+
context: RequestContext,
|
|
27
|
+
data: CTProductVariant,
|
|
28
|
+
options = { includeDiscounts: false },
|
|
29
|
+
): z.output<TPriceSchema> {
|
|
30
|
+
const price = data.price as CTPrice | undefined;
|
|
31
|
+
|
|
32
|
+
if (!price) {
|
|
33
|
+
return this.priceSchema.parse({
|
|
34
|
+
identifier: {
|
|
35
|
+
variant: {
|
|
36
|
+
sku: data.sku || '',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
onSale: false,
|
|
40
|
+
tieredPrices: [],
|
|
41
|
+
unitPrice: {
|
|
42
|
+
value: -1,
|
|
43
|
+
currency: context.languageContext.currencyCode,
|
|
44
|
+
},
|
|
45
|
+
} satisfies Price);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let unitPrice = {
|
|
49
|
+
value: price.value.centAmount / 100,
|
|
50
|
+
currency: price.value.currencyCode as Currency,
|
|
51
|
+
} satisfies MonetaryAmount;
|
|
52
|
+
|
|
53
|
+
if (options.includeDiscounts) {
|
|
54
|
+
const discountedPrice = price.discounted?.value || price.value;
|
|
55
|
+
unitPrice = {
|
|
56
|
+
value: discountedPrice.centAmount / 100,
|
|
57
|
+
currency: price.value.currencyCode as Currency,
|
|
58
|
+
} satisfies MonetaryAmount;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const identifier = {
|
|
62
|
+
variant: {
|
|
63
|
+
sku: data.sku || '',
|
|
64
|
+
},
|
|
65
|
+
} satisfies PriceIdentifier;
|
|
66
|
+
|
|
67
|
+
const result = {
|
|
68
|
+
identifier,
|
|
69
|
+
onSale: options.includeDiscounts && !!price.discounted,
|
|
70
|
+
tieredPrices: [],
|
|
71
|
+
unitPrice,
|
|
72
|
+
} satisfies Price;
|
|
73
|
+
|
|
74
|
+
return this.priceSchema.parse(result);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProductSchema } from "@reactionary/core";
|
|
2
|
+
import { CommercetoolsProductFactory } from "./product.factory.js";
|
|
3
|
+
import { assertNotAny, assertType } from "./utils.example.js";
|
|
4
|
+
|
|
5
|
+
const context = {} as any;
|
|
6
|
+
|
|
7
|
+
const baseFactory = new CommercetoolsProductFactory(ProductSchema);
|
|
8
|
+
const baseProduct = baseFactory.parseProduct(context, {} as any);
|
|
9
|
+
|
|
10
|
+
assertNotAny(baseProduct);
|
|
11
|
+
assertNotAny(baseProduct.name);
|
|
12
|
+
assertType<string>(baseProduct.name);
|
|
13
|
+
// @ts-expect-error base schema does not include extendedValue
|
|
14
|
+
assertType<string>(baseProduct.extendedValue);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ProductSchema } from "@reactionary/core";
|
|
2
|
+
import type { RequestContext } from "@reactionary/core";
|
|
3
|
+
import type { ProductProjection } from "@commercetools/platform-sdk";
|
|
4
|
+
import { CommercetoolsProductFactory } from "./product.factory.js";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
import { assertNotAny, assertType } from "./utils.example.js";
|
|
7
|
+
|
|
8
|
+
const context = {} as any;
|
|
9
|
+
|
|
10
|
+
const ExtendedProductSchema = ProductSchema.safeExtend({
|
|
11
|
+
extendedValue: z.string(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
class ExtendedCommercetoolsProductFactory extends CommercetoolsProductFactory<typeof ExtendedProductSchema> {
|
|
15
|
+
constructor() {
|
|
16
|
+
super(ExtendedProductSchema);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public override parseProduct(context: RequestContext, data: ProductProjection) {
|
|
20
|
+
const base = super.parseProduct(context, data);
|
|
21
|
+
|
|
22
|
+
// PAIN: Needing the satisfies here - possibly unavoidable
|
|
23
|
+
return {
|
|
24
|
+
...base,
|
|
25
|
+
extendedValue: "from-parse",
|
|
26
|
+
} satisfies z.output<typeof ExtendedProductSchema>;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const extendedFactory = new ExtendedCommercetoolsProductFactory();
|
|
31
|
+
const extendedProduct = extendedFactory.parseProduct(context, {} as any);
|
|
32
|
+
|
|
33
|
+
assertNotAny(extendedProduct);
|
|
34
|
+
assertNotAny(extendedProduct.extendedValue);
|
|
35
|
+
assertType<string>(extendedProduct.extendedValue);
|
package/providers/commercetools/src/factories/product/product-factory-schema-extension.example.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ProductSchema } from "@reactionary/core";
|
|
2
|
+
import { CommercetoolsProductFactory } from "./product.factory.js";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
import { assertNotAny, assertType } from "./utils.example.js";
|
|
5
|
+
|
|
6
|
+
const context = {} as any;
|
|
7
|
+
|
|
8
|
+
const ExtendedProductSchema = ProductSchema.safeExtend({
|
|
9
|
+
extendedValue: z.string(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
class ExtendedCommercetoolsProductFactory extends CommercetoolsProductFactory<typeof ExtendedProductSchema> {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(ExtendedProductSchema);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const extendedFactory = new ExtendedCommercetoolsProductFactory();
|
|
19
|
+
const extendedProduct = extendedFactory.parseProduct(context, {} as any);
|
|
20
|
+
|
|
21
|
+
assertNotAny(extendedProduct);
|
|
22
|
+
assertNotAny(extendedProduct.extendedValue);
|
|
23
|
+
assertType<string>(extendedProduct.extendedValue);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ProductSchema } from "@reactionary/core";
|
|
2
|
+
import { CommercetoolsProductFactory } from "./product.factory.js";
|
|
3
|
+
import { withCommercetoolsCapabilities } from "../../core/initialize.js";
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
import { assertNotAny, assertType } from "./utils.example.js";
|
|
6
|
+
|
|
7
|
+
const cache = {} as any;
|
|
8
|
+
const context = {} as any;
|
|
9
|
+
const config = {} as any;
|
|
10
|
+
|
|
11
|
+
const ExtendedProductSchema = ProductSchema.safeExtend({
|
|
12
|
+
extendedValue: z.string(),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
class ExtendedCommercetoolsProductFactory extends CommercetoolsProductFactory<typeof ExtendedProductSchema> {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(ExtendedProductSchema);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const capabilityFactory = withCommercetoolsCapabilities(config, {
|
|
22
|
+
product: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
factory: new ExtendedCommercetoolsProductFactory(),
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const client = capabilityFactory(cache, context);
|
|
29
|
+
|
|
30
|
+
client.product
|
|
31
|
+
.getById({
|
|
32
|
+
identifier: { key: "p-1" },
|
|
33
|
+
})
|
|
34
|
+
.then((x) => {
|
|
35
|
+
assertNotAny(x);
|
|
36
|
+
if (x.success) {
|
|
37
|
+
assertNotAny(x.value);
|
|
38
|
+
assertNotAny(x.value.extendedValue);
|
|
39
|
+
assertType<string>(x.value.extendedValue);
|
|
40
|
+
}
|
|
41
|
+
});
|
package/providers/commercetools/src/factories/product/product-provider-custom-method-only.example.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ProductSchema } from "@reactionary/core";
|
|
2
|
+
import type { ProductFactoryOutput, Result } from "@reactionary/core";
|
|
3
|
+
import { CommercetoolsProductFactory } from "./product.factory.js";
|
|
4
|
+
import { CommercetoolsProductProvider } from "../../providers/product.provider.js";
|
|
5
|
+
import { withCommercetoolsCapabilities } from "../../core/initialize.js";
|
|
6
|
+
import { assertNotAny, assertType } from "./utils.example.js";
|
|
7
|
+
|
|
8
|
+
const cache = {} as any;
|
|
9
|
+
const context = {} as any;
|
|
10
|
+
const config = {} as any;
|
|
11
|
+
const api = {} as any;
|
|
12
|
+
|
|
13
|
+
class ExtendedCommercetoolsProductProvider extends CommercetoolsProductProvider<CommercetoolsProductFactory> {
|
|
14
|
+
public async getByCustomIdentifier(
|
|
15
|
+
identifier: string
|
|
16
|
+
): Promise<Result<ProductFactoryOutput<CommercetoolsProductFactory>>> {
|
|
17
|
+
return this.getById({
|
|
18
|
+
identifier: { key: identifier },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const withExtendedProviderFactory = withCommercetoolsCapabilities(config, {
|
|
24
|
+
product: {
|
|
25
|
+
enabled: true,
|
|
26
|
+
factory: new CommercetoolsProductFactory(ProductSchema),
|
|
27
|
+
provider: ({ cache, context, config, commercetoolsApi }) =>
|
|
28
|
+
new ExtendedCommercetoolsProductProvider(
|
|
29
|
+
cache,
|
|
30
|
+
context,
|
|
31
|
+
config,
|
|
32
|
+
commercetoolsApi,
|
|
33
|
+
new CommercetoolsProductFactory(ProductSchema)
|
|
34
|
+
),
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const client = withExtendedProviderFactory(cache, context);
|
|
39
|
+
|
|
40
|
+
client.product.getByCustomIdentifier("p-2").then((x) => {
|
|
41
|
+
assertNotAny(x);
|
|
42
|
+
if (x.success) {
|
|
43
|
+
assertNotAny(x.value);
|
|
44
|
+
assertNotAny(x.value.name);
|
|
45
|
+
assertType<string>(x.value.name);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ProductQueryByIdSchema, ProductSchema } from "@reactionary/core";
|
|
2
|
+
import type {
|
|
3
|
+
ProductFactoryOutput,
|
|
4
|
+
ProductQueryById as BaseProductQueryById,
|
|
5
|
+
Result,
|
|
6
|
+
} from "@reactionary/core";
|
|
7
|
+
import { CommercetoolsProductFactory } from "./product.factory.js";
|
|
8
|
+
import { CommercetoolsProductProvider } from "../../providers/product.provider.js";
|
|
9
|
+
import * as z from "zod";
|
|
10
|
+
import { assertNotAny, assertType } from "./utils.example.js";
|
|
11
|
+
|
|
12
|
+
const cache = {} as any;
|
|
13
|
+
const context = {} as any;
|
|
14
|
+
const config = {} as any;
|
|
15
|
+
const api = {} as any;
|
|
16
|
+
|
|
17
|
+
const ExtendedProductSchema = ProductSchema.safeExtend({
|
|
18
|
+
extendedValue: z.string(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
class ExtendedCommercetoolsProductFactory extends CommercetoolsProductFactory<typeof ExtendedProductSchema> {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(ExtendedProductSchema);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ExtendedProductQueryByIdSchema = ProductQueryByIdSchema.extend({
|
|
28
|
+
auditTrail: z.boolean(),
|
|
29
|
+
});
|
|
30
|
+
type ExtendedProductQueryById = z.infer<typeof ExtendedProductQueryByIdSchema>;
|
|
31
|
+
|
|
32
|
+
class ExtendedCommercetoolsProductProvider extends CommercetoolsProductProvider<ExtendedCommercetoolsProductFactory> {
|
|
33
|
+
public override async getById(
|
|
34
|
+
payload: ExtendedProductQueryById
|
|
35
|
+
) {
|
|
36
|
+
const basePayload: BaseProductQueryById = {
|
|
37
|
+
identifier: payload.identifier,
|
|
38
|
+
};
|
|
39
|
+
return super.getById(basePayload);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const extendedProvider = new ExtendedCommercetoolsProductProvider(
|
|
44
|
+
cache,
|
|
45
|
+
context,
|
|
46
|
+
config,
|
|
47
|
+
api,
|
|
48
|
+
new ExtendedCommercetoolsProductFactory()
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
extendedProvider.getById({
|
|
52
|
+
identifier: { key: "p-1" },
|
|
53
|
+
auditTrail: true
|
|
54
|
+
}).then((x) => {
|
|
55
|
+
assertNotAny(x);
|
|
56
|
+
if (x.success) {
|
|
57
|
+
assertNotAny(x.value);
|
|
58
|
+
assertNotAny(x.value.extendedValue);
|
|
59
|
+
assertType<string>(x.value.extendedValue);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ImageSchema,
|
|
3
|
+
ProductAttributeIdentifierSchema,
|
|
4
|
+
ProductAttributeSchema,
|
|
5
|
+
ProductAttributeValueIdentifierSchema,
|
|
6
|
+
ProductAttributeValueSchema,
|
|
7
|
+
type AnyProductSchema,
|
|
8
|
+
type Product,
|
|
9
|
+
type ProductFactory,
|
|
10
|
+
type ProductIdentifier,
|
|
11
|
+
type ProductVariant,
|
|
12
|
+
type ProductVariantIdentifier,
|
|
13
|
+
type RequestContext,
|
|
14
|
+
} from '@reactionary/core';
|
|
15
|
+
import type * as z from 'zod';
|
|
16
|
+
import type {
|
|
17
|
+
ProductProjection,
|
|
18
|
+
ProductVariant as CTProductVariant,
|
|
19
|
+
Attribute as CTAttribute,
|
|
20
|
+
} from '@commercetools/platform-sdk';
|
|
21
|
+
import type {
|
|
22
|
+
Image,
|
|
23
|
+
ProductAttribute,
|
|
24
|
+
ProductAttributeIdentifier,
|
|
25
|
+
ProductAttributeValue,
|
|
26
|
+
ProductAttributeValueIdentifier,
|
|
27
|
+
ProductOptionIdentifier,
|
|
28
|
+
ProductVariantOption,
|
|
29
|
+
|
|
30
|
+
ProductSchema} from '@reactionary/core';
|
|
31
|
+
|
|
32
|
+
export class CommercetoolsProductFactory<
|
|
33
|
+
TProductSchema extends AnyProductSchema = typeof ProductSchema,
|
|
34
|
+
> implements ProductFactory<TProductSchema>
|
|
35
|
+
{
|
|
36
|
+
public readonly productSchema: TProductSchema;
|
|
37
|
+
|
|
38
|
+
// PAIN: not being able to assign a default for productSchema (productSchema: TProductSchema = ProductSchema)
|
|
39
|
+
// because it will invalidate TS' conservative instantiation-point generics
|
|
40
|
+
// PAIN: consider whether we can pass in request context here
|
|
41
|
+
constructor(productSchema: TProductSchema) {
|
|
42
|
+
this.productSchema = productSchema;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public parseProduct(
|
|
46
|
+
context: RequestContext,
|
|
47
|
+
data: ProductProjection,
|
|
48
|
+
): z.output<TProductSchema> {
|
|
49
|
+
const identifier = { key: data.key || data.id } satisfies ProductIdentifier;
|
|
50
|
+
const name = data.name[context.languageContext.locale];
|
|
51
|
+
const slug = data.slug[context.languageContext.locale];
|
|
52
|
+
|
|
53
|
+
let description = '';
|
|
54
|
+
if (data.description) {
|
|
55
|
+
description = data.description[context.languageContext.locale];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const variantLevelAttributes =
|
|
59
|
+
data.masterVariant.attributes?.map((x) =>
|
|
60
|
+
this.parseAttribute(context, x),
|
|
61
|
+
) || [];
|
|
62
|
+
|
|
63
|
+
const specialAttributes = [
|
|
64
|
+
'reactionaryaccessories',
|
|
65
|
+
'reactionaryspareparts',
|
|
66
|
+
'reactionaryreplacements',
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const productLevelAttributes =
|
|
70
|
+
data.attributes
|
|
71
|
+
.filter((x) => !specialAttributes.includes(x.name))
|
|
72
|
+
.map((x) => this.parseAttribute(context, x)) || [];
|
|
73
|
+
|
|
74
|
+
const sharedAttributes = [
|
|
75
|
+
...productLevelAttributes,
|
|
76
|
+
...variantLevelAttributes,
|
|
77
|
+
];
|
|
78
|
+
const mainVariant = this.parseVariant(context, data.masterVariant, data);
|
|
79
|
+
|
|
80
|
+
const otherVariants = [];
|
|
81
|
+
for (const variant of data.variants || []) {
|
|
82
|
+
if (variant.id !== data.masterVariant.id) {
|
|
83
|
+
otherVariants.push(this.parseVariant(context, variant, data));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const result = {
|
|
88
|
+
identifier,
|
|
89
|
+
name,
|
|
90
|
+
slug,
|
|
91
|
+
description,
|
|
92
|
+
sharedAttributes,
|
|
93
|
+
mainVariant,
|
|
94
|
+
brand: '',
|
|
95
|
+
longDescription: '',
|
|
96
|
+
manufacturer: '',
|
|
97
|
+
options: [],
|
|
98
|
+
parentCategories: [],
|
|
99
|
+
published: true,
|
|
100
|
+
variants: otherVariants,
|
|
101
|
+
} satisfies Product;
|
|
102
|
+
|
|
103
|
+
// PAIN: having to actually do the parse here to guarantee that the generics are satisfied.
|
|
104
|
+
// probably fair, given that this relationship can't be expressed in TS otherwise...
|
|
105
|
+
// unless we were willing to accept ownership of the responsibility and do an 'as T'
|
|
106
|
+
return this.productSchema.parse(result);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Return true, if the attribute is a defining attribute (ie an option)
|
|
111
|
+
* @param attr a variant attribute
|
|
112
|
+
* @returns true if the attribute is an option
|
|
113
|
+
*/
|
|
114
|
+
protected isVariantAttributeAnOption(attr: CTAttribute): boolean {
|
|
115
|
+
// for now, the assumption is that any variant attribute is a defining attribute (ie an option)
|
|
116
|
+
// ideally this should be verified with the product type.
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
protected parseVariant(
|
|
121
|
+
context: RequestContext,
|
|
122
|
+
variant: CTProductVariant,
|
|
123
|
+
product: ProductProjection,
|
|
124
|
+
): ProductVariant {
|
|
125
|
+
const identifier = {
|
|
126
|
+
sku: variant.sku!,
|
|
127
|
+
} satisfies ProductVariantIdentifier;
|
|
128
|
+
|
|
129
|
+
const images = [
|
|
130
|
+
...(variant.images || []).map((img) =>
|
|
131
|
+
ImageSchema.parse({
|
|
132
|
+
sourceUrl: img.url,
|
|
133
|
+
altText: img.label || '',
|
|
134
|
+
width: img.dimensions?.w,
|
|
135
|
+
height: img.dimensions?.h,
|
|
136
|
+
} satisfies Image),
|
|
137
|
+
),
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const options =
|
|
141
|
+
(variant.attributes ?? [])
|
|
142
|
+
.filter((attr) => this.isVariantAttributeAnOption(attr))
|
|
143
|
+
.map((attr) => {
|
|
144
|
+
const attrVal = this.parseAttributeValue(context, attr);
|
|
145
|
+
const optionIdentifier: ProductOptionIdentifier = {
|
|
146
|
+
key: attr.name,
|
|
147
|
+
};
|
|
148
|
+
const option: ProductVariantOption = {
|
|
149
|
+
identifier: optionIdentifier,
|
|
150
|
+
name: attr.name,
|
|
151
|
+
value: {
|
|
152
|
+
identifier: {
|
|
153
|
+
key: attrVal.value,
|
|
154
|
+
option: optionIdentifier,
|
|
155
|
+
},
|
|
156
|
+
label: attrVal.label,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
return option;
|
|
160
|
+
}) || [];
|
|
161
|
+
|
|
162
|
+
const result = {
|
|
163
|
+
identifier,
|
|
164
|
+
images,
|
|
165
|
+
barcode: '',
|
|
166
|
+
ean: '',
|
|
167
|
+
gtin: '',
|
|
168
|
+
name: product.name[context.languageContext.locale],
|
|
169
|
+
options,
|
|
170
|
+
upc: '',
|
|
171
|
+
} satisfies ProductVariant;
|
|
172
|
+
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected parseAttribute(
|
|
177
|
+
context: RequestContext,
|
|
178
|
+
attr: CTAttribute,
|
|
179
|
+
): ProductAttribute {
|
|
180
|
+
const result = ProductAttributeSchema.parse({
|
|
181
|
+
identifier: ProductAttributeIdentifierSchema.parse({
|
|
182
|
+
key: attr.name,
|
|
183
|
+
} satisfies Partial<ProductAttributeIdentifier>),
|
|
184
|
+
group: '',
|
|
185
|
+
name: attr.name,
|
|
186
|
+
values: [this.parseAttributeValue(context, attr)],
|
|
187
|
+
} satisfies Partial<ProductAttribute>);
|
|
188
|
+
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
protected parseAttributeValue(context: RequestContext, attr: CTAttribute): ProductAttributeValue {
|
|
193
|
+
let attrValue = '';
|
|
194
|
+
if (attr.value && Array.isArray(attr.value)) {
|
|
195
|
+
attrValue = attr.value[0];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (attr.value && typeof attr.value === 'object') {
|
|
199
|
+
if (context.languageContext.locale in attr.value) {
|
|
200
|
+
attrValue = attr.value[context.languageContext.locale];
|
|
201
|
+
} else {
|
|
202
|
+
attrValue = '-';
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (typeof attr.value === 'string') {
|
|
207
|
+
attrValue = attr.value;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const attrVal = ProductAttributeValueSchema.parse({
|
|
211
|
+
identifier: ProductAttributeValueIdentifierSchema.parse({
|
|
212
|
+
key: attrValue,
|
|
213
|
+
} satisfies Partial<ProductAttributeValueIdentifier>),
|
|
214
|
+
value: String(attrValue),
|
|
215
|
+
label: String(attrValue),
|
|
216
|
+
} satisfies Partial<ProductAttributeValue>);
|
|
217
|
+
|
|
218
|
+
return attrVal;
|
|
219
|
+
}
|
|
220
|
+
}
|
package/providers/commercetools/src/factories/product-associations/product-associations.factory.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ProductProjection, ProductVariant as CTProductVariant } from '@commercetools/platform-sdk';
|
|
2
|
+
import type {
|
|
3
|
+
ProductAssociationSchema} from '@reactionary/core';
|
|
4
|
+
import {
|
|
5
|
+
ImageSchema,
|
|
6
|
+
ProductOptionIdentifierSchema,
|
|
7
|
+
ProductSearchResultItemVariantSchema,
|
|
8
|
+
ProductVariantIdentifierSchema,
|
|
9
|
+
ProductVariantOptionSchema,
|
|
10
|
+
type AnyProductAssociationSchema,
|
|
11
|
+
type ProductAssociation,
|
|
12
|
+
type ProductAssociationsFactory,
|
|
13
|
+
type ProductOptionIdentifier,
|
|
14
|
+
type ProductSearchResultItem,
|
|
15
|
+
type ProductSearchResultItemVariant,
|
|
16
|
+
type ProductVariantIdentifier,
|
|
17
|
+
type ProductVariantOption,
|
|
18
|
+
type RequestContext,
|
|
19
|
+
} from '@reactionary/core';
|
|
20
|
+
import type * as z from 'zod';
|
|
21
|
+
|
|
22
|
+
export class CommercetoolsProductAssociationsFactory<
|
|
23
|
+
TProductAssociationSchema extends AnyProductAssociationSchema = typeof ProductAssociationSchema,
|
|
24
|
+
> implements ProductAssociationsFactory<TProductAssociationSchema>
|
|
25
|
+
{
|
|
26
|
+
public readonly productAssociationSchema: TProductAssociationSchema;
|
|
27
|
+
|
|
28
|
+
constructor(productAssociationSchema: TProductAssociationSchema) {
|
|
29
|
+
this.productAssociationSchema = productAssociationSchema;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public parseAssociation(
|
|
33
|
+
context: RequestContext,
|
|
34
|
+
data: {
|
|
35
|
+
sourceProductKey: string;
|
|
36
|
+
relation: 'accessory' | 'sparepart' | 'replacement';
|
|
37
|
+
product: ProductProjection;
|
|
38
|
+
},
|
|
39
|
+
): z.output<TProductAssociationSchema> {
|
|
40
|
+
const product = this.parseSearchResultItem(context, data.product);
|
|
41
|
+
const result = {
|
|
42
|
+
associationIdentifier: {
|
|
43
|
+
key: `${data.sourceProductKey}-${data.relation}-${product.identifier.key}`,
|
|
44
|
+
},
|
|
45
|
+
associationReturnType: 'productSearchResultItem',
|
|
46
|
+
product,
|
|
47
|
+
} satisfies ProductAssociation;
|
|
48
|
+
|
|
49
|
+
return this.productAssociationSchema.parse(result);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected parseSearchResultItem(
|
|
53
|
+
context: RequestContext,
|
|
54
|
+
data: ProductProjection,
|
|
55
|
+
): ProductSearchResultItem {
|
|
56
|
+
const variants = [data.masterVariant, ...data.variants].map((variant) =>
|
|
57
|
+
this.parseVariant(context, variant, data),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
identifier: { key: data.id },
|
|
62
|
+
name: data.name[context.languageContext.locale] || data.id,
|
|
63
|
+
slug: data.slug?.[context.languageContext.locale] || data.id,
|
|
64
|
+
variants,
|
|
65
|
+
} satisfies ProductSearchResultItem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
protected parseVariant(
|
|
69
|
+
context: RequestContext,
|
|
70
|
+
variant: CTProductVariant,
|
|
71
|
+
product: ProductProjection,
|
|
72
|
+
): ProductSearchResultItemVariant {
|
|
73
|
+
const sourceImage = variant.images?.[0];
|
|
74
|
+
const image = ImageSchema.parse({
|
|
75
|
+
sourceUrl: sourceImage?.url || '',
|
|
76
|
+
height: sourceImage?.dimensions.h || undefined,
|
|
77
|
+
width: sourceImage?.dimensions.w || undefined,
|
|
78
|
+
altText: sourceImage?.label || product.name[context.languageContext.locale] || undefined,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const mappedOptions =
|
|
82
|
+
variant.attributes
|
|
83
|
+
?.filter((attribute) => attribute.name === 'Color')
|
|
84
|
+
.map((option) =>
|
|
85
|
+
ProductVariantOptionSchema.parse({
|
|
86
|
+
identifier: ProductOptionIdentifierSchema.parse({
|
|
87
|
+
key: option.name,
|
|
88
|
+
} satisfies Partial<ProductOptionIdentifier>),
|
|
89
|
+
name: option.value || '',
|
|
90
|
+
} satisfies Partial<ProductVariantOption>),
|
|
91
|
+
) || [];
|
|
92
|
+
|
|
93
|
+
const mappedOption = mappedOptions?.[0];
|
|
94
|
+
|
|
95
|
+
return ProductSearchResultItemVariantSchema.parse({
|
|
96
|
+
variant: ProductVariantIdentifierSchema.parse({
|
|
97
|
+
sku: variant.sku || '',
|
|
98
|
+
} satisfies ProductVariantIdentifier),
|
|
99
|
+
image,
|
|
100
|
+
options: mappedOption,
|
|
101
|
+
} satisfies Partial<ProductSearchResultItemVariant>);
|
|
102
|
+
}
|
|
103
|
+
}
|