4
0
forked from crowetic/commerce

Improved types for operations

This commit is contained in:
Luis Alvarez 2021-01-29 19:24:10 -05:00
parent 172b413521
commit e5f0809070
6 changed files with 54 additions and 34 deletions

View File

@ -1,14 +1,15 @@
import { useCallback } from 'react'
import debounce from 'lodash.debounce'
import type { HookFetcher } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import { ValidationError } from '@commerce/utils/errors'
import useCartUpdateItem from '@commerce/cart/use-update-item'
import { normalizeCart } from '../lib/normalize'
import type {
Cart,
BigcommerceCart,
UpdateCartItemBody,
UpdateCartItemInput,
Cart,
BigcommerceCart,
LineItem,
} from '../types'
import { fetcher as removeFetcher } from './use-remove-item'
import useCart from './use-cart'
@ -29,12 +30,12 @@ export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async (
return removeFetcher(null, { itemId }, fetch)
}
} else if (item.quantity) {
throw new CommerceError({
throw new ValidationError({
message: 'The item quantity has to be a valid integer',
})
}
const data = await fetch<BigcommerceCart>({
const data = await fetch<BigcommerceCart, UpdateCartItemBody>({
...defaultOpts,
...options,
body: { itemId, item },
@ -44,7 +45,9 @@ export const fetcher: HookFetcher<Cart | null, UpdateCartItemBody> = async (
}
function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
const useUpdateItem = (item?: any) => {
const useUpdateItem = <T extends LineItem | undefined = undefined>(
item?: T
) => {
const { mutate } = useCart()
const fn = useCartUpdateItem<Cart | null, UpdateCartItemBody>(
defaultOpts,
@ -52,26 +55,31 @@ function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
)
return useCallback(
debounce(async (input: UpdateCartItemInput) => {
console.log('INPUT', input, {
itemId: input.id ?? item?.id,
item: {
productId: input.productId ?? item?.product_id,
variantId: input.productId ?? item?.variant_id,
quantity: input.quantity,
},
})
const data = await fn({
itemId: input.id ?? item?.id,
item: {
productId: input.productId ?? item?.product_id,
variantId: input.productId ?? item?.variant_id,
quantity: input.quantity,
},
})
await mutate(data, false)
return data
}, cfg?.wait ?? 500),
debounce(
async (
input: T extends LineItem
? Partial<UpdateCartItemInput>
: UpdateCartItemInput
) => {
const itemId = input.id ?? item?.id
const productId = input.productId ?? item?.productId
const variantId = input.productId ?? item?.variantId
if (!itemId || !productId || !variantId) {
throw new ValidationError({
message: 'Invalid input used for this operation',
})
}
const data = await fn({
itemId,
item: { productId, variantId, quantity: input.quantity },
})
await mutate(data, false)
return data
},
cfg?.wait ?? 500
),
[fn, mutate]
)
}

View File

@ -1,4 +1,4 @@
import type { Cart as BigcommerceCart } from '../api/cart'
import type { Cart, BigcommerceCart, LineItem } from '../types'
import update from './immutability'
function normalizeProductOption(productOption: any) {
@ -90,6 +90,7 @@ function normalizeLineItem(item: any): LineItem {
return {
id: item.id,
variantId: String(item.variant_id),
productId: String(item.product_id),
name: item.name,
quantity: item.quantity,
variant: {

View File

@ -39,6 +39,7 @@ export type OptionSelections = {
}
export interface CartItemBody extends Core.CartItemBody {
productId: string // The product id is always required for BC
optionSelections?: OptionSelections
}
@ -46,7 +47,8 @@ export interface UpdateCartItemBody extends Core.UpdateCartItemBody {
item: CartItemBody
}
export interface UpdateCartItemInput extends Core.UpdateCartItemInput {}
export interface UpdateCartItemInput
extends Core.UpdateCartItemInput<CartItemBody> {}
export interface UpdateCartItemHandlerBody
extends Core.UpdateCartItemHandlerBody {}

View File

@ -6,6 +6,7 @@ export interface Discount {
export interface LineItem {
id: string
variantId: string
productId: string
name: string
quantity: number
discounts: Discount[]
@ -88,8 +89,8 @@ export interface Cart {
// Base cart item body used for cart mutations
export interface CartItemBody {
productId: string
variantId: string
productId?: string
quantity?: number
}
@ -100,8 +101,8 @@ export interface UpdateCartItemBody {
}
// Input expected by the `useUpdateItem` hook
export interface UpdateCartItemInput extends Partial<CartItemBody> {
id?: string
export type UpdateCartItemInput<T extends CartItemBody> = T & {
id: string
}
// Body expected by the update operation handler

View File

@ -26,6 +26,14 @@ export class CommerceError extends Error {
}
}
// Used for errors that come from a bad implementation of the hooks
export class ValidationError extends CommerceError {
constructor(options: ErrorProps) {
super(options)
this.code = 'validation_error'
}
}
export class FetcherError extends CommerceError {
status: number

View File

@ -1,18 +1,18 @@
// Core fetcher added by CommerceProvider
export type Fetcher<T> = (options: FetcherOptions) => T | Promise<T>
export type FetcherOptions = {
export type FetcherOptions<Body = any> = {
url?: string
query?: string
method?: string
variables?: any
body?: any
body?: Body
}
export type HookFetcher<Data, Input = null, Result = any> = (
options: HookFetcherOptions | null,
input: Input,
fetch: <T = Result>(options: FetcherOptions) => Promise<T>
fetch: <T = Result, Body = any>(options: FetcherOptions<Body>) => Promise<T>
) => Data | Promise<Data>
export type HookFetcherOptions = {