@evenicanpm/storefront-core 2.0.0 → 2.0.1

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.
Files changed (125) hide show
  1. package/.storybook/main.ts +2 -3
  2. package/.storybook/preview.tsx +0 -2
  3. package/__mocks__/create-mutation.ts +16 -8
  4. package/__mocks__/next-image.tsx +12 -3
  5. package/package.json +2 -2
  6. package/src/api-manager/datasources/d365/d365-categories.datasource.ts +2 -3
  7. package/src/api-manager/datasources/d365/d365-order.datasource.ts +4 -4
  8. package/src/api-manager/datasources/d365/d365-organization.datasource.ts +15 -8
  9. package/src/api-manager/datasources/d365/d365-product.datasource.ts +31 -19
  10. package/src/api-manager/datasources/d365/d365-user.datasource.ts +47 -19
  11. package/src/api-manager/datasources/e4/address/e4-address.remaps.ts +2 -2
  12. package/src/api-manager/datasources/e4/cart/e4-cart.remaps.ts +1 -1
  13. package/src/api-manager/datasources/e4/product/e4-product.remaps.ts +3 -3
  14. package/src/api-manager/datasources/e4/user/e4-user.datasource.ts +4 -8
  15. package/src/api-manager/index.ts +10 -127
  16. package/src/api-manager/lib/get-graphql-client.ts +2 -2
  17. package/src/api-manager/schemas/product.schema.ts +9 -0
  18. package/src/api-manager/services/create-extension-query.ts +1 -1
  19. package/src/api-manager/services/create-query.ts +106 -266
  20. package/src/api-manager/types/Datasource.ts +0 -3
  21. package/src/cms/blocks/components/category-card.tsx +0 -1
  22. package/src/cms/blocks/components/category-carousel/index.tsx +0 -1
  23. package/src/cms/blocks/components/featured-categories.tsx +1 -2
  24. package/src/cms/blocks/components/featured-products.tsx +1 -2
  25. package/src/cms/blocks/components/footer/index.tsx +0 -1
  26. package/src/cms/blocks/components/footer/sections/footer-app-store.tsx +1 -1
  27. package/src/cms/blocks/components/footer/sections/footer-contact.tsx +0 -1
  28. package/src/cms/blocks/components/footer/sections/footer-links.tsx +1 -1
  29. package/src/cms/blocks/components/footer/sections/footer-logo.tsx +0 -1
  30. package/src/cms/blocks/components/footer/sections/footer-social-links.tsx +0 -1
  31. package/src/cms/blocks/components/footer.tsx +0 -1
  32. package/src/cms/blocks/components/hero-carousel/index.tsx +0 -1
  33. package/src/cms/blocks/components/hero.tsx +0 -1
  34. package/src/cms/blocks/components/image.tsx +0 -1
  35. package/src/cms/blocks/components/media-card.tsx +0 -1
  36. package/src/cms/blocks/components/product-carousel/index.tsx +0 -1
  37. package/src/cms/blocks/components/product-section-fullwidth/index.tsx +0 -1
  38. package/src/cms/blocks/components/rich-text.tsx +0 -1
  39. package/src/cms/blocks/components/seo/index.tsx +1 -2
  40. package/src/cms/blocks/components/services/index.tsx +0 -1
  41. package/src/cms/blocks/components/services/service-card.tsx +0 -1
  42. package/src/cms/blocks/components/shared/featured-product-card.tsx +0 -1
  43. package/src/cms/blocks/components/shared/product-category-item.tsx +1 -1
  44. package/src/cms/blocks/components/shared/product-grid.tsx +0 -1
  45. package/src/cms/blocks/components/shared/top-categories-card.tsx +0 -1
  46. package/src/cms/blocks/components/shared/top-rating-product-card.tsx +0 -1
  47. package/src/cms/blocks/icons/components/category.tsx +0 -1
  48. package/src/cms/blocks/icons/components/dotted-star.tsx +0 -1
  49. package/src/cms/blocks/icons/components/fallback-icon.tsx +0 -1
  50. package/src/cms/blocks/icons/components/gift-box.tsx +0 -1
  51. package/src/cms/blocks/icons/components/light.tsx +0 -1
  52. package/src/cms/blocks/icons/components/new-arrival.tsx +0 -1
  53. package/src/cms/blocks/icons/components/rank-badge.tsx +0 -1
  54. package/src/components/BazaarTextField.tsx +7 -7
  55. package/src/components/CountryInput.stories.tsx +4 -2
  56. package/src/components/T/__tests__/T.test.tsx +9 -13
  57. package/src/components/T/index.tsx +0 -1
  58. package/src/components/Typography.tsx +0 -1
  59. package/src/components/_tests_/site-logo.test.tsx +4 -5
  60. package/src/components/categories/category-list/category-list.tsx +1 -1
  61. package/src/components/countries-input.tsx +1 -1
  62. package/src/components/nav-link/nav-link.tsx +2 -8
  63. package/src/components/navbar/mega-menu/mega-menu.tsx +7 -1
  64. package/src/components/navbar/mobile-menu/mobile-menu.test.tsx +2 -1
  65. package/src/components/navbar/nav-list/cms-nav.test.tsx +0 -1
  66. package/src/components/no-records.tsx +0 -1
  67. package/src/components/product-cards/favorite-button.tsx +0 -1
  68. package/src/components/product-cards/product-card/styles/index.ts +2 -2
  69. package/src/components/product-cards/product-card-plp-list/ProductCardPlpList.stories.tsx +0 -1
  70. package/src/components/product-cards/product-card-search/ProductCardSearch.stories.tsx +0 -1
  71. package/src/components/product-dimensions/Dimensions.stories.tsx +2 -4
  72. package/src/components/product-dimensions/compound/dimensions-group-list-chip.tsx +1 -1
  73. package/src/components/product-dimensions/compound/dimensions.tsx +1 -1
  74. package/src/components/product-quantity-buttons/ProductQuantity.stories.tsx +1 -3
  75. package/src/components/product-quantity-buttons/__tests__/product-quantity-buttons.test.tsx +3 -2
  76. package/src/components/product-quantity-buttons/compound/quantity-buttons-root.tsx +1 -1
  77. package/src/components/product-quantity-buttons/index.tsx +0 -1
  78. package/src/components/product-quantity-variants/ProductQuantity.stories.tsx +1 -3
  79. package/src/components/products-view/ProductsGrid.stories.tsx +1 -3
  80. package/src/components/products-view/ProductsList.stories.tsx +1 -3
  81. package/src/components/products-view/__tests__/products-view.test.tsx +20 -9
  82. package/src/components/products-view/index.tsx +1 -1
  83. package/src/components/scrollbar/Scrollbar.stories.tsx +1 -3
  84. package/src/components/scrollbar/__tests__/scrollbar.test.tsx +7 -1
  85. package/src/components/search-bar/SearchBarLarge.stories.tsx +1 -3
  86. package/src/components/search-bar/compound/results-item-lg.tsx +0 -1
  87. package/src/components/search-bar/compound/results-item-sm.tsx +0 -1
  88. package/src/components/search-bar/compound/textfield-adornment-category.tsx +0 -1
  89. package/src/components/search-bar/compound/textfield.tsx +0 -1
  90. package/src/components/search-bar/utils/findCategoryNameById.ts +3 -3
  91. package/src/components/section/__tests__/section.test.tsx +14 -7
  92. package/src/components/side-nav/SideNav.stories.tsx +1 -1
  93. package/src/components/site-logo.tsx +8 -1
  94. package/src/components/wishlist-dialogs/add-to-wishlist/add-to-wishlist-button.tsx +1 -1
  95. package/src/components/wishlist-dialogs/add-to-wishlist/compound/add-to-wishlist-bottom-ui.tsx +1 -1
  96. package/src/components/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-header.tsx +0 -1
  97. package/src/components/wishlist-dialogs/create-wishlist/compound/create-wishlist-bottom-ui.tsx +1 -1
  98. package/src/hooks/use-nextauth-session.ts +4 -1
  99. package/src/lib/lib.ts +1 -1
  100. package/src/lib/page-slugs.ts +0 -2
  101. package/src/lib/refiner-utils.ts +3 -3
  102. package/src/pages/account/account-navigation.tsx +17 -20
  103. package/src/pages/account/addresses/address-form.tsx +15 -7
  104. package/src/pages/account/dashboard-header.tsx +1 -1
  105. package/src/pages/cart/checkout-form.tsx +1 -1
  106. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +1 -1
  107. package/src/pages/checkout/checkout-alt-form/steps/address/new-address-form.tsx +1 -1
  108. package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +5 -2
  109. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +1 -1
  110. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +1 -1
  111. package/src/pages/confirmation/address.tsx +1 -1
  112. package/src/pages/confirmation/confirmation-summary.tsx +1 -1
  113. package/src/pages/product-details/currency.ts +1 -1
  114. package/src/pages/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +4 -1
  115. package/src/pages/product-list/breadcrumbs.tsx +0 -1
  116. package/src/pages/product-list/list-filter.tsx +0 -1
  117. package/src/pages/product-list/product-list-context.tsx +1 -1
  118. package/src/pages/product-list/product-list-view.tsx +2 -2
  119. package/src/pages/product-list/selected-facets.tsx +1 -1
  120. package/src/pages/quickorder/context.ts +8 -2
  121. package/src/pages/quickorder/order-upload.tsx +26 -8
  122. package/src/pages/quickorder/provider.tsx +8 -9
  123. package/src/pages/quickorder/quick-order.tsx +17 -11
  124. package/src/providers/nav-provider/index.tsx +1 -1
  125. package/tsconfig.json +0 -1
@@ -1,12 +1,11 @@
1
+ import { dirname, join, resolve } from "node:path";
1
2
  import type { StorybookConfig } from "@storybook/react-webpack5";
2
3
 
3
- import { dirname, join, resolve } from "path";
4
-
5
4
  /**
6
5
  * This function is used to resolve the absolute path of a package.
7
6
  * It is needed in projects that use Yarn PnP or are set up within a monorepo.
8
7
  */
9
- function getAbsolutePath(value: string): any {
8
+ function getAbsolutePath(value: string): string {
10
9
  return dirname(require.resolve(join(value, "package.json")));
11
10
  }
12
11
  const config: StorybookConfig = {
@@ -1,8 +1,6 @@
1
1
  import type { Preview } from "@storybook/react-webpack5";
2
2
  import { QueryClientProvider } from "@tanstack/react-query";
3
3
  import { NextIntlClientProvider } from "next-intl";
4
- import React from "react";
5
- import { sb } from "storybook/test";
6
4
  import messages from "../../../../../storefront/messages/en-US.json";
7
5
  import { queryClient } from "../__mocks__/query-client.ts";
8
6
  import { NotificationProvider } from "../src/providers/notifications/notification-context.tsx";
@@ -1,23 +1,31 @@
1
1
  import { queryClient } from "./query-client";
2
2
 
3
+ type MutationInput = {
4
+ cartLine?: Array<{ Quantity?: number }>;
5
+ } & Record<string, unknown>;
6
+
7
+ type CartMockData = {
8
+ CartLines: Array<Record<string, unknown>>;
9
+ } & Record<string, unknown>;
10
+
3
11
  /**
4
12
  *
5
13
  * Mocking up at the react-query level because our components
6
14
  * rely on react-query for triggering re-renders and state subscriptions.
7
15
  *
8
16
  */
9
- export const useCreateMutation = (apiManagerPath: any) => {
17
+ export const useCreateMutation = (apiManagerPath: readonly unknown[]) => {
10
18
  switch (apiManagerPath[1]) {
11
19
  case "updateCartLines":
12
20
  return {
13
- mutate: (input: any) => {
21
+ mutate: (input: MutationInput) => {
14
22
  console.log(input);
15
- queryClient.setQueryData(["cart"], (oldData: any) => ({
23
+ queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
16
24
  ...oldData,
17
25
  CartLines: [
18
26
  {
19
27
  ...oldData.CartLines[0],
20
- Quantity: input?.cartLine[0]?.Quantity,
28
+ Quantity: input.cartLine?.[0]?.Quantity,
21
29
  },
22
30
  ],
23
31
  }));
@@ -25,9 +33,9 @@ export const useCreateMutation = (apiManagerPath: any) => {
25
33
  };
26
34
  case "deleteCartLine":
27
35
  return {
28
- mutate: (input: any) => {
36
+ mutate: (input: MutationInput) => {
29
37
  console.log(input);
30
- queryClient.setQueryData(["cart"], (oldData: any) => ({
38
+ queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
31
39
  ...oldData,
32
40
  CartLines: [],
33
41
  }));
@@ -35,9 +43,9 @@ export const useCreateMutation = (apiManagerPath: any) => {
35
43
  };
36
44
  case "addToCart":
37
45
  return {
38
- mutate: (input: any) => {
46
+ mutate: (input: MutationInput) => {
39
47
  console.log(input);
40
- queryClient.setQueryData(["cart"], (oldData: any) => ({
48
+ queryClient.setQueryData(["cart"], (oldData: CartMockData) => ({
41
49
  ...oldData,
42
50
  CartLines: [
43
51
  {
@@ -1,9 +1,18 @@
1
1
  // __mocks__/next/image.js
2
- const Image = (props) => {
2
+ type ImageProps = {
3
+ alt?: string;
4
+ } & Record<string, unknown>;
5
+
6
+ const Image = (props: ImageProps) => {
3
7
  const { alt = "", ...rest } = props;
4
8
 
5
- // eslint-disable-next-line @next/next/no-img-element
6
- return <img alt={alt} {...rest} />;
9
+ return {
10
+ $$typeof: Symbol.for("react.element"),
11
+ type: "img",
12
+ key: null,
13
+ ref: null,
14
+ props: { alt, ...rest },
15
+ };
7
16
  };
8
17
 
9
18
  export default Image;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evenicanpm/storefront-core",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Core module for D365/e4 Headless Storefront",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -49,5 +49,5 @@
49
49
  "esbuild": "0.19.11"
50
50
  }
51
51
  },
52
- "gitHead": "7552c1e700a0027b8a65506c77cb10dae092fdfb"
52
+ "gitHead": "bbaff05f6f592114ad7a25caeee531e4fa1b14cf"
53
53
  }
@@ -1,5 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
-
3
1
  import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
4
2
  import type { CategoryApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
5
3
  import {
@@ -7,6 +5,7 @@ import {
7
5
  mapCategoryToHierarchy,
8
6
  } from "@evenicanpm/storefront-core/src/lib/category-helpers";
9
7
  import { getCategoriesAsync } from "@msdyn365-commerce/retail-proxy/dist/DataActions/CategoriesDataActions.g";
8
+ import type { IContext } from "@msdyn365-commerce/retail-proxy/dist/Interfaces/ICallerContext";
10
9
 
11
10
  class D365CategoriesDatasource
12
11
  extends D365DatasourceBase
@@ -16,7 +15,7 @@ class D365CategoriesDatasource
16
15
  const context = this.context;
17
16
  try {
18
17
  const categories = await getCategoriesAsync(
19
- context as any,
18
+ context as unknown as IContext,
20
19
  context.requestContext.apiSettings.channelId,
21
20
  0,
22
21
  );
@@ -1,10 +1,9 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
-
3
1
  import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
4
2
  import { D365ProductDatasource } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-product.datasource";
5
3
  import type {
6
4
  GetSalesOrderDetailsInput,
7
5
  SalesOrder,
6
+ SalesOrderInput,
8
7
  } from "@evenicanpm/storefront-core/src/api-manager/schemas/order.schema";
9
8
  import type { OrderApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
10
9
  import {
@@ -12,11 +11,12 @@ import {
12
11
  type ICallerContext,
13
12
  SearchLocation,
14
13
  } from "@msdyn365-commerce/retail-proxy";
14
+ import type { IContext } from "@msdyn365-commerce/retail-proxy/dist/Interfaces/ICallerContext";
15
15
  import { getSalesOrderDetailsByTransactionIdAsync } from "@msdyn365-commerce/retail-proxy/dist/DataActions/SalesOrdersDataActions.g";
16
16
  import uniq from "lodash/uniq";
17
17
 
18
18
  class D365OrderDatasource extends D365DatasourceBase implements OrderApi {
19
- getSalesOrders(): Promise<any> {
19
+ getSalesOrders(_input: SalesOrderInput): Promise<SalesOrder[]> {
20
20
  throw new Error("Method not implemented.");
21
21
  }
22
22
  async getSalesOrderDetails(
@@ -26,7 +26,7 @@ class D365OrderDatasource extends D365DatasourceBase implements OrderApi {
26
26
  const { transactionId } = input;
27
27
  try {
28
28
  const data = await getSalesOrderDetailsByTransactionIdAsync(
29
- context as any,
29
+ context as unknown as IContext,
30
30
  transactionId,
31
31
  SearchLocation.All,
32
32
  );
@@ -1,5 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
-
3
1
  import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
4
2
  import type {
5
3
  ChannelConfiguration,
@@ -11,6 +9,7 @@ import type { OrganizationApi } from "@evenicanpm/storefront-core/src/api-manage
11
9
  import type {
12
10
  ChannelDeliveryOption,
13
11
  ICallerContext,
12
+ IContext,
14
13
  } from "@msdyn365-commerce/retail-proxy";
15
14
  import {
16
15
  getChannelDeliveryOptionsAsync,
@@ -23,15 +22,23 @@ class D365OrganizationDatasource
23
22
  extends D365DatasourceBase
24
23
  implements OrganizationApi
25
24
  {
25
+ private getRetailContext(context: ICallerContext): IContext {
26
+ return { callerContext: context };
27
+ }
28
+
26
29
  async getStoresList(
27
30
  input: GetOrgUnitLocationsByAreaInput,
28
31
  ): Promise<OrgUnitLocationReturnType[]> {
29
32
  const context = this.context;
30
33
  const { queryResultSettings, searchArea, deliveryModeTypeFilter } = input;
31
- const ctx = { ...context } as any;
32
- ctx.queryResultSettings = queryResultSettings;
34
+ const ctx: IContext & {
35
+ queryResultSettings?: GetOrgUnitLocationsByAreaInput["queryResultSettings"];
36
+ } = {
37
+ ...this.getRetailContext(context),
38
+ queryResultSettings,
39
+ };
33
40
  const locations = await getOrgUnitLocationsByAreaAsync(
34
- ctx as any,
41
+ ctx,
35
42
  searchArea,
36
43
  deliveryModeTypeFilter,
37
44
  );
@@ -56,7 +63,7 @@ class D365OrganizationDatasource
56
63
 
57
64
  async getChannelConfiguration(): Promise<ChannelConfiguration> {
58
65
  const context = this.context;
59
- return getOrgUnitConfigurationAsync(context as any);
66
+ return getOrgUnitConfigurationAsync(this.getRetailContext(context));
60
67
  }
61
68
 
62
69
  async getStoreHours(
@@ -65,7 +72,7 @@ class D365OrganizationDatasource
65
72
  context: ICallerContext,
66
73
  ): Promise<OrgUnitLocationReturnType> {
67
74
  const storeHours = await getStoreHoursAsync(
68
- context as any,
75
+ this.getRetailContext(context),
69
76
  store.OrgUnitNumber,
70
77
  );
71
78
  return {
@@ -81,7 +88,7 @@ class D365OrganizationDatasource
81
88
  ): Promise<ChannelDeliveryOption[] | undefined> {
82
89
  const DELIVERY_OPTION_TYPE = 4; //This is to get all pickup modes, not sure where the number comes from.
83
90
  const channelDeliveryOptions = await getChannelDeliveryOptionsAsync(
84
- context as any,
91
+ this.getRetailContext(context),
85
92
  channelList,
86
93
  DELIVERY_OPTION_TYPE,
87
94
  );
@@ -1,5 +1,3 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
-
3
1
  import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
4
2
  import D365CategoriesDatasource from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-categories.datasource";
5
3
  import { createDimSearchCriteria } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/utils/create-search-criteria";
@@ -17,9 +15,12 @@ import type {
17
15
  SearchByCriteriaResponse,
18
16
  searchProductsInput,
19
17
  } from "@evenicanpm/storefront-core/src/api-manager/schemas/product.schema";
18
+ import type { CategoriesQuery } from "@evenicanpm/storefront-core/src/api-manager/datasources/e4/graphqlRequestSdk";
20
19
  import type { ProductApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
21
20
  import {
22
21
  executeAction,
22
+ type ICallerContext,
23
+ type IContext,
23
24
  type InventoryAvailabilitySearchCriteria,
24
25
  type IQueryResultSettings,
25
26
  type ProductPrice,
@@ -56,6 +57,10 @@ const setFullProductImgUrl = async (
56
57
  };
57
58
 
58
59
  class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
60
+ private getRetailContext(context: ICallerContext): IContext {
61
+ return { callerContext: context };
62
+ }
63
+
59
64
  /**
60
65
  * Gets product by id, sets full image url and dimension values
61
66
  * @params input GetProductByIdInput
@@ -66,7 +71,7 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
66
71
  const channelId = context.requestContext.apiSettings.channelId;
67
72
 
68
73
  const product = await getByIdAsync(
69
- context as any,
74
+ this.getRetailContext(context),
70
75
  Number(input.id),
71
76
  channelId,
72
77
  );
@@ -79,14 +84,14 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
79
84
  const dimensionPromises =
80
85
  product?.Dimensions?.map(async ({ DimensionTypeValue }) => {
81
86
  return getDimensionValuesWithEstimatedAvailabilitiesAsync(
82
- context as any,
87
+ this.getRetailContext(context),
83
88
  product?.RecordId,
84
89
  createDimSearchCriteria(DimensionTypeValue),
85
90
  );
86
91
  }) || [];
87
92
 
88
93
  const attributesPromise = getAttributeValuesAsync(
89
- context as any,
94
+ this.getRetailContext(context),
90
95
  product?.RecordId,
91
96
  channelId,
92
97
  0,
@@ -107,7 +112,7 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
107
112
  async getProductByIds(input: GetProductByIdsInput) {
108
113
  const context = this.context;
109
114
  const products = await getByIdsAsync(
110
- context as any,
115
+ this.getRetailContext(context),
111
116
  context.requestContext.apiSettings.channelId,
112
117
  input?.id.map(Number),
113
118
  );
@@ -159,8 +164,10 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
159
164
  (r) => r.RefinerSourceValue !== ProductRefinerSource.Price,
160
165
  );
161
166
 
162
- const ctx = { ...context } as any;
163
- ctx.queryResultSettings = queryResultSettingsWithCount;
167
+ const ctx: IContext & { queryResultSettings?: IQueryResultSettings } = {
168
+ ...this.getRetailContext(context),
169
+ queryResultSettings: queryResultSettingsWithCount,
170
+ };
164
171
  const searchRequest = createSearchByCriteriaInput(
165
172
  queryResultSettingsWithCount,
166
173
  searchCriteria,
@@ -178,7 +185,9 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
178
185
  refinersPromise,
179
186
  ]);
180
187
 
181
- const count = (searchRequest as any)?._totalCount ?? productResults.length;
188
+ const count =
189
+ (searchRequest as { _totalCount?: number })._totalCount ??
190
+ productResults.length;
182
191
  const promises = [];
183
192
  for (const product of productResults) {
184
193
  promises.push(
@@ -198,7 +207,9 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
198
207
 
199
208
  async searchProducts(input: searchProductsInput) {
200
209
  const context = this.context;
201
- const ctx = { ...context } as any;
210
+ const ctx: IContext & { queryResultSettings?: IQueryResultSettings } = {
211
+ ...this.getRetailContext(context),
212
+ };
202
213
  const queryResultSettings: IQueryResultSettings = {
203
214
  Paging: {
204
215
  Skip: 0,
@@ -222,11 +233,10 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
222
233
  ItemId: id,
223
234
  }));
224
235
  }
225
- const observableResults = await searchByCriteriaAsync(
226
- ctx as any,
227
- searchCriteria,
236
+ const observableResults = await searchByCriteriaAsync(ctx, searchCriteria);
237
+ const results: ProductSearchResult[] = Array.from(
238
+ observableResults as Iterable<ProductSearchResult>,
228
239
  );
229
- const results: ProductSearchResult[] = Array.from(observableResults as any);
230
240
  const promises = [];
231
241
  for (const product of results) {
232
242
  promises.push(
@@ -240,18 +250,20 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
240
250
  return results;
241
251
  }
242
252
 
243
- async getCategoryList(): Promise<any> {
253
+ async getCategoryList(): Promise<CategoriesQuery> {
244
254
  // TODO ? Why is the get category list here and calling the category data source. Can we decide if we want to have a separate one for categories or remove this?
245
255
  // agree with above statement - sam
246
256
  const categoriesDatasource = new D365CategoriesDatasource(this.context);
247
257
  const categories = await categoriesDatasource.getCategories();
248
- return { categories: { category: categories } };
258
+ return {
259
+ categories: { category: categories },
260
+ } as unknown as CategoriesQuery;
249
261
  }
250
262
 
251
263
  async getRelatedProducts(input: GetRelatedProductsInput) {
252
264
  const context = this.context;
253
265
  return getRelatedProductsAsync(
254
- context as any,
266
+ this.getRetailContext(context),
255
267
  context.requestContext.apiSettings.channelId,
256
268
  input.id,
257
269
  0,
@@ -270,7 +282,7 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
270
282
  FilterByChannelFulfillmentGroup: true,
271
283
  };
272
284
  const data = await getEstimatedAvailabilityAsync(
273
- context as any,
285
+ this.getRetailContext(context),
274
286
  searchCriteria,
275
287
  );
276
288
  return data;
@@ -290,7 +302,7 @@ class D365ProductDatasource extends D365DatasourceBase implements ProductApi {
290
302
 
291
303
  try {
292
304
  const data = await getActivePricesAsync(
293
- context as any,
305
+ this.getRetailContext(context),
294
306
  projectionDomain,
295
307
  input.productIds,
296
308
  new Date(),
@@ -1,11 +1,11 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
-
3
1
  import { D365DatasourceBase } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-base.datasource";
4
2
  import { D365ProductDatasource } from "@evenicanpm/storefront-core/src/api-manager/datasources/d365/d365-product.datasource";
5
3
  import type { UserApi } from "@evenicanpm/storefront-core/src/api-manager/types/Datasource";
6
4
  import {
7
5
  type CommerceProperty,
8
6
  type Customer,
7
+ type ICallerContext,
8
+ type IContext,
9
9
  type ProductList,
10
10
  type ProductListSearchCriteria,
11
11
  ProductListType,
@@ -39,10 +39,29 @@ import type {
39
39
  WishlistLinesInput,
40
40
  } from "@/schemas/user.schema";
41
41
 
42
+ type IdTokenClaims = jwt.JwtPayload & {
43
+ emails?: string[];
44
+ email?: string;
45
+ given_name?: string;
46
+ family_name?: string;
47
+ };
48
+
49
+ function decodeIdTokenClaims(token: string): IdTokenClaims {
50
+ const decoded = jwt.decode(token);
51
+ return decoded as IdTokenClaims;
52
+ }
53
+
42
54
  class D365UserDatasource extends D365DatasourceBase implements UserApi {
55
+ private getRetailContext(context: ICallerContext): IContext {
56
+ return { callerContext: context };
57
+ }
58
+
43
59
  async updateUser(user: User): Promise<User> {
44
60
  const context = this.context;
45
- const existing: Customer = await readAsync(context as any, "");
61
+ const existing: Customer = await readAsync(
62
+ this.getRetailContext(context),
63
+ "",
64
+ );
46
65
  existing.Addresses ??= [];
47
66
  existing.Addresses = existing.Addresses.map((addr) => ({
48
67
  ...addr,
@@ -55,11 +74,13 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
55
74
  );
56
75
  existing.ExtensionProperties.push({
57
76
  Key: propKey,
58
- Value: { StringValue: user.DefaultShippingAddressId } as any,
77
+ Value: { StringValue: user.DefaultShippingAddressId } as NonNullable<
78
+ CommerceProperty["Value"]
79
+ >,
59
80
  });
60
81
  const updated: Customer = await updateAsync(
61
- context as any,
62
- existing as any,
82
+ this.getRetailContext(context),
83
+ existing,
63
84
  );
64
85
  const mappedAddresses = (updated.Addresses ?? []).map((a) => ({
65
86
  ...a,
@@ -76,10 +97,10 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
76
97
  if (!context.requestContext.user.isAuthenticated) return null;
77
98
  let customer: Customer;
78
99
  try {
79
- customer = await readAsync(context as any, "");
100
+ customer = await readAsync(this.getRetailContext(context), "");
80
101
  } catch {
81
- const token = jwt.decode(context.requestContext.user.token) as any;
82
- customer = await createCustomerAsync(context as any, {
102
+ const token = decodeIdTokenClaims(context.requestContext.user.token);
103
+ customer = await createCustomerAsync(this.getRetailContext(context), {
83
104
  AccountNumber: "",
84
105
  Email: Array.isArray(token.emails) ? token.emails[0] : token.email,
85
106
  FirstName: token.given_name,
@@ -113,11 +134,11 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
113
134
 
114
135
  try {
115
136
  const wishlists = await searchAsync(
116
- context as any,
137
+ this.getRetailContext(context),
117
138
  productListSearchCriteria,
118
139
  );
119
140
  const wishlistProductDataPromises = wishlists.map((wishlist) => {
120
- return wishlistReadAsync(context as any, wishlist.Id);
141
+ return wishlistReadAsync(this.getRetailContext(context), wishlist.Id);
121
142
  });
122
143
  const wishlistProductData = await Promise.all(
123
144
  wishlistProductDataPromises,
@@ -134,7 +155,10 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
134
155
  const context = this.context;
135
156
  let wishlist: Wishlist;
136
157
  try {
137
- wishlist = await wishlistReadAsync(context as any, input.Id);
158
+ wishlist = await wishlistReadAsync(
159
+ this.getRetailContext(context),
160
+ input.Id,
161
+ );
138
162
  } catch (error) {
139
163
  throw new Error(
140
164
  `Error getting wishlist details: ${JSON.stringify(error)}`,
@@ -182,7 +206,7 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
182
206
  };
183
207
  try {
184
208
  const wishlist = await createProductListAsync(
185
- context as any,
209
+ this.getRetailContext(context),
186
210
  productList,
187
211
  );
188
212
  return wishlist;
@@ -197,7 +221,7 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
197
221
  throw new Error("Wishlist ID is required for deletion.");
198
222
  }
199
223
  try {
200
- await deleteAsync(context as any, { Id: input.Id });
224
+ await deleteAsync(this.getRetailContext(context), { Id: input.Id });
201
225
  } catch (error) {
202
226
  throw new Error(`Error deleting wishlist: ${error}`);
203
227
  }
@@ -210,7 +234,7 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
210
234
 
211
235
  try {
212
236
  const updatedProductListLines = await updateProductListLinesAsync(
213
- context as any,
237
+ this.getRetailContext(context),
214
238
  id,
215
239
  productListLines,
216
240
  );
@@ -228,7 +252,7 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
228
252
 
229
253
  try {
230
254
  const updatedProductListLines = await addProductListLinesAsync(
231
- context as any,
255
+ this.getRetailContext(context),
232
256
  id,
233
257
  productListLines,
234
258
  );
@@ -245,7 +269,11 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
245
269
  const id = input.Id;
246
270
  const productListLines = input.ProductListLines;
247
271
  try {
248
- await removeProductListLinesAsync(context as any, id, productListLines);
272
+ await removeProductListLinesAsync(
273
+ this.getRetailContext(context),
274
+ id,
275
+ productListLines,
276
+ );
249
277
  } catch (error) {
250
278
  throw new Error(`Error removing from wishlist: ${error}`);
251
279
  }
@@ -259,8 +287,8 @@ class D365UserDatasource extends D365DatasourceBase implements UserApi {
259
287
  const isQuantityAggregate = true;
260
288
 
261
289
  try {
262
- const wishlist = copyCartToProductListAsync(
263
- context as any,
290
+ const wishlist = await copyCartToProductListAsync(
291
+ this.getRetailContext(context),
264
292
  productListId,
265
293
  cartId,
266
294
  isRewrite,
@@ -16,7 +16,7 @@ export const addressMap = {
16
16
  {
17
17
  key: "[].RecordId",
18
18
  transform: (id: string) => {
19
- if (id !== null) return Number.parseInt(id);
19
+ if (id !== null) return Number.parseInt(id, 10);
20
20
  },
21
21
  },
22
22
  ],
@@ -25,7 +25,7 @@ export const addressMap = {
25
25
  "[].addressType.id": {
26
26
  key: "[].AddressTypeValue",
27
27
  transform: (id: string) => {
28
- if (id !== null) return Number.parseInt(id);
28
+ if (id !== null) return Number.parseInt(id, 10);
29
29
  },
30
30
  },
31
31
  "[].address1": "[].Street",
@@ -44,7 +44,7 @@ export const cartLineMap = {
44
44
  "[].product.id": {
45
45
  key: "[].ProductId",
46
46
  transform: (id: string) => {
47
- if (id !== null) return Number.parseInt(id);
47
+ if (id !== null) return Number.parseInt(id, 10);
48
48
  },
49
49
  },
50
50
  "[].product.code": "[].ItemId",
@@ -34,7 +34,7 @@ export const productMap = {
34
34
  {
35
35
  key: "RecordId",
36
36
  transform: (id: string) => {
37
- if (id !== null) return Number.parseInt(id);
37
+ if (id !== null) return Number.parseInt(id, 10);
38
38
  },
39
39
  },
40
40
  ],
@@ -54,13 +54,13 @@ export const productPricesMap = {
54
54
  {
55
55
  key: "ProductId",
56
56
  transform: (id: string) => {
57
- if (id !== null) return Number.parseInt(id);
57
+ if (id !== null) return Number.parseInt(id, 10);
58
58
  },
59
59
  },
60
60
  {
61
61
  key: "ListingId",
62
62
  transform: (id: string) => {
63
- if (id !== null) return Number.parseInt(id);
63
+ if (id !== null) return Number.parseInt(id, 10);
64
64
  },
65
65
  },
66
66
  ],
@@ -123,23 +123,19 @@ const user: UserApi = {
123
123
  throw new Error("Error creating wishlist");
124
124
  }
125
125
  },
126
- // @ts-expect-error
127
- async updateWishlist(input: WishlistLinesInput): Promise<Wishlist> {
126
+ async updateWishlist(_input: WishlistLinesInput): Promise<Wishlist> {
128
127
  throw new Error("updateWishlist not implemented");
129
128
  },
130
129
 
131
- // @ts-expect-error
132
- async addProductsToWishlist(input: WishlistLinesInput): Promise<Wishlist> {
130
+ async addProductsToWishlist(_input: WishlistLinesInput): Promise<Wishlist> {
133
131
  throw new Error("addProductsToWishlist not implemented");
134
132
  },
135
133
 
136
- // @ts-expect-error
137
- async removeProductsFromWishlist(input: WishlistLinesInput): Promise<void> {
134
+ async removeProductsFromWishlist(_input: WishlistLinesInput): Promise<void> {
138
135
  throw new Error("removeProductsFromWishlist not implemented");
139
136
  },
140
137
 
141
- // @ts-expect-error
142
- async copyCartToWishlist(input: CopyCartToWishlistInput): Promise<Wishlist> {
138
+ async copyCartToWishlist(_input: CopyCartToWishlistInput): Promise<Wishlist> {
143
139
  throw new Error("copyCartToWishlist not implemented");
144
140
  },
145
141
  };