4
0
forked from crowetic/commerce

Allow API operations to be more customizable

This commit is contained in:
Luis Alvarez 2020-10-11 00:19:11 -05:00
parent 4c43278e67
commit 1c3714bf51
5 changed files with 149 additions and 26 deletions

View 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

View File

@ -1,9 +1,11 @@
import { serialize, CookieSerializeOptions } from 'cookie'
import isAllowedMethod from './utils/is-allowed-method'
import isAllowedMethod from '../utils/is-allowed-method'
import createApiHandler, {
BigcommerceApiHandler,
} from './utils/create-api-handler'
import { BigcommerceApiError } from './utils/errors'
BigcommerceHandler,
} from '../utils/create-api-handler'
import { BigcommerceApiError } from '../utils/errors'
import getCart from './handlers/get-cart'
type Body<T> = Partial<T> | undefined
@ -40,10 +42,19 @@ export type Cart = {
// TODO: add missing fields
}
export type CartHandlers = {
getCart: BigcommerceHandler<Cart, { cartId?: string }>
}
const METHODS = ['GET', 'POST', 'PUT', 'DELETE']
// 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
const { cookies } = req
@ -52,22 +63,7 @@ const cartApi: BigcommerceApiHandler<Cart> = async (req, res, config) => {
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(config.cartCookie))
} else {
throw error
}
}
return res.status(200).json({ data: result.data ?? null })
return await handlers['getCart']({ req, res, config, body: { cartId } })
}
// Create or add an item to the cart
@ -192,4 +188,10 @@ const parseItem = (item: ItemBody) => ({
variant_id: item.variantId,
})
export default createApiHandler(cartApi)
const handlers = {
getCart,
}
const h = createApiHandler(cartApi, handlers)
export default h

View 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)

View File

@ -1,23 +1,47 @@
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'
import { BigcommerceConfig, getConfig } from '..'
export type BigcommerceApiHandler<T = any> = (
export type BigcommerceApiHandler<
T = any,
H extends BigcommerceHandlers = {}
> = (
req: NextApiRequest,
res: NextApiResponse<BigcommerceApiResponse<T>>,
config: BigcommerceConfig
config: BigcommerceConfig,
handlers: H
) => 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> = {
data: T | null
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({
config,
}: { config?: BigcommerceConfig } = {}): NextApiHandler {
operations,
}: {
config?: BigcommerceConfig
operations?: Partial<H>
} = {}): NextApiHandler {
const ops = { ...operations, ...handlers }
return function apiHandler(req, res) {
return handler(req, res, getConfig(config))
return handler(req, res, getConfig(config), ops)
}
}
}

View 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)
}