moved products catalog endpoint

This commit is contained in:
Luis Alvarez 2021-05-21 19:49:04 -05:00
parent d2abdabf8c
commit 564e9cfa8d
17 changed files with 144 additions and 145 deletions

View File

@ -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, {})

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
export * from '@commerce/types/product'

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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