mirror of
https://github.com/vercel/commerce.git
synced 2025-06-19 05:31:22 +00:00
Adding types, fixes and other updates
This commit is contained in:
parent
e6df8dfb40
commit
b2fbaab5d5
@ -1,8 +1,9 @@
|
||||
import { GetAPISchema } from '@commerce/api'
|
||||
import type { GetAPISchema } from '@commerce/api'
|
||||
import type { AddItemOperation } from '@commerce/types'
|
||||
import getCart from './get-cart'
|
||||
import addItem from './add-item'
|
||||
import updateItem from './handlers/update-item'
|
||||
import removeItem from './handlers/remove-item'
|
||||
import updateItem from './update-item'
|
||||
import removeItem from './remove-item'
|
||||
import type {
|
||||
GetCartHandlerBody,
|
||||
AddCartItemHandlerBody,
|
||||
@ -18,14 +19,10 @@ export type CartAPI = GetAPISchema<
|
||||
endpoint: {
|
||||
options: {}
|
||||
operations: {
|
||||
getCart: {
|
||||
data: Cart | null
|
||||
body: GetCartHandlerBody
|
||||
options: { yay: string }
|
||||
}
|
||||
addItem: { data: Cart; body: AddCartItemHandlerBody; options: {} }
|
||||
updateItem: { data: Cart; body: UpdateCartItemHandlerBody; options: {} }
|
||||
removeItem: { data: Cart; body: RemoveCartItemHandlerBody; options: {} }
|
||||
getCart: { data: Cart | null; body: GetCartHandlerBody }
|
||||
addItem: { data: Cart; body: AddItemOperation['body'] }
|
||||
updateItem: { data: Cart; body: UpdateCartItemHandlerBody }
|
||||
removeItem: { data: Cart; body: RemoveCartItemHandlerBody }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,4 +30,4 @@ export type CartAPI = GetAPISchema<
|
||||
|
||||
export type CartEndpoint = CartAPI['endpoint']
|
||||
|
||||
export const operations = { getCart, addItem }
|
||||
export const operations = { getCart, addItem, updateItem, removeItem }
|
||||
|
34
framework/bigcommerce/api/cart/remove-item.ts
Normal file
34
framework/bigcommerce/api/cart/remove-item.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { normalizeCart } from '@framework/lib/normalize'
|
||||
import getCartCookie from '../utils/get-cart-cookie'
|
||||
import type { CartEndpoint } from '.'
|
||||
|
||||
const removeItem: CartEndpoint['operations']['removeItem'] = async ({
|
||||
res,
|
||||
body: { cartId, itemId },
|
||||
config,
|
||||
}) => {
|
||||
if (!cartId || !itemId) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
const result = await config.storeApiFetch<{ data: any } | null>(
|
||||
`/v3/carts/${cartId}/items/${itemId}?include=line_items.physical_items.options`,
|
||||
{ method: 'DELETE' }
|
||||
)
|
||||
const data = result?.data ?? null
|
||||
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
data
|
||||
? // Update the cart cookie
|
||||
getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge)
|
||||
: // Remove the cart cookie if the cart was removed (empty items)
|
||||
getCartCookie(config.cartCookie)
|
||||
)
|
||||
res.status(200).json({ data: normalizeCart(data) })
|
||||
}
|
||||
|
||||
export default removeItem
|
36
framework/bigcommerce/api/cart/update-item.ts
Normal file
36
framework/bigcommerce/api/cart/update-item.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { normalizeCart } from '@framework/lib/normalize'
|
||||
import { parseCartItem } from '../utils/parse-item'
|
||||
import getCartCookie from '../utils/get-cart-cookie'
|
||||
import type { CartEndpoint } from '.'
|
||||
|
||||
const updateItem: CartEndpoint['operations']['updateItem'] = async ({
|
||||
res,
|
||||
body: { cartId, itemId, item },
|
||||
config,
|
||||
}) => {
|
||||
if (!cartId || !itemId || !item) {
|
||||
return res.status(400).json({
|
||||
data: null,
|
||||
errors: [{ message: 'Invalid request' }],
|
||||
})
|
||||
}
|
||||
|
||||
const { data } = await config.storeApiFetch(
|
||||
`/v3/carts/${cartId}/items/${itemId}?include=line_items.physical_items.options`,
|
||||
{
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
line_item: parseCartItem(item),
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
// Update the cart cookie
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
getCartCookie(config.cartCookie, cartId, config.cartCookieMaxAge)
|
||||
)
|
||||
res.status(200).json({ data: normalizeCart(data) })
|
||||
}
|
||||
|
||||
export default updateItem
|
@ -1,3 +1,4 @@
|
||||
import type { NextApiHandler } from 'next'
|
||||
import type { RequestInit } from '@vercel/fetch'
|
||||
import {
|
||||
CommerceAPI as CoreCommerceAPI,
|
||||
@ -6,6 +7,8 @@ import {
|
||||
import fetchGraphqlApi from './utils/fetch-graphql-api'
|
||||
import fetchStoreApi from './utils/fetch-store-api'
|
||||
|
||||
import type { CartAPI } from './cart'
|
||||
|
||||
export interface BigcommerceConfig extends CommerceAPIConfig {
|
||||
// Indicates if the returned metadata with translations should be applied to the
|
||||
// data or returned as it is
|
||||
@ -104,11 +107,20 @@ export const provider = {
|
||||
|
||||
export type Provider = typeof provider
|
||||
|
||||
export class CommerceAPI<
|
||||
P extends Provider = Provider
|
||||
> extends CoreCommerceAPI<P> {
|
||||
constructor(readonly provider: P = provider) {
|
||||
super(provider)
|
||||
export type APIs = CartAPI
|
||||
|
||||
export class CommerceAPI extends CoreCommerceAPI<Provider> {
|
||||
constructor(customProvider: Provider = provider) {
|
||||
super(customProvider)
|
||||
}
|
||||
|
||||
endpoint<E extends APIs>(
|
||||
context: E['endpoint'] & {
|
||||
config?: Provider['config']
|
||||
options?: E['schema']['endpoint']['options']
|
||||
}
|
||||
): NextApiHandler {
|
||||
return this.endpoint(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,6 @@ export type BigcommerceCart = {
|
||||
|
||||
export type Cart = Core.Cart & {
|
||||
lineItems: LineItem[]
|
||||
core: string
|
||||
}
|
||||
|
||||
export type LineItem = Core.LineItem
|
||||
|
@ -1,19 +1,7 @@
|
||||
import type { NextApiHandler } from 'next'
|
||||
import type { RequestInit, Response } from '@vercel/fetch'
|
||||
import type { APIEndpoint, APIHandler } from './utils/types'
|
||||
import type { Cart } from '../types'
|
||||
|
||||
export type CartSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
operations: {
|
||||
getCart: { data?: Cart | null; body?: any }
|
||||
addItem: { data?: Cart; body?: any }
|
||||
updateItem: { data?: Cart; body?: any }
|
||||
removeItem: { data?: Cart; body?: any }
|
||||
}
|
||||
}
|
||||
}
|
||||
import type { CartSchema } from '../types'
|
||||
|
||||
export type APISchemas = CartSchema
|
||||
|
||||
@ -78,10 +66,10 @@ export class CommerceAPI<P extends APIProvider = APIProvider> {
|
||||
Object.assign(this.provider.config, newConfig)
|
||||
}
|
||||
|
||||
endpoint<E extends GetAPISchema<this>>(
|
||||
context: E['endpoint'] & {
|
||||
endpoint<T extends GetAPISchema<any, any>>(
|
||||
context: T['endpoint'] & {
|
||||
config?: P['config']
|
||||
options?: E['schema']['endpoint']['options']
|
||||
options?: T['schema']['endpoint']['options']
|
||||
}
|
||||
): NextApiHandler {
|
||||
const commerce = this
|
||||
@ -93,7 +81,7 @@ export class CommerceAPI<P extends APIProvider = APIProvider> {
|
||||
res,
|
||||
commerce,
|
||||
config: cfg,
|
||||
handlers: context.operations,
|
||||
operations: context.operations,
|
||||
options: context.options ?? {},
|
||||
})
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export type APIHandlerContext<
|
||||
res: NextApiResponse<APIResponse<Data>>
|
||||
commerce: C
|
||||
config: C['provider']['config']
|
||||
handlers: H
|
||||
operations: H
|
||||
/**
|
||||
* Custom configs that may be used by a particular handler
|
||||
*/
|
||||
|
147
framework/commerce/types/cart.ts
Normal file
147
framework/commerce/types/cart.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import type { Discount, Measurement, Image } from './common'
|
||||
|
||||
export type LineItem = {
|
||||
id: string
|
||||
variantId: string
|
||||
productId: string
|
||||
name: string
|
||||
quantity: number
|
||||
discounts: Discount[]
|
||||
// A human-friendly unique string automatically generated from the product’s name
|
||||
path: string
|
||||
variant: ProductVariant
|
||||
}
|
||||
|
||||
export type ProductVariant = {
|
||||
id: string
|
||||
// The SKU (stock keeping unit) associated with the product variant.
|
||||
sku: string
|
||||
// The product variant’s title, or the product's name.
|
||||
name: string
|
||||
// Whether a customer needs to provide a shipping address when placing
|
||||
// an order for the product variant.
|
||||
requiresShipping: boolean
|
||||
// The product variant’s price after all discounts are applied.
|
||||
price: number
|
||||
// Product variant’s price, as quoted by the manufacturer/distributor.
|
||||
listPrice: number
|
||||
// Image associated with the product variant. Falls back to the product image
|
||||
// if no image is available.
|
||||
image?: Image
|
||||
// Indicates whether this product variant is in stock.
|
||||
isInStock?: boolean
|
||||
// Indicates if the product variant is available for sale.
|
||||
availableForSale?: boolean
|
||||
// The variant's weight. If a weight was not explicitly specified on the
|
||||
// variant this will be the product's weight.
|
||||
weight?: Measurement
|
||||
// The variant's height. If a height was not explicitly specified on the
|
||||
// variant, this will be the product's height.
|
||||
height?: Measurement
|
||||
// The variant's width. If a width was not explicitly specified on the
|
||||
// variant, this will be the product's width.
|
||||
width?: Measurement
|
||||
// The variant's depth. If a depth was not explicitly specified on the
|
||||
// variant, this will be the product's depth.
|
||||
depth?: Measurement
|
||||
}
|
||||
|
||||
// Shopping cart, a.k.a Checkout
|
||||
export type Cart = {
|
||||
id: string
|
||||
// ID of the customer to which the cart belongs.
|
||||
customerId?: string
|
||||
// The email assigned to this cart
|
||||
email?: string
|
||||
// The date and time when the cart was created.
|
||||
createdAt: string
|
||||
// The currency used for this cart
|
||||
currency: { code: string }
|
||||
// Specifies if taxes are included in the line items.
|
||||
taxesIncluded: boolean
|
||||
lineItems: LineItem[]
|
||||
// The sum of all the prices of all the items in the cart.
|
||||
// Duties, taxes, shipping and discounts excluded.
|
||||
lineItemsSubtotalPrice: number
|
||||
// Price of the cart before duties, shipping and taxes.
|
||||
subtotalPrice: number
|
||||
// The sum of all the prices of all the items in the cart.
|
||||
// Duties, taxes and discounts included.
|
||||
totalPrice: number
|
||||
// Discounts that have been applied on the cart.
|
||||
discounts?: Discount[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Base cart item body used for cart mutations
|
||||
*/
|
||||
export type CartItemBody = {
|
||||
variantId: string
|
||||
productId?: string
|
||||
quantity?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks schema
|
||||
*/
|
||||
|
||||
export type CartHooks = {
|
||||
getCart: GetCartHook
|
||||
addItem: AddItemHook
|
||||
updateItem: UpdateItemHook
|
||||
remoteItem: RemoveItemHook
|
||||
}
|
||||
|
||||
export type GetCartHook = {
|
||||
data: Cart | null
|
||||
}
|
||||
|
||||
export type AddItemHook = {
|
||||
data: Cart
|
||||
body: { item: CartItemBody }
|
||||
}
|
||||
|
||||
export type UpdateItemHook = {
|
||||
data: Cart
|
||||
body: { itemId: string; item: CartItemBody }
|
||||
}
|
||||
|
||||
export type RemoveItemHook = {
|
||||
data: Cart | null
|
||||
body: { itemId: string }
|
||||
}
|
||||
|
||||
/**
|
||||
* API Schema
|
||||
*/
|
||||
|
||||
export type CartSchema = {
|
||||
endpoint: {
|
||||
options: {}
|
||||
operations: CartOperations
|
||||
}
|
||||
}
|
||||
|
||||
export type CartOperations = {
|
||||
getCart: GetCartOperation
|
||||
addItem: AddItemOperation
|
||||
updateItem: UpdateItemOperation
|
||||
removeItem: RemoveItemOperation
|
||||
}
|
||||
|
||||
export type GetCartOperation = {
|
||||
data: Cart | null
|
||||
body: { cartId?: string }
|
||||
}
|
||||
|
||||
export type AddItemOperation = AddItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type UpdateItemOperation = UpdateItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
||||
|
||||
export type RemoveItemOperation = RemoveItemHook & {
|
||||
body: { cartId: string }
|
||||
}
|
16
framework/commerce/types/common.ts
Normal file
16
framework/commerce/types/common.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export type Discount = {
|
||||
// The value of the discount, can be an amount or percentage
|
||||
value: number
|
||||
}
|
||||
|
||||
export type Measurement = {
|
||||
value: number
|
||||
unit: 'KILOGRAMS' | 'GRAMS' | 'POUNDS' | 'OUNCES'
|
||||
}
|
||||
|
||||
export type Image = {
|
||||
url: string
|
||||
altText?: string
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
130
framework/commerce/types/index.ts
Normal file
130
framework/commerce/types/index.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import type { Wishlist as BCWishlist } from '../../bigcommerce/api/wishlist'
|
||||
import type { Customer as BCCustomer } from '../../bigcommerce/api/customers'
|
||||
import type { SearchProductsData as BCSearchProductsData } from '../../bigcommerce/api/catalog/products'
|
||||
|
||||
export * from './cart'
|
||||
export * from './common'
|
||||
|
||||
// TODO: Properly define this type
|
||||
export interface Wishlist extends BCWishlist {}
|
||||
|
||||
// TODO: Properly define this type
|
||||
export interface Customer extends BCCustomer {}
|
||||
|
||||
// TODO: Properly define this type
|
||||
export interface SearchProductsData extends BCSearchProductsData {}
|
||||
|
||||
/**
|
||||
* Cart mutations
|
||||
*/
|
||||
|
||||
// Base cart item body used for cart mutations
|
||||
export type CartItemBody = {
|
||||
variantId: string
|
||||
productId?: string
|
||||
quantity?: number
|
||||
}
|
||||
|
||||
// Body used by the `getCart` operation handler
|
||||
export type GetCartHandlerBody = {
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
// Body used by the add item to cart operation
|
||||
export type AddCartItemBody<T extends CartItemBody> = {
|
||||
item: T
|
||||
}
|
||||
|
||||
// Body expected by the add item to cart operation handler
|
||||
export type AddCartItemHandlerBody<T extends CartItemBody> = Partial<
|
||||
AddCartItemBody<T>
|
||||
> & {
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
// Body used by the update cart item operation
|
||||
export type UpdateCartItemBody<T extends CartItemBody> = {
|
||||
itemId: string
|
||||
item: T
|
||||
}
|
||||
|
||||
// Body expected by the update cart item operation handler
|
||||
export type UpdateCartItemHandlerBody<T extends CartItemBody> = Partial<
|
||||
UpdateCartItemBody<T>
|
||||
> & {
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
// Body used by the remove cart item operation
|
||||
export type RemoveCartItemBody = {
|
||||
itemId: string
|
||||
}
|
||||
|
||||
// Body expected by the remove cart item operation handler
|
||||
export type RemoveCartItemHandlerBody = Partial<RemoveCartItemBody> & {
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
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,5 +1,8 @@
|
||||
import cart from '@commerce/api/endpoints/cart'
|
||||
import { operations } from '@framework/api/cart'
|
||||
import { CartAPI, operations } from '@framework/api/cart'
|
||||
import commerce from '@lib/api/commerce'
|
||||
|
||||
export default commerce.endpoint({ handler: cart, operations })
|
||||
export default commerce.endpoint({
|
||||
handler: cart as CartAPI['endpoint']['handler'],
|
||||
operations,
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user