@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.
Files changed (191) hide show
  1. package/README.md +28 -14
  2. package/core/src/client/client-builder.ts +54 -6
  3. package/core/src/factories/cart.factory.ts +40 -0
  4. package/core/src/factories/category.factory.ts +40 -0
  5. package/core/src/factories/checkout.factory.ts +59 -0
  6. package/core/src/factories/identity.factory.ts +26 -0
  7. package/core/src/factories/index.ts +15 -0
  8. package/core/src/factories/inventory.factory.ts +26 -0
  9. package/core/src/factories/order-search.factory.ts +33 -0
  10. package/core/src/factories/order.factory.ts +21 -0
  11. package/core/src/factories/price.factory.ts +29 -0
  12. package/core/src/factories/product-associations.factory.ts +30 -0
  13. package/core/src/factories/product-list.factory.ts +82 -0
  14. package/core/src/factories/product-reviews.factory.ts +58 -0
  15. package/core/src/factories/product-search.factory.ts +31 -0
  16. package/core/src/factories/product.factory.ts +21 -0
  17. package/core/src/factories/profile.factory.ts +23 -0
  18. package/core/src/factories/store.factory.ts +21 -0
  19. package/core/src/index.ts +3 -1
  20. package/core/src/providers/cart.provider.ts +12 -10
  21. package/core/src/providers/category.provider.ts +9 -7
  22. package/core/src/providers/checkout.provider.ts +14 -10
  23. package/core/src/providers/identity.provider.ts +7 -7
  24. package/core/src/providers/inventory.provider.ts +4 -4
  25. package/core/src/providers/order-search.provider.ts +4 -2
  26. package/core/src/providers/order.provider.ts +4 -4
  27. package/core/src/providers/price.provider.ts +5 -5
  28. package/core/src/providers/product-associations.provider.ts +6 -4
  29. package/core/src/providers/product-list.provider.ts +13 -8
  30. package/core/src/providers/product-reviews.provider.ts +8 -4
  31. package/core/src/providers/product-search.provider.ts +15 -28
  32. package/core/src/providers/product.provider.ts +6 -6
  33. package/core/src/providers/profile.provider.ts +8 -8
  34. package/core/src/providers/store.provider.ts +2 -2
  35. package/core/src/test/client-builder.spec.ts +81 -0
  36. package/examples/node/package.json +7 -7
  37. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +10 -4
  38. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +8 -2
  39. package/examples/node/src/basic/basic-node-setup.spec.ts +5 -1
  40. package/examples/node/src/basic/client-creation.spec.ts +3 -3
  41. package/examples/node/src/utils.ts +41 -41
  42. package/examples/node/tsconfig.lib.json +2 -1
  43. package/package.json +1 -1
  44. package/providers/algolia/src/core/initialize.ts +76 -21
  45. package/providers/algolia/src/core/initialize.types.ts +107 -0
  46. package/providers/algolia/src/factories/index.ts +1 -0
  47. package/providers/algolia/src/factories/product-search/product-search.factory.ts +182 -0
  48. package/providers/algolia/src/index.ts +2 -2
  49. package/providers/algolia/src/providers/analytics.provider.ts +4 -4
  50. package/providers/algolia/src/providers/product-search.provider.ts +67 -184
  51. package/providers/algolia/src/schema/capabilities.schema.ts +81 -7
  52. package/providers/algolia/src/test/analytics.spec.ts +9 -1
  53. package/providers/algolia/src/test/client-builder-product-search-extension.example.ts +85 -0
  54. package/providers/commercetools/src/core/capability-descriptors.ts +324 -0
  55. package/providers/commercetools/src/core/initialize.ts +35 -151
  56. package/providers/commercetools/src/core/initialize.types.ts +174 -0
  57. package/providers/commercetools/src/factories/cart/cart.factory.ts +142 -0
  58. package/providers/commercetools/src/factories/category/category.factory.ts +77 -0
  59. package/providers/commercetools/src/factories/checkout/checkout-initializer-overrides.example.ts +94 -0
  60. package/providers/commercetools/src/factories/checkout/checkout.factory.ts +338 -0
  61. package/providers/commercetools/src/factories/identity/identity.factory.ts +26 -0
  62. package/providers/commercetools/src/factories/inventory/inventory.factory.ts +49 -0
  63. package/providers/commercetools/src/factories/order/order.factory.ts +149 -0
  64. package/providers/commercetools/src/factories/order-search/order-search.factory.ts +108 -0
  65. package/providers/commercetools/src/factories/price/price.factory.ts +76 -0
  66. package/providers/commercetools/src/factories/product/product-factory-baseline.example.ts +14 -0
  67. package/providers/commercetools/src/factories/product/product-factory-schema-and-parse-extension.example.ts +35 -0
  68. package/providers/commercetools/src/factories/product/product-factory-schema-extension.example.ts +23 -0
  69. package/providers/commercetools/src/factories/product/product-initializer-factory-extension.example.ts +41 -0
  70. package/providers/commercetools/src/factories/product/product-provider-custom-method-only.example.ts +47 -0
  71. package/providers/commercetools/src/factories/product/product-provider-schema-signature-extension.example.ts +61 -0
  72. package/providers/commercetools/src/factories/product/product.factory.ts +220 -0
  73. package/providers/commercetools/src/factories/product/utils.example.ts +9 -0
  74. package/providers/commercetools/src/factories/product-associations/product-associations.factory.ts +103 -0
  75. package/providers/commercetools/src/factories/product-list/product-list.factory.ts +122 -0
  76. package/providers/commercetools/src/factories/product-reviews/product-reviews.factory.ts +81 -0
  77. package/providers/commercetools/src/factories/product-search/product-search.factory.ts +182 -0
  78. package/providers/commercetools/src/factories/profile/profile.factory.ts +94 -0
  79. package/providers/commercetools/src/factories/store/store.factory.ts +49 -0
  80. package/providers/commercetools/src/index.ts +15 -0
  81. package/providers/commercetools/src/providers/cart.provider.ts +67 -193
  82. package/providers/commercetools/src/providers/category.provider.ts +24 -64
  83. package/providers/commercetools/src/providers/checkout.provider.ts +50 -322
  84. package/providers/commercetools/src/providers/identity.provider.ts +35 -15
  85. package/providers/commercetools/src/providers/inventory.provider.ts +13 -31
  86. package/providers/commercetools/src/providers/order-search.provider.ts +16 -110
  87. package/providers/commercetools/src/providers/order.provider.ts +13 -144
  88. package/providers/commercetools/src/providers/price.provider.ts +37 -51
  89. package/providers/commercetools/src/providers/product-associations.provider.ts +39 -104
  90. package/providers/commercetools/src/providers/product-list.provider.ts +38 -23
  91. package/providers/commercetools/src/providers/product-reviews.provider.ts +34 -14
  92. package/providers/commercetools/src/providers/product-search.provider.ts +17 -170
  93. package/providers/commercetools/src/providers/product.provider.ts +20 -199
  94. package/providers/commercetools/src/providers/profile.provider.ts +27 -73
  95. package/providers/commercetools/src/providers/store.provider.ts +13 -31
  96. package/providers/commercetools/src/schema/capabilities.schema.ts +258 -20
  97. package/providers/commercetools/src/test/caching.spec.ts +18 -2
  98. package/providers/commercetools/src/test/client-builder-merge-extensions.example.ts +125 -0
  99. package/providers/fake/src/core/initialize.ts +213 -44
  100. package/providers/fake/src/core/initialize.types.ts +164 -0
  101. package/providers/fake/src/factories/cart/cart.factory.ts +34 -0
  102. package/providers/fake/src/factories/category/category.factory.ts +40 -0
  103. package/providers/fake/src/factories/checkout/checkout.factory.ts +53 -0
  104. package/providers/fake/src/factories/identity/identity.factory.ts +25 -0
  105. package/providers/fake/src/factories/index.ts +14 -0
  106. package/providers/fake/src/factories/inventory/inventory.factory.ts +25 -0
  107. package/providers/fake/src/factories/order/order.factory.ts +22 -0
  108. package/providers/fake/src/factories/order-search/order-search.factory.ts +27 -0
  109. package/providers/fake/src/factories/price/price.factory.ts +26 -0
  110. package/providers/fake/src/factories/product/product.factory.ts +22 -0
  111. package/providers/fake/src/factories/product-associations/product-associations.factory.ts +25 -0
  112. package/providers/fake/src/factories/product-reviews/product-reviews.factory.ts +53 -0
  113. package/providers/fake/src/factories/product-search/product-search.factory.ts +27 -0
  114. package/providers/fake/src/factories/profile/profile.factory.ts +22 -0
  115. package/providers/fake/src/factories/store/store.factory.ts +22 -0
  116. package/providers/fake/src/index.ts +2 -0
  117. package/providers/fake/src/providers/cart.provider.ts +23 -14
  118. package/providers/fake/src/providers/category.provider.ts +120 -105
  119. package/providers/fake/src/providers/checkout.provider.ts +39 -20
  120. package/providers/fake/src/providers/identity.provider.ts +40 -34
  121. package/providers/fake/src/providers/inventory.provider.ts +26 -24
  122. package/providers/fake/src/providers/order-search.provider.ts +38 -30
  123. package/providers/fake/src/providers/order.provider.ts +21 -37
  124. package/providers/fake/src/providers/price.provider.ts +42 -34
  125. package/providers/fake/src/providers/product-associations.provider.ts +23 -10
  126. package/providers/fake/src/providers/product-reviews.provider.ts +71 -69
  127. package/providers/fake/src/providers/product-search.provider.ts +43 -70
  128. package/providers/fake/src/providers/product.provider.ts +34 -32
  129. package/providers/fake/src/providers/profile.provider.ts +62 -55
  130. package/providers/fake/src/providers/store.provider.ts +38 -22
  131. package/providers/fake/src/schema/capabilities.schema.ts +175 -18
  132. package/providers/fake/src/test/cart.provider.spec.ts +20 -3
  133. package/providers/fake/src/test/category.provider.spec.ts +4 -1
  134. package/providers/fake/src/test/checkout.provider.spec.ts +12 -2
  135. package/providers/fake/src/test/client-builder-product-extension.example.ts +75 -0
  136. package/providers/fake/src/test/order-search.provider.spec.ts +4 -7
  137. package/providers/fake/src/test/order.provider.spec.ts +4 -6
  138. package/providers/fake/src/test/price.provider.spec.ts +3 -1
  139. package/providers/fake/src/test/product.provider.spec.ts +8 -2
  140. package/providers/fake/src/test/profile.provider.spec.ts +4 -2
  141. package/providers/google-analytics/src/core/initialize.ts +37 -12
  142. package/providers/google-analytics/src/core/initialize.types.ts +47 -0
  143. package/providers/google-analytics/src/index.ts +1 -0
  144. package/providers/google-analytics/src/schema/capabilities.schema.ts +31 -5
  145. package/providers/medusa/src/core/initialize.ts +324 -81
  146. package/providers/medusa/src/core/initialize.types.ts +184 -0
  147. package/providers/medusa/src/factories/cart/cart.factory.ts +34 -0
  148. package/providers/medusa/src/factories/category/category.factory.ts +37 -0
  149. package/providers/medusa/src/factories/checkout/checkout.factory.ts +50 -0
  150. package/providers/medusa/src/factories/identity/identity.factory.ts +22 -0
  151. package/providers/medusa/src/factories/index.ts +12 -0
  152. package/providers/medusa/src/factories/inventory/inventory.factory.ts +25 -0
  153. package/providers/medusa/src/factories/order/order.factory.ts +22 -0
  154. package/providers/medusa/src/factories/order-search/order-search.factory.ts +27 -0
  155. package/providers/medusa/src/factories/price/price.factory.ts +26 -0
  156. package/providers/medusa/src/factories/product/product.factory.ts +22 -0
  157. package/providers/medusa/src/factories/product-associations/product-associations.factory.ts +25 -0
  158. package/providers/medusa/src/factories/product-search/product-search.factory.ts +27 -0
  159. package/providers/medusa/src/factories/profile/profile.factory.ts +22 -0
  160. package/providers/medusa/src/index.ts +2 -0
  161. package/providers/medusa/src/providers/cart.provider.ts +33 -20
  162. package/providers/medusa/src/providers/category.provider.ts +30 -12
  163. package/providers/medusa/src/providers/checkout.provider.ts +42 -17
  164. package/providers/medusa/src/providers/identity.provider.ts +1 -1
  165. package/providers/medusa/src/providers/inventory.provider.ts +21 -7
  166. package/providers/medusa/src/providers/order-search.provider.ts +16 -5
  167. package/providers/medusa/src/providers/order.provider.ts +17 -5
  168. package/providers/medusa/src/providers/price.provider.ts +26 -7
  169. package/providers/medusa/src/providers/product-associations.provider.ts +19 -8
  170. package/providers/medusa/src/providers/product-search.provider.ts +19 -31
  171. package/providers/medusa/src/providers/product.provider.ts +47 -11
  172. package/providers/medusa/src/providers/profile.provider.ts +35 -11
  173. package/providers/medusa/src/schema/capabilities.schema.ts +229 -18
  174. package/providers/medusa/src/test/cart.provider.spec.ts +18 -2
  175. package/providers/medusa/src/test/category.provider.spec.ts +4 -1
  176. package/providers/medusa/src/test/checkout.spec.ts +9 -9
  177. package/providers/medusa/src/test/inventory.provider.spec.ts +3 -1
  178. package/providers/medusa/src/test/large-cart.provider.spec.ts +8 -2
  179. package/providers/medusa/src/test/price.provider.spec.ts +8 -1
  180. package/providers/medusa/src/test/product.provider.spec.ts +3 -1
  181. package/providers/medusa/src/test/search.provider.spec.ts +16 -3
  182. package/providers/meilisearch/src/core/initialize.ts +88 -21
  183. package/providers/meilisearch/src/core/initialize.types.ts +119 -0
  184. package/providers/meilisearch/src/factories/index.ts +2 -0
  185. package/providers/meilisearch/src/factories/order-search/order-search.factory.ts +27 -0
  186. package/providers/meilisearch/src/factories/product-search/product-search.factory.ts +27 -0
  187. package/providers/meilisearch/src/index.ts +2 -0
  188. package/providers/meilisearch/src/providers/index.ts +1 -0
  189. package/providers/meilisearch/src/providers/order-search.provider.ts +21 -6
  190. package/providers/meilisearch/src/providers/product-search.provider.ts +24 -8
  191. package/providers/meilisearch/src/schema/capabilities.schema.ts +95 -8
@@ -1,43 +1,24 @@
1
1
  import type {
2
- ProductSearchFacetResult as CTProductSearchFacetResult,
3
- ProductSearchFacetResultBucket as CTProductSearchFacetResultBucket,
4
- ProductVariant as CTProductVariant,
5
- ProductPagedSearchResponse,
6
- ProductProjection,
7
2
  ProductSearchFacetExpression,
8
3
  } from '@commercetools/platform-sdk';
9
4
  import type {
10
5
  Cache,
11
6
  FacetIdentifier,
12
7
  FacetValueIdentifier,
13
- ProductOptionIdentifier,
8
+ ProductSearchFactory,
9
+ ProductSearchFactoryOutput,
10
+ ProductSearchFactoryWithOutput,
14
11
  ProductSearchQueryByTerm,
15
12
  ProductSearchQueryCreateNavigationFilter,
16
- ProductSearchResult,
17
- ProductSearchResultFacet,
18
- ProductSearchResultFacetValue,
19
- ProductSearchResultItem,
20
- ProductSearchResultItemVariant,
21
- ProductVariantIdentifier,
22
- ProductVariantOption,
23
13
  RequestContext,
24
14
  Result,
25
- SearchIdentifier,
26
15
  } from '@reactionary/core';
27
16
  import {
28
- FacetIdentifierSchema,
29
17
  FacetValueIdentifierSchema,
30
- ImageSchema,
31
- ProductOptionIdentifierSchema,
32
18
  ProductSearchProvider,
33
19
  ProductSearchQueryByTermSchema,
34
20
  ProductSearchQueryCreateNavigationFilterSchema,
35
- ProductSearchResultFacetSchema,
36
- ProductSearchResultFacetValueSchema,
37
- ProductSearchResultItemVariantSchema,
38
21
  ProductSearchResultSchema,
39
- ProductVariantIdentifierSchema,
40
- ProductVariantOptionSchema,
41
22
  Reactionary,
42
23
  success,
43
24
  } from '@reactionary/core';
@@ -46,23 +27,29 @@ import type { CommercetoolsConfiguration } from '../schema/configuration.schema.
46
27
  import createDebug from 'debug';
47
28
  import type { CommercetoolsAPI } from '../core/client.js';
48
29
  import { CommercetoolsCategoryLookupSchema, CommercetoolsResolveCategoryQueryByKeySchema, type CommercetoolsCategoryLookup, type CommercetoolsResolveCategoryQueryById, type CommercetoolsResolveCategoryQueryByKey } from '../schema/commercetools.schema.js';
30
+ import type { CommercetoolsProductSearchFactory } from '../factories/product-search/product-search.factory.js';
49
31
 
50
32
  const debug = createDebug('reactionary:commercetools:search');
51
33
 
52
- export class CommercetoolsSearchProvider extends ProductSearchProvider {
34
+ export class CommercetoolsSearchProvider<
35
+ TFactory extends ProductSearchFactory = CommercetoolsProductSearchFactory,
36
+ > extends ProductSearchProvider<ProductSearchFactoryOutput<TFactory>> {
53
37
  protected config: CommercetoolsConfiguration;
54
38
  protected commercetools: CommercetoolsAPI;
39
+ protected factory: ProductSearchFactoryWithOutput<TFactory>;
55
40
 
56
41
  constructor(
57
42
  config: CommercetoolsConfiguration,
58
43
  cache: Cache,
59
44
  context: RequestContext,
60
- commercetools: CommercetoolsAPI
45
+ commercetools: CommercetoolsAPI,
46
+ factory: ProductSearchFactoryWithOutput<TFactory>,
61
47
  ) {
62
48
  super(cache, context);
63
49
 
64
50
  this.config = config;
65
51
  this.commercetools = commercetools;
52
+ this.factory = factory;
66
53
  }
67
54
 
68
55
  protected async getClient() {
@@ -275,7 +262,7 @@ export class CommercetoolsSearchProvider extends ProductSearchProvider {
275
262
  })
276
263
  public override async queryByTerm(
277
264
  payload: ProductSearchQueryByTerm
278
- ): Promise<Result<ProductSearchResult>> {
265
+ ): Promise<Result<ProductSearchFactoryOutput<TFactory>>> {
279
266
  const client = await this.getClient();
280
267
 
281
268
  const facetsToReturn = await this.getFacetsToReturn(payload);
@@ -327,10 +314,11 @@ export class CommercetoolsSearchProvider extends ProductSearchProvider {
327
314
  .execute();
328
315
 
329
316
  const responseBody = response.body;
330
- const result = this.parsePaginatedResult(
317
+ const result = this.factory.parseSearchResult(
318
+ this.context,
331
319
  responseBody,
332
- payload
333
- ) as ProductSearchResult;
320
+ payload,
321
+ );
334
322
 
335
323
 
336
324
  // ok, we have to patch up the categories facet to have the category keys instead of ids
@@ -358,7 +346,7 @@ export class CommercetoolsSearchProvider extends ProductSearchProvider {
358
346
  return success(result);
359
347
  }
360
348
 
361
- protected async patchCategoryFacetValues(result: ProductSearchResult) {
349
+ protected async patchCategoryFacetValues(result: ProductSearchFactoryOutput<TFactory>) {
362
350
  const categoryFacet = result.facets.find(
363
351
  (f) => f.identifier.key === 'categories'
364
352
  );
@@ -384,145 +372,4 @@ export class CommercetoolsSearchProvider extends ProductSearchProvider {
384
372
  }
385
373
 
386
374
 
387
- protected parseSingle(body: ProductProjection) {
388
- const identifier = { key: body.id };
389
- const name = body.name[this.context.languageContext.locale] || body.id;
390
- const slug = body.slug?.[this.context.languageContext.locale] || body.id;
391
- const variants = [body.masterVariant, ...body.variants].map((variant) =>
392
- this.parseVariant(variant, body)
393
- );
394
-
395
- const product = {
396
- identifier,
397
- name,
398
- slug,
399
- variants,
400
- } satisfies ProductSearchResultItem;
401
-
402
- return product;
403
- }
404
-
405
- protected parsePaginatedResult(
406
- body: ProductPagedSearchResponse,
407
- query: ProductSearchQueryByTerm
408
- ) {
409
- const identifier = {
410
- ...query.search,
411
- } satisfies SearchIdentifier;
412
-
413
- const products: ProductSearchResultItem[] = body.results.map((p) =>
414
- this.parseSingle(p.productProjection!)
415
- );
416
- const facets: ProductSearchResultFacet[] = [];
417
-
418
- for (const facet of body.facets) {
419
- const facetIdentifier = FacetIdentifierSchema.parse({
420
- key: facet.name,
421
- } satisfies Partial<FacetIdentifier>);
422
-
423
- const candidateFacet = this.parseFacet(facetIdentifier, facet);
424
- if (candidateFacet.values.length > 0) {
425
- facets.push(candidateFacet);
426
- }
427
- }
428
-
429
- const result = {
430
- identifier,
431
- pageNumber: (Math.ceil(body.offset / body.limit) || 0) + 1,
432
- pageSize: body.limit,
433
- totalCount: body.total || 0,
434
- totalPages: Math.ceil((body.total || 0) / body.limit || 0) + 1,
435
- items: products,
436
- facets,
437
- } satisfies ProductSearchResult;
438
-
439
- return result;
440
- }
441
-
442
- /**
443
- * See version 0.0.81 for ProductProjection based facet parsing
444
- * @param facetIdentifier
445
- * @param facetValue
446
- * @returns
447
- */
448
- protected parseFacet(
449
- facetIdentifier: FacetIdentifier,
450
- facet: CTProductSearchFacetResult
451
- ): ProductSearchResultFacet {
452
- const result: ProductSearchResultFacet =
453
- ProductSearchResultFacetSchema.parse({
454
- identifier: facetIdentifier,
455
- name: facet.name,
456
- values: [],
457
- });
458
-
459
- const distinctFacet = facet as CTProductSearchFacetResultBucket;
460
- if (distinctFacet) {
461
- distinctFacet.buckets.forEach((bucket) => {
462
- const facetValueIdentifier = FacetValueIdentifierSchema.parse({
463
- facet: facetIdentifier,
464
- key: bucket.key,
465
- } satisfies Partial<FacetValueIdentifier>);
466
-
467
- result.values.push(
468
- this.parseFacetValue(facetValueIdentifier, bucket.key, bucket.count)
469
- );
470
- });
471
- }
472
-
473
- return result;
474
- }
475
-
476
- protected parseFacetValue(
477
- facetValueIdentifier: FacetValueIdentifier,
478
- label: string,
479
- count: number
480
- ): ProductSearchResultFacetValue {
481
-
482
- return ProductSearchResultFacetValueSchema.parse({
483
- identifier: facetValueIdentifier,
484
- name: label,
485
- count: count,
486
- active: false,
487
- } satisfies Partial<ProductSearchResultFacetValue>);
488
- }
489
-
490
- protected parseVariant(
491
- variant: CTProductVariant,
492
- product: ProductProjection
493
- ): ProductSearchResultItemVariant {
494
- const sourceImage = variant.images?.[0];
495
-
496
- const img = ImageSchema.parse({
497
- sourceUrl: sourceImage?.url || '',
498
- height: sourceImage?.dimensions.h || undefined,
499
- width: sourceImage?.dimensions.w || undefined,
500
- altText:
501
- sourceImage?.label ||
502
- product.name[this.context.languageContext.locale] ||
503
- undefined,
504
- });
505
-
506
- const mappedOptions =
507
- variant.attributes
508
- ?.filter((x) => x.name === 'Color')
509
- .map((opt) =>
510
- ProductVariantOptionSchema.parse({
511
- identifier: ProductOptionIdentifierSchema.parse({
512
- key: opt.name,
513
- } satisfies Partial<ProductOptionIdentifier>),
514
- name: opt.value || '',
515
- } satisfies Partial<ProductVariantOption>)
516
- ) || [];
517
-
518
- const mappedOption = mappedOptions?.[0];
519
-
520
- return ProductSearchResultItemVariantSchema.parse({
521
- variant: ProductVariantIdentifierSchema.parse({
522
- sku: variant.sku || '',
523
- } satisfies ProductVariantIdentifier),
524
- image: img,
525
- options: mappedOption,
526
- } satisfies Partial<ProductSearchResultItemVariant>);
527
- }
528
375
  }
@@ -1,9 +1,4 @@
1
1
  import {
2
- ImageSchema,
3
- ProductAttributeIdentifierSchema,
4
- ProductAttributeSchema,
5
- ProductAttributeValueIdentifierSchema,
6
- ProductAttributeValueSchema,
7
2
  ProductProvider,
8
3
  ProductQueryByIdSchema,
9
4
  ProductQueryBySKUSchema,
@@ -12,48 +7,34 @@ import {
12
7
  Reactionary,
13
8
  success,
14
9
  error,
10
+ type ProductFactory,
11
+ type ProductFactoryOutput,
12
+ type ProductFactoryWithOutput,
15
13
  } from '@reactionary/core';
16
14
  import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
17
15
  import type {
18
- ProductProjection,
19
- ProductVariant as CTProductVariant,
20
- Attribute as CTAttribute,
21
- } from '@commercetools/platform-sdk';
22
- import type {
23
- Product,
24
- ProductVariant,
25
16
  ProductQueryById,
26
17
  ProductQueryBySKU,
27
18
  ProductQueryBySlug,
28
- ProductVariantIdentifier,
29
19
  RequestContext,
30
- ProductAttribute,
31
- ProductAttributeIdentifier,
32
- ProductAttributeValue,
33
- ProductAttributeValueIdentifier,
34
- ProductIdentifier,
35
- ProductVariantOption,
36
- ProductOptionIdentifier,
37
20
  Result,
38
21
  } from '@reactionary/core';
39
- import type { Cache, Image } from '@reactionary/core';
22
+ import type { Cache } from '@reactionary/core';
40
23
  import type { CommercetoolsAPI } from '../core/client.js';
41
24
  import type { NotFoundError } from '@reactionary/core';
25
+ import type { CommercetoolsProductFactory } from '../factories/product/product.factory.js';
42
26
 
43
- export class CommercetoolsProductProvider extends ProductProvider {
44
- protected config: CommercetoolsConfiguration;
45
- protected commercetools: CommercetoolsAPI;
46
-
27
+ export class CommercetoolsProductProvider<
28
+ TFactory extends ProductFactory = CommercetoolsProductFactory
29
+ > extends ProductProvider<ProductFactoryOutput<TFactory>> {
47
30
  constructor(
48
- config: CommercetoolsConfiguration,
49
31
  cache: Cache,
50
32
  context: RequestContext,
51
- commercetools: CommercetoolsAPI
33
+ protected config: CommercetoolsConfiguration,
34
+ protected commercetools: CommercetoolsAPI,
35
+ protected factory: ProductFactoryWithOutput<TFactory>
52
36
  ) {
53
37
  super(cache, context);
54
-
55
- this.config = config;
56
- this.commercetools = commercetools;
57
38
  }
58
39
 
59
40
  protected async getClient() {
@@ -73,13 +54,14 @@ export class CommercetoolsProductProvider extends ProductProvider {
73
54
  })
74
55
  public override async getById(
75
56
  payload: ProductQueryById
76
- ): Promise<Result<Product>> {
57
+ ): Promise<Result<ProductFactoryOutput<TFactory>>> {
77
58
  const client = await this.getClient();
78
59
  const remote = await client
79
60
  .withKey({ key: payload.identifier.key })
80
61
  .get()
81
62
  .execute();
82
- const value = this.parseSingle(remote.body);
63
+
64
+ const value = this.factory.parseProduct(this.context, remote.body);
83
65
 
84
66
  return success(value);
85
67
  }
@@ -94,7 +76,7 @@ export class CommercetoolsProductProvider extends ProductProvider {
94
76
  })
95
77
  public override async getBySlug(
96
78
  payload: ProductQueryBySlug
97
- ): Promise<Result<Product, NotFoundError>> {
79
+ ): Promise<Result<ProductFactoryOutput<TFactory>, NotFoundError>> {
98
80
  const client = await this.getClient();
99
81
 
100
82
  const remote = await client
@@ -113,7 +95,7 @@ export class CommercetoolsProductProvider extends ProductProvider {
113
95
  identifier: payload.slug,
114
96
  });
115
97
  }
116
- const result = this.parseSingle(remote.body.results[0]);
98
+ const result = this.factory.parseProduct(this.context, remote.body.results[0]);
117
99
 
118
100
  return success(result);
119
101
  }
@@ -128,7 +110,7 @@ export class CommercetoolsProductProvider extends ProductProvider {
128
110
  })
129
111
  public override async getBySKU(
130
112
  payload: ProductQueryBySKU
131
- ): Promise<Result<Product>> {
113
+ ): Promise<Result<ProductFactoryOutput<TFactory>>> {
132
114
  const client = await this.getClient();
133
115
 
134
116
  const remote = await client
@@ -142,171 +124,10 @@ export class CommercetoolsProductProvider extends ProductProvider {
142
124
  })
143
125
  .execute();
144
126
 
145
- const result = this.parseSingle(remote.body.results[0]);
127
+ // PAIN: the lack of knowing the non-generic type signature of the factory (the scopedown to CT Product Provider)
128
+ // this is due to the Typescript construct for ProductFactoryOutput<TFactory>
129
+ const result = this.factory.parseProduct(this.context, remote.body.results[0]);
146
130
 
147
131
  return success(result);
148
132
  }
149
-
150
- protected parseSingle(data: ProductProjection): Product {
151
- const identifier = { key: data.key || data.id } satisfies ProductIdentifier;
152
- const name = data.name[this.context.languageContext.locale];
153
- const slug = data.slug[this.context.languageContext.locale];
154
-
155
- let description = '';
156
- if (data.description) {
157
- description = data.description[this.context.languageContext.locale];
158
- }
159
-
160
- const variantLevelAttributes =
161
- data.masterVariant.attributes?.map((x) => this.parseAttribute(x)) || [];
162
-
163
-
164
- const specialAttributes = [
165
- 'reactionaryaccessories',
166
- 'reactionaryspareparts',
167
- 'reactionaryreplacements'
168
- ]
169
-
170
- const productLevelAttributes =
171
- data.attributes.filter(x => !specialAttributes.includes(x.name)).map((x) => this.parseAttribute(x)) || [];
172
-
173
- const sharedAttributes = [
174
- ...productLevelAttributes,
175
- ...variantLevelAttributes,
176
- ];
177
- const mainVariant = this.parseVariant(data.masterVariant, data);
178
-
179
- const otherVariants = [];
180
- for (const variant of data.variants || []) {
181
- if (variant.id !== data.masterVariant.id) {
182
- otherVariants.push(this.parseVariant(variant, data));
183
- }
184
- }
185
-
186
- const result = {
187
- identifier,
188
- name,
189
- slug,
190
- description,
191
- sharedAttributes,
192
- mainVariant,
193
- brand: '',
194
- longDescription: '',
195
- manufacturer: '',
196
- options: [],
197
- parentCategories: [],
198
- published: true,
199
- variants: otherVariants,
200
- } satisfies Product;
201
-
202
- return result;
203
- }
204
-
205
- /**
206
- * Return true, if the attribute is a defining attribute (ie an option)
207
- * @param attr a variant attribute
208
- * @returns true if the attribute is an option
209
- */
210
- protected isVariantAttributeAnOption(attr: CTAttribute): boolean {
211
- // for now, the assumption is that any variant attribute is a defining attribute (ie an option)
212
- // ideally this should be verified with the product type.
213
- return true;
214
- }
215
-
216
- protected parseVariant(
217
- variant: CTProductVariant,
218
- product: ProductProjection
219
- ): ProductVariant {
220
- const identifier = {
221
- sku: variant.sku!,
222
- } satisfies ProductVariantIdentifier;
223
-
224
- const images = [
225
- ...(variant.images || []).map((img) =>
226
- ImageSchema.parse({
227
- sourceUrl: img.url,
228
- altText: img.label || '',
229
- width: img.dimensions?.w,
230
- height: img.dimensions?.h,
231
- } satisfies Image)
232
- ),
233
- ];
234
-
235
- const options =
236
- (variant.attributes ?? [])
237
- .filter((attr) => this.isVariantAttributeAnOption(attr))
238
- .map((attr) => {
239
- const attrVal = this.parseAttributeValue(attr);
240
- const optionIdentifier: ProductOptionIdentifier = {
241
- key: attr.name,
242
- };
243
- const option: ProductVariantOption = {
244
- identifier: optionIdentifier,
245
- name: attr.name,
246
- value: {
247
- identifier: {
248
- key: attrVal.value,
249
- option: optionIdentifier,
250
- },
251
- label: attrVal.label,
252
- },
253
- };
254
- return option;
255
- }) || [];
256
-
257
- const result = {
258
- identifier,
259
- images,
260
- barcode: '',
261
- ean: '',
262
- gtin: '',
263
- name: product.name[this.context.languageContext.locale],
264
- options,
265
- upc: '',
266
- } satisfies ProductVariant;
267
-
268
- return result;
269
- }
270
-
271
- protected parseAttribute(attr: CTAttribute): ProductAttribute {
272
- const result = ProductAttributeSchema.parse({
273
- identifier: ProductAttributeIdentifierSchema.parse({
274
- key: attr.name,
275
- } satisfies Partial<ProductAttributeIdentifier>),
276
- group: '',
277
- name: attr.name,
278
- values: [this.parseAttributeValue(attr)],
279
- } satisfies Partial<ProductAttribute>);
280
-
281
- return result;
282
- }
283
-
284
- protected parseAttributeValue(attr: CTAttribute): ProductAttributeValue {
285
- let attrValue = '';
286
- if (attr.value && Array.isArray(attr.value)) {
287
- attrValue = attr.value[0];
288
- }
289
-
290
- if (attr.value && typeof attr.value === 'object') {
291
- if (this.context.languageContext.locale in attr.value) {
292
- attrValue = attr.value[this.context.languageContext.locale];
293
- } else {
294
- attrValue = '-';
295
- }
296
- }
297
-
298
- if (typeof attr.value === 'string') {
299
- attrValue = attr.value;
300
- }
301
-
302
- const attrVal = ProductAttributeValueSchema.parse({
303
- identifier: ProductAttributeValueIdentifierSchema.parse({
304
- key: attrValue,
305
- } satisfies Partial<ProductAttributeValueIdentifier>),
306
- value: String(attrValue),
307
- label: String(attrValue),
308
- } satisfies Partial<ProductAttributeValue>);
309
-
310
- return attrVal;
311
- }
312
133
  }