feat: convert errors into ApolloError (#1225)

This commit is contained in:
Oliver Hoff 2021-07-19 15:45:40 +02:00 committed by GitHub
parent 08b6b0e7d9
commit 334310d5e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 38 deletions

View File

@ -4,5 +4,6 @@
"javascriptreact",
"vue"
],
"eslint.enable": true
"eslint.enable": true,
"typescript.tsdk": "node_modules/typescript/lib"
}

View File

@ -1,10 +1,11 @@
import { DocumentNode } from 'graphql'
import { MutationOptions, OperationVariables, FetchResult, TypedDocumentNode } from '@apollo/client/core'
import { MutationOptions, OperationVariables, FetchResult, TypedDocumentNode, ApolloError } from '@apollo/client/core'
import { ref, onBeforeUnmount, isRef, Ref, getCurrentInstance } from 'vue-demi'
import { useApolloClient } from './useApolloClient'
import { ReactiveFunction } from './util/ReactiveFunction'
import { useEventHook } from './util/useEventHook'
import { trackMutation } from './util/loadingTracking'
import { toApolloError } from './util/toApolloError'
/**
* `useMutation` options for mutations that don't require `variables`.
@ -27,12 +28,12 @@ export type MutateFunction<TResult, TVariables> = (variables?: TVariables | null
export interface UseMutationReturn<TResult, TVariables> {
mutate: MutateFunction<TResult, TVariables>
loading: Ref<boolean>
error: Ref<Error | null>
error: Ref<ApolloError | null>
called: Ref<boolean>
onDone: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>) => void) => {
off: () => void
}
onError: (fn: (param: Error) => void) => {
onError: (fn: (param: ApolloError) => void) => {
off: () => void
}
}
@ -47,11 +48,11 @@ export function useMutation<
const vm = getCurrentInstance()
const loading = ref<boolean>(false)
vm && trackMutation(loading)
const error = ref<Error | null>(null)
const error = ref<ApolloError | null>(null)
const called = ref<boolean>(false)
const doneEvent = useEventHook<FetchResult<TResult, Record<string, any>, Record<string, any>>>()
const errorEvent = useEventHook<Error>()
const errorEvent = useEventHook<ApolloError>()
// Apollo Client
const { resolveClient } = useApolloClient()
@ -94,11 +95,12 @@ export function useMutation<
doneEvent.trigger(result)
return result
} catch (e) {
error.value = e
const apolloError = toApolloError(e)
error.value = apolloError
loading.value = false
errorEvent.trigger(e)
errorEvent.trigger(apolloError)
if (currentOptions.throws === 'always' || (currentOptions.throws !== 'never' && !errorEvent.getCount())) {
throw e
throw apolloError
}
}
return null

View File

@ -20,6 +20,7 @@ import {
FetchMoreOptions,
ObservableSubscription,
TypedDocumentNode,
ApolloError,
} from '@apollo/client/core'
import { throttle, debounce } from 'throttle-debounce'
import { useApolloClient } from './useApolloClient'
@ -28,6 +29,7 @@ import { paramToRef } from './util/paramToRef'
import { paramToReactive } from './util/paramToReactive'
import { useEventHook } from './util/useEventHook'
import { trackQuery } from './util/loadingTracking'
import { toApolloError } from './util/toApolloError'
import type { CurrentInstance } from './util/types'
@ -58,7 +60,7 @@ export interface UseQueryReturn<TResult, TVariables> {
result: Ref<TResult | undefined>
loading: Ref<boolean>
networkStatus: Ref<number | undefined>
error: Ref<Error | null>
error: Ref<ApolloError | null>
start: () => void
stop: () => void
restart: () => void
@ -73,7 +75,7 @@ export interface UseQueryReturn<TResult, TVariables> {
onResult: (fn: (param: ApolloQueryResult<TResult>) => void) => {
off: () => void
}
onError: (fn: (param: Error) => void) => {
onError: (fn: (param: ApolloError) => void) => {
off: () => void
}
}
@ -154,8 +156,8 @@ export function useQueryImpl<
*/
const result = ref<TResult | undefined>()
const resultEvent = useEventHook<ApolloQueryResult<TResult>>()
const error = ref<Error | null>(null)
const errorEvent = useEventHook<Error>()
const error = ref<ApolloError | null>(null)
const errorEvent = useEventHook<ApolloError>()
// Loading
@ -168,7 +170,7 @@ export function useQueryImpl<
// SSR
let firstResolve: (() => void) | undefined
let firstReject: ((error: Error) => void) | undefined
let firstReject: ((apolloError: ApolloError) => void) | undefined
onServerPrefetch?.(() => {
if (!isEnabled.value || (isServer && currentOptions.value?.prefetch === false)) return
@ -178,8 +180,8 @@ export function useQueryImpl<
firstResolve = undefined
firstReject = undefined
}
firstReject = (error: Error) => {
reject(error)
firstReject = (apolloError: ApolloError) => {
reject(apolloError)
firstResolve = undefined
firstReject = undefined
}
@ -258,15 +260,10 @@ export function useQueryImpl<
processNextResult(queryResult)
// Result errors
// This is set when `errorPolicy` is `all`
if (queryResult.errors?.length) {
const e = new Error(`GraphQL error: ${queryResult.errors.map(e => e.message).join(' | ')}`)
Object.assign(e, {
graphQLErrors: queryResult.errors,
networkError: null,
})
processError(e)
// ApolloQueryResult.error may be set at the same time as we get a result
// when `errorPolicy` is `all`
if (queryResult.error !== undefined) {
processError(queryResult.error)
} else {
if (firstResolve) {
firstResolve()
@ -282,22 +279,25 @@ export function useQueryImpl<
resultEvent.trigger(queryResult)
}
function onError (queryError: any) {
function onError (queryError: unknown) {
// any error should already be an ApolloError, but we make sure
const apolloError = toApolloError(queryError)
processNextResult((query.value as ObservableQuery<TResult, TVariables>).getCurrentResult())
processError(queryError)
processError(apolloError)
if (firstReject) {
firstReject(queryError)
firstReject(apolloError)
stop()
}
// The observable closes the sub if an error occurs
resubscribeToQuery()
}
function processError (queryError: any) {
error.value = queryError
function processError (apolloError: ApolloError) {
error.value = apolloError
loading.value = false
networkStatus.value = 8
errorEvent.trigger(queryError)
errorEvent.trigger(apolloError)
}
function resubscribeToQuery () {

View File

@ -16,6 +16,7 @@ import {
Observable,
ObservableSubscription,
TypedDocumentNode,
ApolloError,
} from '@apollo/client/core'
import { throttle, debounce } from 'throttle-debounce'
import { ReactiveFunction } from './util/ReactiveFunction'
@ -26,6 +27,7 @@ import { useEventHook } from './util/useEventHook'
import { trackSubscription } from './util/loadingTracking'
import type { CurrentInstance } from './util/types'
import { toApolloError } from './util/toApolloError'
export interface UseSubscriptionOptions <
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -45,7 +47,7 @@ type OptionsParameter<TResult, TVariables> = UseSubscriptionOptions<TResult, TVa
export interface UseSubscriptionReturn<TResult, TVariables> {
result: Ref<TResult | null | undefined>
loading: Ref<boolean>
error: Ref<Error | null>
error: Ref<ApolloError | null>
start: () => void
stop: () => void
restart: () => void
@ -56,7 +58,7 @@ export interface UseSubscriptionReturn<TResult, TVariables> {
onResult: (fn: (param: FetchResult<TResult, Record<string, any>, Record<string, any>>) => void) => {
off: () => void
}
onError: (fn: (param: Error) => void) => {
onError: (fn: (param: ApolloError) => void) => {
off: () => void
}
}
@ -119,8 +121,8 @@ export function useSubscription <
const result = ref<TResult | null | undefined>()
const resultEvent = useEventHook<FetchResult<TResult>>()
const error = ref<Error | null>(null)
const errorEvent = useEventHook<Error>()
const error = ref<ApolloError | null>(null)
const errorEvent = useEventHook<ApolloError>()
const loading = ref(false)
vm && trackSubscription(loading)
@ -157,10 +159,12 @@ export function useSubscription <
resultEvent.trigger(fetchResult)
}
function onError (fetchError: any) {
error.value = fetchError
function onError (fetchError: unknown) {
const apolloError = toApolloError(fetchError)
error.value = apolloError
loading.value = false
errorEvent.trigger(fetchError)
errorEvent.trigger(apolloError)
}
function stop () {

View File

@ -0,0 +1,16 @@
import { ApolloError, isApolloError } from '@apollo/client'
export function toApolloError (error: unknown): ApolloError {
if (!(error instanceof Error)) {
return new ApolloError({
networkError: Object.assign(new Error(), { originalError: error }),
errorMessage: String(error),
})
}
if (isApolloError(error)) {
return error
}
return new ApolloError({ networkError: error, errorMessage: error.message })
}