@reactionary/source 0.0.52 → 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/.env-template +19 -0
  2. package/.github/workflows/pull-request.yml +3 -1
  3. package/.github/workflows/release.yml +9 -0
  4. package/.vscode/extensions.json +0 -2
  5. package/LICENSE +21 -0
  6. package/README.md +175 -23
  7. package/core/package.json +6 -3
  8. package/core/src/cache/cache.interface.ts +1 -0
  9. package/core/src/cache/index.ts +4 -0
  10. package/core/src/cache/memory-cache.ts +30 -2
  11. package/core/src/cache/noop-cache.ts +15 -1
  12. package/core/src/cache/redis-cache.ts +20 -0
  13. package/core/src/client/client-builder.ts +71 -54
  14. package/core/src/client/client.ts +9 -47
  15. package/core/src/client/index.ts +2 -0
  16. package/core/src/decorators/index.ts +1 -0
  17. package/core/src/decorators/reactionary.decorator.ts +203 -34
  18. package/core/src/index.ts +6 -19
  19. package/core/src/initialization.ts +1 -18
  20. package/core/src/metrics/metrics.ts +67 -0
  21. package/core/src/providers/analytics.provider.ts +1 -6
  22. package/core/src/providers/base.provider.ts +5 -69
  23. package/core/src/providers/cart.provider.ts +15 -55
  24. package/core/src/providers/category.provider.ts +7 -11
  25. package/core/src/providers/checkout.provider.ts +17 -15
  26. package/core/src/providers/identity.provider.ts +6 -8
  27. package/core/src/providers/index.ts +2 -1
  28. package/core/src/providers/inventory.provider.ts +15 -5
  29. package/core/src/providers/order-search.provider.ts +29 -0
  30. package/core/src/providers/order.provider.ts +47 -15
  31. package/core/src/providers/price.provider.ts +30 -36
  32. package/core/src/providers/product-search.provider.ts +61 -0
  33. package/core/src/providers/product.provider.ts +71 -12
  34. package/core/src/providers/profile.provider.ts +74 -14
  35. package/core/src/providers/store.provider.ts +3 -5
  36. package/core/src/schemas/capabilities.schema.ts +10 -3
  37. package/core/src/schemas/errors/generic.error.ts +9 -0
  38. package/core/src/schemas/errors/index.ts +4 -0
  39. package/core/src/schemas/errors/invalid-input.error.ts +9 -0
  40. package/core/src/schemas/errors/invalid-output.error.ts +9 -0
  41. package/core/src/schemas/errors/not-found.error.ts +9 -0
  42. package/core/src/schemas/index.ts +7 -0
  43. package/core/src/schemas/models/analytics.model.ts +2 -1
  44. package/core/src/schemas/models/base.model.ts +6 -24
  45. package/core/src/schemas/models/cart.model.ts +5 -8
  46. package/core/src/schemas/models/category.model.ts +4 -9
  47. package/core/src/schemas/models/checkout.model.ts +6 -7
  48. package/core/src/schemas/models/cost.model.ts +4 -3
  49. package/core/src/schemas/models/currency.model.ts +2 -1
  50. package/core/src/schemas/models/identifiers.model.ts +106 -62
  51. package/core/src/schemas/models/identity.model.ts +10 -19
  52. package/core/src/schemas/models/index.ts +2 -1
  53. package/core/src/schemas/models/inventory.model.ts +8 -5
  54. package/core/src/schemas/models/order-search.model.ts +28 -0
  55. package/core/src/schemas/models/order.model.ts +20 -26
  56. package/core/src/schemas/models/payment.model.ts +14 -17
  57. package/core/src/schemas/models/price.model.ts +11 -11
  58. package/core/src/schemas/models/product-search.model.ts +42 -0
  59. package/core/src/schemas/models/product.model.ts +64 -22
  60. package/core/src/schemas/models/profile.model.ts +19 -22
  61. package/core/src/schemas/models/shipping-method.model.ts +24 -29
  62. package/core/src/schemas/models/store.model.ts +9 -5
  63. package/core/src/schemas/mutations/analytics.mutation.ts +8 -7
  64. package/core/src/schemas/mutations/base.mutation.ts +2 -1
  65. package/core/src/schemas/mutations/cart.mutation.ts +33 -33
  66. package/core/src/schemas/mutations/checkout.mutation.ts +23 -30
  67. package/core/src/schemas/mutations/identity.mutation.ts +4 -3
  68. package/core/src/schemas/mutations/profile.mutation.ts +38 -3
  69. package/core/src/schemas/queries/base.query.ts +2 -1
  70. package/core/src/schemas/queries/cart.query.ts +3 -3
  71. package/core/src/schemas/queries/category.query.ts +18 -18
  72. package/core/src/schemas/queries/checkout.query.ts +7 -9
  73. package/core/src/schemas/queries/identity.query.ts +2 -1
  74. package/core/src/schemas/queries/index.ts +2 -1
  75. package/core/src/schemas/queries/inventory.query.ts +5 -5
  76. package/core/src/schemas/queries/order-search.query.ts +10 -0
  77. package/core/src/schemas/queries/order.query.ts +3 -2
  78. package/core/src/schemas/queries/price.query.ts +10 -4
  79. package/core/src/schemas/queries/product-search.query.ts +16 -0
  80. package/core/src/schemas/queries/product.query.ts +13 -6
  81. package/core/src/schemas/queries/profile.query.ts +5 -2
  82. package/core/src/schemas/queries/store.query.ts +6 -5
  83. package/core/src/schemas/result.ts +107 -0
  84. package/core/src/schemas/session.schema.ts +4 -4
  85. package/core/src/test/reactionary.decorator.spec.ts +249 -0
  86. package/core/src/zod-utils.ts +19 -0
  87. package/core/tsconfig.json +1 -1
  88. package/core/tsconfig.spec.json +2 -26
  89. package/core/vitest.config.ts +14 -0
  90. package/documentation/1-purpose.md +114 -0
  91. package/documentation/2-getting-started.md +229 -0
  92. package/documentation/3-querying-and-changing-data.md +74 -0
  93. package/documentation/4-product-data.md +107 -0
  94. package/documentation/5-cart-and-checkout.md +211 -0
  95. package/documentation/6-product-search.md +143 -0
  96. package/documentation/7-marketing.md +3 -0
  97. package/eslint.config.mjs +1 -0
  98. package/examples/node/eslint.config.mjs +1 -4
  99. package/examples/node/package.json +10 -3
  100. package/examples/node/project.json +4 -1
  101. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +22 -23
  102. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +15 -11
  103. package/examples/node/src/basic/basic-node-setup.spec.ts +44 -28
  104. package/examples/node/src/basic/client-creation.spec.ts +53 -0
  105. package/examples/node/src/capabilities/cart.spec.ts +255 -0
  106. package/examples/node/src/capabilities/category.spec.ts +193 -0
  107. package/examples/node/src/capabilities/checkout.spec.ts +341 -0
  108. package/examples/node/src/capabilities/identity.spec.ts +93 -0
  109. package/examples/node/src/capabilities/inventory.spec.ts +66 -0
  110. package/examples/node/src/capabilities/order-search.spec.ts +265 -0
  111. package/examples/node/src/capabilities/order.spec.ts +91 -0
  112. package/examples/node/src/capabilities/price.spec.ts +51 -0
  113. package/examples/node/src/capabilities/product-search.spec.ts +293 -0
  114. package/examples/node/src/capabilities/product.spec.ts +122 -0
  115. package/examples/node/src/capabilities/profile.spec.ts +316 -0
  116. package/examples/node/src/capabilities/store.spec.ts +26 -0
  117. package/examples/node/src/utils.ts +147 -0
  118. package/examples/node/tsconfig.json +9 -12
  119. package/examples/node/tsconfig.lib.json +1 -2
  120. package/examples/node/tsconfig.spec.json +2 -14
  121. package/examples/node/vitest.config.ts +14 -0
  122. package/migrations.json +22 -5
  123. package/nx.json +8 -47
  124. package/package.json +24 -96
  125. package/providers/algolia/README.md +39 -2
  126. package/providers/algolia/package.json +2 -1
  127. package/providers/algolia/src/core/initialize.ts +7 -14
  128. package/providers/algolia/src/index.ts +2 -4
  129. package/providers/algolia/src/providers/index.ts +1 -0
  130. package/providers/algolia/src/providers/product-search.provider.ts +241 -0
  131. package/providers/algolia/src/schema/capabilities.schema.ts +2 -3
  132. package/providers/algolia/src/schema/index.ts +3 -0
  133. package/providers/algolia/src/schema/search.schema.ts +8 -8
  134. package/providers/algolia/tsconfig.json +1 -1
  135. package/providers/algolia/tsconfig.lib.json +1 -1
  136. package/providers/algolia/tsconfig.spec.json +2 -14
  137. package/providers/algolia/vitest.config.ts +14 -0
  138. package/providers/commercetools/README.md +30 -3
  139. package/providers/commercetools/package.json +2 -1
  140. package/providers/commercetools/src/core/client.ts +178 -99
  141. package/providers/commercetools/src/core/initialize.ts +130 -74
  142. package/providers/commercetools/src/core/token-cache.ts +45 -0
  143. package/providers/commercetools/src/index.ts +3 -2
  144. package/providers/commercetools/src/providers/cart.provider.ts +281 -341
  145. package/providers/commercetools/src/providers/category.provider.ts +223 -138
  146. package/providers/commercetools/src/providers/checkout.provider.ts +631 -449
  147. package/providers/commercetools/src/providers/identity.provider.ts +50 -29
  148. package/providers/commercetools/src/providers/index.ts +2 -2
  149. package/providers/commercetools/src/providers/inventory.provider.ts +76 -74
  150. package/providers/commercetools/src/providers/order-search.provider.ts +220 -0
  151. package/providers/commercetools/src/providers/order.provider.ts +96 -61
  152. package/providers/commercetools/src/providers/price.provider.ts +147 -117
  153. package/providers/commercetools/src/providers/product-search.provider.ts +528 -0
  154. package/providers/commercetools/src/providers/product.provider.ts +249 -74
  155. package/providers/commercetools/src/providers/profile.provider.ts +445 -28
  156. package/providers/commercetools/src/providers/store.provider.ts +54 -40
  157. package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
  158. package/providers/commercetools/src/schema/commercetools.schema.ts +17 -3
  159. package/providers/commercetools/src/schema/configuration.schema.ts +1 -0
  160. package/providers/commercetools/src/schema/session.schema.ts +7 -0
  161. package/providers/commercetools/src/test/caching.spec.ts +82 -0
  162. package/providers/commercetools/src/test/identity.spec.ts +109 -0
  163. package/providers/commercetools/src/test/test-utils.ts +21 -19
  164. package/providers/commercetools/tsconfig.json +1 -1
  165. package/providers/commercetools/tsconfig.lib.json +1 -1
  166. package/providers/commercetools/tsconfig.spec.json +2 -14
  167. package/providers/commercetools/vitest.config.ts +15 -0
  168. package/providers/fake/README.md +20 -4
  169. package/providers/fake/package.json +2 -1
  170. package/providers/fake/src/core/initialize.ts +47 -49
  171. package/providers/fake/src/providers/analytics.provider.ts +5 -7
  172. package/providers/fake/src/providers/cart.provider.ts +163 -92
  173. package/providers/fake/src/providers/category.provider.ts +78 -50
  174. package/providers/fake/src/providers/checkout.provider.ts +254 -0
  175. package/providers/fake/src/providers/identity.provider.ts +57 -65
  176. package/providers/fake/src/providers/index.ts +6 -2
  177. package/providers/fake/src/providers/inventory.provider.ts +40 -36
  178. package/providers/fake/src/providers/order-search.provider.ts +78 -0
  179. package/providers/fake/src/providers/order.provider.ts +106 -0
  180. package/providers/fake/src/providers/price.provider.ts +93 -41
  181. package/providers/fake/src/providers/product-search.provider.ts +206 -0
  182. package/providers/fake/src/providers/product.provider.ts +56 -41
  183. package/providers/fake/src/providers/profile.provider.ts +147 -0
  184. package/providers/fake/src/providers/store.provider.ts +30 -20
  185. package/providers/fake/src/schema/capabilities.schema.ts +5 -1
  186. package/providers/fake/src/test/cart.provider.spec.ts +59 -80
  187. package/providers/fake/src/test/category.provider.spec.ts +145 -87
  188. package/providers/fake/src/test/checkout.provider.spec.ts +222 -0
  189. package/providers/fake/src/test/order-search.provider.spec.ts +50 -0
  190. package/providers/fake/src/test/order.provider.spec.ts +44 -0
  191. package/providers/fake/src/test/price.provider.spec.ts +50 -45
  192. package/providers/fake/src/test/product.provider.spec.ts +15 -7
  193. package/providers/fake/src/test/profile.provider.spec.ts +167 -0
  194. package/providers/fake/tsconfig.json +1 -1
  195. package/providers/fake/tsconfig.lib.json +1 -1
  196. package/providers/fake/tsconfig.spec.json +2 -12
  197. package/providers/fake/vitest.config.ts +14 -0
  198. package/providers/medusa/README.md +30 -0
  199. package/providers/medusa/TESTING.md +98 -0
  200. package/providers/medusa/eslint.config.mjs +19 -0
  201. package/providers/medusa/package.json +22 -0
  202. package/providers/medusa/project.json +34 -0
  203. package/providers/medusa/src/core/client.ts +370 -0
  204. package/providers/medusa/src/core/initialize.ts +78 -0
  205. package/providers/medusa/src/index.ts +13 -0
  206. package/providers/medusa/src/providers/cart.provider.ts +575 -0
  207. package/providers/medusa/src/providers/category.provider.ts +247 -0
  208. package/providers/medusa/src/providers/checkout.provider.ts +636 -0
  209. package/providers/medusa/src/providers/identity.provider.ts +137 -0
  210. package/providers/medusa/src/providers/inventory.provider.ts +173 -0
  211. package/providers/medusa/src/providers/order-search.provider.ts +202 -0
  212. package/providers/medusa/src/providers/order.provider.ts +226 -0
  213. package/providers/medusa/src/providers/price.provider.ts +140 -0
  214. package/providers/medusa/src/providers/product-search.provider.ts +243 -0
  215. package/providers/medusa/src/providers/product.provider.ts +261 -0
  216. package/providers/medusa/src/providers/profile.provider.ts +392 -0
  217. package/providers/medusa/src/schema/capabilities.schema.ts +18 -0
  218. package/providers/medusa/src/schema/configuration.schema.ts +11 -0
  219. package/providers/medusa/src/schema/medusa.schema.ts +31 -0
  220. package/providers/medusa/src/test/cart.provider.spec.ts +240 -0
  221. package/providers/medusa/src/test/category.provider.spec.ts +231 -0
  222. package/providers/medusa/src/test/checkout.spec.ts +349 -0
  223. package/providers/medusa/src/test/identity.provider.spec.ts +122 -0
  224. package/providers/medusa/src/test/inventory.provider.spec.ts +88 -0
  225. package/providers/medusa/src/test/large-cart.provider.spec.ts +103 -0
  226. package/providers/medusa/src/test/price.provider.spec.ts +104 -0
  227. package/providers/medusa/src/test/product.provider.spec.ts +146 -0
  228. package/providers/medusa/src/test/search.provider.spec.ts +203 -0
  229. package/providers/medusa/src/test/test-utils.ts +13 -0
  230. package/providers/medusa/src/utils/medusa-helpers.ts +89 -0
  231. package/providers/medusa/tsconfig.json +21 -0
  232. package/providers/medusa/tsconfig.lib.json +9 -0
  233. package/providers/medusa/tsconfig.spec.json +4 -0
  234. package/providers/medusa/vitest.config.ts +15 -0
  235. package/providers/meilisearch/README.md +48 -0
  236. package/providers/meilisearch/eslint.config.mjs +22 -0
  237. package/providers/meilisearch/package.json +13 -0
  238. package/providers/meilisearch/project.json +34 -0
  239. package/providers/meilisearch/src/core/initialize.ts +21 -0
  240. package/providers/meilisearch/src/index.ts +6 -0
  241. package/providers/meilisearch/src/providers/index.ts +1 -0
  242. package/providers/meilisearch/src/providers/order-search.provider.ts +222 -0
  243. package/providers/meilisearch/src/providers/product-search.provider.ts +251 -0
  244. package/providers/meilisearch/src/schema/capabilities.schema.ts +10 -0
  245. package/providers/meilisearch/src/schema/configuration.schema.ts +11 -0
  246. package/providers/meilisearch/src/schema/index.ts +3 -0
  247. package/providers/meilisearch/src/schema/search.schema.ts +14 -0
  248. package/providers/meilisearch/tsconfig.json +24 -0
  249. package/providers/meilisearch/tsconfig.lib.json +10 -0
  250. package/providers/meilisearch/tsconfig.spec.json +4 -0
  251. package/providers/meilisearch/vitest.config.ts +14 -0
  252. package/providers/posthog/package.json +2 -1
  253. package/providers/posthog/tsconfig.json +1 -1
  254. package/tsconfig.base.json +5 -0
  255. package/vitest.config.ts +10 -0
  256. package/core/src/providers/search.provider.ts +0 -18
  257. package/core/src/schemas/models/search.model.ts +0 -36
  258. package/core/src/schemas/queries/search.query.ts +0 -9
  259. package/examples/next/.swcrc +0 -30
  260. package/examples/next/eslint.config.mjs +0 -21
  261. package/examples/next/index.d.ts +0 -6
  262. package/examples/next/next-env.d.ts +0 -5
  263. package/examples/next/next.config.js +0 -31
  264. package/examples/next/project.json +0 -9
  265. package/examples/next/public/.gitkeep +0 -0
  266. package/examples/next/public/favicon.ico +0 -0
  267. package/examples/next/src/app/global.css +0 -0
  268. package/examples/next/src/app/layout.tsx +0 -18
  269. package/examples/next/src/app/page.module.scss +0 -2
  270. package/examples/next/src/app/page.tsx +0 -47
  271. package/examples/next/src/instrumentation.ts +0 -9
  272. package/examples/next/tsconfig.json +0 -44
  273. package/examples/node/jest.config.ts +0 -10
  274. package/jest.config.ts +0 -6
  275. package/jest.preset.js +0 -3
  276. package/providers/algolia/jest.config.ts +0 -10
  277. package/providers/algolia/src/providers/product.provider.ts +0 -66
  278. package/providers/algolia/src/providers/search.provider.ts +0 -106
  279. package/providers/algolia/src/test/search.provider.spec.ts +0 -91
  280. package/providers/commercetools/jest.config.cjs +0 -10
  281. package/providers/commercetools/src/providers/search.provider.ts +0 -96
  282. package/providers/commercetools/src/test/cart.provider.spec.ts +0 -199
  283. package/providers/commercetools/src/test/category.provider.spec.ts +0 -168
  284. package/providers/commercetools/src/test/checkout.provider.spec.ts +0 -312
  285. package/providers/commercetools/src/test/identity.provider.spec.ts +0 -88
  286. package/providers/commercetools/src/test/inventory.provider.spec.ts +0 -41
  287. package/providers/commercetools/src/test/price.provider.spec.ts +0 -81
  288. package/providers/commercetools/src/test/product.provider.spec.ts +0 -80
  289. package/providers/commercetools/src/test/profile.provider.spec.ts +0 -49
  290. package/providers/commercetools/src/test/search.provider.spec.ts +0 -61
  291. package/providers/commercetools/src/test/store.provider.spec.ts +0 -37
  292. package/providers/fake/jest.config.cjs +0 -10
  293. package/providers/fake/src/providers/search.provider.ts +0 -132
@@ -1,74 +1,135 @@
1
1
  import {
2
- ProductProvider
2
+ ImageSchema,
3
+ ProductAttributeIdentifierSchema,
4
+ ProductAttributeSchema,
5
+ ProductAttributeValueIdentifierSchema,
6
+ ProductAttributeValueSchema,
7
+ ProductProvider,
8
+ ProductQueryByIdSchema,
9
+ ProductQueryBySKUSchema,
10
+ ProductQueryBySlugSchema,
11
+ ProductSchema,
12
+ Reactionary,
13
+ success,
14
+ error,
3
15
  } from '@reactionary/core';
4
- import { CommercetoolsClient } from '../core/client.js';
5
- import type { z } from 'zod';
6
16
  import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
7
- import type { ProductProjection } from '@commercetools/platform-sdk';
8
- import type { Product, ProductQueryById, ProductQueryBySKU, ProductQueryBySlug, RequestContext } from '@reactionary/core';
9
- import type { Cache } from '@reactionary/core';
17
+ 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
+ ProductQueryById,
26
+ ProductQueryBySKU,
27
+ ProductQueryBySlug,
28
+ ProductVariantIdentifier,
29
+ RequestContext,
30
+ ProductAttribute,
31
+ ProductAttributeIdentifier,
32
+ ProductAttributeValue,
33
+ ProductAttributeValueIdentifier,
34
+ ProductIdentifier,
35
+ ProductVariantOption,
36
+ ProductOptionIdentifier,
37
+ Result,
38
+ } from '@reactionary/core';
39
+ import type { Cache, Image } from '@reactionary/core';
40
+ import type { CommercetoolsAPI } from '../core/client.js';
41
+ import type { NotFoundError } from '@reactionary/core';
10
42
 
11
- export class CommercetoolsProductProvider<
12
- T extends Product = Product
13
- > extends ProductProvider<T> {
43
+ export class CommercetoolsProductProvider extends ProductProvider {
14
44
  protected config: CommercetoolsConfiguration;
45
+ protected commercetools: CommercetoolsAPI;
15
46
 
16
- constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, cache: Cache) {
17
- super(schema, cache);
47
+ constructor(
48
+ config: CommercetoolsConfiguration,
49
+ cache: Cache,
50
+ context: RequestContext,
51
+ commercetools: CommercetoolsAPI
52
+ ) {
53
+ super(cache, context);
18
54
 
19
55
  this.config = config;
56
+ this.commercetools = commercetools;
20
57
  }
21
58
 
22
- protected async getClient(reqCtx: RequestContext) {
23
-
24
- const client = await new CommercetoolsClient(this.config).getClient(reqCtx);
25
- return client.withProjectKey({ projectKey: this.config.projectKey }).productProjections();
59
+ protected async getClient() {
60
+ const client = await this.commercetools.getClient();
61
+ return client
62
+ .withProjectKey({ projectKey: this.config.projectKey })
63
+ .productProjections();
26
64
  }
27
65
 
66
+ @Reactionary({
67
+ inputSchema: ProductQueryByIdSchema,
68
+ outputSchema: ProductSchema,
69
+ cache: true,
70
+ cacheTimeToLiveInSeconds: 300,
71
+ currencyDependentCaching: false,
72
+ localeDependentCaching: true
73
+ })
28
74
  public override async getById(
29
- payload: ProductQueryById,
30
- reqCtx: RequestContext
31
- ): Promise<T> {
32
- const client = await this.getClient(reqCtx);
33
-
34
- try {
35
- const remote = await client
36
- .withId({ ID: payload.id })
37
- .get()
38
- .execute();
39
-
40
- return this.parseSingle(remote.body, reqCtx);
41
- } catch(error) {
42
- return this.createEmptyProduct(payload.id);
43
- }
75
+ payload: ProductQueryById
76
+ ): Promise<Result<Product>> {
77
+ const client = await this.getClient();
78
+ const remote = await client
79
+ .withKey({ key: payload.identifier.key })
80
+ .get()
81
+ .execute();
82
+ const value = this.parseSingle(remote.body);
83
+
84
+ return success(value);
44
85
  }
45
86
 
87
+ @Reactionary({
88
+ inputSchema: ProductQueryBySlugSchema,
89
+ outputSchema: ProductSchema,
90
+ cache: true,
91
+ cacheTimeToLiveInSeconds: 300,
92
+ currencyDependentCaching: false,
93
+ localeDependentCaching: true
94
+ })
46
95
  public override async getBySlug(
47
- payload: ProductQueryBySlug,
48
- reqCtx: RequestContext
49
- ): Promise<T | null> {
50
- const client = await this.getClient(reqCtx);
96
+ payload: ProductQueryBySlug
97
+ ): Promise<Result<Product, NotFoundError>> {
98
+ const client = await this.getClient();
51
99
 
52
100
  const remote = await client
53
101
  .get({
54
102
  queryArgs: {
55
- where: 'slug(en-US = :slug)',
56
- 'var.slug': payload.slug
57
- }
103
+ // FIXME: Hardcoded locale
104
+ where: 'slug(en = :slug)',
105
+ 'var.slug': payload.slug,
106
+ },
58
107
  })
59
108
  .execute();
60
109
 
61
110
  if (remote.body.count === 0) {
62
- return null;
111
+ return error<NotFoundError>({
112
+ type: 'NotFound',
113
+ identifier: payload.slug,
114
+ });
63
115
  }
64
- return this.parseSingle(remote.body.results[0], reqCtx);
116
+ const result = this.parseSingle(remote.body.results[0]);
117
+
118
+ return success(result);
65
119
  }
66
120
 
121
+ @Reactionary({
122
+ inputSchema: ProductQueryBySKUSchema,
123
+ outputSchema: ProductSchema,
124
+ cache: true,
125
+ cacheTimeToLiveInSeconds: 300,
126
+ currencyDependentCaching: false,
127
+ localeDependentCaching: true
128
+ })
67
129
  public override async getBySKU(
68
- payload: ProductQueryBySKU,
69
- reqCtx: RequestContext
70
- ): Promise<T> {
71
- const client = await this.getClient(reqCtx);
130
+ payload: ProductQueryBySKU
131
+ ): Promise<Result<Product>> {
132
+ const client = await this.getClient();
72
133
 
73
134
  const remote = await client
74
135
  .get({
@@ -76,53 +137,167 @@ export class CommercetoolsProductProvider<
76
137
  staged: false,
77
138
  limit: 1,
78
139
  where: 'variants(sku in (:skus)) OR (masterVariant(sku in (:skus))) ',
79
- 'var.skus': [payload].map(p => p.sku.key),
80
- }
140
+ 'var.skus': [payload].map((p) => p.variant.sku),
141
+ },
81
142
  })
82
143
  .execute();
83
144
 
84
- return this.parseSingle(remote.body.results[0], reqCtx);
85
- }
86
-
87
-
88
- protected override parseSingle(dataIn: unknown, reqCtx: RequestContext): T {
89
- const data = dataIn as ProductProjection;
90
- const base = this.newModel();
145
+ const result = this.parseSingle(remote.body.results[0]);
91
146
 
147
+ return success(result);
148
+ }
92
149
 
93
- base.identifier = { key: data.id };
94
- base.name = data.name[reqCtx.languageContext.locale];
95
- base.slug = data.slug[reqCtx.languageContext.locale];
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];
96
154
 
155
+ let description = '';
97
156
  if (data.description) {
98
- base.description = data.description[reqCtx.languageContext.locale];
99
- }
100
-
101
- if (data.masterVariant.images && data.masterVariant.images.length > 0) {
102
- base.image = data.masterVariant.images[0].url;
157
+ description = data.description[this.context.languageContext.locale];
103
158
  }
104
159
 
105
- base.images = [];
106
- base.attributes = [];
107
- base.skus = [];
160
+ const variantLevelAttributes =
161
+ data.masterVariant.attributes?.map((x) => this.parseAttribute(x)) || [];
162
+ const productLevelAttributes =
163
+ data.attributes.map((x) => this.parseAttribute(x)) || [];
164
+ const sharedAttributes = [
165
+ ...productLevelAttributes,
166
+ ...variantLevelAttributes,
167
+ ];
168
+ const mainVariant = this.parseVariant(data.masterVariant, data);
108
169
 
109
- const variants = [data.masterVariant, ...data.variants];
110
- for (const variant of variants) {
111
- if (variant.sku) {
112
- base.skus.push({
113
- identifier: { key: variant.sku },
114
- });
170
+ const otherVariants = [];
171
+ for (const variant of data.variants || []) {
172
+ if (variant.id !== data.masterVariant.id) {
173
+ otherVariants.push(this.parseVariant(variant, data));
115
174
  }
116
175
  }
117
176
 
118
- base.meta = {
119
- cache: { hit: false, key: this.generateCacheKeySingle(base.identifier, reqCtx) },
120
- placeholder: false
121
- };
177
+ const result = {
178
+ identifier,
179
+ name,
180
+ slug,
181
+ description,
182
+ sharedAttributes,
183
+ mainVariant,
184
+ brand: '',
185
+ longDescription: '',
186
+ manufacturer: '',
187
+ options: [],
188
+ parentCategories: [],
189
+ published: true,
190
+ variants: otherVariants,
191
+ } satisfies Product;
192
+
193
+ return result;
194
+ }
122
195
 
123
- return this.assert(base);
196
+ /**
197
+ * Return true, if the attribute is a defining attribute (ie an option)
198
+ * @param attr a variant attribute
199
+ * @returns true if the attribute is an option
200
+ */
201
+ protected isVariantAttributeAnOption(attr: CTAttribute): boolean {
202
+ // for now, the assumption is that any variant attribute is a defining attribute (ie an option)
203
+ // ideally this should be verified with the product type.
204
+ return true;
124
205
  }
125
206
 
207
+ protected parseVariant(
208
+ variant: CTProductVariant,
209
+ product: ProductProjection
210
+ ): ProductVariant {
211
+ const identifier = {
212
+ sku: variant.sku!,
213
+ } satisfies ProductVariantIdentifier;
214
+
215
+ const images = [
216
+ ...(variant.images || []).map((img) =>
217
+ ImageSchema.parse({
218
+ sourceUrl: img.url,
219
+ altText: img.label || '',
220
+ width: img.dimensions?.w,
221
+ height: img.dimensions?.h,
222
+ } satisfies Image)
223
+ ),
224
+ ];
126
225
 
226
+ const options =
227
+ (variant.attributes ?? [])
228
+ .filter((attr) => this.isVariantAttributeAnOption(attr))
229
+ .map((attr) => {
230
+ const attrVal = this.parseAttributeValue(attr);
231
+ const optionIdentifier: ProductOptionIdentifier = {
232
+ key: attr.name,
233
+ };
234
+ const option: ProductVariantOption = {
235
+ identifier: optionIdentifier,
236
+ name: attr.name,
237
+ value: {
238
+ identifier: {
239
+ key: attrVal.value,
240
+ option: optionIdentifier,
241
+ },
242
+ label: attrVal.label,
243
+ },
244
+ };
245
+ return option;
246
+ }) || [];
127
247
 
248
+ const result = {
249
+ identifier,
250
+ images,
251
+ barcode: '',
252
+ ean: '',
253
+ gtin: '',
254
+ name: product.name[this.context.languageContext.locale],
255
+ options,
256
+ upc: '',
257
+ } satisfies ProductVariant;
258
+
259
+ return result;
260
+ }
261
+
262
+ protected parseAttribute(attr: CTAttribute): ProductAttribute {
263
+ const result = ProductAttributeSchema.parse({
264
+ identifier: ProductAttributeIdentifierSchema.parse({
265
+ key: attr.name,
266
+ } satisfies Partial<ProductAttributeIdentifier>),
267
+ group: '',
268
+ name: attr.name,
269
+ values: [this.parseAttributeValue(attr)],
270
+ } satisfies Partial<ProductAttribute>);
271
+
272
+ return result;
273
+ }
274
+
275
+ protected parseAttributeValue(attr: CTAttribute): ProductAttributeValue {
276
+ let attrValue = '';
277
+ if (attr.value && Array.isArray(attr.value)) {
278
+ attrValue = attr.value[0];
279
+ }
280
+
281
+ if (attr.value && typeof attr.value === 'object') {
282
+ if (this.context.languageContext.locale in attr.value) {
283
+ attrValue = attr.value[this.context.languageContext.locale];
284
+ } else {
285
+ attrValue = '-';
286
+ }
287
+ }
288
+
289
+ if (typeof attr.value === 'string') {
290
+ attrValue = attr.value;
291
+ }
292
+
293
+ const attrVal = ProductAttributeValueSchema.parse({
294
+ identifier: ProductAttributeValueIdentifierSchema.parse({
295
+ key: attrValue,
296
+ } satisfies Partial<ProductAttributeValueIdentifier>),
297
+ value: String(attrValue),
298
+ label: String(attrValue),
299
+ } satisfies Partial<ProductAttributeValue>);
300
+
301
+ return attrVal;
302
+ }
128
303
  }