@reactionary/source 0.2.15 → 0.2.17
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/src/schemas/models/identifiers.model.ts +1 -1
- package/examples/node/package.json +6 -6
- package/examples/node/src/capabilities/order-search.spec.ts +165 -59
- package/examples/node/src/utils.ts +10 -0
- package/package.json +1 -1
- package/providers/commercetools/src/providers/order-search.provider.ts +2 -2
- package/providers/medusa/src/providers/order-search.provider.ts +3 -2
- package/providers/meilisearch/src/core/initialize.ts +5 -0
- package/providers/meilisearch/src/index.ts +1 -0
- package/providers/meilisearch/src/providers/order-search.provider.ts +224 -0
- package/providers/meilisearch/src/schema/capabilities.schema.ts +1 -0
- package/providers/meilisearch/src/schema/configuration.schema.ts +1 -0
|
@@ -131,7 +131,7 @@ export const OrderSearchIdentifierSchema = z.looseObject({
|
|
|
131
131
|
term: z.string().describe('The search term used to find orders. Not all providers may support term-based search for orders.'),
|
|
132
132
|
partNumber: z.array(z.string()).optional().describe('An optional list part number to filter orders by specific products. Will be ANDed together.'),
|
|
133
133
|
orderStatus: z.array(OrderStatusSchema).optional().describe('An optional list of order statuses to filter the search results.'),
|
|
134
|
-
|
|
134
|
+
user: IdentityIdentifierSchema.optional().describe('An optional user ID to filter orders by specific users. Mostly for b2b usecases with hierachial order access.'),
|
|
135
135
|
startDate: z.string().optional().describe('An optional start date to filter orders from a specific date onwards. ISO8601'),
|
|
136
136
|
endDate: z.string().optional().describe('An optional end date to filter orders up to a specific date. ISO8601'),
|
|
137
137
|
filters: z.array(z.string()).describe('Additional filters applied to the search results.'),
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reactionary/examples-node",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.17",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "src/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@reactionary/core": "0.2.
|
|
8
|
-
"@reactionary/provider-commercetools": "0.2.
|
|
9
|
-
"@reactionary/provider-algolia": "0.2.
|
|
10
|
-
"@reactionary/provider-medusa": "0.2.
|
|
11
|
-
"@reactionary/provider-meilisearch": "0.2.
|
|
7
|
+
"@reactionary/core": "0.2.17",
|
|
8
|
+
"@reactionary/provider-commercetools": "0.2.17",
|
|
9
|
+
"@reactionary/provider-algolia": "0.2.17",
|
|
10
|
+
"@reactionary/provider-medusa": "0.2.17",
|
|
11
|
+
"@reactionary/provider-meilisearch": "0.2.17"
|
|
12
12
|
},
|
|
13
13
|
"type": "module"
|
|
14
14
|
}
|
|
@@ -5,10 +5,12 @@ import type { ProductSearchQueryCreateNavigationFilter } from '@reactionary/core
|
|
|
5
5
|
|
|
6
6
|
const testData = {
|
|
7
7
|
searchTerm: '',
|
|
8
|
-
sku: '0766623301831'
|
|
8
|
+
sku: '0766623301831',
|
|
9
|
+
customerName: 'Eileen Harvey',
|
|
10
|
+
email: 'eileen.harvey@example.com',
|
|
9
11
|
};
|
|
10
12
|
|
|
11
|
-
describe.each([PrimaryProvider.
|
|
13
|
+
describe.each([PrimaryProvider.MEILISEARCH])(
|
|
12
14
|
'Order Search Capability - %s',
|
|
13
15
|
(provider) => {
|
|
14
16
|
let client: ReturnType<typeof createClient>;
|
|
@@ -36,14 +38,12 @@ describe.each([PrimaryProvider.COMMERCETOOLS])(
|
|
|
36
38
|
});
|
|
37
39
|
|
|
38
40
|
it('can be called by guest users', async () => {
|
|
39
|
-
const updatedCart = await client.cart.add(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
);
|
|
41
|
+
const updatedCart = await client.cart.add({
|
|
42
|
+
quantity: 1,
|
|
43
|
+
variant: {
|
|
44
|
+
sku: testData.sku,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
47
|
const identity = await client.identity.getSelf({});
|
|
48
48
|
|
|
49
49
|
if (!identity.success) {
|
|
@@ -70,14 +70,13 @@ describe.each([PrimaryProvider.COMMERCETOOLS])(
|
|
|
70
70
|
|
|
71
71
|
it('can be called by an authenticated user', async () => {
|
|
72
72
|
const time = new Date().getTime();
|
|
73
|
-
const identity = await client.identity.register(
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
);
|
|
73
|
+
const identity = await client.identity.register({
|
|
74
|
+
username: `test-user+${time}@example.com`,
|
|
75
|
+
password: 'love2test',
|
|
76
|
+
});
|
|
79
77
|
|
|
80
78
|
if (!identity.success) {
|
|
79
|
+
|
|
81
80
|
assert.fail();
|
|
82
81
|
}
|
|
83
82
|
|
|
@@ -96,64 +95,171 @@ describe.each([PrimaryProvider.COMMERCETOOLS])(
|
|
|
96
95
|
if (!result.success) {
|
|
97
96
|
assert.fail(JSON.stringify(result.error));
|
|
98
97
|
}
|
|
99
|
-
|
|
100
|
-
});
|
|
101
|
-
it.skip('can filter by startDate', async () => {
|
|
102
|
-
/** TODO */
|
|
103
98
|
});
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
/** TODO */
|
|
107
|
-
});
|
|
100
|
+
describe('filters', () => {
|
|
108
101
|
|
|
109
|
-
it.skip('can filter by orderStatus', async () => {
|
|
110
|
-
/** TODO */
|
|
111
|
-
});
|
|
112
102
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
103
|
+
beforeEach(async () => {
|
|
104
|
+
const time = new Date().getTime();
|
|
105
|
+
const identity = await client.identity.login({ username: testData.email,
|
|
106
|
+
password: 'Test1234!'
|
|
107
|
+
});
|
|
116
108
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
109
|
+
if (!identity.success) {
|
|
110
|
+
assert.fail();
|
|
111
|
+
}
|
|
112
|
+
expect(identity.value.type).toBe('Registered');
|
|
113
|
+
});
|
|
120
114
|
|
|
115
|
+
it('has some test orders', async () => {
|
|
116
|
+
const result = await client.orderSearch.queryByTerm({
|
|
117
|
+
search: {
|
|
118
|
+
term: '',
|
|
119
|
+
paginationOptions: {
|
|
120
|
+
pageNumber: 1,
|
|
121
|
+
pageSize: 10,
|
|
122
|
+
},
|
|
123
|
+
filters: [],
|
|
124
|
+
},
|
|
125
|
+
});
|
|
121
126
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
if (!result.success) {
|
|
128
|
+
assert.fail(JSON.stringify(result.error));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
expect(result.value.items.length).toBeGreaterThan(0);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('can filter by part number', async () => {
|
|
135
|
+
|
|
136
|
+
const result = await client.orderSearch.queryByTerm({
|
|
137
|
+
search: {
|
|
138
|
+
term: '',
|
|
139
|
+
paginationOptions: {
|
|
140
|
+
pageNumber: 1,
|
|
141
|
+
pageSize: 10,
|
|
142
|
+
},
|
|
143
|
+
filters: [],
|
|
129
144
|
},
|
|
130
|
-
|
|
131
|
-
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (!result.success) {
|
|
148
|
+
assert.fail(JSON.stringify(result.error));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
const result2 = await client.orderSearch.queryByTerm({
|
|
153
|
+
search: {
|
|
154
|
+
term: '',
|
|
155
|
+
partNumber: [testData.sku],
|
|
156
|
+
paginationOptions: {
|
|
157
|
+
pageNumber: 1,
|
|
158
|
+
pageSize: 10,
|
|
159
|
+
},
|
|
160
|
+
filters: [],
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
if (!result2.success) {
|
|
165
|
+
assert.fail(JSON.stringify(result2.error));
|
|
166
|
+
}
|
|
167
|
+
expect(result2.value.items.length).toBeGreaterThan(0);
|
|
168
|
+
expect(result2.value.totalCount).toBeLessThanOrEqual(result.value.totalCount);
|
|
132
169
|
});
|
|
133
170
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
171
|
+
it('can filter by search term', async () => {
|
|
172
|
+
const result = await client.orderSearch.queryByTerm({
|
|
173
|
+
search: {
|
|
174
|
+
term: 'cable',
|
|
175
|
+
paginationOptions: {
|
|
176
|
+
pageNumber: 1,
|
|
177
|
+
pageSize: 10,
|
|
178
|
+
},
|
|
179
|
+
filters: [],
|
|
180
|
+
},
|
|
181
|
+
});
|
|
137
182
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
183
|
+
if (!result.success) {
|
|
184
|
+
assert.fail(JSON.stringify(result.error));
|
|
185
|
+
}
|
|
186
|
+
expect(result.value.items.length).toBeGreaterThan(0);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
it('can paginate results', async () => {
|
|
192
|
+
const result = await client.orderSearch.queryByTerm({
|
|
193
|
+
search: {
|
|
194
|
+
term: '',
|
|
195
|
+
paginationOptions: {
|
|
196
|
+
pageNumber: 1,
|
|
197
|
+
pageSize: 1,
|
|
198
|
+
},
|
|
199
|
+
filters: [],
|
|
144
200
|
},
|
|
145
|
-
|
|
146
|
-
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (!result.success) {
|
|
204
|
+
assert.fail(JSON.stringify(result.error));
|
|
205
|
+
}
|
|
206
|
+
expect(result.value.items.length).toBe(1);
|
|
207
|
+
|
|
208
|
+
const result2 = await client.orderSearch.queryByTerm({
|
|
209
|
+
search: {
|
|
210
|
+
term: '',
|
|
211
|
+
paginationOptions: {
|
|
212
|
+
pageNumber: 2,
|
|
213
|
+
pageSize: 1,
|
|
214
|
+
},
|
|
215
|
+
filters: [],
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (!result2.success) {
|
|
220
|
+
assert.fail(JSON.stringify(result2.error));
|
|
221
|
+
}
|
|
222
|
+
expect(result2.value.items.length).toBe(1);
|
|
223
|
+
expect(result2.value.items[0].identifier.key).not.toBe(
|
|
224
|
+
result.value.items[0].identifier.key
|
|
225
|
+
);
|
|
147
226
|
});
|
|
148
227
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
228
|
+
it('can filter by order status', async () => {
|
|
229
|
+
const result = await client.orderSearch.queryByTerm({
|
|
230
|
+
search: {
|
|
231
|
+
term: '',
|
|
232
|
+
orderStatus: ['Shipped'],
|
|
233
|
+
paginationOptions: {
|
|
234
|
+
pageNumber: 1,
|
|
235
|
+
pageSize: 10,
|
|
236
|
+
},
|
|
237
|
+
filters: [],
|
|
238
|
+
},
|
|
239
|
+
});
|
|
152
240
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
241
|
+
if (!result.success) {
|
|
242
|
+
assert.fail(JSON.stringify(result.error));
|
|
243
|
+
}
|
|
244
|
+
expect(result.value.items.length).toBeGreaterThan(0);
|
|
245
|
+
|
|
246
|
+
const result2 = await client.orderSearch.queryByTerm({
|
|
247
|
+
search: {
|
|
248
|
+
term: '',
|
|
249
|
+
orderStatus: ['Cancelled'],
|
|
250
|
+
paginationOptions: {
|
|
251
|
+
pageNumber: 1,
|
|
252
|
+
pageSize: 10,
|
|
253
|
+
},
|
|
254
|
+
filters: [],
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (!result2.success) {
|
|
259
|
+
assert.fail(JSON.stringify(result2.error));
|
|
260
|
+
}
|
|
261
|
+
expect(result2.value.items.length).toBe(0);
|
|
262
|
+
});
|
|
156
263
|
});
|
|
157
264
|
}
|
|
158
|
-
|
|
159
265
|
);
|
|
@@ -23,6 +23,7 @@ export function getMeilisearchTestConfiguration() {
|
|
|
23
23
|
apiUrl: process.env['MEILISEARCH_API_URL'] || '',
|
|
24
24
|
indexName: process.env['MEILISEARCH_INDEX'] || '',
|
|
25
25
|
useAIEmbedding: process.env['MEILISEARCH_USE_AI_EMBEDDING'] || undefined,
|
|
26
|
+
orderIndexName: process.env['MEILISEARCH_ORDER_INDEX'] || 'order',
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -90,6 +91,7 @@ export function createClient(provider: PrimaryProvider) {
|
|
|
90
91
|
order: true,
|
|
91
92
|
price: true,
|
|
92
93
|
productSearch: true,
|
|
94
|
+
orderSearch: true,
|
|
93
95
|
store: true,
|
|
94
96
|
profile: true
|
|
95
97
|
})
|
|
@@ -110,6 +112,7 @@ export function createClient(provider: PrimaryProvider) {
|
|
|
110
112
|
order: true,
|
|
111
113
|
price: true,
|
|
112
114
|
productSearch: true,
|
|
115
|
+
orderSearch: true,
|
|
113
116
|
store: true,
|
|
114
117
|
profile: true,
|
|
115
118
|
})
|
|
@@ -129,6 +132,13 @@ export function createClient(provider: PrimaryProvider) {
|
|
|
129
132
|
builder = builder.withCapability(
|
|
130
133
|
withMeilisearchCapabilities(getMeilisearchTestConfiguration(), {
|
|
131
134
|
productSearch: true,
|
|
135
|
+
orderSearch: true,
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
builder = builder.withCapability(
|
|
139
|
+
withMedusaCapabilities(getMedusaTestConfiguration(), {
|
|
140
|
+
cart: true,
|
|
141
|
+
identity: true,
|
|
132
142
|
})
|
|
133
143
|
);
|
|
134
144
|
}
|
package/package.json
CHANGED
|
@@ -70,8 +70,8 @@ export class CommercetoolsOrderSearchProvider extends OrderSearchProvider {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
if (payload.search.userId) {
|
|
74
|
-
where.push(`customerId="${payload.search.userId}"`);
|
|
73
|
+
if (payload.search.user && payload.search.user.userId) {
|
|
74
|
+
where.push(`customerId="${payload.search.user.userId}"`);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (payload.search.orderStatus) {
|
|
@@ -67,9 +67,10 @@ export class MedusaOrderSearchProvider extends OrderSearchProvider {
|
|
|
67
67
|
if (payload.search.endDate) {
|
|
68
68
|
debug('Searching orders by end date is not supported in Medusa');
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
/*
|
|
71
|
+
if (payload.search.user && payload.search.user.userId) {
|
|
71
72
|
debug('Searching orders by customer ID is not supported in Medusa');
|
|
72
|
-
}
|
|
73
|
+
} */
|
|
73
74
|
const statusFilter: MedusaOrderStatus[] = (payload.search.orderStatus ?? []).map((status) => {
|
|
74
75
|
let retStatus: MedusaOrderStatus = 'draft';
|
|
75
76
|
if (status === 'AwaitingPayment') {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Cache, ClientFromCapabilities, RequestContext } from "@reactionary/core";
|
|
2
2
|
import { MeilisearchSearchProvider } from "../providers/product-search.provider.js";
|
|
3
|
+
import { MeilisearchOrderSearchProvider } from "../providers/order-search.provider.js";
|
|
3
4
|
import type { MeilisearchCapabilities } from "../schema/capabilities.schema.js";
|
|
4
5
|
import type { MeilisearchConfiguration } from "../schema/configuration.schema.js";
|
|
5
6
|
|
|
@@ -11,6 +12,10 @@ export function withMeilisearchCapabilities<T extends MeilisearchCapabilities>(c
|
|
|
11
12
|
client.productSearch = new MeilisearchSearchProvider(configuration, cache, context);
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
if (capabilities.orderSearch) {
|
|
16
|
+
client.orderSearch = new MeilisearchOrderSearchProvider(configuration, cache, context);
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
return client;
|
|
15
20
|
};
|
|
16
21
|
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Cache,
|
|
3
|
+
type OrderSearchQueryByTerm,
|
|
4
|
+
OrderSearchQueryByTermSchema,
|
|
5
|
+
type OrderSearchResult,
|
|
6
|
+
type OrderSearchResultItem,
|
|
7
|
+
OrderSearchResultSchema,
|
|
8
|
+
OrderSearchProvider,
|
|
9
|
+
Reactionary,
|
|
10
|
+
type RequestContext,
|
|
11
|
+
type Result,
|
|
12
|
+
success,
|
|
13
|
+
type OrderStatus,
|
|
14
|
+
type Address,
|
|
15
|
+
type IdentityIdentifier,
|
|
16
|
+
type MonetaryAmount,
|
|
17
|
+
type Currency,
|
|
18
|
+
type OrderSearchIdentifier,
|
|
19
|
+
AddressIdentifierSchema,
|
|
20
|
+
type AddressIdentifier,
|
|
21
|
+
type OrderInventoryStatus,
|
|
22
|
+
} from '@reactionary/core';
|
|
23
|
+
import { MeiliSearch, type SearchParams, type SearchResponse } from 'meilisearch';
|
|
24
|
+
import type { MeilisearchConfiguration } from '../schema/configuration.schema.js';
|
|
25
|
+
|
|
26
|
+
interface MeilisearchNativeOrderAddress {
|
|
27
|
+
address1: string;
|
|
28
|
+
address2: string;
|
|
29
|
+
city: string;
|
|
30
|
+
postalCode: string;
|
|
31
|
+
country: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface MeilisearchNativeOrderRecord {
|
|
35
|
+
orderIdentifier: string;
|
|
36
|
+
userIdentifier: string;
|
|
37
|
+
customerName: string;
|
|
38
|
+
shippingAddress: MeilisearchNativeOrderAddress;
|
|
39
|
+
orderDate: string;
|
|
40
|
+
orderDateTimestamp: number;
|
|
41
|
+
orderStatus: string;
|
|
42
|
+
inventoryStatus: string;
|
|
43
|
+
totalAmount: number;
|
|
44
|
+
currency: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class MeilisearchOrderSearchProvider extends OrderSearchProvider {
|
|
48
|
+
protected config: MeilisearchConfiguration;
|
|
49
|
+
|
|
50
|
+
constructor(config: MeilisearchConfiguration, cache: Cache, context: RequestContext) {
|
|
51
|
+
super(cache, context);
|
|
52
|
+
this.config = config;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Reactionary({
|
|
56
|
+
inputSchema: OrderSearchQueryByTermSchema,
|
|
57
|
+
outputSchema: OrderSearchResultSchema,
|
|
58
|
+
})
|
|
59
|
+
public async queryByTerm(payload: OrderSearchQueryByTerm): Promise<Result<OrderSearchResult>> {
|
|
60
|
+
const client = new MeiliSearch({
|
|
61
|
+
host: this.config.apiUrl,
|
|
62
|
+
apiKey: this.config.apiKey,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const index = client.index(this.config.orderIndexName);
|
|
66
|
+
|
|
67
|
+
const filters: string[] = [];
|
|
68
|
+
|
|
69
|
+
// Add status filter
|
|
70
|
+
if (payload.search.orderStatus && payload.search.orderStatus.length > 0) {
|
|
71
|
+
const statusFilters = payload.search.orderStatus
|
|
72
|
+
.map((status) => `orderStatus = "${this.mapOrderStatus(status)}"`)
|
|
73
|
+
.join(' OR ');
|
|
74
|
+
filters.push(`(${statusFilters})`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Add user ID filter for B2B use cases with hierarchical order access
|
|
78
|
+
if (payload.search.user) {
|
|
79
|
+
filters.push(`userIdentifier = "${payload.search.user.userId}"`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Add date range filters
|
|
83
|
+
if (payload.search.startDate) {
|
|
84
|
+
filters.push(`orderDateTimestamp >= ${new Date(payload.search.startDate).getTime()}`);
|
|
85
|
+
}
|
|
86
|
+
if (payload.search.endDate) {
|
|
87
|
+
filters.push(`orderDateTimestamp <= ${new Date(payload.search.endDate).getTime()}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if (payload.search.partNumber && payload.search.partNumber.length > 0) {
|
|
92
|
+
const partNumberFilters = payload.search.partNumber
|
|
93
|
+
.map((partNumber) => `items.sku = "${partNumber}"`)
|
|
94
|
+
.join(' OR ');
|
|
95
|
+
filters.push(`(${partNumberFilters})`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const searchOptions: SearchParams = {
|
|
99
|
+
offset:
|
|
100
|
+
(payload.search.paginationOptions.pageNumber - 1) *
|
|
101
|
+
payload.search.paginationOptions.pageSize,
|
|
102
|
+
limit: payload.search.paginationOptions.pageSize,
|
|
103
|
+
filter: filters.length > 0 ? filters.join(' AND ') : undefined,
|
|
104
|
+
sort: ['orderDateTimestamp:desc'],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const remote = await index.search<MeilisearchNativeOrderRecord>(
|
|
108
|
+
payload.search.term || '',
|
|
109
|
+
searchOptions
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const result = this.parsePaginatedResult(remote, payload) as OrderSearchResult;
|
|
113
|
+
|
|
114
|
+
return success(result);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected mapOrderStatus(status: OrderStatus): string {
|
|
118
|
+
// Map from Reactionary OrderStatus to Meilisearch native status
|
|
119
|
+
const statusMap: Record<OrderStatus, string> = {
|
|
120
|
+
AwaitingPayment: 'awaiting_payment',
|
|
121
|
+
ReleasedToFulfillment: 'released_to_fulfillment',
|
|
122
|
+
Shipped: 'shipped',
|
|
123
|
+
Cancelled: 'cancelled',
|
|
124
|
+
};
|
|
125
|
+
return statusMap[status] || status;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
protected mapFromNativeOrderStatus(nativeStatus: string): OrderStatus {
|
|
129
|
+
// Map from Meilisearch native status to Reactionary OrderStatus
|
|
130
|
+
const statusMap: Record<string, OrderStatus> = {
|
|
131
|
+
awaiting_payment: 'AwaitingPayment',
|
|
132
|
+
released_to_fulfillment: 'ReleasedToFulfillment',
|
|
133
|
+
shipped: 'Shipped',
|
|
134
|
+
cancelled: 'Cancelled',
|
|
135
|
+
};
|
|
136
|
+
return statusMap[nativeStatus] || 'AwaitingPayment';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
protected mapFromNativeInventoryStatus(nativeStatus: string): OrderInventoryStatus {
|
|
140
|
+
// Map from Meilisearch native status to Reactionary OrderInventoryStatus
|
|
141
|
+
const statusMap: Record<string, OrderInventoryStatus> = {
|
|
142
|
+
not_allocated: 'NotAllocated',
|
|
143
|
+
allocated: 'Allocated',
|
|
144
|
+
preordered: 'Preordered',
|
|
145
|
+
backordered: 'Backordered',
|
|
146
|
+
};
|
|
147
|
+
return statusMap[nativeStatus] || 'NotAllocated';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
protected composeAddressFromNativeAddress(
|
|
151
|
+
nativeAddress: MeilisearchNativeOrderAddress
|
|
152
|
+
): Address {
|
|
153
|
+
return {
|
|
154
|
+
identifier: AddressIdentifierSchema.parse({
|
|
155
|
+
nickName: 'shipping',
|
|
156
|
+
} satisfies AddressIdentifier),
|
|
157
|
+
firstName: '',
|
|
158
|
+
lastName: '',
|
|
159
|
+
streetAddress: nativeAddress.address1,
|
|
160
|
+
streetNumber: nativeAddress.address2,
|
|
161
|
+
city: nativeAddress.city,
|
|
162
|
+
postalCode: nativeAddress.postalCode,
|
|
163
|
+
countryCode: nativeAddress.country,
|
|
164
|
+
region: '',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
protected parseSingle(body: MeilisearchNativeOrderRecord): OrderSearchResultItem {
|
|
169
|
+
const identifier = { key: body.orderIdentifier };
|
|
170
|
+
const userId: IdentityIdentifier = {
|
|
171
|
+
userId: body.userIdentifier,
|
|
172
|
+
};
|
|
173
|
+
const customerName = body.customerName;
|
|
174
|
+
const shippingAddress = this.composeAddressFromNativeAddress(body.shippingAddress);
|
|
175
|
+
const orderDate = body.orderDate;
|
|
176
|
+
const orderStatus = this.mapFromNativeOrderStatus(body.orderStatus);
|
|
177
|
+
const inventoryStatus = this.mapFromNativeInventoryStatus(body.inventoryStatus);
|
|
178
|
+
|
|
179
|
+
const totalAmount: MonetaryAmount = {
|
|
180
|
+
currency: (body.currency || this.context.languageContext.currencyCode) as Currency,
|
|
181
|
+
value: body.totalAmount,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const order = {
|
|
185
|
+
identifier,
|
|
186
|
+
userId,
|
|
187
|
+
customerName,
|
|
188
|
+
shippingAddress,
|
|
189
|
+
orderDate,
|
|
190
|
+
orderStatus,
|
|
191
|
+
inventoryStatus,
|
|
192
|
+
totalAmount,
|
|
193
|
+
} satisfies OrderSearchResultItem;
|
|
194
|
+
|
|
195
|
+
return order;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
protected parsePaginatedResult(
|
|
199
|
+
body: SearchResponse<MeilisearchNativeOrderRecord>,
|
|
200
|
+
query: OrderSearchQueryByTerm
|
|
201
|
+
): OrderSearchResult {
|
|
202
|
+
const identifier = {
|
|
203
|
+
...query.search,
|
|
204
|
+
} satisfies OrderSearchIdentifier;
|
|
205
|
+
|
|
206
|
+
const orders: OrderSearchResultItem[] = body.hits.map((hit) => {
|
|
207
|
+
return this.parseSingle(hit);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const totalCount = body.estimatedTotalHits || body.hits.length;
|
|
211
|
+
const totalPages = Math.ceil(totalCount / (body.limit || 1));
|
|
212
|
+
|
|
213
|
+
const result = {
|
|
214
|
+
identifier,
|
|
215
|
+
pageNumber: Math.floor((body.offset || 0) / (body.limit || 1) ) + 1,
|
|
216
|
+
pageSize: body.limit || orders.length,
|
|
217
|
+
totalCount,
|
|
218
|
+
totalPages,
|
|
219
|
+
items: orders,
|
|
220
|
+
} satisfies OrderSearchResult;
|
|
221
|
+
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
}
|