Allow API operations to be more customizable
This commit is contained in:
		
							
								
								
									
										29
									
								
								lib/bigcommerce/api/cart/handlers/get-cart.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								lib/bigcommerce/api/cart/handlers/get-cart.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import { BigcommerceApiError } from '../../utils/errors'
 | 
				
			||||||
 | 
					import getCartCookie from '../../utils/get-cart-cookie'
 | 
				
			||||||
 | 
					import type { Cart, CartHandlers } from '..'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Return current cart info
 | 
				
			||||||
 | 
					const getCart: CartHandlers['getCart'] = async ({
 | 
				
			||||||
 | 
					  res,
 | 
				
			||||||
 | 
					  body: { cartId },
 | 
				
			||||||
 | 
					  config,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
					  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(config.cartCookie))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      throw error
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  res.status(200).json({ data: result.data ?? null })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default getCart
 | 
				
			||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
import { serialize, CookieSerializeOptions } from 'cookie'
 | 
					import { serialize, CookieSerializeOptions } from 'cookie'
 | 
				
			||||||
import isAllowedMethod from './utils/is-allowed-method'
 | 
					import isAllowedMethod from '../utils/is-allowed-method'
 | 
				
			||||||
import createApiHandler, {
 | 
					import createApiHandler, {
 | 
				
			||||||
  BigcommerceApiHandler,
 | 
					  BigcommerceApiHandler,
 | 
				
			||||||
} from './utils/create-api-handler'
 | 
					  BigcommerceHandler,
 | 
				
			||||||
import { BigcommerceApiError } from './utils/errors'
 | 
					} from '../utils/create-api-handler'
 | 
				
			||||||
 | 
					import { BigcommerceApiError } from '../utils/errors'
 | 
				
			||||||
 | 
					import getCart from './handlers/get-cart'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Body<T> = Partial<T> | undefined
 | 
					type Body<T> = Partial<T> | undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,10 +42,19 @@ export type Cart = {
 | 
				
			|||||||
  // TODO: add missing fields
 | 
					  // TODO: add missing fields
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CartHandlers = {
 | 
				
			||||||
 | 
					  getCart: BigcommerceHandler<Cart, { cartId?: string }>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const METHODS = ['GET', 'POST', 'PUT', 'DELETE']
 | 
					const METHODS = ['GET', 'POST', 'PUT', 'DELETE']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: a complete implementation should have schema validation for `req.body`
 | 
					// TODO: a complete implementation should have schema validation for `req.body`
 | 
				
			||||||
const cartApi: BigcommerceApiHandler<Cart> = async (req, res, config) => {
 | 
					const cartApi: BigcommerceApiHandler<Cart, CartHandlers> = async (
 | 
				
			||||||
 | 
					  req,
 | 
				
			||||||
 | 
					  res,
 | 
				
			||||||
 | 
					  config,
 | 
				
			||||||
 | 
					  handlers
 | 
				
			||||||
 | 
					) => {
 | 
				
			||||||
  if (!isAllowedMethod(req, res, METHODS)) return
 | 
					  if (!isAllowedMethod(req, res, METHODS)) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { cookies } = req
 | 
					  const { cookies } = req
 | 
				
			||||||
@@ -52,22 +63,7 @@ const cartApi: BigcommerceApiHandler<Cart> = async (req, res, config) => {
 | 
				
			|||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // Return current cart info
 | 
					    // Return current cart info
 | 
				
			||||||
    if (req.method === 'GET') {
 | 
					    if (req.method === 'GET') {
 | 
				
			||||||
      let result: { data?: Cart } = {}
 | 
					      return await handlers['getCart']({ req, res, config, body: { cartId } })
 | 
				
			||||||
 | 
					 | 
				
			||||||
      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(config.cartCookie))
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          throw error
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return res.status(200).json({ data: result.data ?? null })
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create or add an item to the cart
 | 
					    // Create or add an item to the cart
 | 
				
			||||||
@@ -192,4 +188,10 @@ const parseItem = (item: ItemBody) => ({
 | 
				
			|||||||
  variant_id: item.variantId,
 | 
					  variant_id: item.variantId,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default createApiHandler(cartApi)
 | 
					const handlers = {
 | 
				
			||||||
 | 
					  getCart,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const h = createApiHandler(cartApi, handlers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default h
 | 
				
			||||||
							
								
								
									
										48
									
								
								lib/bigcommerce/api/customers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								lib/bigcommerce/api/customers.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					import createApiHandler, {
 | 
				
			||||||
 | 
					  BigcommerceApiHandler,
 | 
				
			||||||
 | 
					  BigcommerceHandler,
 | 
				
			||||||
 | 
					} from './utils/create-api-handler'
 | 
				
			||||||
 | 
					import isAllowedMethod from './utils/is-allowed-method'
 | 
				
			||||||
 | 
					import { BigcommerceApiError } from './utils/errors'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Customer = any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const METHODS = ['POST']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const customersApi: BigcommerceApiHandler<Customer> = async (
 | 
				
			||||||
 | 
					  req,
 | 
				
			||||||
 | 
					  res,
 | 
				
			||||||
 | 
					  config
 | 
				
			||||||
 | 
					) => {
 | 
				
			||||||
 | 
					  if (!isAllowedMethod(req, res, METHODS)) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try {
 | 
				
			||||||
 | 
					    if (req.method === 'POST') {
 | 
				
			||||||
 | 
					      // let result = {} as any
 | 
				
			||||||
 | 
					      // const
 | 
				
			||||||
 | 
					      // result = await config.storeApiFetch('/v3/customers')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch (error) {
 | 
				
			||||||
 | 
					    console.error(error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const message =
 | 
				
			||||||
 | 
					      error instanceof BigcommerceApiError
 | 
				
			||||||
 | 
					        ? 'An unexpected error ocurred with the Bigcommerce API'
 | 
				
			||||||
 | 
					        : 'An unexpected error ocurred'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.status(500).json({ data: null, errors: [{ message }] })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const createCustomer: BigcommerceHandler<Customer> = ({
 | 
				
			||||||
 | 
					  req,
 | 
				
			||||||
 | 
					  res,
 | 
				
			||||||
 | 
					  body,
 | 
				
			||||||
 | 
					  config,
 | 
				
			||||||
 | 
					}) => {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const handlers = {
 | 
				
			||||||
 | 
					  createCustomer,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default createApiHandler(customersApi, handlers)
 | 
				
			||||||
@@ -1,23 +1,47 @@
 | 
				
			|||||||
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
 | 
					import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
 | 
				
			||||||
import { BigcommerceConfig, getConfig } from '..'
 | 
					import { BigcommerceConfig, getConfig } from '..'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type BigcommerceApiHandler<T = any> = (
 | 
					export type BigcommerceApiHandler<
 | 
				
			||||||
 | 
					  T = any,
 | 
				
			||||||
 | 
					  H extends BigcommerceHandlers = {}
 | 
				
			||||||
 | 
					> = (
 | 
				
			||||||
  req: NextApiRequest,
 | 
					  req: NextApiRequest,
 | 
				
			||||||
  res: NextApiResponse<BigcommerceApiResponse<T>>,
 | 
					  res: NextApiResponse<BigcommerceApiResponse<T>>,
 | 
				
			||||||
  config: BigcommerceConfig
 | 
					  config: BigcommerceConfig,
 | 
				
			||||||
 | 
					  handlers: H
 | 
				
			||||||
) => void | Promise<void>
 | 
					) => void | Promise<void>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type BigcommerceHandler<T = any, Body = any> = (options: {
 | 
				
			||||||
 | 
					  req: NextApiRequest
 | 
				
			||||||
 | 
					  res: NextApiResponse<BigcommerceApiResponse<T>>
 | 
				
			||||||
 | 
					  config: BigcommerceConfig
 | 
				
			||||||
 | 
					  body: Body
 | 
				
			||||||
 | 
					}) => void | Promise<void>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type BigcommerceHandlers<T = any> = {
 | 
				
			||||||
 | 
					  [k: string]: BigcommerceHandler<T, any>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type BigcommerceApiResponse<T> = {
 | 
					export type BigcommerceApiResponse<T> = {
 | 
				
			||||||
  data: T | null
 | 
					  data: T | null
 | 
				
			||||||
  errors?: { message: string }[]
 | 
					  errors?: { message: string }[]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function createApiHandler(handler: BigcommerceApiHandler) {
 | 
					export default function createApiHandler<H extends BigcommerceHandlers>(
 | 
				
			||||||
 | 
					  handler: BigcommerceApiHandler<any, H>,
 | 
				
			||||||
 | 
					  handlers: H
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
  return function getApiHandler({
 | 
					  return function getApiHandler({
 | 
				
			||||||
    config,
 | 
					    config,
 | 
				
			||||||
  }: { config?: BigcommerceConfig } = {}): NextApiHandler {
 | 
					    operations,
 | 
				
			||||||
 | 
					  }: {
 | 
				
			||||||
 | 
					    config?: BigcommerceConfig
 | 
				
			||||||
 | 
					    operations?: Partial<H>
 | 
				
			||||||
 | 
					  } = {}): NextApiHandler {
 | 
				
			||||||
 | 
					    const ops = { ...operations, ...handlers }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return function apiHandler(req, res) {
 | 
					    return function apiHandler(req, res) {
 | 
				
			||||||
      return handler(req, res, getConfig(config))
 | 
					      return handler(req, res, getConfig(config), ops)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								lib/bigcommerce/api/utils/get-cart-cookie.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								lib/bigcommerce/api/utils/get-cart-cookie.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					import { serialize, CookieSerializeOptions } from 'cookie'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return serialize(name, cartId || '', options)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user