Improve error handling

This commit is contained in:
Michael Bromley 2021-01-25 21:24:26 +01:00
parent f08ade6c39
commit 5054f64fcf
8 changed files with 249 additions and 114 deletions

View File

@ -1,15 +1,21 @@
import type { ServerResponse } from 'http'
import type { LoginMutation, LoginMutationVariables } from '../schema'
import type { RecursivePartial } from '../api/utils/types'
import concatHeader from '../api/utils/concat-cookie'
import { VendureConfig, getConfig } from '../api'
import { getConfig, VendureConfig } from '../api'
import { CommerceError } from '@commerce/utils/errors'
import { ErrorResult } from '../schema'
export const loginMutation = /* GraphQL */ `
mutation loginServer($email: String!, $password: String!) {
login(username: $email, password: $password) {
...on CurrentUser {
__typename
... on CurrentUser {
id
}
... on ErrorResult {
errorCode
message
}
}
}
`
@ -44,10 +50,11 @@ async function login({
}): Promise<LoginResult> {
config = getConfig(config)
const { data, res } = await config.fetch<RecursivePartial<LoginMutation>>(
query,
{ variables }
)
const { data, res } = await config.fetch<LoginMutation>(query, { variables })
if (data.login.__typename !== 'CurrentUser') {
throw new CommerceError({ message: (data.login as ErrorResult).message })
}
// Bigcommerce returns a Set-Cookie header with the auth cookie
let cookie = res.headers.get('Set-Cookie')
@ -68,7 +75,7 @@ async function login({
}
return {
result: data.login?.result,
result: data.login.id.toString(),
}
}

View File

@ -3,17 +3,25 @@ import type { HookFetcher } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useCommerceLogin from '@commerce/use-login'
import useCustomer from '../customer/use-customer'
import { LoginMutation, LoginMutationVariables } from '@framework/schema'
import {
ErrorResult,
LoginMutation,
LoginMutationVariables,
} from '@framework/schema'
export const loginMutation = /* GraphQL */ `
mutation login($username: String! $password: String!) {
login(username: $username, password: $password) {
__typename
... on CurrentUser {
id
}
mutation login($username: String!, $password: String!) {
login(username: $username, password: $password) {
__typename
... on CurrentUser {
id
}
... on ErrorResult {
errorCode
message
}
}
}
`
export const fetcher: HookFetcher<LoginMutation, LoginMutationVariables> = (
@ -23,28 +31,35 @@ export const fetcher: HookFetcher<LoginMutation, LoginMutationVariables> = (
) => {
if (!(username && password)) {
throw new CommerceError({
message:
'An email address and password are required to login'
message: 'An email address and password are required to login',
})
}
return fetch({
...options,
query: loginMutation,
variables: { username, password }
variables: { username, password },
})
}
export function extendHook(customFetcher: typeof fetcher) {
const useLogin = () => {
const { revalidate } = useCustomer()
const fn = useCommerceLogin<LoginMutation, LoginMutationVariables>({}, customFetcher)
const fn = useCommerceLogin<LoginMutation, LoginMutationVariables>(
{},
customFetcher
)
return useCallback(
async function login(input: { email: string; password: string; }) {
const data = await fn({ username: input.email, password: input.password })
async function login(input: { email: string; password: string }) {
const data = await fn({
username: input.email,
password: input.password,
})
if (data.login.__typename !== 'CurrentUser') {
throw new CommerceError({ message: 'The credentials are not valid' });
throw new CommerceError({
message: (data.login as ErrorResult).message,
})
}
await revalidate()
return data

View File

@ -3,23 +3,32 @@ import type { HookFetcher } from '@commerce/utils/types'
import { CommerceError } from '@commerce/utils/errors'
import useCommerceSignup from '@commerce/use-signup'
import useCustomer from '../customer/use-customer'
import { SignupMutation, SignupMutationVariables } from '@framework/schema'
import {
ErrorResult,
SignupMutation,
SignupMutationVariables,
} from '@framework/schema'
export const signupMutation = /* GraphQL */ `
mutation signup($input: RegisterCustomerInput!) {
registerCustomerAccount(input: $input) {
... on Success {
success
}
mutation signup($input: RegisterCustomerInput!) {
registerCustomerAccount(input: $input) {
__typename
... on Success {
success
}
... on ErrorResult {
errorCode
message
}
}
`;
}
`
export type SignupInput = {
email: string;
firstName: string;
lastName: string;
password: string;
email: string
firstName: string
lastName: string
password: string
}
export const fetcher: HookFetcher<SignupMutation, SignupMutationVariables> = (
@ -27,7 +36,7 @@ export const fetcher: HookFetcher<SignupMutation, SignupMutationVariables> = (
{ input },
fetch
) => {
const { firstName, lastName, emailAddress, password } = input;
const { firstName, lastName, emailAddress, password } = input
if (!(firstName && lastName && emailAddress && password)) {
throw new CommerceError({
message:
@ -45,20 +54,28 @@ export const fetcher: HookFetcher<SignupMutation, SignupMutationVariables> = (
export function extendHook(customFetcher: typeof fetcher) {
const useSignup = () => {
const { revalidate } = useCustomer()
const fn = useCommerceSignup<SignupMutation, SignupMutationVariables>({}, customFetcher)
const fn = useCommerceSignup<SignupMutation, SignupMutationVariables>(
{},
customFetcher
)
return useCallback(
async function signup(input: SignupInput) {
const data = await fn({
const { registerCustomerAccount } = await fn({
input: {
firstName: input.firstName,
lastName: input.lastName,
emailAddress: input.email,
password: input.password,
}
},
})
if (registerCustomerAccount.__typename !== 'Success') {
throw new CommerceError({
message: (registerCustomerAccount as ErrorResult).message,
})
}
await revalidate()
return data
return { registerCustomerAccount }
},
[fn]
)

View File

@ -5,28 +5,37 @@ import useCartAddItem from '@commerce/cart/use-add-item'
import useCart from './use-cart'
import { useCallback } from 'react'
import { cartFragment } from '../api/fragments/cart'
import { AddItemToOrderMutation, AddItemToOrderMutationVariables } from '@framework/schema'
import {
AddItemToOrderMutation,
AddItemToOrderMutationVariables,
ErrorResult,
} from '@framework/schema'
export const addItemToOrderMutation = /* GraphQL */ `
mutation addItemToOrder($variantId: ID!, $quantity: Int!) {
addItemToOrder(productVariantId: $variantId, quantity: $quantity) {
... Cart
__typename
...Cart
... on ErrorResult {
errorCode
message
}
}
}
${cartFragment}
`
export type AddItemInput = { productId?: number; variantId: number; quantity?: number; };
export type AddItemInput = {
productId?: number
variantId: number
quantity?: number
}
export const fetcher: HookFetcher<AddItemToOrderMutation, AddItemToOrderMutationVariables> = (
options,
{ variantId, quantity },
fetch
) => {
if (
quantity &&
(!Number.isInteger(quantity) || quantity! < 1)
) {
export const fetcher: HookFetcher<
AddItemToOrderMutation,
AddItemToOrderMutationVariables
> = (options, { variantId, quantity }, fetch) => {
if (quantity && (!Number.isInteger(quantity) || quantity! < 1)) {
throw new CommerceError({
message: 'The item quantity has to be a valid integer greater than 0',
})
@ -36,9 +45,6 @@ export const fetcher: HookFetcher<AddItemToOrderMutation, AddItemToOrderMutation
...options,
query: addItemToOrderMutation,
variables: { variantId, quantity: quantity || 1 },
}).then(res => {
console.log({ res });
return res;
})
}
@ -49,9 +55,18 @@ export function extendHook(customFetcher: typeof fetcher) {
return useCallback(
async function addItem(input: AddItemInput) {
const data = await fn({ quantity: input.quantity || 1, variantId: input.variantId })
await mutate(data, false)
return data
const { addItemToOrder } = await fn({
quantity: input.quantity || 1,
variantId: input.variantId,
})
if (addItemToOrder.__typename === 'Order') {
await mutate({ addItemToOrder }, false)
} else {
throw new CommerceError({
message: (addItemToOrder as ErrorResult).message,
})
}
return { addItemToOrder }
},
[fn, mutate]
)

View File

@ -14,19 +14,15 @@ export const getCartQuery = /* GraphQL */ `
${cartFragment}
`
export const fetcher: HookFetcher<any, null> = (
options,
input,
fetch
) => {
export const fetcher: HookFetcher<any, null> = (options, input, fetch) => {
return fetch({ ...options, query: getCartQuery })
}
export type CartResult = {
activeOrder?: CartFragment;
addItemToOrder?: CartFragment;
adjustOrderLine?: CartFragment;
removeOrderLine?: CartFragment;
activeOrder?: CartFragment
addItemToOrder?: CartFragment
adjustOrderLine?: CartFragment
removeOrderLine?: CartFragment
}
export function extendHook(
@ -34,20 +30,29 @@ export function extendHook(
swrOptions?: SwrOptions<any | null>
) {
const useCart = () => {
const response = useData<Cart>({ query: getCartQuery }, [], customFetcher, swrOptions)
const response = useData<CartResult>(
{ query: getCartQuery },
[],
customFetcher,
swrOptions
)
const res = useResponse(response, {
normalizer: (data => {
const order = data?.activeOrder || data?.addItemToOrder || data?.adjustOrderLine || data?.removeOrderLine;
return (order ? normalizeCart(order) : null)
}),
normalizer: (data) => {
const order =
data?.activeOrder ||
data?.addItemToOrder ||
data?.adjustOrderLine ||
data?.removeOrderLine
return order ? normalizeCart(order) : null
},
descriptors: {
isEmpty: {
get() {
return response.data?.activeOrder?.totalQuantity === 0
},
enumerable: true
}
}
enumerable: true,
},
},
})
return res

View File

@ -3,23 +3,31 @@ import { HookFetcher } from '@commerce/utils/types'
import useCartRemoveItem from '@commerce/cart/use-remove-item'
import useCart from './use-cart'
import { cartFragment } from '@framework/api/fragments/cart'
import { RemoveOrderLineMutation, RemoveOrderLineMutationVariables } from '@framework/schema'
import {
ErrorResult,
RemoveOrderLineMutation,
RemoveOrderLineMutationVariables,
} from '@framework/schema'
import { CommerceError } from '@commerce/utils/errors'
export const removeOrderLineMutation = /* GraphQL */ `
mutation removeOrderLine($orderLineId: ID!) {
removeOrderLine(orderLineId: $orderLineId) {
__typename
...Cart
... on ErrorResult {
errorCode
message
}
}
}
${cartFragment}
`
export const fetcher: HookFetcher<RemoveOrderLineMutation, RemoveOrderLineMutationVariables> = (
options,
{ orderLineId },
fetch
) => {
export const fetcher: HookFetcher<
RemoveOrderLineMutation,
RemoveOrderLineMutationVariables
> = (options, { orderLineId }, fetch) => {
return fetch({
...options,
query: removeOrderLineMutation,
@ -30,16 +38,20 @@ export const fetcher: HookFetcher<RemoveOrderLineMutation, RemoveOrderLineMutati
export function extendHook(customFetcher: typeof fetcher) {
const useRemoveItem = (item?: any) => {
const { mutate } = useCart()
const fn = useCartRemoveItem<RemoveOrderLineMutation, RemoveOrderLineMutationVariables>(
{},
customFetcher
)
const fn = useCartRemoveItem<
RemoveOrderLineMutation,
RemoveOrderLineMutationVariables
>({}, customFetcher)
return useCallback(
async function removeItem(input: any) {
const { removeOrderLine } = await fn({ orderLineId: input.id })
if (removeOrderLine.__typename === 'Order') {
await mutate({ removeOrderLine }, false)
} else {
throw new CommerceError({
message: (removeOrderLine as ErrorResult).message,
})
}
return { removeOrderLine }
},

View File

@ -4,21 +4,30 @@ import type { HookFetcher } from '@commerce/utils/types'
import useCartUpdateItem from '@commerce/cart/use-update-item'
import useCart from './use-cart'
import { cartFragment } from '@framework/api/fragments/cart'
import { AdjustOrderLineMutation, AdjustOrderLineMutationVariables } from '@framework/schema'
import {
AdjustOrderLineMutation,
AdjustOrderLineMutationVariables,
ErrorResult,
} from '@framework/schema'
import { CommerceError } from '@commerce/utils/errors'
export const adjustOrderLineMutation = /* GraphQL */ `
mutation adjustOrderLine($orderLineId: ID!, $quantity: Int!) {
adjustOrderLine(orderLineId: $orderLineId, quantity: $quantity) {
__typename
...Cart
... on ErrorResult {
errorCode
message
}
}
}
${cartFragment}
`
export const fetcher: HookFetcher<AdjustOrderLineMutation, AdjustOrderLineMutationVariables> = (
options,
{ orderLineId, quantity },
fetch
) => {
export const fetcher: HookFetcher<
AdjustOrderLineMutation,
AdjustOrderLineMutationVariables
> = (options, { orderLineId, quantity }, fetch) => {
return fetch({
...options,
query: adjustOrderLineMutation,
@ -29,10 +38,10 @@ export const fetcher: HookFetcher<AdjustOrderLineMutation, AdjustOrderLineMutati
function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
const useUpdateItem = (item?: any) => {
const { mutate } = useCart()
const fn = useCartUpdateItem<AdjustOrderLineMutation, AdjustOrderLineMutationVariables>(
{},
customFetcher
)
const fn = useCartUpdateItem<
AdjustOrderLineMutation,
AdjustOrderLineMutationVariables
>({}, customFetcher)
return useCallback(
debounce(async (input: any) => {
@ -42,6 +51,10 @@ function extendHook(customFetcher: typeof fetcher, cfg?: { wait?: number }) {
})
if (adjustOrderLine.__typename === 'Order') {
await mutate({ adjustOrderLine }, false)
} else {
throw new CommerceError({
message: (adjustOrderLine as ErrorResult).message,
})
}
return { adjustOrderLine }
}, cfg?.wait ?? 500),

View File

@ -2843,10 +2843,19 @@ export type LoginServerMutationVariables = Exact<{
export type LoginServerMutation = { __typename?: 'Mutation' } & {
login:
| ({ __typename?: 'CurrentUser' } & Pick<CurrentUser, 'id'>)
| { __typename?: 'InvalidCredentialsError' }
| { __typename?: 'NotVerifiedError' }
| { __typename?: 'NativeAuthStrategyError' }
| ({ __typename: 'CurrentUser' } & Pick<CurrentUser, 'id'>)
| ({ __typename: 'InvalidCredentialsError' } & Pick<
InvalidCredentialsError,
'errorCode' | 'message'
>)
| ({ __typename: 'NotVerifiedError' } & Pick<
NotVerifiedError,
'errorCode' | 'message'
>)
| ({ __typename: 'NativeAuthStrategyError' } & Pick<
NativeAuthStrategyError,
'errorCode' | 'message'
>)
}
export type LoginMutationVariables = Exact<{
@ -2857,9 +2866,18 @@ export type LoginMutationVariables = Exact<{
export type LoginMutation = { __typename?: 'Mutation' } & {
login:
| ({ __typename: 'CurrentUser' } & Pick<CurrentUser, 'id'>)
| { __typename: 'InvalidCredentialsError' }
| { __typename: 'NotVerifiedError' }
| { __typename: 'NativeAuthStrategyError' }
| ({ __typename: 'InvalidCredentialsError' } & Pick<
InvalidCredentialsError,
'errorCode' | 'message'
>)
| ({ __typename: 'NotVerifiedError' } & Pick<
NotVerifiedError,
'errorCode' | 'message'
>)
| ({ __typename: 'NativeAuthStrategyError' } & Pick<
NativeAuthStrategyError,
'errorCode' | 'message'
>)
}
export type LogoutMutationVariables = Exact<{ [key: string]: never }>
@ -2874,9 +2892,15 @@ export type SignupMutationVariables = Exact<{
export type SignupMutation = { __typename?: 'Mutation' } & {
registerCustomerAccount:
| ({ __typename?: 'Success' } & Pick<Success, 'success'>)
| { __typename?: 'MissingPasswordError' }
| { __typename?: 'NativeAuthStrategyError' }
| ({ __typename: 'Success' } & Pick<Success, 'success'>)
| ({ __typename: 'MissingPasswordError' } & Pick<
MissingPasswordError,
'errorCode' | 'message'
>)
| ({ __typename: 'NativeAuthStrategyError' } & Pick<
NativeAuthStrategyError,
'errorCode' | 'message'
>)
}
export type AddItemToOrderMutationVariables = Exact<{
@ -2886,11 +2910,23 @@ export type AddItemToOrderMutationVariables = Exact<{
export type AddItemToOrderMutation = { __typename?: 'Mutation' } & {
addItemToOrder:
| ({ __typename?: 'Order' } & CartFragment)
| { __typename?: 'OrderModificationError' }
| { __typename?: 'OrderLimitError' }
| { __typename?: 'NegativeQuantityError' }
| { __typename?: 'InsufficientStockError' }
| ({ __typename: 'Order' } & CartFragment)
| ({ __typename: 'OrderModificationError' } & Pick<
OrderModificationError,
'errorCode' | 'message'
>)
| ({ __typename: 'OrderLimitError' } & Pick<
OrderLimitError,
'errorCode' | 'message'
>)
| ({ __typename: 'NegativeQuantityError' } & Pick<
NegativeQuantityError,
'errorCode' | 'message'
>)
| ({ __typename: 'InsufficientStockError' } & Pick<
InsufficientStockError,
'errorCode' | 'message'
>)
}
export type ActiveOrderQueryVariables = Exact<{ [key: string]: never }>
@ -2906,7 +2942,10 @@ export type RemoveOrderLineMutationVariables = Exact<{
export type RemoveOrderLineMutation = { __typename?: 'Mutation' } & {
removeOrderLine:
| ({ __typename: 'Order' } & CartFragment)
| { __typename: 'OrderModificationError' }
| ({ __typename: 'OrderModificationError' } & Pick<
OrderModificationError,
'errorCode' | 'message'
>)
}
export type AdjustOrderLineMutationVariables = Exact<{
@ -2916,11 +2955,23 @@ export type AdjustOrderLineMutationVariables = Exact<{
export type AdjustOrderLineMutation = { __typename?: 'Mutation' } & {
adjustOrderLine:
| ({ __typename?: 'Order' } & CartFragment)
| { __typename?: 'OrderModificationError' }
| { __typename?: 'OrderLimitError' }
| { __typename?: 'NegativeQuantityError' }
| { __typename?: 'InsufficientStockError' }
| ({ __typename: 'Order' } & CartFragment)
| ({ __typename: 'OrderModificationError' } & Pick<
OrderModificationError,
'errorCode' | 'message'
>)
| ({ __typename: 'OrderLimitError' } & Pick<
OrderLimitError,
'errorCode' | 'message'
>)
| ({ __typename: 'NegativeQuantityError' } & Pick<
NegativeQuantityError,
'errorCode' | 'message'
>)
| ({ __typename: 'InsufficientStockError' } & Pick<
InsufficientStockError,
'errorCode' | 'message'
>)
}
export type GetCollectionsQueryVariables = Exact<{ [key: string]: never }>