diff --git a/framework/bigcommerce/api/catalog/products.ts b/framework/bigcommerce/api/catalog/products.ts deleted file mode 100644 index d52b2de7e..000000000 --- a/framework/bigcommerce/api/catalog/products.ts +++ /dev/null @@ -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, {}) diff --git a/framework/bigcommerce/api/catalog/products/index.ts b/framework/bigcommerce/api/catalog/products/index.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/framework/bigcommerce/api/catalog/handlers/get-products.ts b/framework/bigcommerce/api/endpoints/catalog/products/get-products.ts similarity index 89% rename from framework/bigcommerce/api/catalog/handlers/get-products.ts rename to framework/bigcommerce/api/endpoints/catalog/products/get-products.ts index bedd773b0..e0cc27912 100644 --- a/framework/bigcommerce/api/catalog/handlers/get-products.ts +++ b/framework/bigcommerce/api/endpoints/catalog/products/get-products.ts @@ -1,6 +1,6 @@ -import { Product } from '@commerce/types' -import getAllProducts, { ProductEdge } from '../../../product/get-all-products' -import type { ProductsHandlers } from '../products' +import { Product } from '@commerce/types/product' +import { ProductsEndpoint } from '.' +import getAllProducts from '../../../../product/get-all-products' const SORT: { [key: string]: string | undefined } = { latest: 'id', @@ -11,7 +11,7 @@ const SORT: { [key: string]: string | undefined } = { const LIMIT = 12 // Return current cart info -const getProducts: ProductsHandlers['getProducts'] = async ({ +const getProducts: ProductsEndpoint['handlers']['getProducts'] = async ({ res, body: { search, category, brand, sort }, config, diff --git a/framework/bigcommerce/api/endpoints/catalog/products/index.ts b/framework/bigcommerce/api/endpoints/catalog/products/index.ts new file mode 100644 index 000000000..3e13e68b8 --- /dev/null +++ b/framework/bigcommerce/api/endpoints/catalog/products/index.ts @@ -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 + +export type ProductsEndpoint = ProductsAPI['endpoint'] + +export const handlers = { getProducts } diff --git a/framework/bigcommerce/api/index.ts b/framework/bigcommerce/api/index.ts index 6618b60fb..2dcd31d4f 100644 --- a/framework/bigcommerce/api/index.ts +++ b/framework/bigcommerce/api/index.ts @@ -14,6 +14,7 @@ import type { CustomerAPI } from './endpoints/customer' import type { LoginAPI } from './endpoints/login' import type { LogoutAPI } from './endpoints/logout' import type { SignupAPI } from './endpoints/signup' +import type { ProductsAPI } from './endpoints/catalog/products' import login from './operations/login' import getAllPages from './operations/get-all-pages' @@ -119,7 +120,13 @@ export const 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

= CommerceAPI

diff --git a/framework/bigcommerce/types/product.ts b/framework/bigcommerce/types/product.ts new file mode 100644 index 000000000..c776d58fa --- /dev/null +++ b/framework/bigcommerce/types/product.ts @@ -0,0 +1 @@ +export * from '@commerce/types/product' diff --git a/framework/commerce/api/endpoints/cart.ts b/framework/commerce/api/endpoints/cart.ts index 1f1c734ce..e5a05dbd6 100644 --- a/framework/commerce/api/endpoints/cart.ts +++ b/framework/commerce/api/endpoints/cart.ts @@ -7,14 +7,14 @@ const cartEndpoint: GetAPISchema< any, CartSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, operations, config } = ctx + const { req, res, handlers, config } = ctx if ( !isAllowedOperation(req, res, { - GET: operations['getCart'], - POST: operations['addItem'], - PUT: operations['updateItem'], - DELETE: operations['removeItem'], + GET: handlers['getCart'], + POST: handlers['addItem'], + PUT: handlers['updateItem'], + DELETE: handlers['removeItem'], }) ) { return @@ -27,25 +27,25 @@ const cartEndpoint: GetAPISchema< // Return current cart info if (req.method === 'GET') { const body = { cartId } - return await operations['getCart']({ ...ctx, body }) + return await handlers['getCart']({ ...ctx, body }) } // Create or add an item to the cart if (req.method === 'POST') { const body = { ...req.body, cartId } - return await operations['addItem']({ ...ctx, body }) + return await handlers['addItem']({ ...ctx, body }) } // Update item in cart if (req.method === 'PUT') { const body = { ...req.body, cartId } - return await operations['updateItem']({ ...ctx, body }) + return await handlers['updateItem']({ ...ctx, body }) } // Remove an item from the cart if (req.method === 'DELETE') { const body = { ...req.body, cartId } - return await operations['removeItem']({ ...ctx, body }) + return await handlers['removeItem']({ ...ctx, body }) } } catch (error) { console.error(error) diff --git a/framework/commerce/api/endpoints/catalog/products.ts b/framework/commerce/api/endpoints/catalog/products.ts new file mode 100644 index 000000000..d2a4794be --- /dev/null +++ b/framework/commerce/api/endpoints/catalog/products.ts @@ -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 diff --git a/framework/commerce/api/endpoints/customer.ts b/framework/commerce/api/endpoints/customer.ts index 107650770..cf4699d2a 100644 --- a/framework/commerce/api/endpoints/customer.ts +++ b/framework/commerce/api/endpoints/customer.ts @@ -7,11 +7,11 @@ const customerEndpoint: GetAPISchema< any, CustomerSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, operations } = ctx + const { req, res, handlers } = ctx if ( !isAllowedOperation(req, res, { - GET: operations['getLoggedInCustomer'], + GET: handlers['getLoggedInCustomer'], }) ) { return @@ -19,7 +19,7 @@ const customerEndpoint: GetAPISchema< try { const body = null - return await operations['getLoggedInCustomer']({ ...ctx, body }) + return await handlers['getLoggedInCustomer']({ ...ctx, body }) } catch (error) { console.error(error) diff --git a/framework/commerce/api/endpoints/login.ts b/framework/commerce/api/endpoints/login.ts index b5b70a1da..946df0d2c 100644 --- a/framework/commerce/api/endpoints/login.ts +++ b/framework/commerce/api/endpoints/login.ts @@ -7,11 +7,11 @@ const loginEndpoint: GetAPISchema< any, LoginSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, operations } = ctx + const { req, res, handlers } = ctx if ( !isAllowedOperation(req, res, { - POST: operations['login'], + POST: handlers['login'], }) ) { return @@ -19,7 +19,7 @@ const loginEndpoint: GetAPISchema< try { const body = req.body ?? {} - return await operations['login']({ ...ctx, body }) + return await handlers['login']({ ...ctx, body }) } catch (error) { console.error(error) diff --git a/framework/commerce/api/endpoints/logout.ts b/framework/commerce/api/endpoints/logout.ts index d89823d51..8da11acb0 100644 --- a/framework/commerce/api/endpoints/logout.ts +++ b/framework/commerce/api/endpoints/logout.ts @@ -7,11 +7,11 @@ const logoutEndpoint: GetAPISchema< any, LogoutSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, operations } = ctx + const { req, res, handlers } = ctx if ( !isAllowedOperation(req, res, { - GET: operations['logout'], + GET: handlers['logout'], }) ) { return @@ -21,7 +21,7 @@ const logoutEndpoint: GetAPISchema< const redirectTo = req.query.redirect_to const body = typeof redirectTo === 'string' ? { redirectTo } : {} - return await operations['logout']({ ...ctx, body }) + return await handlers['logout']({ ...ctx, body }) } catch (error) { console.error(error) diff --git a/framework/commerce/api/endpoints/signup.ts b/framework/commerce/api/endpoints/signup.ts index d18542289..aa73ae739 100644 --- a/framework/commerce/api/endpoints/signup.ts +++ b/framework/commerce/api/endpoints/signup.ts @@ -7,11 +7,11 @@ const signupEndpoint: GetAPISchema< any, SignupSchema >['endpoint']['handler'] = async (ctx) => { - const { req, res, operations, config } = ctx + const { req, res, handlers, config } = ctx if ( !isAllowedOperation(req, res, { - POST: operations['signup'], + POST: handlers['signup'], }) ) { return @@ -22,7 +22,7 @@ const signupEndpoint: GetAPISchema< try { const body = { ...req.body, cartId } - return await operations['signup']({ ...ctx, body }) + return await handlers['signup']({ ...ctx, body }) } catch (error) { console.error(error) diff --git a/framework/commerce/api/index.ts b/framework/commerce/api/index.ts index b48d2e48b..6d4c16f2a 100644 --- a/framework/commerce/api/index.ts +++ b/framework/commerce/api/index.ts @@ -6,6 +6,7 @@ import type { CustomerSchema } from '../types/customer' import type { LoginSchema } from '../types/login' import type { LogoutSchema } from '../types/logout' import type { SignupSchema } from '../types/signup' +import type { ProductsSchema } from '@commerce/types/product' import { defaultOperations, OPERATIONS, @@ -19,6 +20,7 @@ export type APISchemas = | LoginSchema | LogoutSchema | SignupSchema + | ProductsSchema export type GetAPISchema< C extends CommerceAPI, diff --git a/framework/commerce/types/index.ts b/framework/commerce/types/index.ts index fc399773d..c187b29bb 100644 --- a/framework/commerce/types/index.ts +++ b/framework/commerce/types/index.ts @@ -12,68 +12,3 @@ export interface Customer extends BCCustomer {} // TODO: Properly define this type 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 -} diff --git a/framework/commerce/types/product.ts b/framework/commerce/types/product.ts index b5047564b..d33cf3743 100644 --- a/framework/commerce/types/product.ts +++ b/framework/commerce/types/product.ts @@ -1,16 +1,66 @@ -// TODO: define this type -export type Product = any +export type ProductImage = { + 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 = { product: Product } -export type ProductSchema = { +export type ProductsSchema = { endpoint: { options: {} handlers: { getProducts: { - data: { product: T['product'] } | null + data: { + products: T['product'][] + found: boolean + } + body: { + search?: string + category?: string + brand?: string + sort?: string + } } } } diff --git a/pages/api/catalog/products.ts b/pages/api/catalog/products.ts new file mode 100644 index 000000000..5e5a4707d --- /dev/null +++ b/pages/api/catalog/products.ts @@ -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, +}) diff --git a/tsconfig.json b/tsconfig.json index e20f37099..9e712fb18 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,8 +22,8 @@ "@components/*": ["components/*"], "@commerce": ["framework/commerce"], "@commerce/*": ["framework/commerce/*"], - "@framework": ["framework/shopify"], - "@framework/*": ["framework/shopify/*"] + "@framework": ["framework/bigcommerce"], + "@framework/*": ["framework/bigcommerce/*"] } }, "include": ["next-env.d.ts", "**/*.d.ts", "**/*.ts", "**/*.tsx", "**/*.js"],