@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,235 +1,320 @@
1
- import { CategoryProvider, createPaginatedResponseSchema } from "@reactionary/core";
2
- import type { CategoryQueryById, CategoryQueryBySlug, CategoryQueryForBreadcrumb, CategoryQueryForChildCategories, CategoryQueryForTopCategories, RequestContext , Cache, Category} from "@reactionary/core";
3
- import type z from "zod";
4
- import type { CommercetoolsConfiguration } from "../schema/configuration.schema.js";
5
- import { CommercetoolsClient } from "../core/client.js";
6
- import type { ByProjectKeyCategoriesRequestBuilder, CategoryPagedQueryResponse, Category as CTCategory } from "@commercetools/platform-sdk";
7
-
8
- export class CommercetoolsCategoryProvider<
9
- T extends Category = Category,
10
- > extends CategoryProvider<T> {
11
-
1
+ import {
2
+ CategoryPaginatedResultSchema,
3
+ CategoryProvider,
4
+ CategoryQueryByIdSchema,
5
+ CategoryQueryBySlugSchema,
6
+ CategoryQueryForBreadcrumbSchema,
7
+ CategoryQueryForChildCategoriesSchema,
8
+ CategoryQueryForTopCategoriesSchema,
9
+ CategorySchema,
10
+ Reactionary,
11
+ success,
12
+ error,
13
+ } from '@reactionary/core';
14
+ import type {
15
+ CategoryQueryById,
16
+ CategoryQueryBySlug,
17
+ CategoryQueryForBreadcrumb,
18
+ CategoryQueryForChildCategories,
19
+ CategoryQueryForTopCategories,
20
+ RequestContext,
21
+ Cache,
22
+ Category,
23
+ CategoryPaginatedResult,
24
+ CategoryIdentifier,
25
+ Result,
26
+ NotFoundError,
27
+ } from '@reactionary/core';
28
+ import z from 'zod';
29
+ import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
30
+ import type {
31
+ ByProjectKeyCategoriesRequestBuilder,
32
+ CategoryPagedQueryResponse,
33
+ Category as CTCategory,
34
+ } from '@commercetools/platform-sdk';
35
+ import type { CommercetoolsAPI } from '../core/client.js';
36
+
37
+ export class CommercetoolsCategoryProvider extends CategoryProvider {
12
38
  protected config: CommercetoolsConfiguration;
39
+ protected commercetools: CommercetoolsAPI;
13
40
 
14
- constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, cache: Cache) {
15
- super(schema, cache);
41
+ constructor(
42
+ config: CommercetoolsConfiguration,
43
+ cache: Cache,
44
+ context: RequestContext,
45
+ commercetools: CommercetoolsAPI
46
+ ) {
47
+ super(cache, context);
16
48
 
17
49
  this.config = config;
50
+ this.commercetools = commercetools;
18
51
  }
19
52
 
20
- protected async getClient(reqCtx: RequestContext): Promise<ByProjectKeyCategoriesRequestBuilder> {
21
- const client = await new CommercetoolsClient(this.config).getClient(reqCtx);
22
- return client.withProjectKey({ projectKey: this.config.projectKey }).categories();
53
+ protected async getClient(): Promise<ByProjectKeyCategoriesRequestBuilder> {
54
+ const client = await this.commercetools.getClient();
55
+ return client
56
+ .withProjectKey({ projectKey: this.config.projectKey })
57
+ .categories();
23
58
  }
24
59
 
25
60
  /**
26
61
  * Look it up by the category ID (key in commercetools), and if not there, return a placeholder.
27
- * @param id
28
- * @param session
29
- * @returns
30
62
  */
31
- public override async getById(payload: CategoryQueryById, reqCtx: RequestContext): Promise<T> {
32
- const client = await this.getClient(reqCtx);
63
+ @Reactionary({
64
+ inputSchema: CategoryQueryByIdSchema,
65
+ outputSchema: CategorySchema,
66
+ })
67
+ public override async getById(
68
+ payload: CategoryQueryById
69
+ ): Promise<Result<Category, NotFoundError>> {
70
+ const client = await this.getClient();
33
71
  try {
34
- const response = await client.withKey({ key: payload.id.key }).get().execute();
35
- return this.parseSingle(response.body, reqCtx);
36
- } catch (error) {
37
- const dummyCategory = this.newModel();
38
- dummyCategory.meta.placeholder = true;
39
- dummyCategory.identifier = { key: payload.id.key };
40
- return dummyCategory;
72
+ const response = await client
73
+ .withKey({ key: payload.id.key })
74
+ .get()
75
+ .execute();
76
+ return success(this.parseSingle(response.body));
77
+ } catch (err) {
78
+ return error<NotFoundError>({
79
+ type: 'NotFound',
80
+ identifier: payload.id,
81
+ });
41
82
  }
42
83
  }
43
84
 
44
85
  /**
45
86
  * Resolve the category by slug, in the users current locale.
46
- * @param slug
47
- * @param session
48
- * @returns
49
87
  */
50
- public override async getBySlug(payload: CategoryQueryBySlug, reqCtx: RequestContext): Promise<T | null> {
51
- const client = await this.getClient(reqCtx);
88
+ @Reactionary({
89
+ inputSchema: CategoryQueryBySlugSchema,
90
+ outputSchema: CategorySchema,
91
+ })
92
+ public override async getBySlug(
93
+ payload: CategoryQueryBySlug
94
+ ): Promise<Result<Category, NotFoundError>> {
95
+ const client = await this.getClient();
52
96
  try {
53
- const response = await client.get({
54
- queryArgs: {
55
- where: `slug(${reqCtx.languageContext.locale}=:slug)`,
56
- 'var.slug': payload.slug,
57
- storeProjection: reqCtx.storeIdentifier.key ,
58
- limit: 1,
59
- withTotal: false,
60
- }
61
- }).execute();
97
+ const response = await client
98
+ .get({
99
+ queryArgs: {
100
+ where: `slug(${this.context.languageContext.locale}=:slug)`,
101
+ 'var.slug': payload.slug,
102
+ storeProjection: this.context.storeIdentifier.key,
103
+ limit: 1,
104
+ withTotal: false,
105
+ },
106
+ })
107
+ .execute();
62
108
  if (response.body.results.length === 0) {
63
- return null;
109
+ return error<NotFoundError>({
110
+ type: 'NotFound',
111
+ identifier: payload.slug,
112
+ });
64
113
  }
65
- return this.parseSingle(response.body.results[0], reqCtx);
66
- } catch (error) {
114
+ return success(this.parseSingle(response.body.results[0]));
115
+ } catch (err) {
67
116
  console.error(`Error fetching category by slug:`, error);
68
- return null;
117
+ return error<NotFoundError>({
118
+ type: 'NotFound',
119
+ identifier: payload.slug,
120
+ });
69
121
  }
70
122
  }
71
123
 
72
124
  /**
73
125
  * Returns the breadcrumb path to the category, i.e. all parents up to the root.
74
126
  * The returned order is from root to leaf.
75
- * @param id
76
- * @param session
77
- * @returns
78
127
  */
79
- public override async getBreadcrumbPathToCategory(payload: CategoryQueryForBreadcrumb, reqCtx: RequestContext): Promise<T[]> {
80
- const client = await this.getClient(reqCtx);
81
- const path: T[] = [];
128
+ @Reactionary({
129
+ inputSchema: CategoryQueryForBreadcrumbSchema,
130
+ outputSchema: z.array(CategorySchema),
131
+ })
132
+ public override async getBreadcrumbPathToCategory(
133
+ payload: CategoryQueryForBreadcrumb
134
+ ): Promise<Result<Category[]>> {
135
+ const client = await this.getClient();
136
+ const path = new Array<Category>();
82
137
  try {
83
- const response = await client.withKey({ key: payload.id.key }).get({
84
- queryArgs: {
85
- expand: 'ancestors[*]'
86
- }
87
- }).execute();
138
+ const response = await client
139
+ .withKey({ key: payload.id.key })
140
+ .get({
141
+ queryArgs: {
142
+ expand: 'ancestors[*]',
143
+ },
144
+ })
145
+ .execute();
88
146
 
89
- const category = this.parseSingle(response.body, reqCtx);
90
- for(const anc of response.body.ancestors || []) {
147
+ const category = this.parseSingle(response.body);
148
+ for (const anc of response.body.ancestors || []) {
91
149
  if (anc.obj) {
92
- const parsedAnc = this.parseSingle(anc.obj, reqCtx);
150
+ const parsedAnc = this.parseSingle(anc.obj);
93
151
  path.push(parsedAnc);
94
152
  }
95
- };
153
+ }
96
154
  path.push(category);
97
155
  } catch (error) {
98
- console.error(`Error fetching category path for ${payload.id.key}:`, error);
156
+ console.error(
157
+ `Error fetching category path for ${payload.id.key}:`,
158
+ error
159
+ );
99
160
  }
100
- return path;
161
+ return success(path);
101
162
  }
102
163
 
103
-
104
164
  /**
105
165
  * Returns a page of child categories for the given parent category ID.
106
166
  * You must provide the pagination options to control the size of the result set.
107
167
  * If you expect your frontend will load many many categories, consider adding a "load more" button, or lazy load the next page.
108
168
  *
109
169
  * This is cached by ID + page number + page size + locale + store
110
- * @param id
111
- * @param paginationOptions
112
- * @param session
113
- * @returns
114
170
  */
115
- public override async findChildCategories(payload: CategoryQueryForChildCategories, reqCtx: RequestContext) {
116
-
171
+ @Reactionary({
172
+ inputSchema: CategoryQueryForChildCategoriesSchema,
173
+ outputSchema: CategoryPaginatedResultSchema,
174
+ })
175
+ public override async findChildCategories(
176
+ payload: CategoryQueryForChildCategories
177
+ ) {
117
178
  // ok, so for Commercetools we can't actually query by the parents key, so we have to first resolve the key to an ID, then query by that.
118
179
  // This is a bit of a pain, but we can cache the result of the first lookup for a short period to mitigate it.
119
180
 
120
- const client = await this.getClient(reqCtx);
181
+ const client = await this.getClient();
121
182
 
122
183
  try {
123
- const parentCategory = await client.withKey({ key: payload.parentId.key }).get().execute();
184
+ const parentCategory = await client
185
+ .withKey({ key: payload.parentId.key })
186
+ .get()
187
+ .execute();
124
188
 
125
189
  // it is true, we could just do a withKey and get the children directly, but that would not be paginated.
126
190
  // So we do it the hard way, and query by parent ID instead.
127
191
  // This also means we can sort the results, which is nice.
128
192
 
129
- const response = await client.get({
193
+ const response = await client
194
+ .get({
130
195
  queryArgs: {
131
196
  where: 'parent(id = :parentId)',
132
197
  'var.parentId': parentCategory.body.id,
133
198
  limit: payload.paginationOptions.pageSize,
134
- offset: (payload.paginationOptions.pageNumber - 1) * payload.paginationOptions.pageSize,
199
+ offset:
200
+ (payload.paginationOptions.pageNumber - 1) *
201
+ payload.paginationOptions.pageSize,
135
202
  sort: 'orderHint asc',
136
- storeProjection: reqCtx.storeIdentifier.key ,
203
+ storeProjection: this.context.storeIdentifier.key,
137
204
  },
138
205
  })
139
206
  .execute();
140
207
 
141
- const result = this.parsePaginatedResult(response.body, reqCtx);
142
- result.meta = {
143
- cache: { hit: false, key: this.generateCacheKeyPaginatedResult('children-of-' + payload.parentId.key, result, reqCtx) },
144
- placeholder: false
145
- };
146
- return result;
208
+ const result = this.parsePaginatedResult(response.body);
209
+ return success(result);
147
210
  } catch (error) {
148
- console.error(`Error fetching category path for ${payload.parentId.key}:`, error);
211
+ console.error(
212
+ `Error fetching category path for ${payload.parentId.key}:`,
213
+ error
214
+ );
149
215
  }
150
- return createPaginatedResponseSchema(this.schema).parse({});
151
- }
152
216
 
153
- public override async findTopCategories(payload: CategoryQueryForTopCategories, reqCtx: RequestContext) {
217
+ const empty = {
218
+ items: [],
219
+ pageNumber: payload.paginationOptions.pageNumber,
220
+ pageSize: payload.paginationOptions.pageSize,
221
+ totalCount: 0,
222
+ totalPages: 0,
223
+ } satisfies CategoryPaginatedResult;
154
224
 
155
- const client = await this.getClient(reqCtx);
225
+ return success(empty);
226
+ }
227
+
228
+ @Reactionary({
229
+ inputSchema: CategoryQueryForTopCategoriesSchema,
230
+ outputSchema: CategoryPaginatedResultSchema,
231
+ })
232
+ public override async findTopCategories(
233
+ payload: CategoryQueryForTopCategories
234
+ ) {
235
+ const client = await this.getClient();
156
236
  try {
157
- const response = await client.get({
237
+ const response = await client
238
+ .get({
158
239
  queryArgs: {
159
240
  where: 'parent is not defined',
160
241
  limit: payload.paginationOptions.pageSize,
161
- offset: (payload.paginationOptions.pageNumber - 1) * payload.paginationOptions.pageSize,
242
+ offset:
243
+ (payload.paginationOptions.pageNumber - 1) *
244
+ payload.paginationOptions.pageSize,
162
245
  sort: 'orderHint asc',
163
- storeProjection: reqCtx.storeIdentifier.key ,
246
+ storeProjection: this.context.storeIdentifier.key,
164
247
  },
165
248
  })
166
249
  .execute();
167
250
 
168
- const result = this.parsePaginatedResult(response.body, reqCtx);
169
- result.meta = {
170
- cache: { hit: false, key: this.generateCacheKeyPaginatedResult('top', result, reqCtx) },
171
- placeholder: false
172
- };
173
- return result;
251
+ const result = this.parsePaginatedResult(response.body);
252
+ return success(result);
174
253
  } catch (error) {
175
254
  console.error(`Error fetching category top categories:`, error);
176
255
  }
177
- return createPaginatedResponseSchema(this.schema).parse({});
178
- }
179
-
180
256
 
257
+ const empty = {
258
+ items: [],
259
+ pageNumber: payload.paginationOptions.pageNumber,
260
+ pageSize: payload.paginationOptions.pageSize,
261
+ totalCount: 0,
262
+ totalPages: 0,
263
+ } satisfies CategoryPaginatedResult;
181
264
 
265
+ return success(empty);
266
+ }
182
267
 
183
- /**
268
+ /**
184
269
  * Handler for parsing a response from a remote provider and converting it
185
270
  * into the typed domain model.
186
271
  */
187
- protected override parseSingle(_body: unknown, reqCtx: RequestContext): T {
272
+ protected parseSingle(_body: unknown): Category {
188
273
  const body = _body as CTCategory;
189
- const languageContext = reqCtx.languageContext;
190
-
191
- const model = this.newModel();
192
-
193
- model.identifier = { key: body.key! };
194
- model.name = body.name[languageContext.locale] || 'No Name';
195
- model.slug = body.slug ? (body.slug[languageContext.locale] || '') : '';
196
- model.text = body.description ? (body.description[languageContext.locale] || '') : '';
197
-
198
- model.parentCategory = body.parent && body.parent.obj && body.parent.obj?.key ? { key: body.parent.obj.key } : undefined;
199
-
200
- model.images = (body.assets || []).filter(asset => asset.sources.length > 0).filter(x => x.sources[0].contentType?.startsWith('image/')).map( (asset) => {
201
- return {
202
- sourceUrl: asset.sources[0].uri,
203
- altText: asset.description?.[languageContext.locale] || asset.name[languageContext.locale] || '',
204
- height: asset.sources[0].dimensions?.h || 0,
205
- width: asset.sources[0].dimensions?.w || 0,
206
- }
207
- });
208
-
209
- model.meta = {
210
- cache: { hit: false, key: this.generateCacheKeySingle(model.identifier, reqCtx) },
211
- placeholder: false
212
- };
213
-
214
- return this.assert(model);
274
+ const languageContext = this.context.languageContext;
275
+
276
+ const identifier = { key: body.key! } satisfies CategoryIdentifier;
277
+ const model = {
278
+ identifier,
279
+ name: body.name[languageContext.locale] || 'No Name',
280
+ slug: body.slug ? body.slug[languageContext.locale] || '' : '',
281
+ text: body.description
282
+ ? body.description[languageContext.locale] || ''
283
+ : '',
284
+ parentCategory:
285
+ body.parent && body.parent.obj && body.parent.obj?.key
286
+ ? { key: body.parent.obj.key }
287
+ : undefined,
288
+ images: (body.assets || [])
289
+ .filter((asset) => asset.sources.length > 0)
290
+ .filter((x) => x.sources[0].contentType?.startsWith('image/'))
291
+ .map((asset) => {
292
+ return {
293
+ sourceUrl: asset.sources[0].uri,
294
+ altText:
295
+ asset.description?.[languageContext.locale] ||
296
+ asset.name[languageContext.locale] ||
297
+ '',
298
+ height: asset.sources[0].dimensions?.h || 0,
299
+ width: asset.sources[0].dimensions?.w || 0,
300
+ };
301
+ }),
302
+ } satisfies Category;
303
+
304
+ return model;
215
305
  }
216
306
 
217
- protected override parsePaginatedResult(_body: unknown, reqCtx: RequestContext) {
218
- const body = _body as CategoryPagedQueryResponse;
219
-
220
- const items = body.results.map(x => this.parseSingle(x, reqCtx));
221
-
222
- const result = createPaginatedResponseSchema(this.schema).parse({
223
- meta: {
224
- cache: { hit: false, key: 'unknown' },
225
- placeholder: false
226
- },
307
+ protected parsePaginatedResult(_body: unknown) {
308
+ const body = _body as CategoryPagedQueryResponse;
309
+ const items = body.results.map((x) => this.parseSingle(x));
310
+ const result = {
227
311
  pageNumber: Math.floor(body.offset / body.count) + 1,
228
312
  pageSize: body.count,
229
313
  totalCount: body.total || 0,
230
314
  totalPages: Math.ceil((body.total ?? 0) / body.count),
231
- items: items
232
- });
315
+ items: items,
316
+ } satisfies CategoryPaginatedResult;
317
+
233
318
  return result;
234
319
  }
235
320
  }