@reactionary/provider-commercetools 0.3.14 → 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.
- package/core/initialize.js +19 -1
- package/index.js +1 -0
- package/package.json +2 -2
- package/providers/index.js +2 -0
- package/providers/product-associations.provider.js +164 -0
- package/providers/product-list.provider.js +389 -0
- package/providers/product.provider.js +6 -1
- package/schema/capabilities.schema.js +2 -0
- package/src/index.d.ts +1 -0
- package/src/providers/index.d.ts +2 -0
- package/src/providers/product-associations.provider.d.ts +24 -0
- package/src/providers/product-list.provider.d.ts +27 -0
- package/src/schema/capabilities.schema.d.ts +2 -0
package/core/initialize.js
CHANGED
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
CommercetoolsOrderSearchProvider,
|
|
18
18
|
CommercetoolsProfileProvider,
|
|
19
19
|
CommercetoolsStoreProvider,
|
|
20
|
-
CommercetoolsProductReviewsProvider
|
|
20
|
+
CommercetoolsProductReviewsProvider,
|
|
21
|
+
CommercetoolsProductAssociationsProvider,
|
|
22
|
+
CommercetoolsProductListProvider
|
|
21
23
|
} from "../providers/index.js";
|
|
22
24
|
import { CommercetoolsAPI } from "./client.js";
|
|
23
25
|
function withCommercetoolsCapabilities(configuration, capabilities) {
|
|
@@ -50,6 +52,22 @@ function withCommercetoolsCapabilities(configuration, capabilities) {
|
|
|
50
52
|
commercetoolsApi
|
|
51
53
|
);
|
|
52
54
|
}
|
|
55
|
+
if (caps.productAssociations) {
|
|
56
|
+
client.productAssociations = new CommercetoolsProductAssociationsProvider(
|
|
57
|
+
config,
|
|
58
|
+
cache,
|
|
59
|
+
context,
|
|
60
|
+
commercetoolsApi
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (caps.productList) {
|
|
64
|
+
client.productList = new CommercetoolsProductListProvider(
|
|
65
|
+
config,
|
|
66
|
+
cache,
|
|
67
|
+
context,
|
|
68
|
+
commercetoolsApi
|
|
69
|
+
);
|
|
70
|
+
}
|
|
53
71
|
if (caps.productReviews) {
|
|
54
72
|
client.productReviews = new CommercetoolsProductReviewsProvider(
|
|
55
73
|
config,
|
package/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from "./providers/identity.provider.js";
|
|
|
5
5
|
export * from "./providers/inventory.provider.js";
|
|
6
6
|
export * from "./providers/order-search.provider.js";
|
|
7
7
|
export * from "./providers/price.provider.js";
|
|
8
|
+
export * from "./providers/product-associations.provider.js";
|
|
8
9
|
export * from "./providers/product.provider.js";
|
|
9
10
|
export * from "./providers/product-reviews.provider.js";
|
|
10
11
|
export * from "./providers/product-search.provider.js";
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reactionary/provider-commercetools",
|
|
3
|
-
"version": "0.3.
|
|
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.
|
|
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",
|
package/providers/index.js
CHANGED
|
@@ -4,6 +4,8 @@ export * from "./identity.provider.js";
|
|
|
4
4
|
export * from "./inventory.provider.js";
|
|
5
5
|
export * from "./order-search.provider.js";
|
|
6
6
|
export * from "./price.provider.js";
|
|
7
|
+
export * from "./product-associations.provider.js";
|
|
8
|
+
export * from "./product-list.provider.js";
|
|
7
9
|
export * from "./product.provider.js";
|
|
8
10
|
export * from "./product-reviews.provider.js";
|
|
9
11
|
export * from "./profile.provider.js";
|
|
@@ -0,0 +1,164 @@
|
|
|
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
|
+
ProductAssociationsProvider,
|
|
14
|
+
Reactionary,
|
|
15
|
+
ImageSchema,
|
|
16
|
+
ProductVariantOptionSchema,
|
|
17
|
+
ProductOptionIdentifierSchema,
|
|
18
|
+
ProductSearchResultItemVariantSchema,
|
|
19
|
+
ProductVariantIdentifierSchema,
|
|
20
|
+
success,
|
|
21
|
+
error
|
|
22
|
+
} from "@reactionary/core";
|
|
23
|
+
class CommercetoolsProductAssociationsProvider extends ProductAssociationsProvider {
|
|
24
|
+
constructor(config, cache, context, commercetools) {
|
|
25
|
+
super(cache, context);
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.commercetools = commercetools;
|
|
28
|
+
}
|
|
29
|
+
async getClient() {
|
|
30
|
+
const client = await this.commercetools.getClient();
|
|
31
|
+
return client.withProjectKey({ projectKey: this.config.projectKey });
|
|
32
|
+
}
|
|
33
|
+
async fetchAssociatedProductsFor(productKey, maxNumberOfAssociations, attributeName) {
|
|
34
|
+
const client = await this.getClient();
|
|
35
|
+
const product = await client.productProjections().withKey({ key: productKey.key }).get().execute().catch((e) => {
|
|
36
|
+
if (e.statusCode === 404) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
throw e;
|
|
40
|
+
});
|
|
41
|
+
if (!product || !product.body) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const associatedProducts = [];
|
|
45
|
+
const associationsAttr = product.body.attributes.find((attr) => attr.name === attributeName);
|
|
46
|
+
if (!associationsAttr) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const associatedProductSearchItems = [];
|
|
50
|
+
const allAssociatedProductPromises = associationsAttr.value.map(async (associatedProductId) => {
|
|
51
|
+
const associatedProduct = client.productProjections().withKey({ key: associatedProductId }).get().execute().catch(() => null);
|
|
52
|
+
return associatedProduct;
|
|
53
|
+
});
|
|
54
|
+
const associatedProductsResults = await Promise.all(allAssociatedProductPromises);
|
|
55
|
+
const validAssociatedProductsResults = associatedProductsResults.filter((result) => !!result).slice(0, maxNumberOfAssociations);
|
|
56
|
+
for (const associatedProductResult of validAssociatedProductsResults) {
|
|
57
|
+
const resultItem = this.parseSingle(associatedProductResult.body);
|
|
58
|
+
if (resultItem) {
|
|
59
|
+
associatedProductSearchItems.push(resultItem);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return associatedProductSearchItems;
|
|
63
|
+
}
|
|
64
|
+
parseSingle(body) {
|
|
65
|
+
const identifier = { key: body.id };
|
|
66
|
+
const name = body.name[this.context.languageContext.locale] || body.id;
|
|
67
|
+
const slug = body.slug?.[this.context.languageContext.locale] || body.id;
|
|
68
|
+
const variants = [body.masterVariant, ...body.variants].map(
|
|
69
|
+
(variant) => this.parseVariant(variant, body)
|
|
70
|
+
);
|
|
71
|
+
const product = {
|
|
72
|
+
identifier,
|
|
73
|
+
name,
|
|
74
|
+
slug,
|
|
75
|
+
variants
|
|
76
|
+
};
|
|
77
|
+
return product;
|
|
78
|
+
}
|
|
79
|
+
async getAccessories(query) {
|
|
80
|
+
const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfAccessories || 4, "reactionaryaccessories");
|
|
81
|
+
const result = associatedProducts.map((product) => ({
|
|
82
|
+
associationIdentifier: {
|
|
83
|
+
key: `${query.forProduct.key}-accessory-${product.identifier.key}`
|
|
84
|
+
},
|
|
85
|
+
associationReturnType: "productSearchResultItem",
|
|
86
|
+
product
|
|
87
|
+
}));
|
|
88
|
+
return success(result);
|
|
89
|
+
}
|
|
90
|
+
async getSpareparts(query) {
|
|
91
|
+
const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfSpareparts || 4, "reactionaryspareparts");
|
|
92
|
+
const result = associatedProducts.map((product) => ({
|
|
93
|
+
associationIdentifier: {
|
|
94
|
+
key: `${query.forProduct.key}-sparepart-${product.identifier.key}`
|
|
95
|
+
},
|
|
96
|
+
associationReturnType: "productSearchResultItem",
|
|
97
|
+
product
|
|
98
|
+
}));
|
|
99
|
+
return success(result);
|
|
100
|
+
}
|
|
101
|
+
async getReplacements(query) {
|
|
102
|
+
const associatedProducts = await this.fetchAssociatedProductsFor(query.forProduct, query.numberOfReplacements || 4, "reactionaryreplacements");
|
|
103
|
+
const result = associatedProducts.map((product) => ({
|
|
104
|
+
associationIdentifier: {
|
|
105
|
+
key: `${query.forProduct.key}-replacement-${product.identifier.key}`
|
|
106
|
+
},
|
|
107
|
+
associationReturnType: "productSearchResultItem",
|
|
108
|
+
product
|
|
109
|
+
}));
|
|
110
|
+
return success(result);
|
|
111
|
+
}
|
|
112
|
+
parseVariant(variant, product) {
|
|
113
|
+
const sourceImage = variant.images?.[0];
|
|
114
|
+
const img = ImageSchema.parse({
|
|
115
|
+
sourceUrl: sourceImage?.url || "",
|
|
116
|
+
height: sourceImage?.dimensions.h || void 0,
|
|
117
|
+
width: sourceImage?.dimensions.w || void 0,
|
|
118
|
+
altText: sourceImage?.label || product.name[this.context.languageContext.locale] || void 0
|
|
119
|
+
});
|
|
120
|
+
const mappedOptions = variant.attributes?.filter((x) => x.name === "Color").map(
|
|
121
|
+
(opt) => ProductVariantOptionSchema.parse({
|
|
122
|
+
identifier: ProductOptionIdentifierSchema.parse({
|
|
123
|
+
key: opt.name
|
|
124
|
+
}),
|
|
125
|
+
name: opt.value || ""
|
|
126
|
+
})
|
|
127
|
+
) || [];
|
|
128
|
+
const mappedOption = mappedOptions?.[0];
|
|
129
|
+
return ProductSearchResultItemVariantSchema.parse({
|
|
130
|
+
variant: ProductVariantIdentifierSchema.parse({
|
|
131
|
+
sku: variant.sku || ""
|
|
132
|
+
}),
|
|
133
|
+
image: img,
|
|
134
|
+
options: mappedOption
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
__decorateClass([
|
|
139
|
+
Reactionary({
|
|
140
|
+
cache: true,
|
|
141
|
+
cacheTimeToLiveInSeconds: 300,
|
|
142
|
+
currencyDependentCaching: false,
|
|
143
|
+
localeDependentCaching: false
|
|
144
|
+
})
|
|
145
|
+
], CommercetoolsProductAssociationsProvider.prototype, "getAccessories", 1);
|
|
146
|
+
__decorateClass([
|
|
147
|
+
Reactionary({
|
|
148
|
+
cache: true,
|
|
149
|
+
cacheTimeToLiveInSeconds: 300,
|
|
150
|
+
currencyDependentCaching: false,
|
|
151
|
+
localeDependentCaching: false
|
|
152
|
+
})
|
|
153
|
+
], CommercetoolsProductAssociationsProvider.prototype, "getSpareparts", 1);
|
|
154
|
+
__decorateClass([
|
|
155
|
+
Reactionary({
|
|
156
|
+
cache: true,
|
|
157
|
+
cacheTimeToLiveInSeconds: 300,
|
|
158
|
+
currencyDependentCaching: false,
|
|
159
|
+
localeDependentCaching: false
|
|
160
|
+
})
|
|
161
|
+
], CommercetoolsProductAssociationsProvider.prototype, "getReplacements", 1);
|
|
162
|
+
export {
|
|
163
|
+
CommercetoolsProductAssociationsProvider
|
|
164
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -80,7 +80,12 @@ class CommercetoolsProductProvider extends ProductProvider {
|
|
|
80
80
|
description = data.description[this.context.languageContext.locale];
|
|
81
81
|
}
|
|
82
82
|
const variantLevelAttributes = data.masterVariant.attributes?.map((x) => this.parseAttribute(x)) || [];
|
|
83
|
-
const
|
|
83
|
+
const specialAttributes = [
|
|
84
|
+
"reactionaryaccessories",
|
|
85
|
+
"reactionaryspareparts",
|
|
86
|
+
"reactionaryreplacements"
|
|
87
|
+
];
|
|
88
|
+
const productLevelAttributes = data.attributes.filter((x) => !specialAttributes.includes(x.name)).map((x) => this.parseAttribute(x)) || [];
|
|
84
89
|
const sharedAttributes = [
|
|
85
90
|
...productLevelAttributes,
|
|
86
91
|
...variantLevelAttributes
|
|
@@ -2,7 +2,9 @@ import { CapabilitiesSchema } from "@reactionary/core";
|
|
|
2
2
|
const CommercetoolsCapabilitiesSchema = CapabilitiesSchema.pick({
|
|
3
3
|
product: true,
|
|
4
4
|
productSearch: true,
|
|
5
|
+
productAssociations: true,
|
|
5
6
|
productReviews: true,
|
|
7
|
+
productList: true,
|
|
6
8
|
identity: true,
|
|
7
9
|
cart: true,
|
|
8
10
|
checkout: true,
|
package/src/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './providers/identity.provider.js';
|
|
|
5
5
|
export * from './providers/inventory.provider.js';
|
|
6
6
|
export * from './providers/order-search.provider.js';
|
|
7
7
|
export * from './providers/price.provider.js';
|
|
8
|
+
export * from './providers/product-associations.provider.js';
|
|
8
9
|
export * from './providers/product.provider.js';
|
|
9
10
|
export * from './providers/product-reviews.provider.js';
|
|
10
11
|
export * from './providers/product-search.provider.js';
|
package/src/providers/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export * from './identity.provider.js';
|
|
|
4
4
|
export * from './inventory.provider.js';
|
|
5
5
|
export * from './order-search.provider.js';
|
|
6
6
|
export * from './price.provider.js';
|
|
7
|
+
export * from './product-associations.provider.js';
|
|
8
|
+
export * from './product-list.provider.js';
|
|
7
9
|
export * from './product.provider.js';
|
|
8
10
|
export * from './product-reviews.provider.js';
|
|
9
11
|
export * from './profile.provider.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ProductAssociationsProvider } from '@reactionary/core';
|
|
2
|
+
import type { ProductIdentifier, ProductAssociation, ProductSearchResultItem, ProductSearchResultItemVariant, ProductAssociationsGetAccessoriesQuery, ProductAssociationsGetSparepartsQuery, ProductAssociationsGetReplacementsQuery, Result, RequestContext, Cache } from '@reactionary/core';
|
|
3
|
+
import type { ProductProjection, ProductVariant as CTProductVariant } from '@commercetools/platform-sdk';
|
|
4
|
+
import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
|
|
5
|
+
import type { CommercetoolsAPI } from '../core/client.js';
|
|
6
|
+
export declare class CommercetoolsProductAssociationsProvider extends ProductAssociationsProvider {
|
|
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").ByProjectKeyRequestBuilder>;
|
|
11
|
+
protected fetchAssociatedProductsFor(productKey: ProductIdentifier, maxNumberOfAssociations: number, attributeName: string): Promise<ProductSearchResultItem[]>;
|
|
12
|
+
protected parseSingle(body: ProductProjection): {
|
|
13
|
+
identifier: {
|
|
14
|
+
key: string;
|
|
15
|
+
};
|
|
16
|
+
name: string;
|
|
17
|
+
slug: string;
|
|
18
|
+
variants: ProductSearchResultItemVariant[];
|
|
19
|
+
};
|
|
20
|
+
getAccessories(query: ProductAssociationsGetAccessoriesQuery): Promise<Result<ProductAssociation[]>>;
|
|
21
|
+
getSpareparts(query: ProductAssociationsGetSparepartsQuery): Promise<Result<ProductAssociation[]>>;
|
|
22
|
+
getReplacements(query: ProductAssociationsGetReplacementsQuery): Promise<Result<ProductAssociation[]>>;
|
|
23
|
+
protected parseVariant(variant: CTProductVariant, product: ProductProjection): ProductSearchResultItemVariant;
|
|
24
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -11,7 +11,9 @@ export declare const CommercetoolsCapabilitiesSchema: z.ZodObject<{
|
|
|
11
11
|
profile: z.ZodOptional<z.ZodBoolean>;
|
|
12
12
|
store: z.ZodOptional<z.ZodBoolean>;
|
|
13
13
|
productSearch: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
+
productAssociations: z.ZodOptional<z.ZodBoolean>;
|
|
14
15
|
productReviews: z.ZodOptional<z.ZodBoolean>;
|
|
16
|
+
productList: z.ZodOptional<z.ZodBoolean>;
|
|
15
17
|
orderSearch: z.ZodOptional<z.ZodBoolean>;
|
|
16
18
|
}, z.core.$loose>;
|
|
17
19
|
export type CommercetoolsCapabilities = z.infer<typeof CommercetoolsCapabilitiesSchema>;
|