@reactionary/source 0.3.18 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -14
- package/core/src/client/client-builder.ts +54 -6
- package/core/src/factories/cart.factory.ts +40 -0
- package/core/src/factories/category.factory.ts +40 -0
- package/core/src/factories/checkout.factory.ts +59 -0
- package/core/src/factories/identity.factory.ts +26 -0
- package/core/src/factories/index.ts +15 -0
- package/core/src/factories/inventory.factory.ts +26 -0
- package/core/src/factories/order-search.factory.ts +33 -0
- package/core/src/factories/order.factory.ts +21 -0
- package/core/src/factories/price.factory.ts +29 -0
- package/core/src/factories/product-associations.factory.ts +30 -0
- package/core/src/factories/product-list.factory.ts +82 -0
- package/core/src/factories/product-reviews.factory.ts +58 -0
- package/core/src/factories/product-search.factory.ts +31 -0
- package/core/src/factories/product.factory.ts +21 -0
- package/core/src/factories/profile.factory.ts +23 -0
- package/core/src/factories/store.factory.ts +21 -0
- package/core/src/index.ts +3 -1
- package/core/src/providers/cart.provider.ts +12 -10
- package/core/src/providers/category.provider.ts +9 -7
- package/core/src/providers/checkout.provider.ts +14 -10
- package/core/src/providers/identity.provider.ts +7 -7
- package/core/src/providers/inventory.provider.ts +4 -4
- package/core/src/providers/order-search.provider.ts +4 -2
- package/core/src/providers/order.provider.ts +4 -4
- package/core/src/providers/price.provider.ts +5 -5
- package/core/src/providers/product-associations.provider.ts +6 -4
- package/core/src/providers/product-list.provider.ts +13 -8
- package/core/src/providers/product-reviews.provider.ts +8 -4
- package/core/src/providers/product-search.provider.ts +15 -28
- package/core/src/providers/product.provider.ts +6 -6
- package/core/src/providers/profile.provider.ts +8 -8
- package/core/src/providers/store.provider.ts +2 -2
- package/core/src/test/client-builder.spec.ts +81 -0
- package/examples/node/package.json +7 -7
- package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +10 -4
- package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +8 -2
- package/examples/node/src/basic/basic-node-setup.spec.ts +5 -1
- package/examples/node/src/basic/client-creation.spec.ts +3 -3
- package/examples/node/src/utils.ts +41 -41
- package/examples/node/tsconfig.lib.json +2 -1
- package/package.json +1 -1
- package/providers/algolia/src/core/initialize.ts +76 -21
- package/providers/algolia/src/core/initialize.types.ts +107 -0
- package/providers/algolia/src/factories/index.ts +1 -0
- package/providers/algolia/src/factories/product-search/product-search.factory.ts +182 -0
- package/providers/algolia/src/index.ts +2 -2
- package/providers/algolia/src/providers/analytics.provider.ts +4 -4
- package/providers/algolia/src/providers/product-search.provider.ts +67 -184
- package/providers/algolia/src/schema/capabilities.schema.ts +81 -7
- package/providers/algolia/src/test/analytics.spec.ts +9 -1
- package/providers/algolia/src/test/client-builder-product-search-extension.example.ts +85 -0
- package/providers/commercetools/src/core/capability-descriptors.ts +324 -0
- package/providers/commercetools/src/core/initialize.ts +35 -151
- package/providers/commercetools/src/core/initialize.types.ts +174 -0
- package/providers/commercetools/src/factories/cart/cart.factory.ts +142 -0
- package/providers/commercetools/src/factories/category/category.factory.ts +77 -0
- package/providers/commercetools/src/factories/checkout/checkout-initializer-overrides.example.ts +94 -0
- package/providers/commercetools/src/factories/checkout/checkout.factory.ts +338 -0
- package/providers/commercetools/src/factories/identity/identity.factory.ts +26 -0
- package/providers/commercetools/src/factories/inventory/inventory.factory.ts +49 -0
- package/providers/commercetools/src/factories/order/order.factory.ts +149 -0
- package/providers/commercetools/src/factories/order-search/order-search.factory.ts +108 -0
- package/providers/commercetools/src/factories/price/price.factory.ts +76 -0
- package/providers/commercetools/src/factories/product/product-factory-baseline.example.ts +14 -0
- package/providers/commercetools/src/factories/product/product-factory-schema-and-parse-extension.example.ts +35 -0
- package/providers/commercetools/src/factories/product/product-factory-schema-extension.example.ts +23 -0
- package/providers/commercetools/src/factories/product/product-initializer-factory-extension.example.ts +41 -0
- package/providers/commercetools/src/factories/product/product-provider-custom-method-only.example.ts +47 -0
- package/providers/commercetools/src/factories/product/product-provider-schema-signature-extension.example.ts +61 -0
- package/providers/commercetools/src/factories/product/product.factory.ts +220 -0
- package/providers/commercetools/src/factories/product/utils.example.ts +9 -0
- package/providers/commercetools/src/factories/product-associations/product-associations.factory.ts +103 -0
- package/providers/commercetools/src/factories/product-list/product-list.factory.ts +122 -0
- package/providers/commercetools/src/factories/product-reviews/product-reviews.factory.ts +81 -0
- package/providers/commercetools/src/factories/product-search/product-search.factory.ts +182 -0
- package/providers/commercetools/src/factories/profile/profile.factory.ts +94 -0
- package/providers/commercetools/src/factories/store/store.factory.ts +49 -0
- package/providers/commercetools/src/index.ts +15 -0
- package/providers/commercetools/src/providers/cart.provider.ts +67 -193
- package/providers/commercetools/src/providers/category.provider.ts +24 -64
- package/providers/commercetools/src/providers/checkout.provider.ts +50 -322
- package/providers/commercetools/src/providers/identity.provider.ts +35 -15
- package/providers/commercetools/src/providers/inventory.provider.ts +13 -31
- package/providers/commercetools/src/providers/order-search.provider.ts +16 -110
- package/providers/commercetools/src/providers/order.provider.ts +13 -144
- package/providers/commercetools/src/providers/price.provider.ts +37 -51
- package/providers/commercetools/src/providers/product-associations.provider.ts +39 -104
- package/providers/commercetools/src/providers/product-list.provider.ts +38 -23
- package/providers/commercetools/src/providers/product-reviews.provider.ts +34 -14
- package/providers/commercetools/src/providers/product-search.provider.ts +17 -170
- package/providers/commercetools/src/providers/product.provider.ts +20 -199
- package/providers/commercetools/src/providers/profile.provider.ts +27 -73
- package/providers/commercetools/src/providers/store.provider.ts +13 -31
- package/providers/commercetools/src/schema/capabilities.schema.ts +258 -20
- package/providers/commercetools/src/test/caching.spec.ts +18 -2
- package/providers/commercetools/src/test/client-builder-merge-extensions.example.ts +125 -0
- package/providers/fake/src/core/initialize.ts +213 -44
- package/providers/fake/src/core/initialize.types.ts +164 -0
- package/providers/fake/src/factories/cart/cart.factory.ts +34 -0
- package/providers/fake/src/factories/category/category.factory.ts +40 -0
- package/providers/fake/src/factories/checkout/checkout.factory.ts +53 -0
- package/providers/fake/src/factories/identity/identity.factory.ts +25 -0
- package/providers/fake/src/factories/index.ts +14 -0
- package/providers/fake/src/factories/inventory/inventory.factory.ts +25 -0
- package/providers/fake/src/factories/order/order.factory.ts +22 -0
- package/providers/fake/src/factories/order-search/order-search.factory.ts +27 -0
- package/providers/fake/src/factories/price/price.factory.ts +26 -0
- package/providers/fake/src/factories/product/product.factory.ts +22 -0
- package/providers/fake/src/factories/product-associations/product-associations.factory.ts +25 -0
- package/providers/fake/src/factories/product-reviews/product-reviews.factory.ts +53 -0
- package/providers/fake/src/factories/product-search/product-search.factory.ts +27 -0
- package/providers/fake/src/factories/profile/profile.factory.ts +22 -0
- package/providers/fake/src/factories/store/store.factory.ts +22 -0
- package/providers/fake/src/index.ts +2 -0
- package/providers/fake/src/providers/cart.provider.ts +23 -14
- package/providers/fake/src/providers/category.provider.ts +120 -105
- package/providers/fake/src/providers/checkout.provider.ts +39 -20
- package/providers/fake/src/providers/identity.provider.ts +40 -34
- package/providers/fake/src/providers/inventory.provider.ts +26 -24
- package/providers/fake/src/providers/order-search.provider.ts +38 -30
- package/providers/fake/src/providers/order.provider.ts +21 -37
- package/providers/fake/src/providers/price.provider.ts +42 -34
- package/providers/fake/src/providers/product-associations.provider.ts +23 -10
- package/providers/fake/src/providers/product-reviews.provider.ts +71 -69
- package/providers/fake/src/providers/product-search.provider.ts +43 -70
- package/providers/fake/src/providers/product.provider.ts +34 -32
- package/providers/fake/src/providers/profile.provider.ts +62 -55
- package/providers/fake/src/providers/store.provider.ts +38 -22
- package/providers/fake/src/schema/capabilities.schema.ts +175 -18
- package/providers/fake/src/test/cart.provider.spec.ts +20 -3
- package/providers/fake/src/test/category.provider.spec.ts +4 -1
- package/providers/fake/src/test/checkout.provider.spec.ts +12 -2
- package/providers/fake/src/test/client-builder-product-extension.example.ts +75 -0
- package/providers/fake/src/test/order-search.provider.spec.ts +4 -7
- package/providers/fake/src/test/order.provider.spec.ts +4 -6
- package/providers/fake/src/test/price.provider.spec.ts +3 -1
- package/providers/fake/src/test/product.provider.spec.ts +8 -2
- package/providers/fake/src/test/profile.provider.spec.ts +4 -2
- package/providers/google-analytics/src/core/initialize.ts +37 -12
- package/providers/google-analytics/src/core/initialize.types.ts +47 -0
- package/providers/google-analytics/src/index.ts +1 -0
- package/providers/google-analytics/src/schema/capabilities.schema.ts +31 -5
- package/providers/medusa/src/core/initialize.ts +324 -81
- package/providers/medusa/src/core/initialize.types.ts +184 -0
- package/providers/medusa/src/factories/cart/cart.factory.ts +34 -0
- package/providers/medusa/src/factories/category/category.factory.ts +37 -0
- package/providers/medusa/src/factories/checkout/checkout.factory.ts +50 -0
- package/providers/medusa/src/factories/identity/identity.factory.ts +22 -0
- package/providers/medusa/src/factories/index.ts +12 -0
- package/providers/medusa/src/factories/inventory/inventory.factory.ts +25 -0
- package/providers/medusa/src/factories/order/order.factory.ts +22 -0
- package/providers/medusa/src/factories/order-search/order-search.factory.ts +27 -0
- package/providers/medusa/src/factories/price/price.factory.ts +26 -0
- package/providers/medusa/src/factories/product/product.factory.ts +22 -0
- package/providers/medusa/src/factories/product-associations/product-associations.factory.ts +25 -0
- package/providers/medusa/src/factories/product-search/product-search.factory.ts +27 -0
- package/providers/medusa/src/factories/profile/profile.factory.ts +22 -0
- package/providers/medusa/src/index.ts +2 -0
- package/providers/medusa/src/providers/cart.provider.ts +33 -20
- package/providers/medusa/src/providers/category.provider.ts +30 -12
- package/providers/medusa/src/providers/checkout.provider.ts +42 -17
- package/providers/medusa/src/providers/identity.provider.ts +1 -1
- package/providers/medusa/src/providers/inventory.provider.ts +21 -7
- package/providers/medusa/src/providers/order-search.provider.ts +16 -5
- package/providers/medusa/src/providers/order.provider.ts +17 -5
- package/providers/medusa/src/providers/price.provider.ts +26 -7
- package/providers/medusa/src/providers/product-associations.provider.ts +19 -8
- package/providers/medusa/src/providers/product-search.provider.ts +19 -31
- package/providers/medusa/src/providers/product.provider.ts +47 -11
- package/providers/medusa/src/providers/profile.provider.ts +35 -11
- package/providers/medusa/src/schema/capabilities.schema.ts +229 -18
- package/providers/medusa/src/test/cart.provider.spec.ts +18 -2
- package/providers/medusa/src/test/category.provider.spec.ts +4 -1
- package/providers/medusa/src/test/checkout.spec.ts +9 -9
- package/providers/medusa/src/test/inventory.provider.spec.ts +3 -1
- package/providers/medusa/src/test/large-cart.provider.spec.ts +8 -2
- package/providers/medusa/src/test/price.provider.spec.ts +8 -1
- package/providers/medusa/src/test/product.provider.spec.ts +3 -1
- package/providers/medusa/src/test/search.provider.spec.ts +16 -3
- package/providers/meilisearch/src/core/initialize.ts +88 -21
- package/providers/meilisearch/src/core/initialize.types.ts +119 -0
- package/providers/meilisearch/src/factories/index.ts +2 -0
- package/providers/meilisearch/src/factories/order-search/order-search.factory.ts +27 -0
- package/providers/meilisearch/src/factories/product-search/product-search.factory.ts +27 -0
- package/providers/meilisearch/src/index.ts +2 -0
- package/providers/meilisearch/src/providers/index.ts +1 -0
- package/providers/meilisearch/src/providers/order-search.provider.ts +21 -6
- package/providers/meilisearch/src/providers/product-search.provider.ts +24 -8
- package/providers/meilisearch/src/schema/capabilities.schema.ts +95 -8
|
@@ -4,233 +4,116 @@ import {
|
|
|
4
4
|
FacetIdentifierSchema,
|
|
5
5
|
type FacetValueIdentifier,
|
|
6
6
|
FacetValueIdentifierSchema,
|
|
7
|
-
ImageSchema,
|
|
8
7
|
ProductSearchProvider,
|
|
8
|
+
type ProductSearchFactory,
|
|
9
|
+
type ProductSearchFactoryOutput,
|
|
10
|
+
type ProductSearchFactoryWithOutput,
|
|
9
11
|
type ProductSearchQueryByTerm,
|
|
10
12
|
ProductSearchQueryByTermSchema,
|
|
11
13
|
type ProductSearchQueryCreateNavigationFilter,
|
|
12
|
-
type ProductSearchResult,
|
|
13
|
-
type ProductSearchResultFacet,
|
|
14
|
-
ProductSearchResultFacetSchema,
|
|
15
|
-
type ProductSearchResultFacetValue,
|
|
16
|
-
ProductSearchResultFacetValueSchema,
|
|
17
|
-
type ProductSearchResultItem,
|
|
18
|
-
type ProductSearchResultItemVariant,
|
|
19
|
-
ProductSearchResultItemVariantSchema,
|
|
20
|
-
ProductSearchResultSchema,
|
|
21
|
-
Reactionary,
|
|
22
14
|
type RequestContext,
|
|
23
15
|
type Result,
|
|
24
|
-
|
|
16
|
+
ProductSearchResultSchema,
|
|
17
|
+
Reactionary,
|
|
18
|
+
success,
|
|
25
19
|
} from '@reactionary/core';
|
|
26
|
-
import { algoliasearch
|
|
20
|
+
import { algoliasearch } from 'algoliasearch';
|
|
27
21
|
import type { AlgoliaConfiguration } from '../schema/configuration.schema.js';
|
|
28
|
-
import type { AlgoliaNativeRecord
|
|
22
|
+
import type { AlgoliaNativeRecord } from '../schema/search.schema.js';
|
|
23
|
+
import type { AlgoliaProductSearchFactory } from '../factories/product-search/product-search.factory.js';
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
export class AlgoliaProductSearchProvider<
|
|
26
|
+
TFactory extends ProductSearchFactory = AlgoliaProductSearchFactory,
|
|
27
|
+
> extends ProductSearchProvider<ProductSearchFactoryOutput<TFactory>> {
|
|
33
28
|
protected config: AlgoliaConfiguration;
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
protected factory: ProductSearchFactoryWithOutput<TFactory>;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
cache: Cache,
|
|
33
|
+
context: RequestContext,
|
|
34
|
+
config: AlgoliaConfiguration,
|
|
35
|
+
factory: ProductSearchFactoryWithOutput<TFactory>,
|
|
36
|
+
) {
|
|
36
37
|
super(cache, context);
|
|
37
38
|
this.config = config;
|
|
39
|
+
this.factory = factory;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
protected queryByTermPayload(payload: ProductSearchQueryByTerm) {
|
|
43
|
+
const facetsThatAreNotCategory = payload.search.facets.filter(
|
|
44
|
+
(x) => x.facet.key !== 'categories',
|
|
45
|
+
);
|
|
46
|
+
const categoryFacet =
|
|
47
|
+
payload.search.facets.find((x) => x.facet.key === 'categories') ||
|
|
48
|
+
payload.search.categoryFilter;
|
|
41
49
|
|
|
42
|
-
const
|
|
43
|
-
const categoryFacet = payload.search.facets.find(x => x.facet.key === 'categories') || payload.search.categoryFilter;
|
|
44
|
-
|
|
45
|
-
const finalFilters = [...payload.search.filters || []];
|
|
46
|
-
|
|
50
|
+
const finalFilters = [...(payload.search.filters || [])];
|
|
47
51
|
|
|
48
52
|
const finalFacetFilters = [
|
|
49
|
-
...facetsThatAreNotCategory.map(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
]
|
|
53
|
+
...facetsThatAreNotCategory.map((x) => `${x.facet.key}:${x.key}`),
|
|
54
|
+
];
|
|
55
|
+
|
|
53
56
|
if (categoryFacet) {
|
|
54
57
|
finalFilters.push(`categories:"${categoryFacet.key}"`);
|
|
55
58
|
}
|
|
59
|
+
|
|
56
60
|
return {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
};
|
|
61
|
+
indexName: this.config.indexName,
|
|
62
|
+
query: payload.search.term,
|
|
63
|
+
page: payload.search.paginationOptions.pageNumber - 1,
|
|
64
|
+
hitsPerPage: payload.search.paginationOptions.pageSize,
|
|
65
|
+
facets: ['*'],
|
|
66
|
+
analytics: true,
|
|
67
|
+
clickAnalytics: true,
|
|
68
|
+
facetFilters: finalFacetFilters,
|
|
69
|
+
filters: finalFilters.join(' AND '),
|
|
70
|
+
};
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
@Reactionary({
|
|
71
74
|
inputSchema: ProductSearchQueryByTermSchema,
|
|
72
|
-
outputSchema: ProductSearchResultSchema
|
|
75
|
+
outputSchema: ProductSearchResultSchema,
|
|
73
76
|
})
|
|
74
77
|
public override async queryByTerm(
|
|
75
|
-
payload: ProductSearchQueryByTerm
|
|
76
|
-
): Promise<Result<
|
|
78
|
+
payload: ProductSearchQueryByTerm,
|
|
79
|
+
): Promise<Result<ProductSearchFactoryOutput<TFactory>>> {
|
|
77
80
|
const client = algoliasearch(this.config.appId, this.config.apiKey);
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
81
82
|
const remote = await client.search<AlgoliaNativeRecord>({
|
|
82
|
-
requests: [
|
|
83
|
-
this.queryByTermPayload(payload)
|
|
84
|
-
],
|
|
83
|
+
requests: [this.queryByTermPayload(payload)],
|
|
85
84
|
});
|
|
86
85
|
|
|
87
|
-
const input = remote.results[0]
|
|
88
|
-
const result = this.
|
|
86
|
+
const input = remote.results[0];
|
|
87
|
+
const result = this.factory.parseSearchResult(this.context, input, payload);
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
for (const selectedFacet of payload.search.facets) {
|
|
90
|
+
const facet = result.facets.find(
|
|
91
|
+
(f) => f.identifier.key === selectedFacet.facet.key,
|
|
92
|
+
);
|
|
93
|
+
if (!facet) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const value = facet.values.find((v) => v.identifier.key === selectedFacet.key);
|
|
97
|
+
if (value) {
|
|
98
|
+
value.active = true;
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
return success(result);
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
public override async createCategoryNavigationFilter(
|
|
105
|
-
|
|
105
|
+
public override async createCategoryNavigationFilter(
|
|
106
|
+
payload: ProductSearchQueryCreateNavigationFilter,
|
|
107
|
+
): Promise<Result<FacetValueIdentifier>> {
|
|
106
108
|
const facetIdentifier = FacetIdentifierSchema.parse({
|
|
107
|
-
key: 'categories'
|
|
108
|
-
});
|
|
109
|
+
key: 'categories',
|
|
110
|
+
} satisfies Partial<FacetIdentifier>);
|
|
111
|
+
|
|
109
112
|
const facetValueIdentifier = FacetValueIdentifierSchema.parse({
|
|
110
113
|
facet: facetIdentifier,
|
|
111
|
-
key: payload.categoryPath.map(c => c.name).join(' > ')
|
|
112
|
-
});
|
|
113
|
-
return success(facetValueIdentifier);
|
|
114
|
-
}
|
|
114
|
+
key: payload.categoryPath.map((c) => c.name).join(' > '),
|
|
115
|
+
} satisfies Partial<FacetValueIdentifier>);
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
protected parseSingle(body: AlgoliaNativeRecord) {
|
|
118
|
-
const product = {
|
|
119
|
-
identifier: { key: body.objectID },
|
|
120
|
-
name: body.name || body.objectID,
|
|
121
|
-
slug: body.slug || body.objectID,
|
|
122
|
-
variants: [ ... (body.variants || []) ].map(variant => this.parseVariant(variant, body)),
|
|
123
|
-
} satisfies ProductSearchResultItem;
|
|
124
|
-
|
|
125
|
-
return product;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
protected override parseVariant(variant: AlgoliaNativeVariant, product: AlgoliaNativeRecord): ProductSearchResultItemVariant {
|
|
129
|
-
const result = ProductSearchResultItemVariantSchema.parse({
|
|
130
|
-
variant: {
|
|
131
|
-
sku: variant.sku
|
|
132
|
-
},
|
|
133
|
-
image: ImageSchema.parse({
|
|
134
|
-
sourceUrl: variant.image,
|
|
135
|
-
altText: product.name || '',
|
|
136
|
-
})
|
|
137
|
-
} satisfies Partial<ProductSearchResultItemVariant>);
|
|
138
|
-
|
|
139
|
-
return result;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
protected parsePaginatedResult(body: SearchResponse<AlgoliaNativeRecord>, query: ProductSearchQueryByTerm) {
|
|
143
|
-
const items = body.hits.map((hit) => this.parseSingle(hit));
|
|
144
|
-
let facets: ProductSearchResultFacet[] = [];
|
|
145
|
-
for (const id in body.facets) {
|
|
146
|
-
const f = body.facets[id];
|
|
147
|
-
const facetId = FacetIdentifierSchema.parse({
|
|
148
|
-
key: id
|
|
149
|
-
})
|
|
150
|
-
const facet = this.parseFacet(facetId, f);
|
|
151
|
-
facets.push(facet);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// we do something to convert the hierachy.lvl.n facet values into something more usable
|
|
156
|
-
const selectedCategoryFacet = query.search.facets.find(x => x.facet.key === 'categories') || query.search.categoryFilter;
|
|
157
|
-
let subCategoryFacet;
|
|
158
|
-
if(selectedCategoryFacet) {
|
|
159
|
-
const valueDepth = selectedCategoryFacet.key.split(' > ').length;
|
|
160
|
-
// ok, so input defined a facet value from level X, we return hierarchy.lvl.(X+1) as subcategories.
|
|
161
|
-
// hierarchy counts from 0, so length is already pointing to 'lvl.(X+1)'
|
|
162
|
-
subCategoryFacet = facets.find(f => f.identifier.key === `hierarchy.lvl${valueDepth}`);
|
|
163
|
-
} else {
|
|
164
|
-
// and remap lvl0 to 'categories'
|
|
165
|
-
subCategoryFacet = facets.find(f => f.identifier.key === 'hierarchy.lvl0');
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if(subCategoryFacet) {
|
|
169
|
-
// remap to 'categories' facet
|
|
170
|
-
subCategoryFacet.identifier = FacetIdentifierSchema.parse({
|
|
171
|
-
key: 'categories'
|
|
172
|
-
});
|
|
173
|
-
subCategoryFacet.name = 'Categories';
|
|
174
|
-
for(const v of subCategoryFacet.values) {
|
|
175
|
-
const pathParts = v.identifier.key.split(' > ');
|
|
176
|
-
v.identifier.facet = subCategoryFacet.identifier;
|
|
177
|
-
v.name = pathParts[pathParts.length -1];
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// remove other hierarchy facets
|
|
182
|
-
facets = facets.filter(f => !f.identifier.key.startsWith('hierarchy.lvl'));
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const result = {
|
|
187
|
-
identifier: {
|
|
188
|
-
term: query.search.term,
|
|
189
|
-
facets: query.search.facets,
|
|
190
|
-
filters: query.search.filters,
|
|
191
|
-
paginationOptions: query.search.paginationOptions,
|
|
192
|
-
index: body.index || '',
|
|
193
|
-
key: body.queryID || '',
|
|
194
|
-
},
|
|
195
|
-
pageNumber: (body.page || 0) + 1,
|
|
196
|
-
pageSize: body.hitsPerPage || 0,
|
|
197
|
-
totalCount: body.nbHits || 0,
|
|
198
|
-
totalPages: body.nbPages || 0,
|
|
199
|
-
items: items,
|
|
200
|
-
facets,
|
|
201
|
-
} satisfies AlgoliaProductSearchResult;
|
|
202
|
-
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
protected parseFacet(facetIdentifier: FacetIdentifier, facetValues: Record<string, number>) : ProductSearchResultFacet {
|
|
207
|
-
const result: ProductSearchResultFacet = ProductSearchResultFacetSchema.parse({
|
|
208
|
-
identifier: facetIdentifier,
|
|
209
|
-
name: facetIdentifier.key,
|
|
210
|
-
values: []
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
for (const vid in facetValues) {
|
|
214
|
-
const fv = facetValues[vid];
|
|
215
|
-
|
|
216
|
-
const facetValueIdentifier = FacetValueIdentifierSchema.parse({
|
|
217
|
-
facet: facetIdentifier,
|
|
218
|
-
key: vid
|
|
219
|
-
} satisfies Partial<FacetValueIdentifier>);
|
|
220
|
-
|
|
221
|
-
result.values.push(this.parseFacetValue(facetValueIdentifier, vid, fv));
|
|
222
|
-
}
|
|
223
|
-
return result;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
protected parseFacetValue(facetValueIdentifier: FacetValueIdentifier, label: string, count: number) : ProductSearchResultFacetValue {
|
|
227
|
-
return ProductSearchResultFacetValueSchema.parse({
|
|
228
|
-
identifier: facetValueIdentifier,
|
|
229
|
-
name: label,
|
|
230
|
-
count: count,
|
|
231
|
-
active: false,
|
|
232
|
-
} satisfies Partial<ProductSearchResultFacetValue>);
|
|
117
|
+
return success(facetValueIdentifier);
|
|
233
118
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
119
|
}
|
|
@@ -1,10 +1,84 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
AnalyticsProvider,
|
|
3
|
+
Cache,
|
|
4
|
+
ProductRecommendationsProvider,
|
|
5
|
+
ProductSearchFactory,
|
|
6
|
+
ProductSearchFactoryWithOutput,
|
|
7
|
+
ProductSearchProvider,
|
|
8
|
+
RequestContext,
|
|
9
|
+
} from '@reactionary/core';
|
|
10
|
+
import { CapabilitiesSchema } from '@reactionary/core';
|
|
11
|
+
import type { AlgoliaConfiguration } from './configuration.schema.js';
|
|
12
|
+
import * as z from 'zod';
|
|
13
|
+
|
|
14
|
+
const ProductSearchCapabilitySchema = z.looseObject({
|
|
15
|
+
enabled: z.boolean(),
|
|
16
|
+
factory: z.unknown().optional(),
|
|
17
|
+
provider: z.unknown().optional(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const ProviderCapabilitySchema = z.looseObject({
|
|
21
|
+
enabled: z.boolean(),
|
|
22
|
+
provider: z.unknown().optional(),
|
|
23
|
+
});
|
|
3
24
|
|
|
4
25
|
export const AlgoliaCapabilitiesSchema = CapabilitiesSchema.pick({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
})
|
|
26
|
+
productSearch: true,
|
|
27
|
+
analytics: true,
|
|
28
|
+
productRecommendations: true,
|
|
29
|
+
})
|
|
30
|
+
.extend({
|
|
31
|
+
productSearch: ProductSearchCapabilitySchema.optional(),
|
|
32
|
+
analytics: ProviderCapabilitySchema.optional(),
|
|
33
|
+
productRecommendations: ProviderCapabilitySchema.optional(),
|
|
34
|
+
})
|
|
35
|
+
.partial();
|
|
36
|
+
|
|
37
|
+
export interface AlgoliaProviderFactoryArgs {
|
|
38
|
+
cache: Cache;
|
|
39
|
+
context: RequestContext;
|
|
40
|
+
config: AlgoliaConfiguration;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface AlgoliaProductSearchProviderFactoryArgs<
|
|
44
|
+
TFactory extends ProductSearchFactory = ProductSearchFactory,
|
|
45
|
+
> extends AlgoliaProviderFactoryArgs {
|
|
46
|
+
factory: ProductSearchFactoryWithOutput<TFactory>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface AlgoliaProductSearchCapabilityConfig<
|
|
50
|
+
TFactory extends ProductSearchFactory = ProductSearchFactory,
|
|
51
|
+
TProvider extends ProductSearchProvider = ProductSearchProvider,
|
|
52
|
+
> {
|
|
53
|
+
enabled: boolean;
|
|
54
|
+
factory?: ProductSearchFactoryWithOutput<TFactory>;
|
|
55
|
+
provider?: (args: AlgoliaProductSearchProviderFactoryArgs<TFactory>) => TProvider;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface AlgoliaAnalyticsCapabilityConfig<
|
|
59
|
+
TProvider extends AnalyticsProvider = AnalyticsProvider,
|
|
60
|
+
> {
|
|
61
|
+
enabled: boolean;
|
|
62
|
+
provider?: (args: AlgoliaProviderFactoryArgs) => TProvider;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface AlgoliaProductRecommendationsCapabilityConfig<
|
|
66
|
+
TProvider extends ProductRecommendationsProvider = ProductRecommendationsProvider,
|
|
67
|
+
> {
|
|
68
|
+
enabled: boolean;
|
|
69
|
+
provider?: (args: AlgoliaProviderFactoryArgs) => TProvider;
|
|
70
|
+
}
|
|
9
71
|
|
|
10
|
-
export type AlgoliaCapabilities
|
|
72
|
+
export type AlgoliaCapabilities<
|
|
73
|
+
TProductSearchFactory extends ProductSearchFactory = ProductSearchFactory,
|
|
74
|
+
TProductSearchProvider extends ProductSearchProvider = ProductSearchProvider,
|
|
75
|
+
TAnalyticsProvider extends AnalyticsProvider = AnalyticsProvider,
|
|
76
|
+
TProductRecommendationsProvider extends ProductRecommendationsProvider = ProductRecommendationsProvider,
|
|
77
|
+
> = {
|
|
78
|
+
productSearch?: AlgoliaProductSearchCapabilityConfig<
|
|
79
|
+
TProductSearchFactory,
|
|
80
|
+
TProductSearchProvider
|
|
81
|
+
>;
|
|
82
|
+
analytics?: AlgoliaAnalyticsCapabilityConfig<TAnalyticsProvider>;
|
|
83
|
+
productRecommendations?: AlgoliaProductRecommendationsCapabilityConfig<TProductRecommendationsProvider>;
|
|
84
|
+
};
|
|
@@ -3,6 +3,8 @@ import { AlgoliaAnalyticsProvider } from '../providers/analytics.provider.js';
|
|
|
3
3
|
import { createInitialRequestContext, NoOpCache } from '@reactionary/core';
|
|
4
4
|
import type { AlgoliaConfiguration } from '../schema/configuration.schema.js';
|
|
5
5
|
import { AlgoliaProductSearchProvider } from '../providers/product-search.provider.js';
|
|
6
|
+
import { AlgoliaProductSearchFactory } from '../factories/product-search/product-search.factory.js';
|
|
7
|
+
import { AlgoliaProductSearchResultSchema } from '../schema/search.schema.js';
|
|
6
8
|
|
|
7
9
|
describe('Analytics event tracking', async () => {
|
|
8
10
|
const config = {
|
|
@@ -13,7 +15,13 @@ describe('Analytics event tracking', async () => {
|
|
|
13
15
|
const cache = new NoOpCache();
|
|
14
16
|
const context = createInitialRequestContext();
|
|
15
17
|
|
|
16
|
-
const
|
|
18
|
+
const searchFactory = new AlgoliaProductSearchFactory(AlgoliaProductSearchResultSchema);
|
|
19
|
+
const search = new AlgoliaProductSearchProvider<typeof searchFactory>(
|
|
20
|
+
cache,
|
|
21
|
+
context,
|
|
22
|
+
config,
|
|
23
|
+
searchFactory,
|
|
24
|
+
);
|
|
17
25
|
const analytics = new AlgoliaAnalyticsProvider(cache, context, config);
|
|
18
26
|
const searchResult = await search.queryByTerm({
|
|
19
27
|
search: {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientBuilder,
|
|
3
|
+
NoOpCache,
|
|
4
|
+
createInitialRequestContext,
|
|
5
|
+
type ProductSearchQueryByTerm,
|
|
6
|
+
type RequestContext,
|
|
7
|
+
} from '@reactionary/core';
|
|
8
|
+
import * as z from 'zod';
|
|
9
|
+
import type { AlgoliaConfiguration } from '../schema/configuration.schema.js';
|
|
10
|
+
import { withAlgoliaCapabilities } from '../core/initialize.js';
|
|
11
|
+
import { AlgoliaProductSearchFactory } from '../factories/product-search/product-search.factory.js';
|
|
12
|
+
import { AlgoliaProductSearchResultSchema } from '../schema/search.schema.js';
|
|
13
|
+
|
|
14
|
+
const assertType = <T>(_value: T) => {
|
|
15
|
+
void _value;
|
|
16
|
+
};
|
|
17
|
+
type IsAny<T> = 0 extends (1 & T) ? true : false;
|
|
18
|
+
const assertNotAny = <T>(_value: IsAny<T> extends true ? never : T) => {
|
|
19
|
+
void _value;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const ExtendedProductSearchResultSchema = AlgoliaProductSearchResultSchema.safeExtend(
|
|
23
|
+
{
|
|
24
|
+
extendedMeta: z.string(),
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
class ExtendedAlgoliaProductSearchFactory extends AlgoliaProductSearchFactory<
|
|
29
|
+
typeof ExtendedProductSearchResultSchema
|
|
30
|
+
> {
|
|
31
|
+
constructor() {
|
|
32
|
+
super(ExtendedProductSearchResultSchema);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public override parseSearchResult(
|
|
36
|
+
context: RequestContext,
|
|
37
|
+
data: unknown,
|
|
38
|
+
query: ProductSearchQueryByTerm,
|
|
39
|
+
) {
|
|
40
|
+
const base = super.parseSearchResult(context, data, query);
|
|
41
|
+
return this.productSearchResultSchema.parse({
|
|
42
|
+
...base,
|
|
43
|
+
extendedMeta: 'from-factory',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const config = {
|
|
49
|
+
appId: 'ALGOLIA_APP_ID',
|
|
50
|
+
apiKey: 'ALGOLIA_API_KEY',
|
|
51
|
+
indexName: 'ALGOLIA_INDEX',
|
|
52
|
+
} satisfies AlgoliaConfiguration;
|
|
53
|
+
|
|
54
|
+
const client = new ClientBuilder(createInitialRequestContext())
|
|
55
|
+
.withCache(new NoOpCache())
|
|
56
|
+
.withCapability(
|
|
57
|
+
withAlgoliaCapabilities(config, {
|
|
58
|
+
productSearch: {
|
|
59
|
+
enabled: true,
|
|
60
|
+
factory: new ExtendedAlgoliaProductSearchFactory(),
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
)
|
|
64
|
+
.build();
|
|
65
|
+
|
|
66
|
+
client.productSearch
|
|
67
|
+
.queryByTerm({
|
|
68
|
+
search: {
|
|
69
|
+
term: 'test',
|
|
70
|
+
facets: [],
|
|
71
|
+
filters: [],
|
|
72
|
+
paginationOptions: {
|
|
73
|
+
pageNumber: 1,
|
|
74
|
+
pageSize: 10,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
.then((result) => {
|
|
79
|
+
assertNotAny(result);
|
|
80
|
+
if (result.success) {
|
|
81
|
+
assertNotAny(result.value);
|
|
82
|
+
assertNotAny(result.value.extendedMeta);
|
|
83
|
+
assertType<string>(result.value.extendedMeta);
|
|
84
|
+
}
|
|
85
|
+
});
|