@reactionary/provider-commercetools 0.3.15 → 0.3.16

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.
@@ -18,7 +18,8 @@ import {
18
18
  CommercetoolsProfileProvider,
19
19
  CommercetoolsStoreProvider,
20
20
  CommercetoolsProductReviewsProvider,
21
- CommercetoolsProductAssociationsProvider
21
+ CommercetoolsProductAssociationsProvider,
22
+ CommercetoolsProductListProvider
22
23
  } from "../providers/index.js";
23
24
  import { CommercetoolsAPI } from "./client.js";
24
25
  function withCommercetoolsCapabilities(configuration, capabilities) {
@@ -59,6 +60,14 @@ function withCommercetoolsCapabilities(configuration, capabilities) {
59
60
  commercetoolsApi
60
61
  );
61
62
  }
63
+ if (caps.productList) {
64
+ client.productList = new CommercetoolsProductListProvider(
65
+ config,
66
+ cache,
67
+ context,
68
+ commercetoolsApi
69
+ );
70
+ }
62
71
  if (caps.productReviews) {
63
72
  client.productReviews = new CommercetoolsProductReviewsProvider(
64
73
  config,
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@reactionary/provider-commercetools",
3
- "version": "0.3.15",
3
+ "version": "0.3.16",
4
4
  "main": "index.js",
5
5
  "types": "src/index.d.ts",
6
6
  "dependencies": {
7
- "@reactionary/core": "0.3.15",
7
+ "@reactionary/core": "0.3.16",
8
8
  "debug": "^4.4.3",
9
9
  "zod": "4.1.9",
10
10
  "@commercetools/ts-client": "^4.2.1",
@@ -5,6 +5,7 @@ export * from "./inventory.provider.js";
5
5
  export * from "./order-search.provider.js";
6
6
  export * from "./price.provider.js";
7
7
  export * from "./product-associations.provider.js";
8
+ export * from "./product-list.provider.js";
8
9
  export * from "./product.provider.js";
9
10
  export * from "./product-reviews.provider.js";
10
11
  export * from "./profile.provider.js";
@@ -0,0 +1,389 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ error,
14
+ ProductListItemMutationCreateSchema,
15
+ ProductListItemMutationDeleteSchema,
16
+ ProductListItemMutationUpdateSchema,
17
+ ProductListItemPaginatedResultsSchema,
18
+ ProductListItemQuerySchema,
19
+ ProductListItemSchema,
20
+ ProductListMutationCreateSchema,
21
+ ProductListMutationDeleteSchema,
22
+ ProductListMutationUpdateSchema,
23
+ ProductListPaginatedResultsSchema,
24
+ ProductListProvider,
25
+ ProductListQueryByIdSchema,
26
+ ProductListQuerySchema,
27
+ ProductListSchema,
28
+ Reactionary,
29
+ success
30
+ } from "@reactionary/core";
31
+ class CommercetoolsProductListProvider extends ProductListProvider {
32
+ constructor(config, cache, context, commercetools) {
33
+ super(cache, context);
34
+ this.config = config;
35
+ this.commercetools = commercetools;
36
+ }
37
+ async getClient() {
38
+ const client = await this.commercetools.getClient();
39
+ return client.withProjectKey({ projectKey: this.config.projectKey }).me();
40
+ }
41
+ async getById(payload) {
42
+ try {
43
+ const client = await this.getClient();
44
+ const response = await client.shoppingLists().withId({ ID: payload.identifier.key }).get().execute();
45
+ return success(this.parseSingle(response.body));
46
+ } catch (err) {
47
+ if (err.statusCode === 404) {
48
+ return error({
49
+ type: "NotFound",
50
+ identifier: payload
51
+ });
52
+ } else {
53
+ throw err;
54
+ }
55
+ }
56
+ }
57
+ async queryLists(payload) {
58
+ if (this.context.session.identityContext?.identity?.type !== "Registered") {
59
+ return success({
60
+ items: [],
61
+ pageNumber: 1,
62
+ pageSize: payload.search.paginationOptions.pageSize,
63
+ totalCount: 0,
64
+ totalPages: 0,
65
+ identifier: payload.search
66
+ });
67
+ }
68
+ const client = await this.getClient();
69
+ const response = await client.shoppingLists().get({
70
+ queryArgs: {
71
+ where: `custom(fields(listType=:listType))`,
72
+ sort: "createdAt desc",
73
+ "var.listType": payload.search.listType,
74
+ limit: payload.search.paginationOptions.pageSize,
75
+ offset: (payload.search.paginationOptions.pageNumber - 1) * payload.search.paginationOptions.pageSize
76
+ }
77
+ }).execute();
78
+ return success({
79
+ items: response.body.results.map((list) => this.parseSingle(list)),
80
+ pageNumber: payload.search.paginationOptions.pageNumber,
81
+ pageSize: payload.search.paginationOptions.pageSize,
82
+ totalCount: response.body.total || 0,
83
+ totalPages: Math.ceil((response.body.total || 0) / payload.search.paginationOptions.pageSize),
84
+ identifier: payload.search
85
+ });
86
+ }
87
+ async addList(mutation) {
88
+ const client = await this.getClient();
89
+ const localeString = this.getLocaleString();
90
+ if (this.context.session.identityContext?.identity?.type !== "Registered") {
91
+ return error({
92
+ type: "InvalidInput",
93
+ error: "Only registered users can have product lists"
94
+ });
95
+ }
96
+ const draft = {
97
+ name: { [localeString]: mutation.list.name },
98
+ description: mutation.list.description ? { [localeString]: mutation.list.description } : void 0,
99
+ custom: {
100
+ type: {
101
+ key: "reactionaryShoppingList",
102
+ typeId: "type"
103
+ },
104
+ fields: {
105
+ listType: mutation.list.type,
106
+ imageUrl: mutation.list.image ? mutation.list.image.sourceUrl : void 0,
107
+ publishedDate: mutation.list.publishDate ? new Date(mutation.list.publishDate) : void 0,
108
+ published: mutation.list.published || true
109
+ }
110
+ }
111
+ };
112
+ const response = await client.shoppingLists().post({
113
+ body: draft
114
+ }).execute();
115
+ return success(this.parseSingle(response.body));
116
+ }
117
+ async updateList(mutation) {
118
+ const client = await this.getClient();
119
+ const actions = [];
120
+ const localeString = this.getLocaleString();
121
+ if (mutation.name) {
122
+ actions.push({
123
+ action: "changeName",
124
+ name: { [localeString]: mutation.name }
125
+ });
126
+ }
127
+ if (mutation.description !== void 0) {
128
+ actions.push({
129
+ action: "setDescription",
130
+ description: mutation.description ? { [localeString]: mutation.description } : void 0
131
+ });
132
+ }
133
+ if (mutation.published) {
134
+ actions.push({
135
+ action: "setCustomField",
136
+ name: "published",
137
+ value: mutation.published
138
+ });
139
+ }
140
+ if (mutation.publishDate) {
141
+ actions.push({
142
+ action: "setCustomField",
143
+ name: "publishedDate",
144
+ value: new Date(mutation.publishDate)
145
+ });
146
+ }
147
+ if (mutation.image) {
148
+ actions.push({
149
+ action: "setCustomField",
150
+ name: "imageUrl",
151
+ value: mutation.image.sourceUrl
152
+ });
153
+ }
154
+ const update = {
155
+ version: 0,
156
+ // The auto-correcting middleware will deal with the version
157
+ actions
158
+ };
159
+ const response = await client.shoppingLists().withId({ ID: mutation.list.key }).post({ body: update }).execute();
160
+ return success(this.parseSingle(response.body));
161
+ }
162
+ async deleteList(mutation) {
163
+ const client = await this.getClient();
164
+ const newestVersion = await client.shoppingLists().withId({ ID: mutation.list.key }).get().execute().then((response) => response.body.version);
165
+ await client.shoppingLists().withId({ ID: mutation.list.key }).delete({
166
+ queryArgs: {
167
+ version: newestVersion
168
+ }
169
+ }).execute();
170
+ return success(void 0);
171
+ }
172
+ async queryListItems(query) {
173
+ const client = await this.getClient();
174
+ const response = await client.shoppingLists().withId({ ID: query.search.list.key }).get({
175
+ queryArgs: { expand: "lineItems[*].variant" }
176
+ }).execute();
177
+ const items = response.body.lineItems.map((lineItem) => this.parseProductListItem(query.search.list, lineItem));
178
+ return success({
179
+ items: items.slice((query.search.paginationOptions.pageNumber - 1) * query.search.paginationOptions.pageSize, query.search.paginationOptions.pageNumber * query.search.paginationOptions.pageSize),
180
+ identifier: query.search,
181
+ pageNumber: query.search.paginationOptions.pageNumber,
182
+ pageSize: query.search.paginationOptions.pageSize,
183
+ totalCount: items.length,
184
+ totalPages: Math.ceil(items.length / query.search.paginationOptions.pageSize)
185
+ });
186
+ }
187
+ async addItem(mutation) {
188
+ const client = await this.getClient();
189
+ const lineItemDraft = {
190
+ action: "addLineItem",
191
+ sku: mutation.listItem.variant.sku,
192
+ quantity: mutation.listItem.quantity,
193
+ custom: {
194
+ type: {
195
+ key: "reactionaryShoppingListItem",
196
+ typeId: "type"
197
+ },
198
+ fields: {
199
+ notes: mutation.listItem.notes || "",
200
+ order: mutation.listItem.order || 1
201
+ }
202
+ }
203
+ };
204
+ const update = {
205
+ version: 0,
206
+ // The auto-correcting middleware will deal with the version
207
+ actions: [lineItemDraft]
208
+ };
209
+ const response = await client.shoppingLists().withId({ ID: mutation.list.key }).post({ body: update, queryArgs: { expand: "lineItems[*].variant" } }).execute();
210
+ const lastItem = response.body.lineItems[response.body.lineItems.length - 1];
211
+ if (lastItem.variant?.sku !== mutation.listItem.variant.sku) {
212
+ throw new Error("The added line item is not the last item in the list, cannot reliably determine the identifier of the added item");
213
+ }
214
+ return success(this.parseProductListItem(mutation.list, lastItem));
215
+ }
216
+ async deleteItem(mutation) {
217
+ const client = await this.getClient();
218
+ const update = {
219
+ version: 0,
220
+ // The auto-correcting middleware will deal with the version
221
+ actions: [{
222
+ action: "removeLineItem",
223
+ lineItemId: mutation.listItem.key
224
+ }]
225
+ };
226
+ await client.shoppingLists().withId({ ID: mutation.listItem.list.key }).post({ body: update }).execute();
227
+ return success(void 0);
228
+ }
229
+ async updateItem(mutation) {
230
+ const client = await this.getClient();
231
+ const actions = [];
232
+ if (mutation.quantity !== void 0) {
233
+ actions.push({
234
+ action: "changeLineItemQuantity",
235
+ lineItemId: mutation.listItem.key,
236
+ quantity: mutation.quantity
237
+ });
238
+ }
239
+ if (mutation.notes !== void 0) {
240
+ actions.push({
241
+ action: "setLineItemCustomField",
242
+ lineItemId: mutation.listItem.key,
243
+ name: "notes",
244
+ value: mutation.notes
245
+ });
246
+ }
247
+ if (mutation.order !== void 0) {
248
+ actions.push({
249
+ action: "setLineItemCustomField",
250
+ lineItemId: mutation.listItem.key,
251
+ name: "order",
252
+ value: mutation.order
253
+ });
254
+ }
255
+ const update = {
256
+ version: 0,
257
+ actions
258
+ };
259
+ const response = await client.shoppingLists().withId({ ID: mutation.listItem.list.key }).post({ body: update, queryArgs: { expand: "lineItems[*].variant" } }).execute();
260
+ const updatedLineItem = response.body.lineItems.find((item) => item.id === mutation.listItem.key);
261
+ if (!updatedLineItem) {
262
+ throw new Error("Failed to find the updated line item in response");
263
+ }
264
+ return success(this.parseProductListItem(mutation.listItem.list, updatedLineItem));
265
+ }
266
+ /**
267
+ * It is not clear to me, why i'd want my CUSTOMER defined resources to be localized, since that means, if he changes visual language,
268
+ * the names of his lists will disappear, since they are not translated. But maybe there are usecases for this, so we should support it, but default to english.
269
+ **/
270
+ getLocaleString() {
271
+ return "en";
272
+ }
273
+ parseSingle(list) {
274
+ const localeString = this.getLocaleString();
275
+ const listType = list.custom?.fields["listType"] || "favorite";
276
+ const image = list.custom?.fields["imageUrl"];
277
+ const published = list.custom?.fields["published"] && true;
278
+ const publishDateDate = list.custom?.fields["publishedDate"];
279
+ let publishedDate;
280
+ if (publishDateDate) {
281
+ publishedDate = new Date(publishDateDate).toISOString();
282
+ }
283
+ return {
284
+ identifier: {
285
+ listType,
286
+ key: list.id
287
+ },
288
+ type: listType,
289
+ name: list.name[localeString] || "Unnamed List",
290
+ description: list.description?.[localeString] || "",
291
+ published,
292
+ publishDate: publishedDate,
293
+ image: {
294
+ sourceUrl: image || "",
295
+ altText: list.name[localeString] || "List Image"
296
+ }
297
+ };
298
+ }
299
+ parseProductListItem(listIdentifier, lineItem) {
300
+ const localeString = this.getLocaleString();
301
+ return {
302
+ identifier: {
303
+ list: listIdentifier,
304
+ key: lineItem.id
305
+ },
306
+ variant: {
307
+ sku: lineItem.variant?.sku || ""
308
+ },
309
+ quantity: lineItem.quantity,
310
+ notes: lineItem.custom?.fields["notes"] || "",
311
+ order: lineItem.custom?.fields["order"] || 1
312
+ // Commercetools doesn't have explicit ordering
313
+ };
314
+ }
315
+ }
316
+ __decorateClass([
317
+ Reactionary({
318
+ cache: true,
319
+ cacheTimeToLiveInSeconds: 300,
320
+ currencyDependentCaching: false,
321
+ localeDependentCaching: false,
322
+ inputSchema: ProductListQueryByIdSchema,
323
+ outputSchema: ProductListSchema
324
+ })
325
+ ], CommercetoolsProductListProvider.prototype, "getById", 1);
326
+ __decorateClass([
327
+ Reactionary({
328
+ cache: true,
329
+ cacheTimeToLiveInSeconds: 300,
330
+ currencyDependentCaching: false,
331
+ localeDependentCaching: false,
332
+ inputSchema: ProductListQuerySchema,
333
+ outputSchema: ProductListPaginatedResultsSchema
334
+ })
335
+ ], CommercetoolsProductListProvider.prototype, "queryLists", 1);
336
+ __decorateClass([
337
+ Reactionary({
338
+ cache: false,
339
+ // Mutations should not be cached
340
+ inputSchema: ProductListMutationCreateSchema,
341
+ outputSchema: ProductListSchema
342
+ })
343
+ ], CommercetoolsProductListProvider.prototype, "addList", 1);
344
+ __decorateClass([
345
+ Reactionary({
346
+ cache: false,
347
+ inputSchema: ProductListMutationUpdateSchema,
348
+ outputSchema: ProductListSchema
349
+ })
350
+ ], CommercetoolsProductListProvider.prototype, "updateList", 1);
351
+ __decorateClass([
352
+ Reactionary({
353
+ cache: false,
354
+ inputSchema: ProductListMutationDeleteSchema
355
+ })
356
+ ], CommercetoolsProductListProvider.prototype, "deleteList", 1);
357
+ __decorateClass([
358
+ Reactionary({
359
+ cache: true,
360
+ cacheTimeToLiveInSeconds: 300,
361
+ currencyDependentCaching: false,
362
+ localeDependentCaching: false,
363
+ inputSchema: ProductListItemQuerySchema,
364
+ outputSchema: ProductListItemPaginatedResultsSchema
365
+ })
366
+ ], CommercetoolsProductListProvider.prototype, "queryListItems", 1);
367
+ __decorateClass([
368
+ Reactionary({
369
+ cache: false,
370
+ inputSchema: ProductListItemMutationCreateSchema,
371
+ outputSchema: ProductListItemSchema
372
+ })
373
+ ], CommercetoolsProductListProvider.prototype, "addItem", 1);
374
+ __decorateClass([
375
+ Reactionary({
376
+ cache: false,
377
+ inputSchema: ProductListItemMutationDeleteSchema
378
+ })
379
+ ], CommercetoolsProductListProvider.prototype, "deleteItem", 1);
380
+ __decorateClass([
381
+ Reactionary({
382
+ cache: false,
383
+ inputSchema: ProductListItemMutationUpdateSchema,
384
+ outputSchema: ProductListItemSchema
385
+ })
386
+ ], CommercetoolsProductListProvider.prototype, "updateItem", 1);
387
+ export {
388
+ CommercetoolsProductListProvider
389
+ };
@@ -4,6 +4,7 @@ const CommercetoolsCapabilitiesSchema = CapabilitiesSchema.pick({
4
4
  productSearch: true,
5
5
  productAssociations: true,
6
6
  productReviews: true,
7
+ productList: true,
7
8
  identity: true,
8
9
  cart: true,
9
10
  checkout: true,
@@ -5,6 +5,7 @@ export * from './inventory.provider.js';
5
5
  export * from './order-search.provider.js';
6
6
  export * from './price.provider.js';
7
7
  export * from './product-associations.provider.js';
8
+ export * from './product-list.provider.js';
8
9
  export * from './product.provider.js';
9
10
  export * from './product-reviews.provider.js';
10
11
  export * from './profile.provider.js';
@@ -0,0 +1,27 @@
1
+ import type { ShoppingList, ShoppingListLineItem } from '@commercetools/platform-sdk';
2
+ import type { Cache, ProductList, ProductListIdentifier, ProductListItem, ProductListItemMutationCreate, ProductListItemMutationDelete, ProductListItemMutationUpdate, ProductListItemPaginatedResult, ProductListItemsQuery, ProductListMutationCreate, ProductListMutationDelete, ProductListMutationUpdate, ProductListPaginatedResult, ProductListQuery, ProductListQueryById, RequestContext, Result } from '@reactionary/core';
3
+ import { ProductListProvider } from '@reactionary/core';
4
+ import type { CommercetoolsAPI } from '../core/client.js';
5
+ import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
6
+ export declare class CommercetoolsProductListProvider extends ProductListProvider {
7
+ protected config: CommercetoolsConfiguration;
8
+ protected commercetools: CommercetoolsAPI;
9
+ constructor(config: CommercetoolsConfiguration, cache: Cache, context: RequestContext, commercetools: CommercetoolsAPI);
10
+ protected getClient(): Promise<import("@commercetools/platform-sdk").ByProjectKeyMeRequestBuilder>;
11
+ getById(payload: ProductListQueryById): Promise<Result<ProductList>>;
12
+ queryLists(payload: ProductListQuery): Promise<Result<ProductListPaginatedResult>>;
13
+ addList(mutation: ProductListMutationCreate): Promise<Result<ProductList>>;
14
+ updateList(mutation: ProductListMutationUpdate): Promise<Result<ProductList>>;
15
+ deleteList(mutation: ProductListMutationDelete): Promise<Result<void>>;
16
+ queryListItems(query: ProductListItemsQuery): Promise<Result<ProductListItemPaginatedResult>>;
17
+ addItem(mutation: ProductListItemMutationCreate): Promise<Result<ProductListItem>>;
18
+ deleteItem(mutation: ProductListItemMutationDelete): Promise<Result<void>>;
19
+ updateItem(mutation: ProductListItemMutationUpdate): Promise<Result<ProductListItem>>;
20
+ /**
21
+ * It is not clear to me, why i'd want my CUSTOMER defined resources to be localized, since that means, if he changes visual language,
22
+ * the names of his lists will disappear, since they are not translated. But maybe there are usecases for this, so we should support it, but default to english.
23
+ **/
24
+ protected getLocaleString(): string;
25
+ protected parseSingle(list: ShoppingList): ProductList;
26
+ protected parseProductListItem(listIdentifier: ProductListIdentifier, lineItem: ShoppingListLineItem): ProductListItem;
27
+ }
@@ -13,6 +13,7 @@ export declare const CommercetoolsCapabilitiesSchema: z.ZodObject<{
13
13
  productSearch: z.ZodOptional<z.ZodBoolean>;
14
14
  productAssociations: z.ZodOptional<z.ZodBoolean>;
15
15
  productReviews: z.ZodOptional<z.ZodBoolean>;
16
+ productList: z.ZodOptional<z.ZodBoolean>;
16
17
  orderSearch: z.ZodOptional<z.ZodBoolean>;
17
18
  }, z.core.$loose>;
18
19
  export type CommercetoolsCapabilities = z.infer<typeof CommercetoolsCapabilitiesSchema>;