4
0
forked from crowetic/commerce

Merge branch 'master' of github.com:okbel/e-comm-example

This commit is contained in:
Belen Curcio 2020-10-04 16:39:33 -03:00
commit 62f29fe766
7 changed files with 87 additions and 45 deletions

View File

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

View File

@ -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,

View File

@ -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

View File

@ -7,9 +7,14 @@ import {
export type Cart = any
export const CartProvider: FC = ({ children }) => {
return <CommerceCartProvider query="">{children}</CommerceCartProvider>
return <CommerceCartProvider url="/api/cart">{children}</CommerceCartProvider>
}
export function useCart() {
return useCommerceCart<Cart>()
const cart = useCommerceCart<Cart>()
// TODO: Do something to make this prop work
cart.isEmpty = true
return cart
}

View File

@ -2,6 +2,7 @@ export interface CommerceAPIConfig {
commerceUrl: string
apiToken: string
cartCookie: string
cartCookieMaxAge: number
fetch<Q, V = any>(
query: string,
queryData?: CommerceAPIFetchOptions<V>

View File

@ -21,11 +21,9 @@ const CartProvider: FC<CartProviderProps> = ({ 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 (
<CartContext.Provider value={{ ...response, isEmpty }}>
<CartContext.Provider value={{ ...response, isEmpty: true }}>
{children}
</CartContext.Provider>
)

View File

@ -0,0 +1,3 @@
import cartApi from '@lib/bigcommerce/api/cart'
export default cartApi()