From ec1377850eb084fbc8ab70e98389b6215c692870 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Sat, 3 Oct 2020 19:59:15 -0500 Subject: [PATCH 1/3] Updated cart hook --- lib/bigcommerce/cart.tsx | 9 +++++++-- lib/commerce/cart.tsx | 4 +--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/bigcommerce/cart.tsx b/lib/bigcommerce/cart.tsx index d57736e91..1b5f6b47e 100644 --- a/lib/bigcommerce/cart.tsx +++ b/lib/bigcommerce/cart.tsx @@ -7,9 +7,14 @@ import { export type Cart = any export const CartProvider: FC = ({ children }) => { - return {children} + return {children} } export function useCart() { - return useCommerceCart() + const cart = useCommerceCart() + + // TODO: Do something to make this prop work + cart.isEmpty = true + + return cart } diff --git a/lib/commerce/cart.tsx b/lib/commerce/cart.tsx index 8c1335780..c11ecd017 100644 --- a/lib/commerce/cart.tsx +++ b/lib/commerce/cart.tsx @@ -21,11 +21,9 @@ const CartProvider: FC = ({ children, query, url }) => { const { fetcher } = useCommerce() const cartId = getCartCookie() const response = useSWR(() => (cartId ? [url, query] : null), fetcher) - // TODO: Do something to make this prop work - const isEmpty = true return ( - + {children} ) From ca959f6491ac63d62f9c725d4690972eb021d926 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Sat, 3 Oct 2020 20:49:09 -0500 Subject: [PATCH 2/3] Added add to cart method --- lib/bigcommerce/api/cart.ts | 100 ++++++++++++++++++++-------- lib/bigcommerce/api/index.ts | 2 + lib/bigcommerce/api/utils/errors.ts | 13 +--- lib/commerce/api/index.ts | 1 + 4 files changed, 76 insertions(+), 40 deletions(-) diff --git a/lib/bigcommerce/api/cart.ts b/lib/bigcommerce/api/cart.ts index ebf5c50b1..e21d3c436 100644 --- a/lib/bigcommerce/api/cart.ts +++ b/lib/bigcommerce/api/cart.ts @@ -15,43 +15,87 @@ const cartApi: BigcommerceApiHandler = async (req, res, config) => { const { cookies } = req const cartId = cookies[config.cartCookie] - // Return current cart info - if (req.method === 'GET') { - let result: { data?: Cart } = {} + try { + // Return current cart info + if (req.method === 'GET') { + let result: { data?: Cart } = {} - try { - result = await config.storeApiFetch( - `/v3/carts/${cartId}?include=redirect_urls` - ) - } catch (error) { - if (error instanceof BigcommerceApiError && error.status === 404) { - // Remove the cookie if it exists but the cart wasn't found - res.setHeader('Set-Cookie', getCartCookie(name)) - } else { - throw error + try { + result = await config.storeApiFetch( + `/v3/carts/${cartId}?include=redirect_urls` + ) + } catch (error) { + if (error instanceof BigcommerceApiError && error.status === 404) { + // Remove the cookie if it exists but the cart wasn't found + res.setHeader('Set-Cookie', getCartCookie(name)) + } else { + throw error + } } + + return res.status(200).json({ cart: result.data ?? null }) } - return res.status(200).json({ cart: result.data ?? null }) + // Create or add a product to the cart + if (req.method === 'POST') { + const { product } = req.body + + if (!product) { + return res.status(400).json({ + errors: [{ message: 'Missing product' }], + }) + } + + const options = { + method: 'POST', + body: JSON.stringify({ + line_items: [parseProduct(product)], + }), + } + const { data } = cartId + ? await config.storeApiFetch(`/v3/carts/${cartId}/items`, options) + : await config.storeApiFetch('/v3/carts', options) + + // Create or update the cart cookie + res.setHeader( + 'Set-Cookie', + getCartCookie(name, data.id, config.cartCookieMaxAge) + ) + + // There's no need to send any additional data here, the UI can use this response to display a + // "success" for the operation and revalidate the GET request for this same endpoint right after. + return res.status(200).json({ done: true }) + } + } catch (error) { + const message = + error instanceof BigcommerceApiError + ? 'An unexpected error ocurred with the Bigcommerce API' + : 'An unexpected error ocurred' + + res.status(500).json({ errors: [{ message }] }) } } -const ONE_DAY = 60 * 60 * 24 -const MAX_AGE = ONE_DAY * 30 - -function getCartCookie(name: string, cartId?: string) { - const options: CookieSerializeOptions = cartId - ? { - maxAge: MAX_AGE, - expires: new Date(Date.now() + MAX_AGE * 1000), - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - path: '/', - sameSite: 'lax', - } - : { maxAge: -1, path: '/' } // Removes the cookie +function getCartCookie(name: string, cartId?: string, maxAge?: number) { + const options: CookieSerializeOptions = + cartId && maxAge + ? { + maxAge, + expires: new Date(Date.now() + maxAge * 1000), + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + path: '/', + sameSite: 'lax', + } + : { maxAge: -1, path: '/' } // Removes the cookie return serialize(name, cartId || '', options) } +const parseProduct = (product: any) => ({ + quantity: product.quantity, + product_id: product.productId, + variant_id: product.variantId, +}) + export default createApiHandler(cartApi) diff --git a/lib/bigcommerce/api/index.ts b/lib/bigcommerce/api/index.ts index 752ef038d..69e26f523 100644 --- a/lib/bigcommerce/api/index.ts +++ b/lib/bigcommerce/api/index.ts @@ -103,10 +103,12 @@ export class Config { } } +const ONE_DAY = 60 * 60 * 24 const config = new Config({ commerceUrl: API_URL, apiToken: API_TOKEN, cartCookie: process.env.BIGCOMMERCE_CART_COOKIE ?? 'bc_cartId', + cartCookieMaxAge: ONE_DAY * 30, fetch: fetchGraphqlApi, // REST API only storeApiUrl: STORE_API_URL, diff --git a/lib/bigcommerce/api/utils/errors.ts b/lib/bigcommerce/api/utils/errors.ts index 70f97953e..1cdd72214 100644 --- a/lib/bigcommerce/api/utils/errors.ts +++ b/lib/bigcommerce/api/utils/errors.ts @@ -1,16 +1,5 @@ // Used for GraphQL errors -export class BigcommerceError extends Error { - status?: number - - constructor(msg: string, res?: Response) { - super(msg) - this.name = 'BigcommerceError' - - if (res) { - this.status = res.status - } - } -} +export class BigcommerceGraphQLError extends Error {} export class BigcommerceApiError extends Error { status: number diff --git a/lib/commerce/api/index.ts b/lib/commerce/api/index.ts index dcf95498f..6c4bf0544 100644 --- a/lib/commerce/api/index.ts +++ b/lib/commerce/api/index.ts @@ -2,6 +2,7 @@ export interface CommerceAPIConfig { commerceUrl: string apiToken: string cartCookie: string + cartCookieMaxAge: number fetch( query: string, queryData?: CommerceAPIFetchOptions From b8d50ea528eb04d5ef13ed1e7cf2cf2d9de17ee5 Mon Sep 17 00:00:00 2001 From: Luis Alvarez Date: Sat, 3 Oct 2020 20:51:24 -0500 Subject: [PATCH 3/3] Added cart API endpoint --- pages/api/bigcommerce/cart.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pages/api/bigcommerce/cart.ts diff --git a/pages/api/bigcommerce/cart.ts b/pages/api/bigcommerce/cart.ts new file mode 100644 index 000000000..a294b05c4 --- /dev/null +++ b/pages/api/bigcommerce/cart.ts @@ -0,0 +1,3 @@ +import cartApi from '@lib/bigcommerce/api/cart' + +export default cartApi()