From 9b07496a98c9eff8dbff96c6a32c5488fa8b0be6 Mon Sep 17 00:00:00 2001 From: Robert Field Date: Mon, 26 Aug 2024 18:45:58 +0100 Subject: [PATCH] feat: migrate to new client --- examples/simple/e2e/product-list-page.spec.ts | 9 +- .../app/(store)/products/[productId]/page.tsx | 6 +- .../featured-products/FeaturedProducts.tsx | 79 +++++++------ .../header/navigation/MobileNavBar.tsx | 2 +- .../header/navigation/NavBarPopover.tsx | 2 +- .../header/navigation/NavItemContent.tsx | 2 +- .../simple/src/lib/build-breadcrumb-lookup.ts | 2 +- .../simple/src/lib/build-site-navigation.ts | 104 ------------------ .../lib/epcc-server-side-implicit-client.ts | 20 ++-- .../simple/src/lib/get-store-initial-state.ts | 15 ++- examples/simple/src/services/cart.ts | 20 +++- examples/simple/src/services/hierarchy.ts | 52 ++++++--- 12 files changed, 128 insertions(+), 185 deletions(-) delete mode 100644 examples/simple/src/lib/build-site-navigation.ts diff --git a/examples/simple/e2e/product-list-page.spec.ts b/examples/simple/e2e/product-list-page.spec.ts index 9d193c82..f6c29613 100644 --- a/examples/simple/e2e/product-list-page.spec.ts +++ b/examples/simple/e2e/product-list-page.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from "@playwright/test"; import { gateway } from "@elasticpath/js-sdk"; -import { buildSiteNavigation } from "../src/lib/build-site-navigation"; +import { buildSiteNavigation } from "@elasticpath/react-shopper-hooks"; const host = process.env.NEXT_PUBLIC_EPCC_ENDPOINT_URL; const client_id = process.env.NEXT_PUBLIC_EPCC_CLIENT_ID; @@ -19,9 +19,8 @@ test("should be able to use quick view to view full product details", async ({ /* Get the cart id from the cookie */ const allCookies = await page.context().cookies(); - const cartId = allCookies.find( - (cookie) => cookie.name === "_store_ep_cart", - )?.value; + const cartId = allCookies.find((cookie) => cookie.name === "_store_ep_cart") + ?.value; const nav = await buildSiteNavigation(client); @@ -34,7 +33,7 @@ test("should be able to use quick view to view full product details", async ({ ); } - await page.getByRole("button", {name: "Shop Now"}).click(); + await page.getByRole("button", { name: "Shop Now" }).click(); /* Check to make sure the page has navigated to the product list page for Men's / T-Shirts */ await expect(page).toHaveURL(`/search`); diff --git a/examples/simple/src/app/(store)/products/[productId]/page.tsx b/examples/simple/src/app/(store)/products/[productId]/page.tsx index 0cb8b013..2fde0aed 100644 --- a/examples/simple/src/app/(store)/products/[productId]/page.tsx +++ b/examples/simple/src/app/(store)/products/[productId]/page.tsx @@ -4,7 +4,7 @@ import { getServerSideImplicitClient } from "../../../../lib/epcc-server-side-im import { notFound } from "next/navigation"; import { parseProductResponse } from "@elasticpath/react-shopper-hooks"; import React from "react"; -import { getByContextProduct, createClient } from "@epcc-sdk/sdks-shopper"; +import { getByContextProduct, client } from "@epcc-sdk/sdks-shopper"; import { applyDefaultNextMiddleware } from "@epcc-sdk/sdks-nextjs"; import { epccEnv } from "../../../../lib/resolve-epcc-env"; import { ProductResponse, ShopperCatalogResource } from "@elasticpath/js-sdk"; @@ -15,11 +15,11 @@ type Props = { params: { productId: string }; }; -createClient({ +client.setConfig({ baseUrl: `https://${epccEnv.host}`, }); -applyDefaultNextMiddleware(); +applyDefaultNextMiddleware(client); export async function generateMetadata({ params: { productId }, diff --git a/examples/simple/src/components/featured-products/FeaturedProducts.tsx b/examples/simple/src/components/featured-products/FeaturedProducts.tsx index 909ec1ef..317652f3 100644 --- a/examples/simple/src/components/featured-products/FeaturedProducts.tsx +++ b/examples/simple/src/components/featured-products/FeaturedProducts.tsx @@ -5,6 +5,7 @@ import { ArrowRightIcon, EyeSlashIcon } from "@heroicons/react/24/outline"; import Image from "next/image"; import { getServerSideImplicitClient } from "../../lib/epcc-server-side-implicit-client"; import { fetchFeaturedProducts } from "./fetchFeaturedProducts"; +import { extractProductImage } from "@epcc-sdk/sdks-shopper"; interface IFeaturedProductsProps { title: string; @@ -19,7 +20,13 @@ export default async function FeaturedProducts({ linkProps, }: IFeaturedProductsProps) { const client = getServerSideImplicitClient(); - const products = await fetchFeaturedProducts(client); + const featuredProductResponse = await fetchFeaturedProducts(client); + + if (!featuredProductResponse.response.ok) { + return null; + } + + const products = featuredProductResponse.data?.data ?? []; return (
- {products.map((product) => ( - -
  • -
    -
    - {product.main_image?.link.href ? ( - {product.main_image?.file_name!} - ) : ( -
    - -
    - )} + {products.map((product) => { + const mainImage = extractProductImage( + product, + featuredProductResponse.data?.included?.main_images, + ); + return ( + +
  • +
    +
    + {mainImage?.link?.href ? ( + {mainImage?.file_name!} + ) : ( +
    + +
    + )} +
    -
  • -

    - {product.attributes.name} -

    -

    - {product.meta.display_price?.without_tax?.formatted} -

    - - - ))} +

    + {product.attributes?.name} +

    +

    + {product.meta?.display_price?.without_tax?.formatted} +

    + + + ); + })} ); diff --git a/examples/simple/src/components/header/navigation/MobileNavBar.tsx b/examples/simple/src/components/header/navigation/MobileNavBar.tsx index 06f832d4..10c9d449 100644 --- a/examples/simple/src/components/header/navigation/MobileNavBar.tsx +++ b/examples/simple/src/components/header/navigation/MobileNavBar.tsx @@ -3,8 +3,8 @@ import Link from "next/link"; import EpIcon from "../../icons/ep-icon"; import { MobileNavBarButton } from "./MobileNavBarButton"; import { getServerSideImplicitClient } from "../../../lib/epcc-server-side-implicit-client"; -import { buildSiteNavigation } from "../../../lib/build-site-navigation"; import { Cart } from "../../cart/CartSheet"; +import { buildSiteNavigation } from "@elasticpath/react-shopper-hooks"; export default async function MobileNavBar() { const client = getServerSideImplicitClient(); diff --git a/examples/simple/src/components/header/navigation/NavBarPopover.tsx b/examples/simple/src/components/header/navigation/NavBarPopover.tsx index 1409d5c7..04331f9a 100644 --- a/examples/simple/src/components/header/navigation/NavBarPopover.tsx +++ b/examples/simple/src/components/header/navigation/NavBarPopover.tsx @@ -1,6 +1,6 @@ "use client"; import { ReactElement } from "react"; -import { NavigationNode } from "../../../lib/build-site-navigation"; +import { NavigationNode } from "@elasticpath/react-shopper-hooks"; import { NavigationMenu, NavigationMenuContent, diff --git a/examples/simple/src/components/header/navigation/NavItemContent.tsx b/examples/simple/src/components/header/navigation/NavItemContent.tsx index dec7fce9..ffd0d015 100644 --- a/examples/simple/src/components/header/navigation/NavItemContent.tsx +++ b/examples/simple/src/components/header/navigation/NavItemContent.tsx @@ -1,5 +1,5 @@ import Link from "next/link"; -import { NavigationNode } from "../../../lib/build-site-navigation"; +import { NavigationNode } from "@elasticpath/react-shopper-hooks"; import { ArrowRightIcon } from "@heroicons/react/20/solid"; interface IProps { diff --git a/examples/simple/src/lib/build-breadcrumb-lookup.ts b/examples/simple/src/lib/build-breadcrumb-lookup.ts index 4d170da9..7f5f3f9d 100644 --- a/examples/simple/src/lib/build-breadcrumb-lookup.ts +++ b/examples/simple/src/lib/build-breadcrumb-lookup.ts @@ -1,4 +1,4 @@ -import { NavigationNode } from "./build-site-navigation"; +import { NavigationNode } from "@elasticpath/react-shopper-hooks"; import { BreadcrumbLookup } from "./types/breadcrumb-lookup"; export function buildBreadcrumbLookup( diff --git a/examples/simple/src/lib/build-site-navigation.ts b/examples/simple/src/lib/build-site-navigation.ts deleted file mode 100644 index d50c7f34..00000000 --- a/examples/simple/src/lib/build-site-navigation.ts +++ /dev/null @@ -1,104 +0,0 @@ -import type { Hierarchy,ElasticPath } from "@elasticpath/js-sdk"; -import { - getHierarchies, - getHierarchyChildren, - getHierarchyNodes, -} from "../services/hierarchy"; - -interface ISchema { - name: string; - slug: string; - href: string; - id: string; - children: ISchema[]; -} - -export interface NavigationNode { - name: string; - slug: string; - href: string; - id: string; - children: NavigationNode[]; -} - -export async function buildSiteNavigation( - client: ElasticPath, -): Promise { - // Fetch hierarchies to be used as top level nav - const hierarchies = await getHierarchies(client); - return constructTree(hierarchies, client); -} - -/** - * Construct hierarchy tree, limited to 5 hierarchies at the top level - */ -function constructTree( - hierarchies: Hierarchy[], - client: ElasticPath, -): Promise { - const tree = hierarchies - .slice(0, 4) - .map((hierarchy) => - createNode({ - name: hierarchy.attributes.name, - id: hierarchy.id, - slug: hierarchy.attributes.slug, - }), - ) - .map(async (hierarchy) => { - // Fetch first-level nav ('parent nodes') - the direct children of each hierarchy - const directChildren = await getHierarchyChildren(hierarchy.id, client); - // Fetch all nodes in each hierarchy (i.e. all 'child nodes' belonging to a hierarchy) - const allNodes = await getHierarchyNodes(hierarchy.id, client); - - // Build 2nd level by finding all 'child nodes' belonging to each first level featured-nodes - const directs = directChildren.slice(0, 4).map((child) => { - const children: ISchema[] = allNodes - .filter((node) => node?.relationships?.parent.data.id === child.id) - .map((node) => - createNode({ - name: node.attributes.name, - id: node.id, - slug: node.attributes.slug, - hrefBase: `${hierarchy.href}/${child.attributes.slug}`, - }), - ); - - return createNode({ - name: child.attributes.name, - id: child.id, - slug: child.attributes.slug, - hrefBase: hierarchy.href, - children, - }); - }); - - return { ...hierarchy, children: directs }; - }); - - return Promise.all(tree); -} - -interface CreateNodeDefinition { - name: string; - id: string; - slug?: string; - hrefBase?: string; - children?: ISchema[]; -} - -function createNode({ - name, - id, - slug = "missing-slug", - hrefBase = "", - children = [], -}: CreateNodeDefinition): ISchema { - return { - name, - id, - slug, - href: `${hrefBase}/${slug}`, - children, - }; -} diff --git a/examples/simple/src/lib/epcc-server-side-implicit-client.ts b/examples/simple/src/lib/epcc-server-side-implicit-client.ts index dfba3600..768b9b7d 100644 --- a/examples/simple/src/lib/epcc-server-side-implicit-client.ts +++ b/examples/simple/src/lib/epcc-server-side-implicit-client.ts @@ -6,23 +6,21 @@ import { COOKIE_PREFIX_KEY } from "./resolve-cart-env"; import { EP_CURRENCY_CODE } from "./resolve-ep-currency-code"; import { CREDENTIALS_COOKIE_NAME } from "./cookie-constants"; import { cookies } from "next/headers"; +import { client, createClient } from "@epcc-sdk/sdks-shopper"; +import { applyDefaultNextMiddleware } from "@epcc-sdk/sdks-nextjs"; const customHeaders = resolveEpccCustomRuleHeaders(); const { client_id, host } = epccEnv; +client.setConfig({ + baseUrl: `https://${epccEnv.host}`, +}); + +applyDefaultNextMiddleware(client); + export function getServerSideImplicitClient() { - const credentialsCookie = cookies().get(CREDENTIALS_COOKIE_NAME); - - return gateway({ - name: COOKIE_PREFIX_KEY, - client_id, - host, - currency: EP_CURRENCY_CODE, - ...(customHeaders ? { headers: customHeaders } : {}), - reauth: false, - storage: createServerSideNextCookieStorageFactory(credentialsCookie?.value), - }); + return client; } function createServerSideNextCookieStorageFactory( diff --git a/examples/simple/src/lib/get-store-initial-state.ts b/examples/simple/src/lib/get-store-initial-state.ts index dafe837b..8b775904 100644 --- a/examples/simple/src/lib/get-store-initial-state.ts +++ b/examples/simple/src/lib/get-store-initial-state.ts @@ -1,17 +1,24 @@ -import { ElasticPath } from "@elasticpath/js-sdk"; import { InitialState } from "@elasticpath/react-shopper-hooks"; -import { buildSiteNavigation } from "./build-site-navigation"; +import { buildSiteNavigation } from "@elasticpath/react-shopper-hooks"; import { getCartCookieServer } from "./cart-cookie-server"; import { getCart } from "../services/cart"; +import type { Client } from "@epcc-sdk/sdks-shopper"; +import { Cart, CartIncluded, ResourceIncluded } from "@elasticpath/js-sdk"; export async function getStoreInitialState( - client: ElasticPath, + client: Client, ): Promise { const nav = await buildSiteNavigation(client); const cartCookie = getCartCookieServer(); - const cart = await getCart(cartCookie, client); + const cartResponse = await getCart(cartCookie, client); + + if (!cartResponse.response.ok) { + throw new Error("Failed to get cart"); + } + + const cart = cartResponse.data as ResourceIncluded; return { cart, diff --git a/examples/simple/src/services/cart.ts b/examples/simple/src/services/cart.ts index e92757e4..0e23c0bb 100644 --- a/examples/simple/src/services/cart.ts +++ b/examples/simple/src/services/cart.ts @@ -1,9 +1,19 @@ -import type {ElasticPath } from "@elasticpath/js-sdk"; -import { Cart, CartIncluded, ResourceIncluded } from "@elasticpath/js-sdk"; +import { + client as elasticPathClient, + getCart as getCartClient, +} from "@epcc-sdk/sdks-shopper"; export async function getCart( cartId: string, - client: ElasticPath, -): Promise> { - return client.Cart(cartId).With("items").Get(); + client?: typeof elasticPathClient, +) { + return await getCartClient({ + client, + path: { + cartID: cartId, + }, + query: { + include: "items", + }, + }); } diff --git a/examples/simple/src/services/hierarchy.ts b/examples/simple/src/services/hierarchy.ts index 6fafee61..22063afc 100644 --- a/examples/simple/src/services/hierarchy.ts +++ b/examples/simple/src/services/hierarchy.ts @@ -1,28 +1,48 @@ -import type { Node, Hierarchy } from "@elasticpath/js-sdk"; -import {ElasticPath } from "@elasticpath/js-sdk"; +import type { client as elasticPathClient } from "@epcc-sdk/sdks-shopper"; +import { + getByContextAllHierarchies, + getByContextHierarchyChildNodes, + getByContextHierarchyNodes, +} from "@epcc-sdk/sdks-shopper"; -export async function getHierarchies(client: ElasticPath): Promise { - const result = await client.ShopperCatalog.Hierarchies.All(); - return result.data; +export async function getHierarchies(client?: typeof elasticPathClient) { + return await getByContextAllHierarchies({ + client, + query: { + "page[limit]": 100, + "page[offset]": 0, + }, + }); } export async function getHierarchyChildren( hierarchyId: string, - client: ElasticPath, -): Promise { - const result = await client.ShopperCatalog.Hierarchies.GetHierarchyChildren({ - hierarchyId, + client?: typeof elasticPathClient, +) { + return await getByContextHierarchyChildNodes({ + client, + path: { + hierarchy_id: hierarchyId, + }, + query: { + "page[limit]": 100, + "page[offset]": 0, + }, }); - return result.data; } export async function getHierarchyNodes( hierarchyId: string, - client: ElasticPath, -): Promise { - const result = await client.ShopperCatalog.Hierarchies.GetHierarchyNodes({ - hierarchyId, + client?: typeof elasticPathClient, +) { + return await getByContextHierarchyNodes({ + client, + path: { + hierarchy_id: hierarchyId, + }, + query: { + "page[limit]": 100, + "page[offset]": 0, + }, }); - - return result.data; }