@reactionary/source 0.0.41 → 0.0.48

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 (125) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/.env-template +8 -5
  3. package/.vscode/settings.json +5 -0
  4. package/README.md +41 -0
  5. package/core/package.json +3 -1
  6. package/core/src/cache/cache.interface.ts +14 -18
  7. package/core/src/cache/memory-cache.ts +56 -0
  8. package/core/src/cache/noop-cache.ts +5 -23
  9. package/core/src/cache/redis-cache.ts +28 -38
  10. package/core/src/client/client-builder.ts +3 -3
  11. package/core/src/client/client.ts +11 -9
  12. package/core/src/decorators/reactionary.decorator.ts +80 -8
  13. package/core/src/index.ts +5 -29
  14. package/core/src/initialization.ts +43 -0
  15. package/core/src/providers/analytics.provider.ts +1 -1
  16. package/core/src/providers/base.provider.ts +61 -25
  17. package/core/src/providers/cart-payment.provider.ts +57 -0
  18. package/core/src/providers/cart.provider.ts +131 -8
  19. package/core/src/providers/category.provider.ts +9 -9
  20. package/core/src/providers/identity.provider.ts +8 -7
  21. package/core/src/providers/index.ts +12 -0
  22. package/core/src/providers/inventory.provider.ts +4 -4
  23. package/core/src/providers/price.provider.ts +7 -7
  24. package/core/src/providers/product.provider.ts +17 -5
  25. package/core/src/providers/profile.provider.ts +22 -0
  26. package/core/src/providers/search.provider.ts +4 -4
  27. package/core/src/providers/store.provider.ts +14 -0
  28. package/core/src/schemas/capabilities.schema.ts +3 -1
  29. package/core/src/schemas/models/analytics.model.ts +1 -1
  30. package/core/src/schemas/models/cart.model.ts +16 -3
  31. package/core/src/schemas/models/identifiers.model.ts +90 -22
  32. package/core/src/schemas/models/identity.model.ts +23 -7
  33. package/core/src/schemas/models/index.ts +15 -0
  34. package/core/src/schemas/models/payment.model.ts +41 -0
  35. package/core/src/schemas/models/profile.model.ts +35 -0
  36. package/core/src/schemas/models/shipping-method.model.ts +14 -0
  37. package/core/src/schemas/models/store.model.ts +11 -0
  38. package/core/src/schemas/mutations/cart-payment.mutation.ts +21 -0
  39. package/core/src/schemas/mutations/cart.mutation.ts +62 -3
  40. package/core/src/schemas/mutations/identity.mutation.ts +8 -1
  41. package/core/src/schemas/mutations/index.ts +10 -0
  42. package/core/src/schemas/mutations/profile.mutation.ts +9 -0
  43. package/core/src/schemas/queries/cart-payment.query.ts +12 -0
  44. package/core/src/schemas/queries/cart.query.ts +1 -1
  45. package/core/src/schemas/queries/identity.query.ts +1 -1
  46. package/core/src/schemas/queries/index.ts +3 -0
  47. package/core/src/schemas/queries/inventory.query.ts +4 -12
  48. package/core/src/schemas/queries/price.query.ts +1 -1
  49. package/core/src/schemas/queries/profile.query.ts +7 -0
  50. package/core/src/schemas/queries/search.query.ts +1 -1
  51. package/core/src/schemas/queries/store.query.ts +11 -0
  52. package/core/src/schemas/session.schema.ts +31 -6
  53. package/eslint.config.mjs +7 -0
  54. package/examples/next/src/app/page.tsx +4 -12
  55. package/examples/node/package.json +1 -3
  56. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +9 -8
  57. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +4 -3
  58. package/examples/node/src/basic/basic-node-setup.spec.ts +4 -5
  59. package/nx.json +1 -0
  60. package/otel/src/metrics.ts +2 -1
  61. package/otel/src/provider-instrumentation.ts +2 -1
  62. package/otel/src/tracer.ts +7 -6
  63. package/otel/src/trpc-middleware.ts +3 -2
  64. package/package.json +2 -1
  65. package/providers/algolia/src/core/initialize.ts +4 -3
  66. package/providers/algolia/src/providers/product.provider.ts +15 -13
  67. package/providers/algolia/src/providers/search.provider.ts +9 -9
  68. package/providers/algolia/src/schema/capabilities.schema.ts +1 -1
  69. package/providers/algolia/src/test/search.provider.spec.ts +10 -10
  70. package/providers/algolia/src/test/test-utils.ts +9 -4
  71. package/providers/commercetools/README.md +27 -0
  72. package/providers/commercetools/src/core/client.ts +164 -117
  73. package/providers/commercetools/src/core/initialize.ts +24 -14
  74. package/providers/commercetools/src/providers/cart-payment.provider.ts +193 -0
  75. package/providers/commercetools/src/providers/cart.provider.ts +402 -125
  76. package/providers/commercetools/src/providers/category.provider.ts +35 -35
  77. package/providers/commercetools/src/providers/identity.provider.ts +23 -75
  78. package/providers/commercetools/src/providers/index.ts +2 -0
  79. package/providers/commercetools/src/providers/inventory.provider.ts +69 -40
  80. package/providers/commercetools/src/providers/price.provider.ts +79 -47
  81. package/providers/commercetools/src/providers/product.provider.ts +36 -30
  82. package/providers/commercetools/src/providers/profile.provider.ts +61 -0
  83. package/providers/commercetools/src/providers/search.provider.ts +16 -12
  84. package/providers/commercetools/src/providers/store.provider.ts +78 -0
  85. package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
  86. package/providers/commercetools/src/schema/commercetools.schema.ts +18 -0
  87. package/providers/commercetools/src/schema/configuration.schema.ts +2 -1
  88. package/providers/commercetools/src/test/cart-payment.provider.spec.ts +145 -0
  89. package/providers/commercetools/src/test/cart.provider.spec.ts +82 -22
  90. package/providers/commercetools/src/test/category.provider.spec.ts +18 -17
  91. package/providers/commercetools/src/test/identity.provider.spec.ts +88 -0
  92. package/providers/commercetools/src/test/inventory.provider.spec.ts +41 -0
  93. package/providers/commercetools/src/test/price.provider.spec.ts +9 -8
  94. package/providers/commercetools/src/test/product.provider.spec.ts +33 -5
  95. package/providers/commercetools/src/test/profile.provider.spec.ts +49 -0
  96. package/providers/commercetools/src/test/search.provider.spec.ts +8 -7
  97. package/providers/commercetools/src/test/store.provider.spec.ts +37 -0
  98. package/providers/commercetools/src/test/test-utils.ts +7 -31
  99. package/providers/fake/src/core/initialize.ts +96 -38
  100. package/providers/fake/src/providers/analytics.provider.ts +6 -5
  101. package/providers/fake/src/providers/cart.provider.ts +66 -19
  102. package/providers/fake/src/providers/category.provider.ts +12 -12
  103. package/providers/fake/src/providers/identity.provider.ts +22 -14
  104. package/providers/fake/src/providers/index.ts +1 -0
  105. package/providers/fake/src/providers/inventory.provider.ts +13 -13
  106. package/providers/fake/src/providers/price.provider.ts +13 -13
  107. package/providers/fake/src/providers/product.provider.ts +13 -10
  108. package/providers/fake/src/providers/search.provider.ts +7 -5
  109. package/providers/fake/src/providers/store.provider.ts +47 -0
  110. package/providers/fake/src/schema/capabilities.schema.ts +4 -1
  111. package/providers/fake/src/test/cart.provider.spec.ts +18 -18
  112. package/providers/fake/src/test/category.provider.spec.ts +55 -37
  113. package/providers/fake/src/test/price.provider.spec.ts +9 -14
  114. package/providers/fake/src/test/product.provider.spec.ts +27 -0
  115. package/providers/fake/src/test/test-utils.ts +2 -28
  116. package/providers/posthog/src/core/initialize.ts +3 -3
  117. package/providers/posthog/src/schema/capabilities.schema.ts +1 -1
  118. package/trpc/src/client.ts +42 -41
  119. package/trpc/src/index.ts +4 -3
  120. package/trpc/src/integration.spec.ts +11 -11
  121. package/trpc/src/server.ts +26 -24
  122. package/trpc/src/test-utils.ts +9 -4
  123. package/trpc/src/types.ts +24 -22
  124. package/core/src/cache/cache-evaluation.interface.ts +0 -19
  125. package/examples/node/src/test-utils.ts +0 -26
@@ -1,15 +1,15 @@
1
1
  import {
2
- Product,
2
+ type Product,
3
+ type ProductQueryById,
4
+ type ProductQueryBySlug,
5
+ type RequestContext,
6
+ type Cache as ReactinaryCache,
3
7
  ProductProvider,
4
- ProductQueryById,
5
- ProductQueryBySlug,
6
- Session,
7
- Cache as ReactinaryCache,
8
+ Reactionary,
8
9
  } from '@reactionary/core';
9
- import z from 'zod';
10
- import { FakeConfiguration } from '../schema/configuration.schema';
10
+ import type z from 'zod';
11
+ import type { FakeConfiguration } from '../schema/configuration.schema';
11
12
  import { base, en, Faker } from '@faker-js/faker';
12
- import { traced } from '@reactionary/otel';
13
13
 
14
14
  export class FakeProductProvider<
15
15
  T extends Product = Product
@@ -22,16 +22,19 @@ export class FakeProductProvider<
22
22
  this.config = config;
23
23
  }
24
24
 
25
+ @Reactionary({})
25
26
  public override async getById(
26
27
  payload: ProductQueryById,
27
- _session: Session
28
+ _reqCtx: RequestContext
28
29
  ): Promise<T> {
29
30
  return this.parseSingle(payload);
30
31
  }
31
32
 
33
+ // FIXME: Should we have a get-by-sku here? Since thats whats coming back on cart items...
34
+
32
35
  public override async getBySlug(
33
36
  payload: ProductQueryBySlug,
34
- _session: Session
37
+ _reqCtx: RequestContext
35
38
  ): Promise<T> {
36
39
  return this.parseSingle(payload);
37
40
  }
@@ -1,13 +1,15 @@
1
1
  import {
2
- SearchProvider,
2
+ SearchProvider
3
+ } from '@reactionary/core';
4
+ import type {
3
5
  SearchResult,
4
6
  SearchResultFacet,
5
7
  SearchResultProduct,
6
8
  Cache as ReactionaryCache,
7
9
  } from '@reactionary/core';
8
- import type { SearchQueryByTerm, Session } from '@reactionary/core';
9
- import z from 'zod';
10
- import { FakeConfiguration } from '../schema/configuration.schema';
10
+ import type { RequestContext, SearchQueryByTerm } from '@reactionary/core';
11
+ import type z from 'zod';
12
+ import type { FakeConfiguration } from '../schema/configuration.schema';
11
13
  import { Faker, en, base } from '@faker-js/faker';
12
14
  import { jitter } from '../utilities/jitter';
13
15
  import { traced } from '@reactionary/otel';
@@ -26,7 +28,7 @@ export class FakeSearchProvider<
26
28
  @traced()
27
29
  public override async queryByTerm(
28
30
  payload: SearchQueryByTerm,
29
- _session: Session
31
+ _reqCtx: RequestContext
30
32
  ): Promise<SearchResult> {
31
33
  await jitter(this.config.jitter.mean, this.config.jitter.deviation);
32
34
 
@@ -0,0 +1,47 @@
1
+ import type {
2
+ Inventory,
3
+ Cache,
4
+ RequestContext,
5
+ StoreQueryByProximity,
6
+ Store,
7
+ } from '@reactionary/core';
8
+ import { InventoryProvider, StoreProvider } from '@reactionary/core';
9
+ import type z from 'zod';
10
+ import type { FakeConfiguration } from '../schema/configuration.schema';
11
+ import { base, en, Faker } from '@faker-js/faker';
12
+
13
+ export class FakeStoreProvider<
14
+ T extends Store = Store
15
+ > extends StoreProvider<T> {
16
+ protected config: FakeConfiguration;
17
+
18
+ constructor(config: FakeConfiguration, schema: z.ZodType<T>, cache: Cache) {
19
+ super(schema, cache);
20
+
21
+ this.config = config;
22
+ }
23
+
24
+ public override async queryByProximity(
25
+ payload: StoreQueryByProximity,
26
+ reqCtx: RequestContext
27
+ ): Promise<T[]> {
28
+ const generator = new Faker({
29
+ seed: 42,
30
+ locale: [en, base],
31
+ });
32
+
33
+ const results = [];
34
+
35
+ for (let i = 0; i < payload.limit; i++) {
36
+ const model = this.newModel();
37
+
38
+ model.name = generator.company.name();
39
+ model.identifier.key = '' + i;
40
+ model.fulfillmentCenter.key = '' + i;
41
+
42
+ results.push(model);
43
+ }
44
+
45
+ return results;
46
+ }
47
+ }
@@ -1,5 +1,5 @@
1
1
  import { CapabilitiesSchema } from "@reactionary/core";
2
- import { z } from 'zod';
2
+ import type { z } from 'zod';
3
3
 
4
4
  export const FakeCapabilitiesSchema = CapabilitiesSchema.pick({
5
5
  product: true,
@@ -7,6 +7,9 @@ export const FakeCapabilitiesSchema = CapabilitiesSchema.pick({
7
7
  identity: true,
8
8
  category: true,
9
9
  cart: true,
10
+ inventory: true,
11
+ store: true,
12
+ price: true,
10
13
  }).partial();
11
14
 
12
15
  export type FakeCapabilities = z.infer<typeof FakeCapabilitiesSchema>;
@@ -1,6 +1,7 @@
1
1
  import 'dotenv/config';
2
- import { CartSchema, CategorySchema, IdentitySchema, NoOpCache, ProductSchema, Session } from '@reactionary/core';
3
- import { createAnonymousTestSession, getFakerTestConfiguration } from './test-utils';
2
+ import type { RequestContext} from '@reactionary/core';
3
+ import { CartSchema, IdentitySchema, NoOpCache, createInitialRequestContext } from '@reactionary/core';
4
+ import { getFakerTestConfiguration } from './test-utils';
4
5
  import { FakeCartProvider } from '../providers/cart.provider';
5
6
  import { FakeIdentityProvider } from '../providers';
6
7
 
@@ -13,7 +14,7 @@ const testData = {
13
14
  describe('Fake Cart Provider', () => {
14
15
  let provider: FakeCartProvider;
15
16
  let identityProvider: FakeIdentityProvider;
16
- let session: Session;
17
+ let reqCtx: RequestContext;
17
18
 
18
19
  beforeAll( () => {
19
20
  provider = new FakeCartProvider(getFakerTestConfiguration(), CartSchema, new NoOpCache());
@@ -21,14 +22,14 @@ describe('Fake Cart Provider', () => {
21
22
  });
22
23
 
23
24
  beforeEach( () => {
24
- session = createAnonymousTestSession()
25
+ reqCtx = createInitialRequestContext()
25
26
  });
26
27
 
27
28
  describe('anonymous sessions', () => {
28
29
  it('should be able to get an empty cart', async () => {
29
30
  const cart = await provider.getById({
30
31
  cart: { key: '' },
31
- }, session);
32
+ }, reqCtx);
32
33
 
33
34
  expect(cart.identifier.key).toBeFalsy();
34
35
  expect(cart.items.length).toBe(0);
@@ -39,22 +40,22 @@ describe('Fake Cart Provider', () => {
39
40
  it('should be able to add an item to a cart', async () => {
40
41
  const cart = await provider.add({
41
42
  cart: { key: '' },
42
- product: {
43
+ sku: {
43
44
  key: testData.skuWithoutTiers,
44
45
  },
45
46
  quantity: 1
46
- }, session);
47
+ }, reqCtx);
47
48
 
48
49
  expect(cart.identifier.key).toBeDefined();
49
50
  expect(cart.items.length).toBe(1);
50
- expect(cart.items[0].product.key).toBe(testData.skuWithoutTiers);
51
+ expect(cart.items[0].sku.key).toBe(testData.skuWithoutTiers);
51
52
  expect(cart.items[0].quantity).toBe(1);
52
53
 
53
54
  expect(cart.items[0].price.totalPrice.value).toBeGreaterThan(0);
54
- expect(cart.items[0].price.totalPrice.currency).toBe(session.languageContext.currencyCode);
55
+ expect(cart.items[0].price.totalPrice.currency).toBe(reqCtx.languageContext.currencyCode);
55
56
 
56
57
  expect(cart.price.grandTotal.value).toBeGreaterThan(0);
57
- expect(cart.price.grandTotal.currency).toBe(session.languageContext.currencyCode);
58
+ expect(cart.price.grandTotal.currency).toBe(reqCtx.languageContext.currencyCode);
58
59
 
59
60
  expect(cart.price.grandTotal.value).toBe(cart.items[0].price.totalPrice.value);
60
61
 
@@ -68,23 +69,22 @@ describe('Fake Cart Provider', () => {
68
69
 
69
70
  const cart = await provider.add({
70
71
  cart: { key: '' },
71
- product: {
72
+ sku: {
72
73
  key: testData.skuWithoutTiers,
73
74
  },
74
75
  quantity: 1
75
- }, session);
76
+ }, reqCtx);
76
77
 
77
78
  const updatedCart = await provider.changeQuantity({
78
79
  cart: cart.identifier,
79
80
  item: cart.items[0].identifier,
80
81
  quantity: 3
81
- }, session);
82
+ }, reqCtx);
82
83
 
83
84
  expect(updatedCart.identifier.key).toBe(cart.identifier.key);
84
85
  expect(updatedCart.items.length).toBe(1);
85
- expect(updatedCart.items[0].product.key).toBe(testData.skuWithoutTiers);
86
+ expect(updatedCart.items[0].sku.key).toBe(testData.skuWithoutTiers);
86
87
  expect(updatedCart.items[0].quantity).toBe(3);
87
-
88
88
  expect(updatedCart.items[0].price.totalPrice.value).toBe(cart.items[0].price.totalPrice.value * 3);
89
89
  expect(updatedCart.items[0].price.unitPrice.value).toBe(cart.items[0].price.unitPrice.value);
90
90
 
@@ -95,16 +95,16 @@ describe('Fake Cart Provider', () => {
95
95
 
96
96
  const cart = await provider.add({
97
97
  cart: { key: '' },
98
- product: {
98
+ sku: {
99
99
  key: testData.skuWithoutTiers,
100
100
  },
101
101
  quantity: 1
102
- }, session);
102
+ }, reqCtx);
103
103
 
104
104
  const updatedCart = await provider.remove({
105
105
  cart: cart.identifier,
106
106
  item: cart.items[0].identifier,
107
- }, session);
107
+ }, reqCtx);
108
108
  expect(updatedCart.identifier.key).toBe(cart.identifier.key);
109
109
  expect(updatedCart.items.length).toBe(0);
110
110
  });
@@ -1,25 +1,29 @@
1
- import 'dotenv/config'
2
-
3
-
4
- import { CategorySchema, NoOpCache, Session } from '@reactionary/core';
1
+ import 'dotenv/config';
2
+ import type { RequestContext } from '@reactionary/core';
3
+ import { CategorySchema, MemoryCache , createInitialRequestContext,} from '@reactionary/core';
5
4
  import { FakeCategoryProvider } from '../providers';
6
- import { createAnonymousTestSession, getFakerTestConfiguration } from './test-utils';
5
+ import { getFakerTestConfiguration } from './test-utils';
6
+
7
7
  describe('Faker Category Provider', () => {
8
8
  let provider: FakeCategoryProvider;
9
- let session: Session;
10
-
11
-
12
-
13
- beforeAll( () => {
14
- provider = new FakeCategoryProvider(getFakerTestConfiguration(), CategorySchema, new NoOpCache());
9
+ let reqCtx: RequestContext;
10
+ const cache = new MemoryCache();
11
+
12
+ beforeAll(() => {
13
+ provider = new FakeCategoryProvider(
14
+ getFakerTestConfiguration(),
15
+ CategorySchema,
16
+ cache
17
+ );
15
18
  });
16
19
 
17
20
  beforeEach( () => {
18
- session = createAnonymousTestSession()
21
+ reqCtx = createInitialRequestContext();
22
+ cache.clear();
19
23
  })
20
24
 
21
25
  it('should be able to get top-categories', async () => {
22
- const result = await provider.findTopCategories({ paginationOptions: { pageSize: 10, pageNumber: 1 }}, session);
26
+ const result = await provider.findTopCategories({ paginationOptions: { pageSize: 10, pageNumber: 1 }}, reqCtx);
23
27
 
24
28
  expect(result.items.length).toBeGreaterThan(0);
25
29
  expect(result.items[0].identifier.key).toBe('grocery');
@@ -30,7 +34,7 @@ describe('Faker Category Provider', () => {
30
34
  });
31
35
 
32
36
  it('should be able to get child categories for a category', async () => {
33
- const result = await provider.findChildCategories({ parentId: { key: 'grocery' }, paginationOptions: { pageSize: 10, pageNumber: 1 }}, session);
37
+ const result = await provider.findChildCategories({ parentId: { key: 'grocery' }, paginationOptions: { pageSize: 10, pageNumber: 1 }}, reqCtx);
34
38
 
35
39
  expect(result.items.length).toBeGreaterThan(0);
36
40
  expect(result.items[0].identifier.key).toBe('grocery-0');
@@ -38,12 +42,10 @@ describe('Faker Category Provider', () => {
38
42
 
39
43
  expect(result.items[1].identifier.key).toBe('grocery-1');
40
44
  expect(result.items[1].name).toBe('Grocery-1');
41
-
42
45
  });
43
46
 
44
-
45
47
  it('should be able to get child categories for a category, paged', async () => {
46
- let result = await provider.findChildCategories({ parentId: { key: 'grocery' }, paginationOptions: { pageSize: 1, pageNumber: 1 }}, session);
48
+ let result = await provider.findChildCategories({ parentId: { key: 'grocery' }, paginationOptions: { pageSize: 1, pageNumber: 1 }}, reqCtx);
47
49
 
48
50
  expect(result.items.length).toBeGreaterThan(0);
49
51
  expect(result.items[0].identifier.key).toBe('grocery-0');
@@ -53,7 +55,7 @@ describe('Faker Category Provider', () => {
53
55
  expect(result.pageSize).toBe(1);
54
56
  expect(result.pageNumber).toBe(1);
55
57
 
56
- result = await provider.findChildCategories({ parentId: { key: 'grocery' }, paginationOptions: { pageSize: 1, pageNumber: 2 }}, session);
58
+ result = await provider.findChildCategories({ parentId: { key: 'grocery' }, paginationOptions: { pageSize: 1, pageNumber: 2 }}, reqCtx);
57
59
 
58
60
  expect(result.items.length).toBeGreaterThan(0);
59
61
  expect(result.items[0].identifier.key).toBe('grocery-1');
@@ -64,9 +66,8 @@ describe('Faker Category Provider', () => {
64
66
  expect(result.pageNumber).toBe(2);
65
67
  });
66
68
 
67
-
68
69
  it('can load all breadcrumbs for a category', async () => {
69
- const result = await provider.getBreadcrumbPathToCategory({ id: { key: 'grocery-0-0' } }, session);
70
+ const result = await provider.getBreadcrumbPathToCategory({ id: { key: 'grocery-0-0' } }, reqCtx);
70
71
 
71
72
  expect(result.length).toBeGreaterThan(2);
72
73
  expect(result[0].identifier.key).toBe('grocery');
@@ -80,53 +81,70 @@ describe('Faker Category Provider', () => {
80
81
  expect(result[2].identifier.key).toBe('grocery-0-0');
81
82
  expect(result[2].name).toBe('Grocery-0-0');
82
83
  expect(result[2].slug).toBe('grocery-0-0-slug');
83
-
84
84
  });
85
85
 
86
-
87
86
  it('should be able to get a category by slug', async () => {
88
- const result = await provider.getBySlug({ slug: 'grocery-slug' }, session);
87
+ const result = await provider.getBySlug({ slug: 'grocery-slug' }, reqCtx);
89
88
  expect(result).toBeTruthy();
90
89
  if (result) {
91
90
  expect(result.identifier.key).toBe('grocery');
92
91
  expect(result.name).toBe('Grocery');
93
92
  expect(result.slug).toBe('grocery-slug');
94
93
  expect(result.parentCategory).toBeUndefined();
95
- expect(result.text).not.toBe("");
94
+ expect(result.text).not.toBe('');
96
95
  expect(result.meta.placeholder).toBe(false);
97
96
  }
98
97
  });
99
98
 
100
99
  it('returns null if looking for slug that does not exist', async () => {
101
- const result = await provider.getBySlug({ slug: 'non-existent-slug' }, session);
100
+ const result = await provider.getBySlug({ slug: 'non-existent-slug' }, reqCtx);
102
101
  expect(result).toBeNull();
103
102
  });
104
103
 
105
-
106
-
107
104
  it('should be able to get a category by id', async () => {
108
- const result = await provider.getById({ id: { key: 'grocery'}}, session);
105
+ const result = await provider.getById({ id: { key: 'grocery'}}, reqCtx);
109
106
 
110
107
  expect(result.identifier.key).toBe('grocery');
111
108
  expect(result.name).toBe('Grocery');
112
109
  expect(result.slug).toBe('grocery-slug');
113
110
  expect(result.parentCategory).toBeUndefined();
114
111
 
115
- expect(result.text).not.toBe("");
112
+ expect(result.text).not.toBe('');
116
113
  expect(result.meta.placeholder).toBe(false);
117
-
118
114
  });
119
115
 
120
-
121
-
122
-
123
-
124
116
  it('returns a placeholder if you search for a category that does not exist', async () => {
125
- const result = await provider.getById({ id: { key: 'non-existent-category'}}, session);
117
+ const result = await provider.getById({ id: { key: 'non-existent-category'}}, reqCtx);
126
118
  expect(result.identifier.key).toBe('non-existent-category');
127
119
  expect(result.meta.placeholder).toBe(true);
128
-
129
120
  });
130
121
 
131
-
122
+ describe('caching', () => {
123
+ // TODO: These should probably be elsewhere, since they are really testing the caches and the decorator together.
124
+ // Perhaps as an integration test
125
+
126
+ it('should cache the results for byId', async () => {
127
+ const first = await provider.getById({ id: { key: 'grocery' } }, reqCtx);
128
+ expect(first.meta.cache.hit).toBe(false);
129
+
130
+ const second = await provider.getById(
131
+ { id: { key: 'grocery' } },
132
+ reqCtx
133
+ );
134
+ expect(second.meta.cache.hit).toBe(true);
135
+ });
136
+
137
+ it('can clear a cache entry by dependency id', async () => {
138
+ const first = await provider.getById({ id: { key: 'grocery' } }, reqCtx);
139
+
140
+ const dependencyIds = provider.generateDependencyIdsForModel(first);
141
+ await cache.invalidate(dependencyIds);
142
+
143
+ const second = await provider.getById(
144
+ { id: { key: 'grocery' } },
145
+ reqCtx
146
+ );
147
+ expect(second.meta.cache.hit).toBe(false);
148
+ });
149
+ });
132
150
  });
@@ -1,9 +1,7 @@
1
1
  import 'dotenv/config';
2
-
3
-
4
- import { NoOpCache, PriceSchema, Session } from '@reactionary/core';
5
- import { createAnonymousTestSession, getFakerTestConfiguration } from './test-utils';
6
-
2
+ import type { RequestContext} from '@reactionary/core';
3
+ import { NoOpCache, PriceSchema, createInitialRequestContext } from '@reactionary/core';
4
+ import { getFakerTestConfiguration } from './test-utils';
7
5
  import { FakePriceProvider } from '../providers/price.provider';
8
6
 
9
7
  const testData = {
@@ -11,23 +9,20 @@ const testData = {
11
9
  skuWithTiers: 'GMCT-01-with-tiers'
12
10
  }
13
11
 
14
-
15
12
  describe('Fake Price Provider', () => {
16
13
  let provider: FakePriceProvider;
17
- let session: Session;
18
-
19
-
14
+ let reqCtx: RequestContext;
20
15
 
21
16
  beforeAll( () => {
22
17
  provider = new FakePriceProvider(getFakerTestConfiguration(), PriceSchema, new NoOpCache());
23
18
  });
24
19
 
25
20
  beforeEach( () => {
26
- session = createAnonymousTestSession()
21
+ reqCtx = createInitialRequestContext()
27
22
  })
28
23
 
29
24
  it('should be able to get prices for a product without tiers', async () => {
30
- const result = await provider.getBySKU({ sku: { key: testData.skuWithoutTiers }}, session);
25
+ const result = await provider.getBySKU({ sku: { key: testData.skuWithoutTiers }}, reqCtx);
31
26
 
32
27
  expect(result).toBeTruthy();
33
28
  if (result) {
@@ -39,7 +34,7 @@ describe('Fake Price Provider', () => {
39
34
  });
40
35
 
41
36
  it('should be able to get prices for a product with tiers', async () => {
42
- const result = await provider.getBySKU({ sku: { key: testData.skuWithTiers }}, session);
37
+ const result = await provider.getBySKU({ sku: { key: testData.skuWithTiers }}, reqCtx);
43
38
 
44
39
  expect(result).toBeTruthy();
45
40
  if (result) {
@@ -56,7 +51,7 @@ describe('Fake Price Provider', () => {
56
51
  });
57
52
 
58
53
  it('should return a placeholder price for an unknown SKU', async () => {
59
- const result = await provider.getBySKU({ sku: { key: 'unknown-sku' }}, session);
54
+ const result = await provider.getBySKU({ sku: { key: 'unknown-sku' }}, reqCtx);
60
55
 
61
56
  expect(result).toBeTruthy();
62
57
  if (result) {
@@ -70,7 +65,7 @@ describe('Fake Price Provider', () => {
70
65
 
71
66
  it('can look up multiple prices at once', async () => {
72
67
  const skus = [testData.skuWithTiers, testData.skuWithoutTiers, 'unknown-sku'];
73
- const results = await Promise.all(skus.map( sku => provider.getBySKU({ sku: { key: sku }}, session)));
68
+ const results = await Promise.all(skus.map( sku => provider.getBySKU({ sku: { key: sku }}, reqCtx)));
74
69
 
75
70
  expect(results).toHaveLength(skus.length);
76
71
  expect(results[0].identifier.sku.key).toBe(testData.skuWithTiers);
@@ -0,0 +1,27 @@
1
+ import 'dotenv/config';
2
+
3
+ import type { Session } from '@reactionary/core';
4
+ import { MemoryCache, ProductSchema } from '@reactionary/core';
5
+ import { createAnonymousTestSession, getFakerTestConfiguration } from './test-utils';
6
+ import { FakeProductProvider } from '../providers';
7
+
8
+ describe('Fake Product Provider', () => {
9
+ let provider: FakeProductProvider;
10
+ let session: Session;
11
+
12
+ beforeAll(async () => {
13
+ provider = new FakeProductProvider(getFakerTestConfiguration(), ProductSchema, new MemoryCache());
14
+ });
15
+
16
+ beforeEach( () => {
17
+ session = createAnonymousTestSession()
18
+ })
19
+
20
+ it('should cache repeat product lookups by id', async () => {
21
+ const first = await provider.getById({ id: '1234' }, session);
22
+ expect(first.meta.cache.hit).toBe(false);
23
+
24
+ const second = await provider.getById({ id: '1234' }, session);
25
+ expect(second.meta.cache.hit).toBe(true);
26
+ });
27
+ });
@@ -1,5 +1,4 @@
1
- import { Session } from "@reactionary/core";
2
- import { FakeConfiguration } from "../schema/configuration.schema";
1
+ import type { FakeConfiguration } from '../schema/configuration.schema';
3
2
 
4
3
  export function getFakerTestConfiguration(): FakeConfiguration {
5
4
  return {
@@ -11,32 +10,7 @@ export function getFakerTestConfiguration(): FakeConfiguration {
11
10
  product: 1,
12
11
  search: 1,
13
12
  category: 1,
14
- }
15
- }
16
- }
17
-
18
-
19
- export function createAnonymousTestSession(): Session {
20
- return {
21
- id: 'test-session-id',
22
- identity: {
23
- type: 'Anonymous',
24
- meta: {
25
- cache: { hit: false, key: '' },
26
- placeholder: false,
27
- },
28
- id: '',
29
- token: undefined,
30
- issued: new Date(),
31
- expiry: new Date(new Date().getTime() + 3600 * 1000), // 1 hour from now
32
- },
33
- languageContext: {
34
- locale: 'en-US',
35
- currencyCode: 'USD',
36
- countryCode: 'US',
37
- },
38
- storeIdentifier: {
39
- key: 'the-good-store',
40
13
  },
41
14
  };
42
15
  }
16
+
@@ -1,6 +1,6 @@
1
- import { Client, Cache } from "@reactionary/core";
2
- import { PosthogConfiguration } from "../schema/configuration.schema";
3
- import { PosthogCapabilities } from "../schema/capabilities.schema";
1
+ import type { Client, Cache } from "@reactionary/core";
2
+ import type { PosthogConfiguration } from "../schema/configuration.schema";
3
+ import type { PosthogCapabilities } from "../schema/capabilities.schema";
4
4
 
5
5
  export function withPosthogCapabilities(_configuration: PosthogConfiguration, _capabilities: PosthogCapabilities) {
6
6
  return (_cache: Cache) => {
@@ -1,5 +1,5 @@
1
1
  import { CapabilitiesSchema } from "@reactionary/core";
2
- import { z } from 'zod';
2
+ import type { z } from 'zod';
3
3
 
4
4
  export const PosthogCapabilitiesSchema = CapabilitiesSchema.pick({
5
5
  analytics: true