@reactionary/provider-commercetools 0.3.13 → 0.3.15

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.
@@ -17,7 +17,8 @@ import {
17
17
  CommercetoolsOrderSearchProvider,
18
18
  CommercetoolsProfileProvider,
19
19
  CommercetoolsStoreProvider,
20
- CommercetoolsProductReviewsProvider
20
+ CommercetoolsProductReviewsProvider,
21
+ CommercetoolsProductAssociationsProvider
21
22
  } from "../providers/index.js";
22
23
  import { CommercetoolsAPI } from "./client.js";
23
24
  function withCommercetoolsCapabilities(configuration, capabilities) {
@@ -50,6 +51,14 @@ function withCommercetoolsCapabilities(configuration, capabilities) {
50
51
  commercetoolsApi
51
52
  );
52
53
  }
54
+ if (caps.productAssociations) {
55
+ client.productAssociations = new CommercetoolsProductAssociationsProvider(
56
+ config,
57
+ cache,
58
+ context,
59
+ commercetoolsApi
60
+ );
61
+ }
53
62
  if (caps.productReviews) {
54
63
  client.productReviews = new CommercetoolsProductReviewsProvider(
55
64
  config,
package/index.js CHANGED
@@ -5,6 +5,7 @@ export * from "./providers/identity.provider.js";
5
5
  export * from "./providers/inventory.provider.js";
6
6
  export * from "./providers/order-search.provider.js";
7
7
  export * from "./providers/price.provider.js";
8
+ export * from "./providers/product-associations.provider.js";
8
9
  export * from "./providers/product.provider.js";
9
10
  export * from "./providers/product-reviews.provider.js";
10
11
  export * from "./providers/product-search.provider.js";
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@reactionary/provider-commercetools",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "main": "index.js",
5
5
  "types": "src/index.d.ts",
6
6
  "dependencies": {
7
- "@reactionary/core": "0.3.13",
7
+ "@reactionary/core": "0.3.15",
8
8
  "debug": "^4.4.3",
9
9
  "zod": "4.1.9",
10
10
  "@commercetools/ts-client": "^4.2.1",
@@ -4,6 +4,7 @@ export * from "./identity.provider.js";
4
4
  export * from "./inventory.provider.js";
5
5
  export * from "./order-search.provider.js";
6
6
  export * from "./price.provider.js";
7
+ export * from "./product-associations.provider.js";
7
8
  export * from "./product.provider.js";
8
9
  export * from "./product-reviews.provider.js";
9
10
  export * from "./profile.provider.js";
@@ -0,0 +1,164 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ ProductAssociationsProvider,
14
+ Reactionary,
15
+ ImageSchema,
16
+ ProductVariantOptionSchema,
17
+ ProductOptionIdentifierSchema,
18
+ ProductSearchResultItemVariantSchema,
19
+ ProductVariantIdentifierSchema,
20
+ success,
21
+ error
22
+ } from "@reactionary/core";
23
+ class CommercetoolsProductAssociationsProvider extends ProductAssociationsProvider {
24
+ constructor(config, cache, context, commercetools) {
25
+ super(cache, context);
26
+ this.config = config;
27
+ this.commercetools = commercetools;
28
+ }
29
+ async getClient() {
30
+ const client = await this.commercetools.getClient();
31
+ return client.withProjectKey({ projectKey: this.config.projectKey });
32
+ }
33
+ async fetchAssociatedProductsFor(productKey, maxNumberOfAssociations, attributeName) {
34
+ const client = await this.getClient();
35
+ const product = await client.productProjections().withKey({ key: productKey.key }).get().execute().catch((e) => {
36
+ if (e.statusCode === 404) {
37
+ return null;
38
+ }
39
+ throw e;
40
+ });
41
+ if (!product || !product.body) {
42
+ return [];
43
+ }
44
+ const associatedProducts = [];
45
+ const associationsAttr = product.body.attributes.find((attr) => attr.name === attributeName);
46
+ if (!associationsAttr) {
47
+ return [];
48
+ }
49
+ const associatedProductSearchItems = [];
50
+ const allAssociatedProductPromises = associationsAttr.value.map(async (associatedProductId) => {
51
+ const associatedProduct = client.productProjections().withKey({ key: associatedProductId }).get().execute().catch(() => null);
52
+ return associatedProduct;
53
+ });
54
+ const associatedProductsResults = await Promise.all(allAssociatedProductPromises);
55
+ const validAssociatedProductsResults = associatedProductsResults.filter((result) => !!result).slice(0, maxNumberOfAssociations);
56
+ for (const associatedProductResult of validAssociatedProductsResults) {
57
+ const resultItem = this.parseSingle(associatedProductResult.body);
58
+ if (resultItem) {
59
+ associatedProductSearchItems.push(resultItem);
60
+ }
61
+ }
62
+ return associatedProductSearchItems;
63
+ }
64
+ parseSingle(body) {
65
+ const identifier = { key: body.id };
66
+ const name = body.name[this.context.languageContext.locale] || body.id;
67
+ const slug = body.slug?.[this.context.languageContext.locale] || body.id;
68
+ const variants = [body.masterVariant, ...body.variants].map(
69
+ (variant) => this.parseVariant(variant, body)
70
+ );
71
+ const product = {
72
+ identifier,
73
+ name,
74
+ slug,
75
+ variants
76
+ };
77
+ return product;
78
+ }
79
+ async getAccessories(query) {
80
+ const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfAccessories || 4, "reactionaryaccessories");
81
+ const result = associatedProducts.map((product) => ({
82
+ associationIdentifier: {
83
+ key: `${query.forProduct.key}-accessory-${product.identifier.key}`
84
+ },
85
+ associationReturnType: "productSearchResultItem",
86
+ product
87
+ }));
88
+ return success(result);
89
+ }
90
+ async getSpareparts(query) {
91
+ const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfSpareparts || 4, "reactionaryspareparts");
92
+ const result = associatedProducts.map((product) => ({
93
+ associationIdentifier: {
94
+ key: `${query.forProduct.key}-sparepart-${product.identifier.key}`
95
+ },
96
+ associationReturnType: "productSearchResultItem",
97
+ product
98
+ }));
99
+ return success(result);
100
+ }
101
+ async getReplacements(query) {
102
+ const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfReplacements || 4, "reactionaryreplacements");
103
+ const result = associatedProducts.map((product) => ({
104
+ associationIdentifier: {
105
+ key: `${query.forProduct.key}-replacement-${product.identifier.key}`
106
+ },
107
+ associationReturnType: "productSearchResultItem",
108
+ product
109
+ }));
110
+ return success(result);
111
+ }
112
+ parseVariant(variant, product) {
113
+ const sourceImage = variant.images?.[0];
114
+ const img = ImageSchema.parse({
115
+ sourceUrl: sourceImage?.url || "",
116
+ height: sourceImage?.dimensions.h || void 0,
117
+ width: sourceImage?.dimensions.w || void 0,
118
+ altText: sourceImage?.label || product.name[this.context.languageContext.locale] || void 0
119
+ });
120
+ const mappedOptions = variant.attributes?.filter((x) => x.name === "Color").map(
121
+ (opt) => ProductVariantOptionSchema.parse({
122
+ identifier: ProductOptionIdentifierSchema.parse({
123
+ key: opt.name
124
+ }),
125
+ name: opt.value || ""
126
+ })
127
+ ) || [];
128
+ const mappedOption = mappedOptions?.[0];
129
+ return ProductSearchResultItemVariantSchema.parse({
130
+ variant: ProductVariantIdentifierSchema.parse({
131
+ sku: variant.sku || ""
132
+ }),
133
+ image: img,
134
+ options: mappedOption
135
+ });
136
+ }
137
+ }
138
+ __decorateClass([
139
+ Reactionary({
140
+ cache: true,
141
+ cacheTimeToLiveInSeconds: 300,
142
+ currencyDependentCaching: false,
143
+ localeDependentCaching: false
144
+ })
145
+ ], CommercetoolsProductAssociationsProvider.prototype, "getAccessories", 1);
146
+ __decorateClass([
147
+ Reactionary({
148
+ cache: true,
149
+ cacheTimeToLiveInSeconds: 300,
150
+ currencyDependentCaching: false,
151
+ localeDependentCaching: false
152
+ })
153
+ ], CommercetoolsProductAssociationsProvider.prototype, "getSpareparts", 1);
154
+ __decorateClass([
155
+ Reactionary({
156
+ cache: true,
157
+ cacheTimeToLiveInSeconds: 300,
158
+ currencyDependentCaching: false,
159
+ localeDependentCaching: false
160
+ })
161
+ ], CommercetoolsProductAssociationsProvider.prototype, "getReplacements", 1);
162
+ export {
163
+ CommercetoolsProductAssociationsProvider
164
+ };
@@ -80,7 +80,12 @@ class CommercetoolsProductProvider extends ProductProvider {
80
80
  description = data.description[this.context.languageContext.locale];
81
81
  }
82
82
  const variantLevelAttributes = data.masterVariant.attributes?.map((x) => this.parseAttribute(x)) || [];
83
- const productLevelAttributes = data.attributes.map((x) => this.parseAttribute(x)) || [];
83
+ const specialAttributes = [
84
+ "reactionaryaccessories",
85
+ "reactionaryspareparts",
86
+ "reactionaryreplacements"
87
+ ];
88
+ const productLevelAttributes = data.attributes.filter((x) => !specialAttributes.includes(x.name)).map((x) => this.parseAttribute(x)) || [];
84
89
  const sharedAttributes = [
85
90
  ...productLevelAttributes,
86
91
  ...variantLevelAttributes
@@ -2,6 +2,7 @@ import { CapabilitiesSchema } from "@reactionary/core";
2
2
  const CommercetoolsCapabilitiesSchema = CapabilitiesSchema.pick({
3
3
  product: true,
4
4
  productSearch: true,
5
+ productAssociations: true,
5
6
  productReviews: true,
6
7
  identity: true,
7
8
  cart: true,
package/src/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './providers/identity.provider.js';
5
5
  export * from './providers/inventory.provider.js';
6
6
  export * from './providers/order-search.provider.js';
7
7
  export * from './providers/price.provider.js';
8
+ export * from './providers/product-associations.provider.js';
8
9
  export * from './providers/product.provider.js';
9
10
  export * from './providers/product-reviews.provider.js';
10
11
  export * from './providers/product-search.provider.js';
@@ -4,6 +4,7 @@ export * from './identity.provider.js';
4
4
  export * from './inventory.provider.js';
5
5
  export * from './order-search.provider.js';
6
6
  export * from './price.provider.js';
7
+ export * from './product-associations.provider.js';
7
8
  export * from './product.provider.js';
8
9
  export * from './product-reviews.provider.js';
9
10
  export * from './profile.provider.js';
@@ -0,0 +1,24 @@
1
+ import { ProductAssociationsProvider } from '@reactionary/core';
2
+ import type { ProductIdentifier, ProductAssociation, ProductSearchResultItem, ProductSearchResultItemVariant, ProductAssociationsGetAccessoriesQuery, ProductAssociationsGetSparepartsQuery, ProductAssociationsGetReplacementsQuery, Result, RequestContext, Cache } from '@reactionary/core';
3
+ import type { ProductProjection, ProductVariant as CTProductVariant } from '@commercetools/platform-sdk';
4
+ import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
5
+ import type { CommercetoolsAPI } from '../core/client.js';
6
+ export declare class CommercetoolsProductAssociationsProvider extends ProductAssociationsProvider {
7
+ protected config: CommercetoolsConfiguration;
8
+ protected commercetools: CommercetoolsAPI;
9
+ constructor(config: CommercetoolsConfiguration, cache: Cache, context: RequestContext, commercetools: CommercetoolsAPI);
10
+ protected getClient(): Promise<import("@commercetools/platform-sdk").ByProjectKeyRequestBuilder>;
11
+ protected fetchAssociatedProductsFor(productKey: ProductIdentifier, maxNumberOfAssociations: number, attributeName: string): Promise<ProductSearchResultItem[]>;
12
+ protected parseSingle(body: ProductProjection): {
13
+ identifier: {
14
+ key: string;
15
+ };
16
+ name: string;
17
+ slug: string;
18
+ variants: ProductSearchResultItemVariant[];
19
+ };
20
+ getAccessories(query: ProductAssociationsGetAccessoriesQuery): Promise<Result<ProductAssociation[]>>;
21
+ getSpareparts(query: ProductAssociationsGetSparepartsQuery): Promise<Result<ProductAssociation[]>>;
22
+ getReplacements(query: ProductAssociationsGetReplacementsQuery): Promise<Result<ProductAssociation[]>>;
23
+ protected parseVariant(variant: CTProductVariant, product: ProductProjection): ProductSearchResultItemVariant;
24
+ }
@@ -11,6 +11,7 @@ export declare const CommercetoolsCapabilitiesSchema: z.ZodObject<{
11
11
  profile: z.ZodOptional<z.ZodBoolean>;
12
12
  store: z.ZodOptional<z.ZodBoolean>;
13
13
  productSearch: z.ZodOptional<z.ZodBoolean>;
14
+ productAssociations: z.ZodOptional<z.ZodBoolean>;
14
15
  productReviews: z.ZodOptional<z.ZodBoolean>;
15
16
  orderSearch: z.ZodOptional<z.ZodBoolean>;
16
17
  }, z.core.$loose>;