@reactionary/source 0.0.39 → 0.0.41
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.
- package/core/package.json +1 -1
- package/core/src/decorators/reactionary.decorator.ts +16 -0
- package/core/src/index.ts +2 -0
- package/core/src/schemas/capabilities.schema.ts +1 -1
- package/core/src/schemas/models/base.model.ts +5 -5
- package/core/src/schemas/models/cart.model.ts +1 -1
- package/core/src/schemas/models/identifiers.model.ts +12 -12
- package/core/src/schemas/models/price.model.ts +1 -1
- package/core/src/schemas/models/product.model.ts +2 -2
- package/core/src/schemas/models/search.model.ts +3 -3
- package/core/src/schemas/mutations/base.mutation.ts +1 -1
- package/core/src/schemas/mutations/cart.mutation.ts +6 -6
- package/core/src/schemas/mutations/inventory.mutation.ts +0 -4
- package/core/src/schemas/mutations/price.mutation.ts +0 -4
- package/core/src/schemas/mutations/product.mutation.ts +0 -4
- package/core/src/schemas/mutations/search.mutation.ts +0 -4
- package/core/src/schemas/queries/analytics.query.ts +0 -4
- package/core/src/schemas/queries/base.query.ts +1 -1
- package/core/src/schemas/queries/cart.query.ts +0 -1
- package/core/src/schemas/queries/inventory.query.ts +0 -4
- package/examples/next/src/app/page.tsx +20 -15
- package/examples/node/package.json +3 -1
- package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +13 -5
- package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +11 -1
- package/examples/node/src/basic/basic-node-setup.spec.ts +7 -3
- package/examples/node/src/test-utils.ts +26 -0
- package/otel/src/trace-decorator.ts +6 -5
- package/package.json +5 -4
- package/providers/algolia/package.json +1 -1
- package/providers/algolia/src/schema/configuration.schema.ts +1 -1
- package/providers/algolia/src/test/search.provider.spec.ts +29 -20
- package/providers/algolia/src/test/test-utils.ts +26 -0
- package/providers/commercetools/package.json +2 -2
- package/providers/commercetools/src/core/client.ts +26 -2
- package/providers/commercetools/src/core/initialize.ts +32 -6
- package/providers/commercetools/src/providers/cart.provider.ts +6 -4
- package/providers/commercetools/src/providers/category.provider.ts +2 -2
- package/providers/commercetools/src/providers/price.provider.ts +2 -1
- package/providers/commercetools/src/providers/search.provider.ts +33 -12
- package/providers/commercetools/src/schema/configuration.schema.ts +1 -1
- package/providers/commercetools/src/test/category.provider.spec.ts +1 -14
- package/providers/fake/package.json +1 -1
- package/providers/fake/src/core/initialize.ts +6 -8
- package/providers/fake/src/schema/configuration.schema.ts +3 -3
- package/providers/fake/src/test/category.provider.spec.ts +1 -3
- package/providers/posthog/package.json +1 -1
- package/providers/posthog/src/schema/configuration.schema.ts +1 -1
- package/trpc/package.json +1 -1
- package/trpc/src/integration.spec.ts +24 -23
- package/trpc/src/test-utils.ts +26 -0
- package/trpc/src/transparent-client.spec.ts +22 -26
- package/providers/algolia/src/test/product.provider.spec.ts +0 -18
|
@@ -1,20 +1,27 @@
|
|
|
1
|
-
import { SearchResultSchema } from '@reactionary/core';
|
|
1
|
+
import { NoOpCache, SearchResultSchema } from '@reactionary/core';
|
|
2
2
|
import { AlgoliaSearchProvider } from '../providers/search.provider';
|
|
3
|
+
import { createAnonymousTestSession } from './test-utils';
|
|
3
4
|
|
|
4
5
|
describe('Algolia Search Provider', () => {
|
|
5
|
-
const provider = new AlgoliaSearchProvider(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const provider = new AlgoliaSearchProvider(
|
|
7
|
+
{
|
|
8
|
+
apiKey: process.env['ALGOLIA_API_KEY'] || '',
|
|
9
|
+
appId: process.env['ALGOLIA_APP_ID'] || '',
|
|
10
|
+
indexName: process.env['ALGOLIA_INDEX'] || '',
|
|
11
|
+
},
|
|
12
|
+
SearchResultSchema,
|
|
13
|
+
new NoOpCache()
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const session = createAnonymousTestSession();
|
|
10
17
|
|
|
11
18
|
it('should be able to get a result by term', async () => {
|
|
12
|
-
const result = await provider.
|
|
19
|
+
const result = await provider.queryByTerm({ search: {
|
|
13
20
|
term: 'glass',
|
|
14
21
|
page: 0,
|
|
15
22
|
pageSize: 20,
|
|
16
23
|
facets: [],
|
|
17
|
-
});
|
|
24
|
+
}}, session);
|
|
18
25
|
|
|
19
26
|
expect(result.products.length).toBeGreaterThan(0);
|
|
20
27
|
expect(result.facets.length).toBe(2);
|
|
@@ -23,18 +30,19 @@ describe('Algolia Search Provider', () => {
|
|
|
23
30
|
});
|
|
24
31
|
|
|
25
32
|
it('should be able to paginate', async () => {
|
|
26
|
-
const firstPage = await provider.
|
|
33
|
+
const firstPage = await provider.queryByTerm({ search: {
|
|
27
34
|
term: 'glass',
|
|
28
35
|
page: 0,
|
|
29
36
|
pageSize: 20,
|
|
30
37
|
facets: [],
|
|
31
|
-
});
|
|
32
|
-
|
|
38
|
+
}}, session);
|
|
39
|
+
|
|
40
|
+
const secondPage = await provider.queryByTerm({ search: {
|
|
33
41
|
term: 'glass',
|
|
34
42
|
page: 1,
|
|
35
43
|
pageSize: 20,
|
|
36
44
|
facets: [],
|
|
37
|
-
});
|
|
45
|
+
}}, session);
|
|
38
46
|
|
|
39
47
|
expect(firstPage.identifier.page).toBe(0);
|
|
40
48
|
expect(secondPage.identifier.page).toBe(1);
|
|
@@ -44,18 +52,18 @@ describe('Algolia Search Provider', () => {
|
|
|
44
52
|
});
|
|
45
53
|
|
|
46
54
|
it('should be able to change page size', async () => {
|
|
47
|
-
const smallPage = await provider.
|
|
55
|
+
const smallPage = await provider.queryByTerm({ search: {
|
|
48
56
|
term: 'glass',
|
|
49
57
|
page: 0,
|
|
50
58
|
pageSize: 2,
|
|
51
59
|
facets: [],
|
|
52
|
-
});
|
|
53
|
-
const largePage = await provider.
|
|
60
|
+
}}, session);
|
|
61
|
+
const largePage = await provider.queryByTerm({ search: {
|
|
54
62
|
term: 'glass',
|
|
55
63
|
page: 0,
|
|
56
64
|
pageSize: 30,
|
|
57
65
|
facets: [],
|
|
58
|
-
});
|
|
66
|
+
}}, session);
|
|
59
67
|
|
|
60
68
|
expect(smallPage.products.length).toBe(2);
|
|
61
69
|
expect(smallPage.identifier.pageSize).toBe(2);
|
|
@@ -64,18 +72,19 @@ describe('Algolia Search Provider', () => {
|
|
|
64
72
|
});
|
|
65
73
|
|
|
66
74
|
it('should be able to apply facets', async () => {
|
|
67
|
-
const initial = await provider.
|
|
75
|
+
const initial = await provider.queryByTerm({ search: {
|
|
68
76
|
term: 'glass',
|
|
69
77
|
page: 0,
|
|
70
78
|
pageSize: 2,
|
|
71
79
|
facets: [],
|
|
72
|
-
});
|
|
73
|
-
|
|
80
|
+
}}, session);
|
|
81
|
+
|
|
82
|
+
const filtered = await provider.queryByTerm({ search: {
|
|
74
83
|
term: 'glass',
|
|
75
84
|
page: 0,
|
|
76
85
|
pageSize: 2,
|
|
77
86
|
facets: [initial.facets[0].values[0].identifier],
|
|
78
|
-
});
|
|
87
|
+
}}, session);
|
|
79
88
|
|
|
80
89
|
expect(initial.pages).toBeGreaterThan(filtered.pages);
|
|
81
90
|
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Session } from "@reactionary/core";
|
|
2
|
+
|
|
3
|
+
export function createAnonymousTestSession(): Session {
|
|
4
|
+
return {
|
|
5
|
+
id: 'test-session-id',
|
|
6
|
+
identity: {
|
|
7
|
+
type: 'Anonymous',
|
|
8
|
+
meta: {
|
|
9
|
+
cache: { hit: false, key: '' },
|
|
10
|
+
placeholder: false,
|
|
11
|
+
},
|
|
12
|
+
id: '',
|
|
13
|
+
token: undefined,
|
|
14
|
+
issued: new Date(),
|
|
15
|
+
expiry: new Date(new Date().getTime() + 3600 * 1000), // 1 hour from now
|
|
16
|
+
},
|
|
17
|
+
languageContext: {
|
|
18
|
+
locale: 'en-US',
|
|
19
|
+
currencyCode: 'USD',
|
|
20
|
+
countryCode: 'US',
|
|
21
|
+
},
|
|
22
|
+
storeIdentifier: {
|
|
23
|
+
key: 'the-good-store',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@reactionary/core": "0.0.1",
|
|
8
8
|
"@reactionary/otel": "0.0.1",
|
|
9
|
-
"zod": "4.
|
|
10
|
-
"@commercetools/ts-client": "^
|
|
9
|
+
"zod": "4.1.9",
|
|
10
|
+
"@commercetools/ts-client": "^4.2.1",
|
|
11
11
|
"@commercetools/platform-sdk": "^8.8.0"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -2,7 +2,26 @@ import { ClientBuilder } from '@commercetools/ts-client';
|
|
|
2
2
|
import { createApiBuilderFromCtpClient } from '@commercetools/platform-sdk';
|
|
3
3
|
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
4
4
|
|
|
5
|
-
const ANONYMOUS_SCOPES = [
|
|
5
|
+
const ANONYMOUS_SCOPES = [
|
|
6
|
+
'view_published_products',
|
|
7
|
+
'manage_shopping_lists',
|
|
8
|
+
'view_shipping_methods',
|
|
9
|
+
'manage_customers',
|
|
10
|
+
'view_product_selections',
|
|
11
|
+
'view_categories',
|
|
12
|
+
'view_project_settings',
|
|
13
|
+
'manage_order_edits',
|
|
14
|
+
'view_sessions',
|
|
15
|
+
'view_standalone_prices',
|
|
16
|
+
'manage_orders',
|
|
17
|
+
'view_tax_categories',
|
|
18
|
+
'view_cart_discounts',
|
|
19
|
+
'view_discount_codes',
|
|
20
|
+
'create_anonymous_token',
|
|
21
|
+
'manage_sessions',
|
|
22
|
+
'view_products',
|
|
23
|
+
'view_types',
|
|
24
|
+
];
|
|
6
25
|
const GUEST_SCOPES = [...ANONYMOUS_SCOPES];
|
|
7
26
|
const REGISTERED_SCOPES = [...GUEST_SCOPES];
|
|
8
27
|
|
|
@@ -119,7 +138,10 @@ export class CommercetoolsClient {
|
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
protected createClientWithToken(token: string) {
|
|
122
|
-
const builder = this.createBaseClientBuilder().withExistingTokenFlow(
|
|
141
|
+
const builder = this.createBaseClientBuilder().withExistingTokenFlow(
|
|
142
|
+
`Bearer ${token}`,
|
|
143
|
+
{ force: true }
|
|
144
|
+
);
|
|
123
145
|
|
|
124
146
|
return createApiBuilderFromCtpClient(builder.build());
|
|
125
147
|
}
|
|
@@ -146,6 +168,8 @@ export class CommercetoolsClient {
|
|
|
146
168
|
httpClient: fetch,
|
|
147
169
|
});
|
|
148
170
|
|
|
171
|
+
// CT's telemetry module is currently broken and consequently not included in the above (createTelemetryMiddleware)
|
|
172
|
+
|
|
149
173
|
return builder;
|
|
150
174
|
}
|
|
151
175
|
}
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
CartSchema,
|
|
3
|
+
IdentitySchema,
|
|
4
|
+
InventorySchema,
|
|
5
|
+
PriceSchema,
|
|
6
|
+
ProductSchema,
|
|
7
|
+
SearchResultSchema,
|
|
8
|
+
Cache,
|
|
9
|
+
CategorySchema,
|
|
10
|
+
ProductProvider,
|
|
11
|
+
SearchProvider,
|
|
12
|
+
IdentityProvider,
|
|
13
|
+
CartProvider,
|
|
14
|
+
InventoryProvider,
|
|
15
|
+
PriceProvider,
|
|
16
|
+
CategoryProvider
|
|
17
|
+
} from "@reactionary/core";
|
|
2
18
|
import { CommercetoolsCapabilities } from "../schema/capabilities.schema";
|
|
3
19
|
import { CommercetoolsSearchProvider } from "../providers/search.provider";
|
|
4
20
|
import { CommercetoolsProductProvider } from '../providers/product.provider';
|
|
@@ -9,12 +25,21 @@ import { CommercetoolsInventoryProvider } from "../providers/inventory.provider"
|
|
|
9
25
|
import { CommercetoolsPriceProvider } from "../providers/price.provider";
|
|
10
26
|
import { CommercetoolsCategoryProvider } from "../providers/category.provider";
|
|
11
27
|
|
|
12
|
-
|
|
28
|
+
type CommercetoolsClient<T extends CommercetoolsCapabilities> =
|
|
29
|
+
(T['cart'] extends true ? { cart: CartProvider } : object) &
|
|
30
|
+
(T['product'] extends true ? { product: ProductProvider } : object) &
|
|
31
|
+
(T['search'] extends true ? { search: SearchProvider } : object) &
|
|
32
|
+
(T['identity'] extends true ? { identity: IdentityProvider } : object) &
|
|
33
|
+
(T['category'] extends true ? { category: CategoryProvider } : object) &
|
|
34
|
+
(T['inventory'] extends true ? { inventory: InventoryProvider } : object) &
|
|
35
|
+
(T['price'] extends true ? { price: PriceProvider } : object);
|
|
36
|
+
|
|
37
|
+
export function withCommercetoolsCapabilities<T extends CommercetoolsCapabilities>(
|
|
13
38
|
configuration: CommercetoolsConfiguration,
|
|
14
|
-
capabilities:
|
|
39
|
+
capabilities: T
|
|
15
40
|
) {
|
|
16
|
-
return (cache: Cache) => {
|
|
17
|
-
const client:
|
|
41
|
+
return (cache: Cache): CommercetoolsClient<T> => {
|
|
42
|
+
const client: any = {};
|
|
18
43
|
|
|
19
44
|
if (capabilities.product) {
|
|
20
45
|
client.product = new CommercetoolsProductProvider(configuration, ProductSchema, cache);
|
|
@@ -39,8 +64,9 @@ export function withCommercetoolsCapabilities(
|
|
|
39
64
|
if (capabilities.price) {
|
|
40
65
|
client.price = new CommercetoolsPriceProvider(configuration, PriceSchema, cache);
|
|
41
66
|
}
|
|
67
|
+
|
|
42
68
|
if (capabilities.category) {
|
|
43
|
-
|
|
69
|
+
client.category = new CommercetoolsCategoryProvider(configuration, CategorySchema, cache);
|
|
44
70
|
}
|
|
45
71
|
|
|
46
72
|
return client;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Cart,
|
|
3
3
|
CartItemSchema,
|
|
4
|
+
CartProvider,
|
|
5
|
+
Cache,
|
|
6
|
+
Currency,
|
|
7
|
+
} from '@reactionary/core';
|
|
8
|
+
import type {
|
|
4
9
|
CartMutationItemAdd,
|
|
5
10
|
CartMutationItemQuantityChange,
|
|
6
11
|
CartMutationItemRemove,
|
|
7
|
-
CartProvider,
|
|
8
12
|
CartQueryById,
|
|
9
13
|
Session,
|
|
10
|
-
Cache,
|
|
11
|
-
Currency,
|
|
12
14
|
} from '@reactionary/core';
|
|
13
15
|
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
14
16
|
import { z } from 'zod';
|
|
15
17
|
import { CommercetoolsClient } from '../core/client';
|
|
16
|
-
import { Cart as CTCart } from '@commercetools/platform-sdk';
|
|
18
|
+
import type { Cart as CTCart } from '@commercetools/platform-sdk';
|
|
17
19
|
import { traced } from '@reactionary/otel';
|
|
18
20
|
|
|
19
21
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { CategoryProvider, Cache, Category,
|
|
1
|
+
import { CategoryProvider, Cache, Category, createPaginatedResponseSchema } from "@reactionary/core";
|
|
2
|
+
import type { Session, CategoryQueryById, CategoryQueryBySlug, CategoryQueryForBreadcrumb, CategoryQueryForChildCategories, CategoryQueryForTopCategories } from "@reactionary/core";
|
|
2
3
|
import z from "zod";
|
|
3
4
|
import { CommercetoolsConfiguration } from "../schema/configuration.schema";
|
|
4
5
|
import { CommercetoolsClient } from "../core/client";
|
|
@@ -35,7 +36,6 @@ export class CommercetoolsCategoryProvider<
|
|
|
35
36
|
const response = await client.withKey({ key: payload.id.key }).get().execute();
|
|
36
37
|
return this.parseSingle(response.body, session);
|
|
37
38
|
} catch (error) {
|
|
38
|
-
console.error(`Error fetching category by ID ${payload.id.key}:`, error);
|
|
39
39
|
const dummyCategory = this.newModel();
|
|
40
40
|
dummyCategory.meta.placeholder = true;
|
|
41
41
|
dummyCategory.identifier = { key: payload.id.key };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Price, PriceProvider,
|
|
1
|
+
import { Price, PriceProvider, Cache, Currency, TieredPriceSchema, TieredPrice } from '@reactionary/core';
|
|
2
|
+
import type { PriceQueryBySku, Session } from '@reactionary/core';
|
|
2
3
|
import z from 'zod';
|
|
3
4
|
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
4
5
|
import { CommercetoolsClient } from '../core/client';
|
|
@@ -1,26 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
SearchQueryByTerm,
|
|
1
|
+
import { SearchProvider } from '@reactionary/core';
|
|
2
|
+
import type {
|
|
4
3
|
SearchResult,
|
|
5
4
|
SearchResultProduct,
|
|
6
|
-
Session,
|
|
7
5
|
Cache,
|
|
6
|
+
SearchQueryByTerm,
|
|
7
|
+
Session,
|
|
8
8
|
} from '@reactionary/core';
|
|
9
|
+
|
|
9
10
|
import { CommercetoolsClient } from '../core/client';
|
|
10
11
|
import z from 'zod';
|
|
11
12
|
import { CommercetoolsConfiguration } from '../schema/configuration.schema';
|
|
13
|
+
import { traced } from '@reactionary/otel';
|
|
12
14
|
|
|
13
15
|
export class CommercetoolsSearchProvider<
|
|
14
16
|
T extends SearchResult = SearchResult
|
|
15
17
|
> extends SearchProvider<T> {
|
|
16
18
|
protected config: CommercetoolsConfiguration;
|
|
17
19
|
|
|
18
|
-
constructor(
|
|
20
|
+
constructor(
|
|
21
|
+
config: CommercetoolsConfiguration,
|
|
22
|
+
schema: z.ZodType<T>,
|
|
23
|
+
cache: Cache
|
|
24
|
+
) {
|
|
19
25
|
super(schema, cache);
|
|
20
26
|
|
|
21
27
|
this.config = config;
|
|
22
28
|
}
|
|
23
29
|
|
|
30
|
+
@traced()
|
|
24
31
|
public override async queryByTerm(
|
|
25
32
|
payload: SearchQueryByTerm,
|
|
26
33
|
session: Session
|
|
@@ -43,9 +50,23 @@ export class CommercetoolsSearchProvider<
|
|
|
43
50
|
return this.parseSearchResult(remote, payload, session);
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
protected parseSearchResult(
|
|
53
|
+
protected parseSearchResult(
|
|
54
|
+
remote: unknown,
|
|
55
|
+
payload: SearchQueryByTerm,
|
|
56
|
+
session: Session
|
|
57
|
+
): T {
|
|
47
58
|
const result = this.newModel();
|
|
48
|
-
const remoteData = remote as {
|
|
59
|
+
const remoteData = remote as {
|
|
60
|
+
body: {
|
|
61
|
+
results: Array<{
|
|
62
|
+
id: string;
|
|
63
|
+
name: Record<string, string>;
|
|
64
|
+
slug?: Record<string, string>;
|
|
65
|
+
masterVariant: { images?: Array<{ url?: string }> };
|
|
66
|
+
}>;
|
|
67
|
+
total?: number;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
49
70
|
|
|
50
71
|
result.identifier = payload.search;
|
|
51
72
|
|
|
@@ -54,20 +75,20 @@ export class CommercetoolsSearchProvider<
|
|
|
54
75
|
identifier: { key: p.id },
|
|
55
76
|
name: p.name[session.languageContext.locale] || p.id,
|
|
56
77
|
slug: p.slug?.[session.languageContext.locale] || p.id,
|
|
57
|
-
image: p.masterVariant.images?.[0]?.url || 'https://placehold.co/400'
|
|
78
|
+
image: p.masterVariant.images?.[0]?.url || 'https://placehold.co/400',
|
|
58
79
|
};
|
|
59
80
|
|
|
60
81
|
result.products.push(product);
|
|
61
82
|
}
|
|
62
83
|
|
|
63
|
-
result.pages = Math.ceil(
|
|
84
|
+
result.pages = Math.ceil(
|
|
85
|
+
(remoteData.body.total || 0) / payload.search.pageSize
|
|
86
|
+
);
|
|
64
87
|
result.meta = {
|
|
65
88
|
cache: { hit: false, key: payload.search.term },
|
|
66
|
-
placeholder: false
|
|
89
|
+
placeholder: false,
|
|
67
90
|
};
|
|
68
91
|
|
|
69
92
|
return this.assert(result);
|
|
70
93
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
94
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import 'dotenv/config'
|
|
2
|
-
|
|
3
|
-
import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api';
|
|
4
|
-
|
|
5
|
-
import { CategorySchema, NoOpCache, ProductSchema, Session } from '@reactionary/core';
|
|
2
|
+
import { CategorySchema, NoOpCache, Session } from '@reactionary/core';
|
|
6
3
|
import { CommercetoolsCategoryProvider } from '../providers/category.provider';
|
|
7
4
|
import { createAnonymousTestSession, getCommercetoolsTestConfiguration } from './test-utils';
|
|
8
|
-
import { getTracer, shutdownOtel } from '@reactionary/otel';
|
|
9
5
|
|
|
10
6
|
const testData = {
|
|
11
7
|
topCategories: [
|
|
@@ -168,13 +164,4 @@ describe('Commercetools Category Provider', () => {
|
|
|
168
164
|
expect(result.meta.placeholder).toBe(true);
|
|
169
165
|
|
|
170
166
|
});
|
|
171
|
-
|
|
172
|
-
it('traces execution of getById', async () => {
|
|
173
|
-
const tracer = getTracer();
|
|
174
|
-
const span = tracer.startSpan('test-span');
|
|
175
|
-
const result = await provider.getById({ id: { key: 'home-decor'}}, session);
|
|
176
|
-
span.end();
|
|
177
|
-
await shutdownOtel();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
167
|
});
|
|
@@ -6,13 +6,12 @@ import { FakeCapabilities } from "../schema/capabilities.schema";
|
|
|
6
6
|
import { FakeCategoryProvider } from "../providers/category.provider";
|
|
7
7
|
import { FakeCartProvider } from "../providers";
|
|
8
8
|
|
|
9
|
-
type FakeClient<T extends FakeCapabilities> =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}>;
|
|
9
|
+
type FakeClient<T extends FakeCapabilities> =
|
|
10
|
+
(T['cart'] extends true ? { cart: CartProvider } : object) &
|
|
11
|
+
(T['product'] extends true ? { product: ProductProvider } : object) &
|
|
12
|
+
(T['search'] extends true ? { search: SearchProvider } : object) &
|
|
13
|
+
(T['identity'] extends true ? { identity: IdentityProvider } : object) &
|
|
14
|
+
(T['category'] extends true ? { category: CategoryProvider } : object);
|
|
16
15
|
|
|
17
16
|
export function withFakeCapabilities<T extends FakeCapabilities>(configuration: FakeConfiguration, capabilities: T) {
|
|
18
17
|
return (cache: ReactinaryCache): FakeClient<T> => {
|
|
@@ -30,7 +29,6 @@ export function withFakeCapabilities<T extends FakeCapabilities>(configuration:
|
|
|
30
29
|
client.category = new FakeCategoryProvider(configuration, CategorySchema, cache);
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
|
|
34
32
|
if (capabilities.cart) {
|
|
35
33
|
client.cart = new FakeCartProvider(configuration, CartSchema, cache);
|
|
36
34
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
export const FakeConfigurationSchema = z.
|
|
3
|
+
export const FakeConfigurationSchema = z.looseObject({
|
|
4
4
|
jitter: z
|
|
5
|
-
.
|
|
5
|
+
.looseObject({
|
|
6
6
|
mean: z.number().min(0).max(10000),
|
|
7
7
|
deviation: z.number().min(0).max(5000),
|
|
8
8
|
})
|
|
@@ -10,7 +10,7 @@ export const FakeConfigurationSchema = z.looseInterface({
|
|
|
10
10
|
mean: 0,
|
|
11
11
|
deviation: 0,
|
|
12
12
|
}),
|
|
13
|
-
seeds: z.
|
|
13
|
+
seeds: z.looseObject({
|
|
14
14
|
product: z.number().min(0).max(10000).default(1),
|
|
15
15
|
search: z.number().min(0).max(10000).default(1),
|
|
16
16
|
category: z.number().min(0).max(10000).default(1),
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import 'dotenv/config'
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
import { CategorySchema, NoOpCache,
|
|
5
|
-
|
|
6
|
-
import { getTracer, shutdownOtel } from '@reactionary/otel';
|
|
4
|
+
import { CategorySchema, NoOpCache, Session } from '@reactionary/core';
|
|
7
5
|
import { FakeCategoryProvider } from '../providers';
|
|
8
6
|
import { createAnonymousTestSession, getFakerTestConfiguration } from './test-utils';
|
|
9
7
|
describe('Faker Category Provider', () => {
|
package/trpc/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClientBuilder, NoOpCache } from '@reactionary/core';
|
|
2
2
|
import { withFakeCapabilities } from '@reactionary/provider-fake';
|
|
3
|
-
import { createTRPCServerRouter, createTRPCContext
|
|
4
|
-
import { createTRPCClient
|
|
3
|
+
import { createTRPCServerRouter, createTRPCContext } from './server';
|
|
4
|
+
import { createTRPCClient } from './client';
|
|
5
5
|
import type { TransparentClient } from './types';
|
|
6
6
|
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
|
7
7
|
import { createHTTPHandler } from '@trpc/server/adapters/standalone';
|
|
8
8
|
import * as http from 'http';
|
|
9
|
+
import { createAnonymousTestSession } from './test-utils';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Integration test that actually starts an HTTP server and makes real network calls
|
|
@@ -16,29 +17,31 @@ import * as http from 'http';
|
|
|
16
17
|
global.fetch = global.fetch || require('node-fetch');
|
|
17
18
|
|
|
18
19
|
// Create the server-side client
|
|
19
|
-
const serverClient =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
const serverClient = new ClientBuilder()
|
|
21
|
+
.withCapability(
|
|
22
|
+
withFakeCapabilities(
|
|
23
|
+
{
|
|
24
|
+
jitter: {
|
|
25
|
+
mean: 0,
|
|
26
|
+
deviation: 0,
|
|
27
|
+
},
|
|
28
|
+
seeds: {
|
|
29
|
+
category: 1,
|
|
30
|
+
product: 1,
|
|
31
|
+
search: 1
|
|
32
|
+
}
|
|
29
33
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
);
|
|
34
|
+
{ search: true, product: true, identity: false }
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
.withCache(new NoOpCache())
|
|
38
|
+
.build();
|
|
36
39
|
|
|
37
40
|
// Create TRPC router from the client (do this at module level for type inference)
|
|
38
41
|
const router = createTRPCServerRouter(serverClient);
|
|
39
42
|
type AppRouter = typeof router;
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
xdescribe('TRPC Integration Test - Real HTTP Server', () => {
|
|
42
45
|
let server: http.Server;
|
|
43
46
|
let serverPort: number;
|
|
44
47
|
let trpcProxyClient: ReturnType<typeof createTRPCProxyClient<AppRouter>>;
|
|
@@ -96,9 +99,7 @@ describe('TRPC Integration Test - Real HTTP Server', () => {
|
|
|
96
99
|
}
|
|
97
100
|
});
|
|
98
101
|
|
|
99
|
-
const session =
|
|
100
|
-
id: '1234567890',
|
|
101
|
-
});
|
|
102
|
+
const session = createAnonymousTestSession();
|
|
102
103
|
|
|
103
104
|
describe('Product Provider via HTTP', () => {
|
|
104
105
|
it('should fetch product by slug through real HTTP calls', async () => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Session } from '@reactionary/core';
|
|
2
|
+
|
|
3
|
+
export function createAnonymousTestSession(): Session {
|
|
4
|
+
return {
|
|
5
|
+
id: 'test-session-id',
|
|
6
|
+
identity: {
|
|
7
|
+
type: 'Anonymous',
|
|
8
|
+
meta: {
|
|
9
|
+
cache: { hit: false, key: '' },
|
|
10
|
+
placeholder: false,
|
|
11
|
+
},
|
|
12
|
+
id: '',
|
|
13
|
+
token: undefined,
|
|
14
|
+
issued: new Date(),
|
|
15
|
+
expiry: new Date(new Date().getTime() + 3600 * 1000), // 1 hour from now
|
|
16
|
+
},
|
|
17
|
+
languageContext: {
|
|
18
|
+
locale: 'en-US',
|
|
19
|
+
currencyCode: 'USD',
|
|
20
|
+
countryCode: 'US',
|
|
21
|
+
},
|
|
22
|
+
storeIdentifier: {
|
|
23
|
+
key: 'the-good-store',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|