mirror of
https://github.com/vercel/commerce.git
synced 2025-06-30 18:31:21 +00:00
moved products catalog endpoint
This commit is contained in:
parent
d2abdabf8c
commit
564e9cfa8d
@ -1,48 +0,0 @@
|
|||||||
import type { Product } from '@commerce/types'
|
|
||||||
import isAllowedMethod from '../utils/is-allowed-method'
|
|
||||||
import createApiHandler, {
|
|
||||||
BigcommerceApiHandler,
|
|
||||||
BigcommerceHandler,
|
|
||||||
} from '../utils/create-api-handler'
|
|
||||||
import { BigcommerceApiError } from '../utils/errors'
|
|
||||||
import getProducts from './handlers/get-products'
|
|
||||||
|
|
||||||
export type SearchProductsData = {
|
|
||||||
products: Product[]
|
|
||||||
found: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProductsHandlers = {
|
|
||||||
getProducts: BigcommerceHandler<
|
|
||||||
SearchProductsData,
|
|
||||||
{ search?: string; category?: string; brand?: string; sort?: string }
|
|
||||||
>
|
|
||||||
}
|
|
||||||
|
|
||||||
const METHODS = ['GET']
|
|
||||||
|
|
||||||
// TODO(lf): a complete implementation should have schema validation for `req.body`
|
|
||||||
const productsApi: BigcommerceApiHandler<
|
|
||||||
SearchProductsData,
|
|
||||||
ProductsHandlers
|
|
||||||
> = async (req, res, config, handlers) => {
|
|
||||||
if (!isAllowedMethod(req, res, METHODS)) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const body = req.query
|
|
||||||
return await handlers['getProducts']({ req, res, config, body })
|
|
||||||
} 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 }] })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const handlers = { getProducts }
|
|
||||||
|
|
||||||
export default createApiHandler(productsApi, handlers, {})
|
|
@ -1,6 +1,6 @@
|
|||||||
import { Product } from '@commerce/types'
|
import { Product } from '@commerce/types/product'
|
||||||
import getAllProducts, { ProductEdge } from '../../../product/get-all-products'
|
import { ProductsEndpoint } from '.'
|
||||||
import type { ProductsHandlers } from '../products'
|
import getAllProducts from '../../../../product/get-all-products'
|
||||||
|
|
||||||
const SORT: { [key: string]: string | undefined } = {
|
const SORT: { [key: string]: string | undefined } = {
|
||||||
latest: 'id',
|
latest: 'id',
|
||||||
@ -11,7 +11,7 @@ const SORT: { [key: string]: string | undefined } = {
|
|||||||
const LIMIT = 12
|
const LIMIT = 12
|
||||||
|
|
||||||
// Return current cart info
|
// Return current cart info
|
||||||
const getProducts: ProductsHandlers['getProducts'] = async ({
|
const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({
|
||||||
res,
|
res,
|
||||||
body: { search, category, brand, sort },
|
body: { search, category, brand, sort },
|
||||||
config,
|
config,
|
@ -0,0 +1,10 @@
|
|||||||
|
import type { GetAPISchema } from '@commerce/api'
|
||||||
|
import type { ProductsSchema } from '../../../../types/product'
|
||||||
|
import type { BigcommerceAPI } from '../../..'
|
||||||
|
import getProducts from './get-products'
|
||||||
|
|
||||||
|
export type ProductsAPI = GetAPISchema<BigcommerceAPI, ProductsSchema>
|
||||||
|
|
||||||
|
export type ProductsEndpoint = ProductsAPI['endpoint']
|
||||||
|
|
||||||
|
export const handlers = { getProducts }
|
@ -14,6 +14,7 @@ import type { CustomerAPI } from './endpoints/customer'
|
|||||||
import type { LoginAPI } from './endpoints/login'
|
import type { LoginAPI } from './endpoints/login'
|
||||||
import type { LogoutAPI } from './endpoints/logout'
|
import type { LogoutAPI } from './endpoints/logout'
|
||||||
import type { SignupAPI } from './endpoints/signup'
|
import type { SignupAPI } from './endpoints/signup'
|
||||||
|
import type { ProductsAPI } from './endpoints/catalog/products'
|
||||||
|
|
||||||
import login from './operations/login'
|
import login from './operations/login'
|
||||||
import getAllPages from './operations/get-all-pages'
|
import getAllPages from './operations/get-all-pages'
|
||||||
@ -119,7 +120,13 @@ export const provider = {
|
|||||||
|
|
||||||
export type Provider = typeof provider
|
export type Provider = typeof provider
|
||||||
|
|
||||||
export type APIs = CartAPI | CustomerAPI | LoginAPI | LogoutAPI | SignupAPI
|
export type APIs =
|
||||||
|
| CartAPI
|
||||||
|
| CustomerAPI
|
||||||
|
| LoginAPI
|
||||||
|
| LogoutAPI
|
||||||
|
| SignupAPI
|
||||||
|
| ProductsAPI
|
||||||
|
|
||||||
export type BigcommerceAPI<P extends Provider = Provider> = CommerceAPI<P>
|
export type BigcommerceAPI<P extends Provider = Provider> = CommerceAPI<P>
|
||||||
|
|
||||||
|
1
framework/bigcommerce/types/product.ts
Normal file
1
framework/bigcommerce/types/product.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from '@commerce/types/product'
|
@ -7,14 +7,14 @@ const cartEndpoint: GetAPISchema<
|
|||||||
any,
|
any,
|
||||||
CartSchema
|
CartSchema
|
||||||
>['endpoint']['handler'] = async (ctx) => {
|
>['endpoint']['handler'] = async (ctx) => {
|
||||||
const { req, res, operations, config } = ctx
|
const { req, res, handlers, config } = ctx
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isAllowedOperation(req, res, {
|
!isAllowedOperation(req, res, {
|
||||||
GET: operations['getCart'],
|
GET: handlers['getCart'],
|
||||||
POST: operations['addItem'],
|
POST: handlers['addItem'],
|
||||||
PUT: operations['updateItem'],
|
PUT: handlers['updateItem'],
|
||||||
DELETE: operations['removeItem'],
|
DELETE: handlers['removeItem'],
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
@ -27,25 +27,25 @@ const cartEndpoint: GetAPISchema<
|
|||||||
// Return current cart info
|
// Return current cart info
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const body = { cartId }
|
const body = { cartId }
|
||||||
return await operations['getCart']({ ...ctx, body })
|
return await handlers['getCart']({ ...ctx, body })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create or add an item to the cart
|
// Create or add an item to the cart
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const body = { ...req.body, cartId }
|
const body = { ...req.body, cartId }
|
||||||
return await operations['addItem']({ ...ctx, body })
|
return await handlers['addItem']({ ...ctx, body })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update item in cart
|
// Update item in cart
|
||||||
if (req.method === 'PUT') {
|
if (req.method === 'PUT') {
|
||||||
const body = { ...req.body, cartId }
|
const body = { ...req.body, cartId }
|
||||||
return await operations['updateItem']({ ...ctx, body })
|
return await handlers['updateItem']({ ...ctx, body })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove an item from the cart
|
// Remove an item from the cart
|
||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
const body = { ...req.body, cartId }
|
const body = { ...req.body, cartId }
|
||||||
return await operations['removeItem']({ ...ctx, body })
|
return await handlers['removeItem']({ ...ctx, body })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
31
framework/commerce/api/endpoints/catalog/products.ts
Normal file
31
framework/commerce/api/endpoints/catalog/products.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import type { ProductsSchema } from '../../../types/product'
|
||||||
|
import { CommerceAPIError } from '../../utils/errors'
|
||||||
|
import isAllowedOperation from '../../utils/is-allowed-operation'
|
||||||
|
import type { GetAPISchema } from '../..'
|
||||||
|
|
||||||
|
const productsEndpoint: GetAPISchema<
|
||||||
|
any,
|
||||||
|
ProductsSchema
|
||||||
|
>['endpoint']['handler'] = async (ctx) => {
|
||||||
|
const { req, res, handlers } = ctx
|
||||||
|
|
||||||
|
if (!isAllowedOperation(req, res, { GET: handlers['getProducts'] })) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const body = req.query
|
||||||
|
return await handlers['getProducts']({ ...ctx, body })
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
const message =
|
||||||
|
error instanceof CommerceAPIError
|
||||||
|
? 'An unexpected error ocurred with the Commerce API'
|
||||||
|
: 'An unexpected error ocurred'
|
||||||
|
|
||||||
|
res.status(500).json({ data: null, errors: [{ message }] })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default productsEndpoint
|
@ -7,11 +7,11 @@ const customerEndpoint: GetAPISchema<
|
|||||||
any,
|
any,
|
||||||
CustomerSchema
|
CustomerSchema
|
||||||
>['endpoint']['handler'] = async (ctx) => {
|
>['endpoint']['handler'] = async (ctx) => {
|
||||||
const { req, res, operations } = ctx
|
const { req, res, handlers } = ctx
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isAllowedOperation(req, res, {
|
!isAllowedOperation(req, res, {
|
||||||
GET: operations['getLoggedInCustomer'],
|
GET: handlers['getLoggedInCustomer'],
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
@ -19,7 +19,7 @@ const customerEndpoint: GetAPISchema<
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const body = null
|
const body = null
|
||||||
return await operations['getLoggedInCustomer']({ ...ctx, body })
|
return await handlers['getLoggedInCustomer']({ ...ctx, body })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@ const loginEndpoint: GetAPISchema<
|
|||||||
any,
|
any,
|
||||||
LoginSchema
|
LoginSchema
|
||||||
>['endpoint']['handler'] = async (ctx) => {
|
>['endpoint']['handler'] = async (ctx) => {
|
||||||
const { req, res, operations } = ctx
|
const { req, res, handlers } = ctx
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isAllowedOperation(req, res, {
|
!isAllowedOperation(req, res, {
|
||||||
POST: operations['login'],
|
POST: handlers['login'],
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
@ -19,7 +19,7 @@ const loginEndpoint: GetAPISchema<
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const body = req.body ?? {}
|
const body = req.body ?? {}
|
||||||
return await operations['login']({ ...ctx, body })
|
return await handlers['login']({ ...ctx, body })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@ const logoutEndpoint: GetAPISchema<
|
|||||||
any,
|
any,
|
||||||
LogoutSchema
|
LogoutSchema
|
||||||
>['endpoint']['handler'] = async (ctx) => {
|
>['endpoint']['handler'] = async (ctx) => {
|
||||||
const { req, res, operations } = ctx
|
const { req, res, handlers } = ctx
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isAllowedOperation(req, res, {
|
!isAllowedOperation(req, res, {
|
||||||
GET: operations['logout'],
|
GET: handlers['logout'],
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
@ -21,7 +21,7 @@ const logoutEndpoint: GetAPISchema<
|
|||||||
const redirectTo = req.query.redirect_to
|
const redirectTo = req.query.redirect_to
|
||||||
const body = typeof redirectTo === 'string' ? { redirectTo } : {}
|
const body = typeof redirectTo === 'string' ? { redirectTo } : {}
|
||||||
|
|
||||||
return await operations['logout']({ ...ctx, body })
|
return await handlers['logout']({ ...ctx, body })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@ const signupEndpoint: GetAPISchema<
|
|||||||
any,
|
any,
|
||||||
SignupSchema
|
SignupSchema
|
||||||
>['endpoint']['handler'] = async (ctx) => {
|
>['endpoint']['handler'] = async (ctx) => {
|
||||||
const { req, res, operations, config } = ctx
|
const { req, res, handlers, config } = ctx
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!isAllowedOperation(req, res, {
|
!isAllowedOperation(req, res, {
|
||||||
POST: operations['signup'],
|
POST: handlers['signup'],
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
@ -22,7 +22,7 @@ const signupEndpoint: GetAPISchema<
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const body = { ...req.body, cartId }
|
const body = { ...req.body, cartId }
|
||||||
return await operations['signup']({ ...ctx, body })
|
return await handlers['signup']({ ...ctx, body })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import type { CustomerSchema } from '../types/customer'
|
|||||||
import type { LoginSchema } from '../types/login'
|
import type { LoginSchema } from '../types/login'
|
||||||
import type { LogoutSchema } from '../types/logout'
|
import type { LogoutSchema } from '../types/logout'
|
||||||
import type { SignupSchema } from '../types/signup'
|
import type { SignupSchema } from '../types/signup'
|
||||||
|
import type { ProductsSchema } from '@commerce/types/product'
|
||||||
import {
|
import {
|
||||||
defaultOperations,
|
defaultOperations,
|
||||||
OPERATIONS,
|
OPERATIONS,
|
||||||
@ -19,6 +20,7 @@ export type APISchemas =
|
|||||||
| LoginSchema
|
| LoginSchema
|
||||||
| LogoutSchema
|
| LogoutSchema
|
||||||
| SignupSchema
|
| SignupSchema
|
||||||
|
| ProductsSchema
|
||||||
|
|
||||||
export type GetAPISchema<
|
export type GetAPISchema<
|
||||||
C extends CommerceAPI<any>,
|
C extends CommerceAPI<any>,
|
||||||
|
@ -12,68 +12,3 @@ export interface Customer extends BCCustomer {}
|
|||||||
|
|
||||||
// TODO: Properly define this type
|
// TODO: Properly define this type
|
||||||
export interface SearchProductsData extends BCSearchProductsData {}
|
export interface SearchProductsData extends BCSearchProductsData {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporal types
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface Entity {
|
|
||||||
id: string | number
|
|
||||||
[prop: string]: any
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Product2 {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
description: string
|
|
||||||
sku?: string
|
|
||||||
slug?: string
|
|
||||||
path?: string
|
|
||||||
images: ProductImage[]
|
|
||||||
variants: ProductVariant2[]
|
|
||||||
price: ProductPrice
|
|
||||||
options: ProductOption[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Product extends Entity {
|
|
||||||
name: string
|
|
||||||
description: string
|
|
||||||
descriptionHtml?: string
|
|
||||||
slug?: string
|
|
||||||
path?: string
|
|
||||||
images: ProductImage[]
|
|
||||||
variants: ProductVariant2[]
|
|
||||||
price: ProductPrice
|
|
||||||
options: ProductOption[]
|
|
||||||
sku?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProductOption extends Entity {
|
|
||||||
displayName: string
|
|
||||||
values: ProductOptionValues[]
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProductOptionValues {
|
|
||||||
label: string
|
|
||||||
hexColors?: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProductImage {
|
|
||||||
url: string
|
|
||||||
alt?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProductVariant2 {
|
|
||||||
id: string | number
|
|
||||||
options: ProductOption[]
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProductPrice {
|
|
||||||
value: number
|
|
||||||
currencyCode: 'USD' | 'ARS' | string | undefined
|
|
||||||
retailPrice?: number
|
|
||||||
salePrice?: number
|
|
||||||
listPrice?: number
|
|
||||||
extendedSalePrice?: number
|
|
||||||
extendedListPrice?: number
|
|
||||||
}
|
|
||||||
|
@ -1,16 +1,66 @@
|
|||||||
// TODO: define this type
|
export type ProductImage = {
|
||||||
export type Product = any
|
url: string
|
||||||
|
alt?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProductPrice = {
|
||||||
|
value: number
|
||||||
|
currencyCode?: 'USD' | 'ARS' | string
|
||||||
|
retailPrice?: number
|
||||||
|
salePrice?: number
|
||||||
|
listPrice?: number
|
||||||
|
extendedSalePrice?: number
|
||||||
|
extendedListPrice?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProductOption = {
|
||||||
|
displayName: string
|
||||||
|
values: ProductOptionValues[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProductOptionValues = {
|
||||||
|
label: string
|
||||||
|
hexColors?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProductVariant = {
|
||||||
|
id: string | number
|
||||||
|
options: ProductOption[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Product = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
descriptionHtml?: string
|
||||||
|
sku?: string
|
||||||
|
slug?: string
|
||||||
|
path?: string
|
||||||
|
images: ProductImage[]
|
||||||
|
variants: ProductVariant[]
|
||||||
|
price: ProductPrice
|
||||||
|
options: ProductOption[]
|
||||||
|
}
|
||||||
|
|
||||||
export type ProductTypes = {
|
export type ProductTypes = {
|
||||||
product: Product
|
product: Product
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProductSchema<T extends ProductTypes = ProductTypes> = {
|
export type ProductsSchema<T extends ProductTypes = ProductTypes> = {
|
||||||
endpoint: {
|
endpoint: {
|
||||||
options: {}
|
options: {}
|
||||||
handlers: {
|
handlers: {
|
||||||
getProducts: {
|
getProducts: {
|
||||||
data: { product: T['product'] } | null
|
data: {
|
||||||
|
products: T['product'][]
|
||||||
|
found: boolean
|
||||||
|
}
|
||||||
|
body: {
|
||||||
|
search?: string
|
||||||
|
category?: string
|
||||||
|
brand?: string
|
||||||
|
sort?: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
pages/api/catalog/products.ts
Normal file
11
pages/api/catalog/products.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import products from '@commerce/api/endpoints/catalog/products'
|
||||||
|
import {
|
||||||
|
ProductsAPI,
|
||||||
|
handlers,
|
||||||
|
} from '@framework/api/endpoints/catalog/products'
|
||||||
|
import commerce from '@lib/api/commerce'
|
||||||
|
|
||||||
|
export default commerce.endpoint({
|
||||||
|
handler: products as ProductsAPI['endpoint']['handler'],
|
||||||
|
handlers,
|
||||||
|
})
|
@ -22,8 +22,8 @@
|
|||||||
"@components/*": ["components/*"],
|
"@components/*": ["components/*"],
|
||||||
"@commerce": ["framework/commerce"],
|
"@commerce": ["framework/commerce"],
|
||||||
"@commerce/*": ["framework/commerce/*"],
|
"@commerce/*": ["framework/commerce/*"],
|
||||||
"@framework": ["framework/shopify"],
|
"@framework": ["framework/bigcommerce"],
|
||||||
"@framework/*": ["framework/shopify/*"]
|
"@framework/*": ["framework/bigcommerce/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
|
"include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user