@reactionary/magento 0.10.24 → 0.10.26
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/capabilities/product-search.capability.js +59 -28
- package/factories/product/product.factory.js +6 -1
- package/factories/product-search/product-search.factory.js +6 -1
- package/package.json +2 -2
- package/src/capabilities/product-search.capability.d.ts +23 -0
- package/src/factories/product/product.factory.d.ts +2 -1
- package/src/factories/product-search/product-search.factory.d.ts +2 -1
- package/utils/magento-product.js +6 -1
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
} from "@reactionary/core";
|
|
20
20
|
import createDebug from "debug";
|
|
21
21
|
const debug = createDebug("reactionary:magento:search");
|
|
22
|
+
const CATEGORY_FACET_KEY = "categories";
|
|
23
|
+
const CATEGORY_FIELD = "category_id";
|
|
22
24
|
class MagentoProductSearchCapability extends ProductSearchCapability {
|
|
23
25
|
constructor(config, cache, context, magentoApi, factory) {
|
|
24
26
|
super(cache, context);
|
|
@@ -34,38 +36,26 @@ class MagentoProductSearchCapability extends ProductSearchCapability {
|
|
|
34
36
|
const pageSize = payload.search.paginationOptions.pageSize;
|
|
35
37
|
const currentPage = payload.search.paginationOptions.pageNumber;
|
|
36
38
|
const params = new URLSearchParams();
|
|
37
|
-
|
|
39
|
+
const filterGroups = [];
|
|
38
40
|
if (finalSearch) {
|
|
39
|
-
|
|
40
|
-
`searchCriteria[filterGroups][${filterGroupIndex}][filters][0][field]`,
|
|
41
|
-
"name"
|
|
42
|
-
);
|
|
43
|
-
params.set(
|
|
44
|
-
`searchCriteria[filterGroups][${filterGroupIndex}][filters][0][value]`,
|
|
45
|
-
`%${finalSearch}%`
|
|
46
|
-
);
|
|
47
|
-
params.set(
|
|
48
|
-
`searchCriteria[filterGroups][${filterGroupIndex}][filters][0][condition_type]`,
|
|
49
|
-
"like"
|
|
50
|
-
);
|
|
51
|
-
filterGroupIndex++;
|
|
41
|
+
filterGroups.push([{ field: "name", value: `%${finalSearch}%`, conditionType: "like" }]);
|
|
52
42
|
}
|
|
53
43
|
if (payload.search.categoryFilter?.key) {
|
|
54
44
|
debug(`Applying category filter: ${payload.search.categoryFilter.key}`);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
);
|
|
59
|
-
params.set(
|
|
60
|
-
`searchCriteria[filterGroups][${filterGroupIndex}][filters][0][value]`,
|
|
61
|
-
payload.search.categoryFilter.key
|
|
62
|
-
);
|
|
63
|
-
params.set(
|
|
64
|
-
`searchCriteria[filterGroups][${filterGroupIndex}][filters][0][condition_type]`,
|
|
65
|
-
"eq"
|
|
66
|
-
);
|
|
67
|
-
filterGroupIndex++;
|
|
45
|
+
filterGroups.push([
|
|
46
|
+
{ field: CATEGORY_FIELD, value: payload.search.categoryFilter.key, conditionType: "eq" }
|
|
47
|
+
]);
|
|
68
48
|
}
|
|
49
|
+
filterGroups.push(...this.getFacetFilterGroups(payload.search.facets));
|
|
50
|
+
filterGroups.push(...this.getStorefrontScopeFilterGroups());
|
|
51
|
+
filterGroups.forEach((filters, groupIndex) => {
|
|
52
|
+
filters.forEach((filter, filterIndex) => {
|
|
53
|
+
const prefix = `searchCriteria[filterGroups][${groupIndex}][filters][${filterIndex}]`;
|
|
54
|
+
params.set(`${prefix}[field]`, filter.field);
|
|
55
|
+
params.set(`${prefix}[value]`, filter.value);
|
|
56
|
+
params.set(`${prefix}[condition_type]`, filter.conditionType);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
69
59
|
params.set("searchCriteria[pageSize]", String(pageSize));
|
|
70
60
|
params.set("searchCriteria[currentPage]", String(currentPage));
|
|
71
61
|
const client = await this.magentoApi.getClient();
|
|
@@ -78,9 +68,50 @@ class MagentoProductSearchCapability extends ProductSearchCapability {
|
|
|
78
68
|
}
|
|
79
69
|
return success(result);
|
|
80
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* One filter group per facet code: Magento ORs the filters within a group and ANDs
|
|
73
|
+
* the groups, so values of one facet widen the result and distinct facets narrow it.
|
|
74
|
+
* The facet code is the Magento attribute code and the facet value key its (option) value.
|
|
75
|
+
*
|
|
76
|
+
* The category navigation facet created by {@link createCategoryNavigationFilter} is the
|
|
77
|
+
* exception: Magento's `category_id` filter (ProductCategoryFilter) ANDs the filters of a
|
|
78
|
+
* group, so its values are OR-ed through a single `category_id in (...)` filter instead.
|
|
79
|
+
*/
|
|
80
|
+
getFacetFilterGroups(facets) {
|
|
81
|
+
const groups = /* @__PURE__ */ new Map();
|
|
82
|
+
const categoryIds = [];
|
|
83
|
+
for (const facetValue of facets) {
|
|
84
|
+
if (facetValue.facet.key === CATEGORY_FACET_KEY) {
|
|
85
|
+
categoryIds.push(facetValue.key);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const field = facetValue.facet.key;
|
|
89
|
+
const group = groups.get(field) ?? [];
|
|
90
|
+
group.push({ field, value: facetValue.key, conditionType: "eq" });
|
|
91
|
+
groups.set(field, group);
|
|
92
|
+
}
|
|
93
|
+
if (categoryIds.length > 0) {
|
|
94
|
+
groups.set(CATEGORY_FIELD, [
|
|
95
|
+
{ field: CATEGORY_FIELD, value: categoryIds.join(","), conditionType: "in" }
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
return [...groups.values()];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Restricts results to what a storefront may show: enabled products that are visible in
|
|
102
|
+
* the catalog (visibility 2 = Catalog, 4 = Catalog, Search). The admin REST product search
|
|
103
|
+
* does not apply this itself, so without it disabled products and "Not Visible
|
|
104
|
+
* Individually" (1) configurable children would be listed.
|
|
105
|
+
*/
|
|
106
|
+
getStorefrontScopeFilterGroups() {
|
|
107
|
+
return [
|
|
108
|
+
[{ field: "status", value: "1", conditionType: "eq" }],
|
|
109
|
+
[{ field: "visibility", value: "2,4", conditionType: "in" }]
|
|
110
|
+
];
|
|
111
|
+
}
|
|
81
112
|
async createCategoryNavigationFilter(payload) {
|
|
82
113
|
const facetIdentifier = FacetIdentifierSchema.parse({
|
|
83
|
-
key:
|
|
114
|
+
key: CATEGORY_FACET_KEY
|
|
84
115
|
});
|
|
85
116
|
const facetValueIdentifier = FacetValueIdentifierSchema.parse({
|
|
86
117
|
facet: facetIdentifier,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CategoryIdentifierSchema,
|
|
3
|
+
ProductComplianceDataSchema,
|
|
3
4
|
ProductIdentifierSchema
|
|
4
5
|
} from "@reactionary/core";
|
|
5
6
|
import { getProductKey, parseDamImages } from "../../utils/magento-product.js";
|
|
@@ -27,6 +28,9 @@ class MagentoProductFactory {
|
|
|
27
28
|
}
|
|
28
29
|
config;
|
|
29
30
|
productSchema;
|
|
31
|
+
parseComplianceData(context, data) {
|
|
32
|
+
return ProductComplianceDataSchema.parse({});
|
|
33
|
+
}
|
|
30
34
|
parseProduct(_context, data) {
|
|
31
35
|
const sku = data.sku;
|
|
32
36
|
const identifier = ProductIdentifierSchema.parse({ key: getProductKey(data) });
|
|
@@ -50,7 +54,8 @@ class MagentoProductFactory {
|
|
|
50
54
|
published: true,
|
|
51
55
|
sharedAttributes,
|
|
52
56
|
slug,
|
|
53
|
-
variants: []
|
|
57
|
+
variants: [],
|
|
58
|
+
complianceData: this.parseComplianceData(_context, data)
|
|
54
59
|
};
|
|
55
60
|
return this.productSchema.parse(result);
|
|
56
61
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ImageSchema,
|
|
3
|
+
ProductSearchResultItemComplianceDataSchema,
|
|
3
4
|
ProductSearchResultItemVariantSchema,
|
|
4
5
|
ProductVariantIdentifierSchema
|
|
5
6
|
} from "@reactionary/core";
|
|
@@ -48,6 +49,9 @@ class MagentoProductSearchFactory {
|
|
|
48
49
|
};
|
|
49
50
|
return this.productSearchResultSchema.parse(result);
|
|
50
51
|
}
|
|
52
|
+
parseComplianceData(product) {
|
|
53
|
+
return ProductSearchResultItemComplianceDataSchema.parse({});
|
|
54
|
+
}
|
|
51
55
|
parseProductSearchResultItem(_context, product) {
|
|
52
56
|
const idKey = product.id !== void 0 ? String(product.id) : product.sku;
|
|
53
57
|
const identifier = { key: idKey };
|
|
@@ -61,7 +65,8 @@ class MagentoProductSearchFactory {
|
|
|
61
65
|
identifier,
|
|
62
66
|
name,
|
|
63
67
|
slug,
|
|
64
|
-
variants
|
|
68
|
+
variants,
|
|
69
|
+
complianceData: this.parseComplianceData(product)
|
|
65
70
|
};
|
|
66
71
|
}
|
|
67
72
|
parseVariant(variant, product) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reactionary/magento",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"url": "git+https://github.com/Solteq/reactionary.git"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@reactionary/core": "0.10.
|
|
12
|
+
"@reactionary/core": "0.10.26",
|
|
13
13
|
"zod": "4.1.9",
|
|
14
14
|
"debug": "^4.4.3"
|
|
15
15
|
},
|
|
@@ -2,14 +2,37 @@ import { ProductSearchCapability, type Cache, type FacetIdentifier, type FacetVa
|
|
|
2
2
|
import type { MagentoConfiguration } from '../schema/configuration.schema.js';
|
|
3
3
|
import type { MagentoClient } from '../core/client.js';
|
|
4
4
|
import type { MagentoProductSearchFactory } from '../factories/product-search/product-search.factory.js';
|
|
5
|
+
interface MagentoSearchFilter {
|
|
6
|
+
field: string;
|
|
7
|
+
value: string;
|
|
8
|
+
conditionType: 'eq' | 'in' | 'like';
|
|
9
|
+
}
|
|
5
10
|
export declare class MagentoProductSearchCapability<TFactory extends ProductSearchFactory = MagentoProductSearchFactory> extends ProductSearchCapability<ProductSearchFactoryOutput<TFactory>> {
|
|
6
11
|
magentoApi: MagentoClient;
|
|
7
12
|
protected config: MagentoConfiguration;
|
|
8
13
|
protected factory: ProductSearchFactoryWithOutput<TFactory>;
|
|
9
14
|
constructor(config: MagentoConfiguration, cache: Cache, context: RequestContext, magentoApi: MagentoClient, factory: ProductSearchFactoryWithOutput<TFactory>);
|
|
10
15
|
queryByTerm(payload: ProductSearchQueryByTerm): Promise<Result<ProductSearchFactoryOutput<TFactory>>>;
|
|
16
|
+
/**
|
|
17
|
+
* One filter group per facet code: Magento ORs the filters within a group and ANDs
|
|
18
|
+
* the groups, so values of one facet widen the result and distinct facets narrow it.
|
|
19
|
+
* The facet code is the Magento attribute code and the facet value key its (option) value.
|
|
20
|
+
*
|
|
21
|
+
* The category navigation facet created by {@link createCategoryNavigationFilter} is the
|
|
22
|
+
* exception: Magento's `category_id` filter (ProductCategoryFilter) ANDs the filters of a
|
|
23
|
+
* group, so its values are OR-ed through a single `category_id in (...)` filter instead.
|
|
24
|
+
*/
|
|
25
|
+
protected getFacetFilterGroups(facets: FacetValueIdentifier[]): MagentoSearchFilter[][];
|
|
26
|
+
/**
|
|
27
|
+
* Restricts results to what a storefront may show: enabled products that are visible in
|
|
28
|
+
* the catalog (visibility 2 = Catalog, 4 = Catalog, Search). The admin REST product search
|
|
29
|
+
* does not apply this itself, so without it disabled products and "Not Visible
|
|
30
|
+
* Individually" (1) configurable children would be listed.
|
|
31
|
+
*/
|
|
32
|
+
protected getStorefrontScopeFilterGroups(): MagentoSearchFilter[][];
|
|
11
33
|
createCategoryNavigationFilter(payload: ProductSearchQueryCreateNavigationFilter): Promise<Result<FacetValueIdentifier>>;
|
|
12
34
|
protected parseFacetValue(_facetValueIdentifier: FacetValueIdentifier, _label: string, _count: number): ProductSearchResultFacetValue;
|
|
13
35
|
protected parseFacet(_facetIdentifier: FacetIdentifier, _facetValue: unknown): ProductSearchResultFacet;
|
|
14
36
|
protected parseVariant(): never;
|
|
15
37
|
}
|
|
38
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AnyProductSchema, type Image, type ProductAttribute, type ProductFactory, type ProductSchema, type ProductVariant, type RequestContext } from '@reactionary/core';
|
|
1
|
+
import { type AnyProductSchema, type Image, type ProductAttribute, type ProductComplianceData, type ProductFactory, type ProductSchema, type ProductVariant, type RequestContext } from '@reactionary/core';
|
|
2
2
|
import type * as z from 'zod';
|
|
3
3
|
import type { MagentoConfiguration } from '../../schema/configuration.schema.js';
|
|
4
4
|
import type { MagentoProduct } from '../../schema/magento.types.js';
|
|
@@ -6,6 +6,7 @@ export declare class MagentoProductFactory<TProductSchema extends AnyProductSche
|
|
|
6
6
|
protected config: MagentoConfiguration;
|
|
7
7
|
readonly productSchema: TProductSchema;
|
|
8
8
|
constructor(productSchema: TProductSchema, config: MagentoConfiguration);
|
|
9
|
+
protected parseComplianceData(context: RequestContext, data: MagentoProduct): ProductComplianceData;
|
|
9
10
|
parseProduct(_context: RequestContext, data: MagentoProduct): z.output<TProductSchema>;
|
|
10
11
|
protected parseVariant(product: MagentoProduct): ProductVariant;
|
|
11
12
|
protected parseImages(product: MagentoProduct): Image[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AnyProductSearchResultSchema, type ProductSearchFactory, type ProductSearchQueryByTerm, type ProductSearchResultItem, type ProductSearchResultItemVariant, type ProductSearchResultSchema, type RequestContext } from '@reactionary/core';
|
|
1
|
+
import { type AnyProductSearchResultSchema, type ProductSearchFactory, type ProductSearchQueryByTerm, type ProductSearchResultItem, type ProductSearchResultItemComplianceData, type ProductSearchResultItemVariant, type ProductSearchResultSchema, type RequestContext } from '@reactionary/core';
|
|
2
2
|
import type * as z from 'zod';
|
|
3
3
|
import type { MagentoConfiguration } from '../../schema/configuration.schema.js';
|
|
4
4
|
import type { MagentoProduct, MagentoProductSearchResult } from '../../schema/magento.types.js';
|
|
@@ -7,6 +7,7 @@ export declare class MagentoProductSearchFactory<TProductSearchResultSchema exte
|
|
|
7
7
|
readonly productSearchResultSchema: TProductSearchResultSchema;
|
|
8
8
|
constructor(productSearchResultSchema: TProductSearchResultSchema, config: MagentoConfiguration);
|
|
9
9
|
parseSearchResult(context: RequestContext, data: MagentoProductSearchResult, query: ProductSearchQueryByTerm): z.output<TProductSearchResultSchema>;
|
|
10
|
+
protected parseComplianceData(product: MagentoProduct): ProductSearchResultItemComplianceData;
|
|
10
11
|
protected parseProductSearchResultItem(_context: RequestContext, product: MagentoProduct): ProductSearchResultItem;
|
|
11
12
|
protected parseVariant(variant: MagentoProduct, product: MagentoProduct): ProductSearchResultItemVariant;
|
|
12
13
|
}
|
package/utils/magento-product.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ImageSchema,
|
|
3
|
+
ProductSearchResultItemComplianceDataSchema,
|
|
3
4
|
ProductSearchResultItemVariantSchema,
|
|
4
5
|
ProductVariantIdentifierSchema
|
|
5
6
|
} from "@reactionary/core";
|
|
@@ -96,9 +97,13 @@ function buildProductSearchResultItem(config, product) {
|
|
|
96
97
|
identifier: { key: getProductKey(product) },
|
|
97
98
|
name: product.name || product.sku,
|
|
98
99
|
slug: getCustomAttribute(product, "url_key") ?? getCustomAttribute(product, "url_path") ?? "",
|
|
99
|
-
variants
|
|
100
|
+
variants,
|
|
101
|
+
complianceData: buildProductComplianceData(product)
|
|
100
102
|
};
|
|
101
103
|
}
|
|
104
|
+
function buildProductComplianceData(product) {
|
|
105
|
+
return ProductSearchResultItemComplianceDataSchema.parse({});
|
|
106
|
+
}
|
|
102
107
|
export {
|
|
103
108
|
buildMagentoImageUrl,
|
|
104
109
|
buildProductSearchResultItem,
|