Add checkout endpoint parser & update errors

This commit is contained in:
Catalin Pinte 2022-10-05 14:07:56 +03:00
parent 4bd20ddf0f
commit fa9fe72a68
9 changed files with 34 additions and 64 deletions

View File

@ -13,10 +13,12 @@ const getCheckout: CheckoutEndpoint['handlers']['getCheckout'] = async ({
const { cookies } = req const { cookies } = req
const cartId = cookies[config.cartCookie] const cartId = cookies[config.cartCookie]
const customerToken = cookies[config.customerCookie] const customerToken = cookies[config.customerCookie]
if (!cartId) { if (!cartId) {
res.redirect('/cart') res.redirect('/cart')
return return
} }
const { data } = await config.storeApiFetch<any>( const { data } = await config.storeApiFetch<any>(
`/v3/carts/${cartId}/redirect_urls`, `/v3/carts/${cartId}/redirect_urls`,
{ {

View File

@ -1,6 +1,11 @@
import type { GetAPISchema } from '..' import type { GetAPISchema } from '..'
import type { CheckoutSchema } from '../../types/checkout' import type { CheckoutSchema } from '../../types/checkout'
import {
getCheckoutBodySchema,
submitCheckoutBodySchema,
} from '../../schemas/checkout'
import validateHandlers from '../utils/validate-handlers' import validateHandlers from '../utils/validate-handlers'
const checkoutEndpoint: GetAPISchema< const checkoutEndpoint: GetAPISchema<
@ -19,13 +24,13 @@ const checkoutEndpoint: GetAPISchema<
// Get checkout // Get checkout
if (req.method === 'GET') { if (req.method === 'GET') {
const body = { ...req.body, cartId } const body = getCheckoutBodySchema.parse({ ...req.body, cartId })
return handlers['getCheckout']({ ...ctx, body }) return handlers['getCheckout']({ ...ctx, body })
} }
// Create checkout // Create checkout
if (req.method === 'POST' && handlers['submitCheckout']) { if (req.method === 'POST' && handlers['submitCheckout']) {
const body = { ...req.body, cartId } const body = submitCheckoutBodySchema.parse({ ...req.body, cartId })
return handlers['submitCheckout']({ ...ctx, body }) return handlers['submitCheckout']({ ...ctx, body })
} }
} }

View File

@ -51,7 +51,7 @@ export default function createEndpoints<P extends APIProvider>(
const data = await handlers[path](req, res) const data = await handlers[path](req, res)
// If the handler returns a value but the response hasn't been sent yet, send it // If the handler returns a value but the response hasn't been sent yet, send it
if (!res.headersSent) { if (!res.headersSent && data) {
res.status(200).json({ res.status(200).json({
data, data,
}) })

View File

@ -25,26 +25,19 @@ export class CommerceNetworkError extends Error {
} }
export const normalizeZodIssues = (issues: ZodError['issues']) => export const normalizeZodIssues = (issues: ZodError['issues']) =>
issues.map((e, index) => ({ issues.map(({ path, message }) => `${message} at "${path.join('.')}"`)
message: `Error #${index + 1} ${
e.path.length > 0 ? `Path: ${e.path.join('.')}, ` : ''
}Code: ${e.code}, Message: ${e.message}`,
}))
export const getOperationError = (operation: string, error: unknown) => { export const getOperationError = (operation: string, error: unknown) => {
if (error instanceof ZodError) { if (error instanceof ZodError) {
return new CommerceError({ return new CommerceError({
code: 'SCHEMA_VALIDATION_ERROR', code: 'SCHEMA_VALIDATION_ERROR',
message: message:
`The ${operation} operation returned invalid data and has ${ `Validation ${
error.issues.length error.issues.length === 1 ? 'error' : 'errors'
} parse ${error.issues.length === 1 ? 'error' : 'errors'}: \n` + } at "${operation}" operation: \n` +
normalizeZodIssues(error.issues) normalizeZodIssues(error.issues).join('\n'),
.map((e) => e.message)
.join('\n'),
}) })
} }
return error return error
} }
@ -63,7 +56,7 @@ export const normalizeError = (error: unknown) => {
return { return {
status: 400, status: 400,
data: null, data: null,
errors: normalizeZodIssues(error.issues), errors: normalizeZodIssues(error.issues).map((message) => ({ message })),
} }
} }

View File

@ -0,0 +1,15 @@
import { z } from 'zod'
import { addressFieldsSchema, cardFieldsSchema } from './customer'
export const getCheckoutBodySchema = z.object({
cartId: z.string().optional(),
})
export const submitCheckoutBodySchema = z.object({
cartId: z.string(),
item: z.object({
cartId: z.string().optional(),
card: cardFieldsSchema,
address: addressFieldsSchema,
}),
})

View File

@ -69,7 +69,7 @@ export type CheckoutHooks = {
} }
export type GetCheckoutHandler = GetCheckoutHook & { export type GetCheckoutHandler = GetCheckoutHook & {
body: { cartId: string } body: { cartId?: string }
} }
export type SubmitCheckoutHandler = SubmitCheckoutHook & { export type SubmitCheckoutHandler = SubmitCheckoutHook & {

View File

@ -1,26 +0,0 @@
import { ProductsEndpoint } from '.'
const SORT: { [key: string]: string | undefined } = {
latest: 'id',
trending: 'total_sold',
price: 'price',
}
const LIMIT = 12
// Return current cart info
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
res,
body: { search, categoryId, brandId, sort },
config,
commerce,
}) => {
res.status(200).json({
data: {
products: [],
found: false,
},
})
}
export default getProducts

View File

@ -1,19 +0,0 @@
import { GetAPISchema, createEndpoint } from '@vercel/commerce/api'
import productsEndpoint from '@vercel/commerce/api/endpoints/catalog/products'
import type { ProductsSchema } from '@vercel/commerce/types/product'
import type { ShopifyAPI } from '../../..'
import getProducts from './get-products'
export type ProductsAPI = GetAPISchema<ShopifyAPI, ProductsSchema>
export type ProductsEndpoint = ProductsAPI['endpoint']
export const handlers: ProductsEndpoint['handlers'] = { getProducts }
const productsApi = createEndpoint<ProductsAPI>({
handler: productsEndpoint,
handlers,
})
export default productsApi

View File

@ -23,8 +23,8 @@
"@components/*": ["components/*"], "@components/*": ["components/*"],
"@commerce": ["../packages/commerce/src"], "@commerce": ["../packages/commerce/src"],
"@commerce/*": ["../packages/commerce/src/*"], "@commerce/*": ["../packages/commerce/src/*"],
"@framework": ["../packages/local/src"], "@framework": ["../packages/shopify/src"],
"@framework/*": ["../packages/local/src/*"] "@framework/*": ["../packages/shopify/src/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"], "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],