diff --git a/lib/bigcommerce/api/cart/handlers/add-item.ts b/lib/bigcommerce/api/cart/handlers/add-item.ts new file mode 100644 index 000000000..f5475fb08 --- /dev/null +++ b/lib/bigcommerce/api/cart/handlers/add-item.ts @@ -0,0 +1,37 @@ +import parseItem from '../../utils/parse-item' +import getCartCookie from '../../utils/get-cart-cookie' +import type { CartHandlers } from '..' + +// Return current cart info +const addItem: CartHandlers['addItem'] = async ({ + res, + body: { cartId, item }, + config, +}) => { + if (!item) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Missing item' }], + }) + } + if (!item.quantity) item.quantity = 1 + + const options = { + method: 'POST', + body: JSON.stringify({ + line_items: [parseItem(item)], + }), + } + 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(config.cartCookie, data.id, config.cartCookieMaxAge) + ) + res.status(200).json({ data }) +} + +export default addItem diff --git a/lib/bigcommerce/api/cart/handlers/remove-item.ts b/lib/bigcommerce/api/cart/handlers/remove-item.ts new file mode 100644 index 000000000..2ee5dbe16 --- /dev/null +++ b/lib/bigcommerce/api/cart/handlers/remove-item.ts @@ -0,0 +1,34 @@ +import getCartCookie from '../../utils/get-cart-cookie' +import type { CartHandlers } from '..' + +// Return current cart info +const removeItem: CartHandlers['removeItem'] = async ({ + res, + body: { cartId, itemId }, + config, +}) => { + if (!cartId || !itemId) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Invalid request' }], + }) + } + + const result = await config.storeApiFetch<{ data: any } | null>( + `/v3/carts/${cartId}/items/${itemId}`, + { method: 'DELETE' } + ) + const data = result?.data ?? null + + res.setHeader( + 'Set-Cookie', + data + ? // Update the cart cookie + getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) + : // Remove the cart cookie if the cart was removed (empty items) + getCartCookie(config.cartCookie) + ) + res.status(200).json({ data }) +} + +export default removeItem diff --git a/lib/bigcommerce/api/cart/handlers/update-item.ts b/lib/bigcommerce/api/cart/handlers/update-item.ts new file mode 100644 index 000000000..0a9fcaca7 --- /dev/null +++ b/lib/bigcommerce/api/cart/handlers/update-item.ts @@ -0,0 +1,36 @@ +import parseItem from '../../utils/parse-item' +import getCartCookie from '../../utils/get-cart-cookie' +import type { CartHandlers } from '..' + +// Return current cart info +const updateItem: CartHandlers['updateItem'] = async ({ + res, + body: { cartId, itemId, item }, + config, +}) => { + if (!cartId || !itemId || !item) { + return res.status(400).json({ + data: null, + errors: [{ message: 'Invalid request' }], + }) + } + + const { data } = await config.storeApiFetch( + `/v3/carts/${cartId}/items/${itemId}`, + { + method: 'PUT', + body: JSON.stringify({ + line_item: parseItem(item), + }), + } + ) + + // Update the cart cookie + res.setHeader( + 'Set-Cookie', + getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) + ) + res.status(200).json({ data }) +} + +export default updateItem diff --git a/lib/bigcommerce/api/cart/index.ts b/lib/bigcommerce/api/cart/index.ts index 23e92dbd6..8c307dda8 100644 --- a/lib/bigcommerce/api/cart/index.ts +++ b/lib/bigcommerce/api/cart/index.ts @@ -1,4 +1,3 @@ -import { serialize, CookieSerializeOptions } from 'cookie' import isAllowedMethod from '../utils/is-allowed-method' import createApiHandler, { BigcommerceApiHandler, @@ -6,6 +5,9 @@ import createApiHandler, { } from '../utils/create-api-handler' import { BigcommerceApiError } from '../utils/errors' import getCart from './handlers/get-cart' +import addItem from './handlers/add-item' +import updateItem from './handlers/update-item' +import removeItem from './handlers/remove-item' type Body = Partial | undefined @@ -44,6 +46,15 @@ export type Cart = { export type CartHandlers = { getCart: BigcommerceHandler + addItem: BigcommerceHandler> + updateItem: BigcommerceHandler< + Cart, + { cartId?: string } & Body + > + removeItem: BigcommerceHandler< + Cart, + { cartId?: string } & Body + > } const METHODS = ['GET', 'POST', 'PUT', 'DELETE'] @@ -63,97 +74,26 @@ const cartApi: BigcommerceApiHandler = async ( try { // Return current cart info if (req.method === 'GET') { - return await handlers['getCart']({ req, res, config, body: { cartId } }) + const body = { cartId } + return await handlers['getCart']({ req, res, config, body }) } // Create or add an item to the cart if (req.method === 'POST') { - const { item } = (req.body as Body) ?? {} - - if (!item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Missing item' }], - }) - } - if (!item.quantity) item.quantity = 1 - - const options = { - method: 'POST', - body: JSON.stringify({ - line_items: [parseItem(item)], - }), - } - 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(config.cartCookie, data.id, config.cartCookieMaxAge) - ) - - return res.status(200).json({ data }) + const body = { cartId, ...req.body } + return await handlers['addItem']({ req, res, config, body }) } // Update item in cart if (req.method === 'PUT') { - const { itemId, item } = (req.body as Body) ?? {} - - if (!cartId || !itemId || !item) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - - const { data } = await config.storeApiFetch( - `/v3/carts/${cartId}/items/${itemId}`, - { - method: 'PUT', - body: JSON.stringify({ - line_item: parseItem(item), - }), - } - ) - - // Update the cart cookie - res.setHeader( - 'Set-Cookie', - getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) - ) - - return res.status(200).json({ data }) + const body = { cartId, ...req.body } + return await handlers['updateItem']({ req, res, config, body }) } // Remove an item from the cart if (req.method === 'DELETE') { - const { itemId } = (req.body as Body) ?? {} - - if (!cartId || !itemId) { - return res.status(400).json({ - data: null, - errors: [{ message: 'Invalid request' }], - }) - } - - const result = await config.storeApiFetch<{ data: any } | null>( - `/v3/carts/${cartId}/items/${itemId}`, - { method: 'DELETE' } - ) - const data = result?.data ?? null - - res.setHeader( - 'Set-Cookie', - data - ? // Update the cart cookie - getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge) - : // Remove the cart cookie if the cart was removed (empty items) - getCartCookie(config.cartCookie) - ) - - return res.status(200).json({ data }) + const body = { cartId, ...req.body } + return await handlers['removeItem']({ req, res, config, body }) } } catch (error) { console.error(error) @@ -167,31 +107,6 @@ const cartApi: BigcommerceApiHandler = async ( } } -function getCartCookie(name: string, cartId?: string, maxAge?: number) { - const options: CookieSerializeOptions = - cartId && maxAge - ? { - maxAge, - expires: new Date(Date.now() + maxAge * 1000), - secure: process.env.NODE_ENV === 'production', - path: '/', - sameSite: 'lax', - } - : { maxAge: -1, path: '/' } // Removes the cookie +export const handlers = { getCart, addItem, updateItem, removeItem } - return serialize(name, cartId || '', options) -} - -const parseItem = (item: ItemBody) => ({ - quantity: item.quantity, - product_id: item.productId, - variant_id: item.variantId, -}) - -const handlers = { - getCart, -} - -const h = createApiHandler(cartApi, handlers) - -export default h +export default createApiHandler(cartApi, handlers) diff --git a/lib/bigcommerce/api/utils/parse-item.ts b/lib/bigcommerce/api/utils/parse-item.ts new file mode 100644 index 000000000..02c27bea8 --- /dev/null +++ b/lib/bigcommerce/api/utils/parse-item.ts @@ -0,0 +1,9 @@ +import type { ItemBody } from '../cart' + +const parseItem = (item: ItemBody) => ({ + quantity: item.quantity, + product_id: item.productId, + variant_id: item.variantId, +}) + +export default parseItem