@scayle/storefront-core 7.64.4 → 7.65.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @scayle/storefront-core
2
2
 
3
+ ## 7.65.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Improves the handling of `getOrderDataByCbd` RPC to return descriptive errors in case the validation of the token fails.
8
+
9
+ In case the token can't be validated using the Checkout Secret, this is now returned and a warning on the server is logged.
10
+
11
+ If the token has expired, this is now also returned and logged on the server.
12
+
13
+ - Support passing `categoryId` as an alternative to the category path in `getProductsByCategory` and `getFilters`
14
+
15
+ ### Patch Changes
16
+
17
+ - Fixed cached fallback ttl to be used only if passed `option.ttl` is undefined
18
+
3
19
  ## 7.64.4
4
20
 
5
21
  ### Patch Changes
@@ -15,14 +15,10 @@ class CustomerAPIClient {
15
15
  this.context = context;
16
16
  }
17
17
  get headers() {
18
- const accessHeader = this.context.checkout.accessHeader;
19
18
  return {
20
19
  Authorization: `Bearer ${this.context.accessToken}`,
21
20
  Accept: "application/json",
22
- "Content-Type": "application/json",
23
- ...(accessHeader ? {
24
- "X-Access-Header": accessHeader
25
- } : {})
21
+ "Content-Type": "application/json"
26
22
  };
27
23
  }
28
24
  async sendRequest(request, retry = true) {
@@ -9,12 +9,10 @@ export class CustomerAPIClient {
9
9
  this.context = context;
10
10
  }
11
11
  get headers() {
12
- const accessHeader = this.context.checkout.accessHeader;
13
12
  return {
14
13
  Authorization: `Bearer ${this.context.accessToken}`,
15
14
  Accept: "application/json",
16
- "Content-Type": "application/json",
17
- ...accessHeader ? { "X-Access-Header": accessHeader } : {}
15
+ "Content-Type": "application/json"
18
16
  };
19
17
  }
20
18
  async sendRequest(request, retry = true) {
@@ -71,7 +71,7 @@ class Cached {
71
71
  if (!this.isCacheEnabled) {
72
72
  return;
73
73
  }
74
- const ttl = options?.ttl || 60 * MINUTE;
74
+ const ttl = options?.ttl ?? 60 * MINUTE;
75
75
  if (ttl) {
76
76
  await (0, _utils.timeout)(_constants.CACHE_TIMEOUT, this.cache.set(cacheKey, value, ttl));
77
77
  }
@@ -68,7 +68,7 @@ export class Cached {
68
68
  if (!this.isCacheEnabled) {
69
69
  return;
70
70
  }
71
- const ttl = options?.ttl || 60 * MINUTE;
71
+ const ttl = options?.ttl ?? 60 * MINUTE;
72
72
  if (ttl) {
73
73
  await timeout(CACHE_TIMEOUT, this.cache.set(cacheKey, value, ttl));
74
74
  }
@@ -4,37 +4,35 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.getOrderDataByCbd = void 0;
7
+ var _constants = require("../../constants/index.cjs");
8
+ var _errors = require("../../errors/index.cjs");
7
9
  var _hash = require("../../utils/hash.cjs");
8
10
  const getOrderDataByCbd = exports.getOrderDataByCbd = async function getOrderDataByCbd2({
9
11
  cbdToken
10
12
  }, context) {
11
- if (!cbdToken) {
12
- return;
13
+ const payload = await (0, _hash.verifyOrderSuccessToken)(cbdToken, context.checkout.secret);
14
+ if (!payload) {
15
+ context.log.warn("Unable to verify CBD token. Please check that you configured the correct Checkout Secret");
16
+ return new _errors.ErrorResponse(_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusMessage.BAD_REQUEST, "Unable to verify CBD token.");
13
17
  }
14
- const shopId = context.shopId;
15
- const accessHeader = context.checkout.accessHeader;
16
- const checkoutSecret = context.checkout.secret;
17
- const decodedPayload = await (0, _hash.verifyToken)(cbdToken, checkoutSecret);
18
- if (decodedPayload) {
19
- const payload = JSON.parse(decodedPayload);
20
- const checkoutUsername = context.checkout.user;
21
- const checkoutToken = context.checkout.token;
22
- const checkoutUrl = context.checkout.url;
23
- if (context.checkout.cbdExpiration && Math.floor(Date.now() / 1e3) - payload.issued_at > context.checkout.cbdExpiration) {
24
- context.log.debug("Attempting to use expired CBD token");
25
- return;
18
+ if (context.checkout.cbdExpiration && Math.floor(Date.now() / 1e3) - payload.issued_at > context.checkout.cbdExpiration) {
19
+ context.log.warn("Attempting to use expired CBD token");
20
+ return new _errors.ErrorResponse(_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusMessage.BAD_REQUEST, "The CBD token has expired.");
21
+ }
22
+ const checkoutUrl = context.checkout.url;
23
+ const basicAuth = (0, _hash.encodeBase64)(`${context.checkout.user}:${context.checkout.token}`);
24
+ const response = await fetch(`${checkoutUrl}/api/v1/orders/${payload.order_id}`, {
25
+ headers: {
26
+ Accept: "application/json",
27
+ Authorization: `Basic ${basicAuth}`,
28
+ "X-Shop-Id": context.shopId.toString()
26
29
  }
27
- const accessToken = (0, _hash.encodeBase64)(`${checkoutUsername}:${unescape(checkoutToken)}`);
28
- const response = await fetch(`${checkoutUrl}/api/v1/orders/${payload.order_id}`, {
29
- headers: {
30
- Accept: "application/json",
31
- Authorization: `Basic ${accessToken}`,
32
- "x-shop-id": shopId.toString(),
33
- ...(accessHeader ? {
34
- "X-Access-Header": accessHeader
35
- } : {})
36
- }
37
- });
30
+ });
31
+ if (response.ok) {
38
32
  return await response.json();
39
33
  }
34
+ context.log.error(`Unexpected status code when fetching order from Checkout API: ${response.status}`);
35
+ return new _errors.ErrorResponse(_constants.HttpStatusCode.INTERNAL_SERVER_ERROR, _constants.HttpStatusMessage.INTERNAL_SERVER_ERROR, "Unknown error", {
36
+ detail: `Fetching order failed with status code ${response.status}`
37
+ });
40
38
  };
@@ -1,4 +1,5 @@
1
+ import { ErrorResponse } from '../../errors';
1
2
  import type { Order, RpcContext } from '../../types';
2
3
  export declare const getOrderDataByCbd: ({ cbdToken }: {
3
4
  cbdToken: string;
4
- }, context: RpcContext) => Promise<Order | undefined>;
5
+ }, context: RpcContext) => Promise<Order | ErrorResponse>;
@@ -1,35 +1,55 @@
1
- import { encodeBase64, verifyToken } from "../../utils/hash.mjs";
1
+ import { HttpStatusCode, HttpStatusMessage } from "../../constants/index.mjs";
2
+ import { ErrorResponse } from "../../errors/index.mjs";
3
+ import { encodeBase64, verifyOrderSuccessToken } from "../../utils/hash.mjs";
2
4
  export const getOrderDataByCbd = async function getOrderDataByCbd2({ cbdToken }, context) {
3
- if (!cbdToken) {
4
- return;
5
+ const payload = await verifyOrderSuccessToken(
6
+ cbdToken,
7
+ context.checkout.secret
8
+ );
9
+ if (!payload) {
10
+ context.log.warn(
11
+ "Unable to verify CBD token. Please check that you configured the correct Checkout Secret"
12
+ );
13
+ return new ErrorResponse(
14
+ HttpStatusCode.BAD_REQUEST,
15
+ HttpStatusMessage.BAD_REQUEST,
16
+ "Unable to verify CBD token."
17
+ );
5
18
  }
6
- const shopId = context.shopId;
7
- const accessHeader = context.checkout.accessHeader;
8
- const checkoutSecret = context.checkout.secret;
9
- const decodedPayload = await verifyToken(cbdToken, checkoutSecret);
10
- if (decodedPayload) {
11
- const payload = JSON.parse(decodedPayload);
12
- const checkoutUsername = context.checkout.user;
13
- const checkoutToken = context.checkout.token;
14
- const checkoutUrl = context.checkout.url;
15
- if (context.checkout.cbdExpiration && Math.floor(Date.now() / 1e3) - payload.issued_at > context.checkout.cbdExpiration) {
16
- context.log.debug("Attempting to use expired CBD token");
17
- return;
18
- }
19
- const accessToken = encodeBase64(
20
- `${checkoutUsername}:${unescape(checkoutToken)}`
19
+ if (context.checkout.cbdExpiration && Math.floor(Date.now() / 1e3) - payload.issued_at > context.checkout.cbdExpiration) {
20
+ context.log.warn("Attempting to use expired CBD token");
21
+ return new ErrorResponse(
22
+ HttpStatusCode.BAD_REQUEST,
23
+ HttpStatusMessage.BAD_REQUEST,
24
+ "The CBD token has expired."
21
25
  );
22
- const response = await fetch(
23
- `${checkoutUrl}/api/v1/orders/${payload.order_id}`,
24
- {
25
- headers: {
26
- Accept: "application/json",
27
- Authorization: `Basic ${accessToken}`,
28
- "x-shop-id": shopId.toString(),
29
- ...accessHeader ? { "X-Access-Header": accessHeader } : {}
30
- }
26
+ }
27
+ const checkoutUrl = context.checkout.url;
28
+ const basicAuth = encodeBase64(
29
+ `${context.checkout.user}:${context.checkout.token}`
30
+ );
31
+ const response = await fetch(
32
+ `${checkoutUrl}/api/v1/orders/${payload.order_id}`,
33
+ {
34
+ headers: {
35
+ Accept: "application/json",
36
+ Authorization: `Basic ${basicAuth}`,
37
+ "X-Shop-Id": context.shopId.toString()
31
38
  }
32
- );
39
+ }
40
+ );
41
+ if (response.ok) {
33
42
  return await response.json();
34
43
  }
44
+ context.log.error(
45
+ `Unexpected status code when fetching order from Checkout API: ${response.status}`
46
+ );
47
+ return new ErrorResponse(
48
+ HttpStatusCode.INTERNAL_SERVER_ERROR,
49
+ HttpStatusMessage.INTERNAL_SERVER_ERROR,
50
+ "Unknown error",
51
+ {
52
+ detail: `Fetching order failed with status code ${response.status}`
53
+ }
54
+ );
35
55
  };
@@ -5,10 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getCheckoutToken = void 0;
7
7
  var _jose = require("jose");
8
+ var _types = require("../../../types/index.cjs");
8
9
  var _errors = require("../../../errors/index.cjs");
9
10
  var _constants = require("../../../constants/index.cjs");
10
11
  var _user = require("../user.cjs");
11
12
  const getCheckoutToken = exports.getCheckoutToken = async function getCheckoutToken2(jwtPayload = {}, context) {
13
+ if (!(0, _types.hasSession)(context)) {
14
+ return new _errors.ErrorResponse(_constants.HttpStatusCode.BAD_REQUEST, _constants.HttpStatusMessage.BAD_REQUEST, "No Session found");
15
+ }
12
16
  if (!context.accessToken) {
13
17
  return new _errors.ErrorResponse(_constants.HttpStatusCode.UNAUTHORIZED, _constants.HttpStatusMessage.UNAUTHORIZED, "No access token present");
14
18
  }
@@ -33,7 +37,7 @@ const getCheckoutToken = exports.getCheckoutToken = async function getCheckoutTo
33
37
  carrier,
34
38
  basketId: context.basketKey,
35
39
  campaignKey: context.campaignKey
36
- }).setIssuedAt(now).setNotBefore(now).setExpirationTime("1h").setIssuer(`${"@scayle/storefront-core"}@${"7.64.4"}`).setProtectedHeader({
40
+ }).setIssuedAt(now).setNotBefore(now).setExpirationTime("1h").setIssuer(`${"@scayle/storefront-core"}@${"7.65.0"}`).setProtectedHeader({
37
41
  alg: "HS256",
38
42
  typ: "JWT"
39
43
  }).sign(secret);
@@ -1,8 +1,16 @@
1
1
  import { SignJWT } from "jose";
2
+ import { hasSession } from "../../../types/index.mjs";
2
3
  import { ErrorResponse } from "../../../errors/index.mjs";
3
4
  import { HttpStatusCode, HttpStatusMessage } from "../../../constants/index.mjs";
4
5
  import { getAccessToken } from "../user.mjs";
5
6
  export const getCheckoutToken = async function getCheckoutToken2(jwtPayload = {}, context) {
7
+ if (!hasSession(context)) {
8
+ return new ErrorResponse(
9
+ HttpStatusCode.BAD_REQUEST,
10
+ HttpStatusMessage.BAD_REQUEST,
11
+ "No Session found"
12
+ );
13
+ }
6
14
  if (!context.accessToken) {
7
15
  return new ErrorResponse(
8
16
  HttpStatusCode.UNAUTHORIZED,
@@ -27,7 +35,7 @@ export const getCheckoutToken = async function getCheckoutToken2(jwtPayload = {}
27
35
  carrier,
28
36
  basketId: context.basketKey,
29
37
  campaignKey: context.campaignKey
30
- }).setIssuedAt(now).setNotBefore(now).setExpirationTime("1h").setIssuer(`${"@scayle/storefront-core"}@${"7.64.4"}`).setProtectedHeader({ alg: "HS256", typ: "JWT" }).sign(secret);
38
+ }).setIssuedAt(now).setNotBefore(now).setExpirationTime("1h").setIssuer(`${"@scayle/storefront-core"}@${"7.65.0"}`).setProtectedHeader({ alg: "HS256", typ: "JWT" }).sign(secret);
31
39
  return {
32
40
  accessToken: refreshedAccessToken,
33
41
  checkoutJwt
@@ -68,31 +68,36 @@ const getProductsByReferenceKeys = exports.getProductsByReferenceKeys = async fu
68
68
  pricePromotionKey: options.pricePromotionKey
69
69
  });
70
70
  };
71
- const getProductsCount = exports.getProductsCount = async function getProductsCount2({
72
- category = "/",
73
- categoryId = void 0,
74
- where = void 0,
75
- includeSoldOut = false,
76
- includeSellableForFree = false,
77
- orFiltersOperator = void 0
78
- }, {
71
+ const getProductsCount = exports.getProductsCount = async function getProductsCount2(params, {
79
72
  cached,
80
73
  bapiClient,
81
74
  campaignKey
82
75
  }) {
83
- let result;
84
- if (category !== "/") {
85
- const sanitizedPath = (0, _helpers.splitAndRemoveEmpty)(category);
86
- result = await cached(bapiClient.categories.getByPath, {
87
- cacheKeyPrefix: `getByPath-categories-${sanitizedPath}`
88
- })(sanitizedPath);
76
+ const {
77
+ where = void 0,
78
+ includeSoldOut = false,
79
+ includeSellableForFree = false,
80
+ orFiltersOperator = void 0
81
+ } = params;
82
+ let category;
83
+ let categoryId;
84
+ if ("category" in params && !params.category) {
85
+ category = "/";
86
+ }
87
+ if ("categoryId" in params) {
88
+ categoryId = params.categoryId;
89
+ } else if (category && category !== "/") {
90
+ const result = await cached(bapiClient.categories.getByPath, {
91
+ cacheKeyPrefix: `getByPath-categories-${category}`
92
+ })((0, _helpers.splitAndRemoveEmpty)(category));
93
+ categoryId = result.id;
89
94
  }
90
95
  const productCount = await cached(bapiClient.products.query, {
91
96
  ttl: 15 * _cache.MINUTE,
92
97
  cacheKeyPrefix: "products-query"
93
98
  })({
94
99
  where: {
95
- categoryId: categoryId ?? result?.id,
100
+ categoryId,
96
101
  minPrice: where?.minPrice,
97
102
  maxPrice: where?.maxPrice,
98
103
  attributes: where?.attributes,
@@ -131,32 +136,38 @@ const fetchAllFiltersForCategory = exports.fetchAllFiltersForCategory = async fu
131
136
  return await cached(fetchAndMapFiltersToSlugs, {
132
137
  ttl: 4 * 60 * 60,
133
138
  // cached for 4 hours
134
- cacheKey: `filters-for-category-${category.slug}`
139
+ cacheKey: `filters-for-category-${category.id}`
135
140
  })();
136
141
  };
137
- const getFilters = exports.getFilters = async function getFilters2({
138
- category = "/",
139
- includedFilters,
140
- where = void 0,
141
- includeSoldOut = false,
142
- includeSellableForFree = false,
143
- orFiltersOperator
144
- }, context) {
145
- let result;
142
+ const getFilters = exports.getFilters = async function getFilters2(params, context) {
143
+ const {
144
+ includedFilters,
145
+ where = void 0,
146
+ includeSoldOut = false,
147
+ includeSellableForFree = false,
148
+ orFiltersOperator
149
+ } = params;
150
+ let category;
151
+ let categoryId;
146
152
  const {
147
153
  cached,
148
154
  bapiClient,
149
155
  campaignKey
150
156
  } = context;
151
- if (category !== "/") {
152
- result = await cached(bapiClient.categories.getByPath, {
157
+ if ("category" in params && !params.category) {
158
+ category = "/";
159
+ }
160
+ if ("categoryId" in params) {
161
+ categoryId = params.categoryId;
162
+ } else if (category && category !== "/") {
163
+ const result = await cached(bapiClient.categories.getByPath, {
153
164
  cacheKeyPrefix: `getByPath-categories-${category}`
154
165
  })((0, _helpers.splitAndRemoveEmpty)(category));
166
+ categoryId = result.id;
155
167
  }
156
168
  const response = await fetchAllFiltersForCategory({
157
169
  category: {
158
- id: result?.id,
159
- slug: category
170
+ id: categoryId
160
171
  },
161
172
  includedFilters
162
173
  }, context);
@@ -173,7 +184,7 @@ const getFilters = exports.getFilters = async function getFilters2({
173
184
  cacheKeyPrefix: `get-filters-${category}`
174
185
  })({
175
186
  where: {
176
- categoryId: result?.id,
187
+ categoryId,
177
188
  minPrice: where?.minPrice,
178
189
  maxPrice: where?.maxPrice,
179
190
  term: where?.term,
@@ -189,7 +200,7 @@ const getFilters = exports.getFilters = async function getFilters2({
189
200
  cacheKeyPrefix: `products-query-category-${category === "/" ? "root" : category}`
190
201
  })({
191
202
  where: {
192
- categoryId: result?.id,
203
+ categoryId,
193
204
  minPrice: where?.minPrice,
194
205
  maxPrice: where?.maxPrice,
195
206
  term: where?.term,
@@ -207,34 +218,40 @@ const getFilters = exports.getFilters = async function getFilters2({
207
218
  unfilteredCount: productCount?.pagination.total
208
219
  };
209
220
  };
210
- const getProductsByCategory = exports.getProductsByCategory = async function getProductsByCategory2({
211
- category = "/",
212
- cache,
213
- with: _with,
214
- perPage = 20,
215
- page = 1,
216
- where = void 0,
217
- sort = void 0,
218
- pricePromotionKey = "",
219
- includeSellableForFree = void 0,
220
- includeSoldOut = void 0,
221
- orFiltersOperator = void 0
222
- }, context) {
223
- let result;
221
+ const getProductsByCategory = exports.getProductsByCategory = async function getProductsByCategory2(params, context) {
222
+ const {
223
+ cache,
224
+ with: _with,
225
+ perPage = 20,
226
+ page = 1,
227
+ where = void 0,
228
+ sort = void 0,
229
+ pricePromotionKey = "",
230
+ includeSellableForFree = void 0,
231
+ includeSoldOut = void 0,
232
+ orFiltersOperator = void 0
233
+ } = params;
224
234
  const {
225
235
  cached,
226
236
  bapiClient,
227
237
  campaignKey
228
238
  } = context;
229
- if (category !== "/") {
230
- result = await cached(bapiClient.categories.getByPath, {
239
+ let category;
240
+ let categoryId;
241
+ if ("category" in params && !params.category) {
242
+ category = "/";
243
+ }
244
+ if ("categoryId" in params) {
245
+ categoryId = params.categoryId;
246
+ } else if (category && category !== "/") {
247
+ const result = await cached(bapiClient.categories.getByPath, {
231
248
  cacheKeyPrefix: `getByPath-categories-${category}`
232
249
  })((0, _helpers.splitAndRemoveEmpty)(category));
250
+ categoryId = result.id;
233
251
  }
234
252
  const response = await fetchAllFiltersForCategory({
235
253
  category: {
236
- id: result?.id,
237
- slug: category
254
+ id: categoryId
238
255
  }
239
256
  }, context);
240
257
  if (response instanceof _errors.ErrorResponse) {
@@ -255,7 +272,7 @@ const getProductsByCategory = exports.getProductsByCategory = async function get
255
272
  })({
256
273
  where: {
257
274
  term: where?.term,
258
- categoryId: result?.id,
275
+ categoryId,
259
276
  minPrice: where?.minPrice,
260
277
  maxPrice: where?.maxPrice,
261
278
  attributes: [...sanitizedAttributes, ...(where?.whitelistAttributes || [])],
@@ -4,21 +4,20 @@ import { ErrorResponse } from '../../errors';
4
4
  export declare const getProductById: (options: FetchProductParams, { bapiClient, cached, campaignKey, withParams }: RpcContext) => Promise<Product>;
5
5
  export declare const getProductsByIds: (options: FetchProductsByIdsParams, { bapiClient, cached, campaignKey, withParams }: RpcContext) => Promise<Product[]>;
6
6
  export declare const getProductsByReferenceKeys: (options: FetchProductsByReferenceKeysParams, { bapiClient, cached, campaignKey, withParams }: RpcContext) => Promise<Product[]>;
7
- export declare const getProductsCount: ({ category, categoryId, where, includeSoldOut, includeSellableForFree, orFiltersOperator, }: FetchProductsCountParams, { cached, bapiClient, campaignKey }: RpcContext) => Promise<{
7
+ export declare const getProductsCount: (params: FetchProductsCountParams, { cached, bapiClient, campaignKey }: RpcContext) => Promise<{
8
8
  count: number;
9
9
  }>;
10
10
  export declare const fetchAllFiltersForCategory: ({ includedFilters, category }: {
11
11
  includedFilters?: string[];
12
12
  category: {
13
- slug: string;
14
13
  id?: number;
15
14
  };
16
15
  }, { cached, bapiClient, campaignKey }: RpcContext) => Promise<string[]>;
17
- export declare const getFilters: ({ category, includedFilters, where, includeSoldOut, includeSellableForFree, orFiltersOperator, }: FetchFiltersParams, context: RpcContext) => Promise<(string[] & ErrorResponse) | {
16
+ export declare const getFilters: (params: FetchFiltersParams, context: RpcContext) => Promise<(string[] & ErrorResponse) | {
18
17
  filters: FiltersEndpointResponseData;
19
18
  unfilteredCount: number;
20
19
  }>;
21
- export declare const getProductsByCategory: ({ category, cache, with: _with, perPage, page, where, sort, pricePromotionKey, includeSellableForFree, includeSoldOut, orFiltersOperator, }: FetchProductsByCategoryParams, context: RpcContext) => Promise<(string[] & ErrorResponse) | {
20
+ export declare const getProductsByCategory: (params: FetchProductsByCategoryParams, context: RpcContext) => Promise<(string[] & ErrorResponse) | {
22
21
  products: Product[];
23
22
  pagination: {
24
23
  current: number;
@@ -45,27 +45,32 @@ export const getProductsByReferenceKeys = async function getProductsByReferenceK
45
45
  pricePromotionKey: options.pricePromotionKey
46
46
  });
47
47
  };
48
- export const getProductsCount = async function getProductsCount2({
49
- category = "/",
50
- categoryId = void 0,
51
- where = void 0,
52
- includeSoldOut = false,
53
- includeSellableForFree = false,
54
- orFiltersOperator = void 0
55
- }, { cached, bapiClient, campaignKey }) {
56
- let result;
57
- if (category !== "/") {
58
- const sanitizedPath = splitAndRemoveEmpty(category);
59
- result = await cached(bapiClient.categories.getByPath, {
60
- cacheKeyPrefix: `getByPath-categories-${sanitizedPath}`
61
- })(sanitizedPath);
48
+ export const getProductsCount = async function getProductsCount2(params, { cached, bapiClient, campaignKey }) {
49
+ const {
50
+ where = void 0,
51
+ includeSoldOut = false,
52
+ includeSellableForFree = false,
53
+ orFiltersOperator = void 0
54
+ } = params;
55
+ let category;
56
+ let categoryId;
57
+ if ("category" in params && !params.category) {
58
+ category = "/";
59
+ }
60
+ if ("categoryId" in params) {
61
+ categoryId = params.categoryId;
62
+ } else if (category && category !== "/") {
63
+ const result = await cached(bapiClient.categories.getByPath, {
64
+ cacheKeyPrefix: `getByPath-categories-${category}`
65
+ })(splitAndRemoveEmpty(category));
66
+ categoryId = result.id;
62
67
  }
63
68
  const productCount = await cached(bapiClient.products.query, {
64
69
  ttl: 15 * MINUTE,
65
70
  cacheKeyPrefix: "products-query"
66
71
  })({
67
72
  where: {
68
- categoryId: categoryId ?? result?.id,
73
+ categoryId,
69
74
  minPrice: where?.minPrice,
70
75
  maxPrice: where?.maxPrice,
71
76
  attributes: where?.attributes,
@@ -95,26 +100,36 @@ export const fetchAllFiltersForCategory = async function fetchAllFiltersForCateg
95
100
  return await cached(fetchAndMapFiltersToSlugs, {
96
101
  ttl: 4 * 60 * 60,
97
102
  // cached for 4 hours
98
- cacheKey: `filters-for-category-${category.slug}`
103
+ cacheKey: `filters-for-category-${category.id}`
99
104
  })();
100
105
  };
101
- export const getFilters = async function getFilters2({
102
- category = "/",
103
- includedFilters,
104
- where = void 0,
105
- includeSoldOut = false,
106
- includeSellableForFree = false,
107
- orFiltersOperator
108
- }, context) {
109
- let result;
106
+ export const getFilters = async function getFilters2(params, context) {
107
+ const {
108
+ includedFilters,
109
+ where = void 0,
110
+ includeSoldOut = false,
111
+ includeSellableForFree = false,
112
+ orFiltersOperator
113
+ } = params;
114
+ let category;
115
+ let categoryId;
110
116
  const { cached, bapiClient, campaignKey } = context;
111
- if (category !== "/") {
112
- result = await cached(bapiClient.categories.getByPath, {
117
+ if ("category" in params && !params.category) {
118
+ category = "/";
119
+ }
120
+ if ("categoryId" in params) {
121
+ categoryId = params.categoryId;
122
+ } else if (category && category !== "/") {
123
+ const result = await cached(bapiClient.categories.getByPath, {
113
124
  cacheKeyPrefix: `getByPath-categories-${category}`
114
125
  })(splitAndRemoveEmpty(category));
126
+ categoryId = result.id;
115
127
  }
116
128
  const response = await fetchAllFiltersForCategory(
117
- { category: { id: result?.id, slug: category }, includedFilters },
129
+ {
130
+ category: { id: categoryId },
131
+ includedFilters
132
+ },
118
133
  context
119
134
  );
120
135
  if (response instanceof ErrorResponse) {
@@ -131,7 +146,7 @@ export const getFilters = async function getFilters2({
131
146
  cacheKeyPrefix: `get-filters-${category}`
132
147
  })({
133
148
  where: {
134
- categoryId: result?.id,
149
+ categoryId,
135
150
  minPrice: where?.minPrice,
136
151
  maxPrice: where?.maxPrice,
137
152
  term: where?.term,
@@ -151,7 +166,7 @@ export const getFilters = async function getFilters2({
151
166
  cacheKeyPrefix: `products-query-category-${category === "/" ? "root" : category}`
152
167
  })({
153
168
  where: {
154
- categoryId: result?.id,
169
+ categoryId,
155
170
  minPrice: where?.minPrice,
156
171
  maxPrice: where?.maxPrice,
157
172
  term: where?.term,
@@ -173,28 +188,39 @@ export const getFilters = async function getFilters2({
173
188
  unfilteredCount: productCount?.pagination.total
174
189
  };
175
190
  };
176
- export const getProductsByCategory = async function getProductsByCategory2({
177
- category = "/",
178
- cache,
179
- with: _with,
180
- perPage = 20,
181
- page = 1,
182
- where = void 0,
183
- sort = void 0,
184
- pricePromotionKey = "",
185
- includeSellableForFree = void 0,
186
- includeSoldOut = void 0,
187
- orFiltersOperator = void 0
188
- }, context) {
189
- let result;
191
+ export const getProductsByCategory = async function getProductsByCategory2(params, context) {
192
+ const {
193
+ cache,
194
+ with: _with,
195
+ perPage = 20,
196
+ page = 1,
197
+ where = void 0,
198
+ sort = void 0,
199
+ pricePromotionKey = "",
200
+ includeSellableForFree = void 0,
201
+ includeSoldOut = void 0,
202
+ orFiltersOperator = void 0
203
+ } = params;
190
204
  const { cached, bapiClient, campaignKey } = context;
191
- if (category !== "/") {
192
- result = await cached(bapiClient.categories.getByPath, {
205
+ let category;
206
+ let categoryId;
207
+ if ("category" in params && !params.category) {
208
+ category = "/";
209
+ }
210
+ if ("categoryId" in params) {
211
+ categoryId = params.categoryId;
212
+ } else if (category && category !== "/") {
213
+ const result = await cached(bapiClient.categories.getByPath, {
193
214
  cacheKeyPrefix: `getByPath-categories-${category}`
194
215
  })(splitAndRemoveEmpty(category));
216
+ categoryId = result.id;
195
217
  }
196
218
  const response = await fetchAllFiltersForCategory(
197
- { category: { id: result?.id, slug: category } },
219
+ {
220
+ category: {
221
+ id: categoryId
222
+ }
223
+ },
198
224
  context
199
225
  );
200
226
  if (response instanceof ErrorResponse) {
@@ -215,7 +241,7 @@ export const getProductsByCategory = async function getProductsByCategory2({
215
241
  )({
216
242
  where: {
217
243
  term: where?.term,
218
- categoryId: result?.id,
244
+ categoryId,
219
245
  minPrice: where?.minPrice,
220
246
  maxPrice: where?.maxPrice,
221
247
  attributes: [
@@ -15,8 +15,7 @@ export interface FilterParams {
15
15
  sort?: ProductSortConfig;
16
16
  orFiltersOperator?: ProductsSearchEndpointParameters['orFiltersOperator'];
17
17
  }
18
- export interface FetchFiltersParams {
19
- category: string;
18
+ export type FetchFiltersParams = {
20
19
  includedFilters?: Array<string>;
21
20
  where?: {
22
21
  term?: ProductSearchQuery['term'];
@@ -28,7 +27,13 @@ export interface FetchFiltersParams {
28
27
  includeSoldOut?: boolean;
29
28
  includeSellableForFree?: boolean;
30
29
  orFiltersOperator?: ProductsSearchEndpointParameters['orFiltersOperator'];
31
- }
30
+ } & ({
31
+ category?: string;
32
+ categoryId?: undefined;
33
+ } | {
34
+ categoryId: number;
35
+ category?: undefined;
36
+ });
32
37
  export interface FetchFiltersResponse {
33
38
  filters: FiltersEndpointResponseData;
34
39
  unfilteredCount: number;
@@ -43,8 +43,7 @@ export interface FetchProductsByReferenceKeysParams {
43
43
  with?: ProductWith;
44
44
  pricePromotionKey?: string;
45
45
  }
46
- export interface FetchProductsByCategoryParams {
47
- category: string;
46
+ export type FetchProductsByCategoryParams = {
48
47
  with?: ProductWith;
49
48
  cache?: CacheOptions;
50
49
  includedFilters?: Array<string>;
@@ -63,14 +62,18 @@ export interface FetchProductsByCategoryParams {
63
62
  includeSellableForFree?: boolean;
64
63
  includeSoldOut?: boolean;
65
64
  orFiltersOperator?: ProductsSearchEndpointParameters['orFiltersOperator'];
66
- }
65
+ } & ({
66
+ category?: string;
67
+ categoryId?: undefined;
68
+ } | {
69
+ categoryId: number;
70
+ category?: undefined;
71
+ });
67
72
  export interface FetchProductsByCategoryResponse {
68
73
  products: Product[];
69
74
  pagination: ProductsByIdsEndpointResponseData['pagination'];
70
75
  }
71
- export interface FetchProductsCountParams {
72
- category?: string;
73
- categoryId?: number;
76
+ export type FetchProductsCountParams = {
74
77
  where?: {
75
78
  term?: ProductSearchQuery['term'];
76
79
  minPrice?: ProductSearchQuery['minPrice'];
@@ -80,7 +83,13 @@ export interface FetchProductsCountParams {
80
83
  includeSoldOut?: boolean;
81
84
  includeSellableForFree?: boolean;
82
85
  orFiltersOperator?: ProductsSearchEndpointParameters['orFiltersOperator'];
83
- }
86
+ } & ({
87
+ category?: string;
88
+ categoryId?: undefined;
89
+ } | {
90
+ categoryId: number;
91
+ category?: undefined;
92
+ });
84
93
  export interface FetchProductsCountResponse {
85
94
  count: number;
86
95
  }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.verifyToken = exports.sha256 = exports.md5 = exports.hmac = exports.encodeBase64 = exports.decodeBase64 = exports.buildSignature = exports.buildHashPayload = void 0;
6
+ exports.verifyOrderSuccessToken = exports.sha256 = exports.md5 = exports.hmac = exports.encodeBase64 = exports.decodeBase64 = exports.buildSignature = exports.buildHashPayload = void 0;
7
7
  var _uncrypto = require("uncrypto");
8
8
  const md5 = async value => {
9
9
  if (!process.env.SFC_OMIT_MD5) {
@@ -54,14 +54,15 @@ const decodeBase64 = string => {
54
54
  return new TextDecoder().decode(base64ToBytes(string));
55
55
  };
56
56
  exports.decodeBase64 = decodeBase64;
57
- const verifyToken = async (token, secret) => {
57
+ const verifyOrderSuccessToken = async (token, secret) => {
58
58
  const [encodedPayload, signature] = token.split(".", 2);
59
59
  const expectedSignature = encodeBase64(await hmac(encodedPayload, secret));
60
60
  if (expectedSignature === signature) {
61
- return decodeBase64(encodedPayload);
61
+ return JSON.parse(decodeBase64(encodedPayload));
62
62
  }
63
+ return void 0;
63
64
  };
64
- exports.verifyToken = verifyToken;
65
+ exports.verifyOrderSuccessToken = verifyOrderSuccessToken;
65
66
  const buildHashPayload = payload => encodeBase64(JSON.stringify(payload));
66
67
  exports.buildHashPayload = buildHashPayload;
67
68
  const buildSignature = async (payloadHash, secret) => encodeBase64(await hmac(payloadHash, secret));
@@ -3,6 +3,6 @@ export declare const sha256: (value: string) => Promise<string>;
3
3
  export declare const hmac: (value: string, secret: string, algorithm?: string) => Promise<string>;
4
4
  export declare const encodeBase64: (string: string) => string;
5
5
  export declare const decodeBase64: (string: string) => string;
6
- export declare const verifyToken: (token: string, secret: string) => Promise<string | undefined>;
6
+ export declare const verifyOrderSuccessToken: <T = unknown>(token: string, secret: string) => Promise<T | undefined>;
7
7
  export declare const buildHashPayload: (payload: unknown) => string;
8
8
  export declare const buildSignature: (payloadHash: string, secret: string) => Promise<string>;
@@ -50,12 +50,13 @@ export const decodeBase64 = (string) => {
50
50
  }
51
51
  return new TextDecoder().decode(base64ToBytes(string));
52
52
  };
53
- export const verifyToken = async (token, secret) => {
53
+ export const verifyOrderSuccessToken = async (token, secret) => {
54
54
  const [encodedPayload, signature] = token.split(".", 2);
55
55
  const expectedSignature = encodeBase64(await hmac(encodedPayload, secret));
56
56
  if (expectedSignature === signature) {
57
- return decodeBase64(encodedPayload);
57
+ return JSON.parse(decodeBase64(encodedPayload));
58
58
  }
59
+ return void 0;
59
60
  };
60
61
  export const buildHashPayload = (payload) => encodeBase64(JSON.stringify(payload));
61
62
  export const buildSignature = async (payloadHash, secret) => encodeBase64(await hmac(payloadHash, secret));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-core",
3
- "version": "7.64.4",
3
+ "version": "7.65.0",
4
4
  "description": "Collection of essential utilities to work with the Storefront API",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",