fix(fe2): default error skip logic & error policy (#2875)

This commit is contained in:
Kristaps Fabians Geikins
2024-09-05 11:33:38 +03:00
committed by GitHub
parent 8f60384ec0
commit 04d0ee074f
7 changed files with 53 additions and 74 deletions
@@ -50,7 +50,6 @@ import type { ProjectUpdateInput } from '~~/lib/common/generated/gql/graphql'
import { useUpdateProject } from '~~/lib/projects/composables/projectManagement'
import { graphql } from '~~/lib/common/generated/gql'
import { useTeamInternals } from '~/lib/projects/composables/team'
import { skipLoggingErrorsIfOneFieldError } from '~/lib/common/helpers/graphql'
const projectPageSettingsGeneralQuery = graphql(`
query ProjectPageSettingsGeneral($projectId: String!) {
@@ -72,20 +71,9 @@ const updateProject = useUpdateProject()
const projectId = computed(() => route.params.id as string)
const { result: pageResult } = useQuery(
projectPageSettingsGeneralQuery,
() => ({
projectId: projectId.value
}),
() => ({
// Custom error policy so that a failing invitedTeam resolver (due to access rights)
// doesn't kill the entire query
errorPolicy: 'all',
context: {
skipLoggingErrors: skipLoggingErrorsIfOneFieldError('invitedTeam')
}
})
)
const { result: pageResult } = useQuery(projectPageSettingsGeneralQuery, () => ({
projectId: projectId.value
}))
const project = computed(() => pageResult.value?.project)
@@ -65,7 +65,6 @@
import { graphql } from '~/lib/common/generated/gql'
import { useQuery } from '@vue/apollo-composable'
import { settingsWorkspaceBillingQuery } from '~/lib/settings/graphql/queries'
import { skipLoggingErrorsIfOneFieldError } from '~/lib/common/helpers/graphql'
graphql(`
fragment SettingsWorkspacesBilling_Workspace on Workspace {
@@ -87,18 +86,9 @@ const props = defineProps<{
workspaceId: string
}>()
const { result } = useQuery(
settingsWorkspaceBillingQuery,
() => ({
workspaceId: props.workspaceId
}),
() => ({
errorPolicy: 'all',
context: {
skipLoggingErrors: skipLoggingErrorsIfOneFieldError('billing')
}
})
)
const { result } = useQuery(settingsWorkspaceBillingQuery, () => ({
workspaceId: props.workspaceId
}))
const billing = computed(() => result.value?.workspace.billing)
const versionCount = computed(() => billing.value?.versionsCount)
@@ -33,7 +33,6 @@
import { Roles } from '@speckle/shared'
import { useQuery } from '@vue/apollo-composable'
import { graphql } from '~/lib/common/generated/gql'
import { skipLoggingErrorsIfOneFieldError } from '~/lib/common/helpers/graphql'
import { settingsWorkspacesMembersQuery } from '~/lib/settings/graphql/queries'
import type { LayoutPageTabItem } from '~~/lib/layout/helpers/components'
@@ -48,18 +47,9 @@ const props = defineProps<{
workspaceId: string
}>()
const { result } = useQuery(
settingsWorkspacesMembersQuery,
() => ({
workspaceId: props.workspaceId
}),
() => ({
// Custom error policy so that a failing invitedTeam resolver (due to access rights)
// doesn't kill the entire query
errorPolicy: 'all',
context: skipLoggingErrorsIfOneFieldError(['domains', 'invitedTeam'])
})
)
const { result } = useQuery(settingsWorkspacesMembersQuery, () => ({
workspaceId: props.workspaceId
}))
const isAdmin = computed(() => result.value?.workspace?.role === Roles.Workspace.Admin)
const tabItems = computed<LayoutPageTabItem[]>(() => [
@@ -70,7 +70,6 @@ import type {
WorkspaceProjectList_ProjectCollectionFragment,
WorkspaceProjectsQueryQueryVariables
} from '~~/lib/common/generated/gql/graphql'
import { skipLoggingErrorsIfOneFieldError } from '~/lib/common/helpers/graphql'
import { workspaceRoute } from '~/lib/common/helpers/route'
import { Roles } from '@speckle/shared'
@@ -103,6 +102,9 @@ const {
})
const token = computed(() => route.query.token as Optional<string>)
const pageFetchPolicy = usePageQueryStandardFetchPolicy()
const { result: initialQueryResult } = useQuery(
workspacePageQuery,
() => ({
@@ -113,16 +115,7 @@ const { result: initialQueryResult } = useQuery(
token: token.value || null
}),
() => ({
// Custom error policy so that a failing invitedTeam resolver (due to access rights)
// doesn't kill the entire query
errorPolicy: 'all',
context: {
skipLoggingErrors: skipLoggingErrorsIfOneFieldError([
'billing',
'domains',
'invitedTeam'
])
}
fetchPolicy: pageFetchPolicy.value
})
)
+39 -7
View File
@@ -18,10 +18,10 @@ import {
incomingOverwritesExistingMergeFunction,
mergeAsObjectsFunction
} from '~~/lib/core/helpers/apolloSetup'
import { onError } from '@apollo/client/link/error'
import { onError, type ErrorResponse } from '@apollo/client/link/error'
import { useAppErrorState } from '~~/lib/core/composables/error'
import { isInvalidAuth } from '~~/lib/common/helpers/graphql'
import { isArray, isBoolean, omit } from 'lodash-es'
import { intersection, isArray, isBoolean, omit } from 'lodash-es'
import { useRequestId } from '~/lib/core/composables/server'
const appName = 'frontend-2'
@@ -332,6 +332,21 @@ function createWsClient(params: {
)
}
const coreShouldSkipLoggingErrors = (err: ErrorResponse): boolean => {
// These fields have special auth requirements and will often throw errors that we don't want to log
const specialAuthFields = ['invitedTeam', 'billing', 'domains']
const specialAuthFieldErrorCodes = ['FORBIDDEN', 'UNAUTHORIZED_ACCESS_ERROR']
return !!(
err.graphQLErrors &&
err.graphQLErrors.every(
(e) =>
intersection(e.path || [], specialAuthFields).length > 0 &&
specialAuthFieldErrorCodes.includes(e.extensions?.code as string)
)
)
}
function createLink(params: {
httpEndpoint: string
wsClient?: SubscriptionClient
@@ -349,10 +364,14 @@ function createLink(params: {
'need a token to subscribe'
)
const skipLoggingErrors = res.operation.getContext().skipLoggingErrors
const shouldSkip = isBoolean(skipLoggingErrors)
? skipLoggingErrors
: skipLoggingErrors?.(res)
let shouldSkip = coreShouldSkipLoggingErrors(res)
const skipLoggingErrorsResolver = res.operation.getContext().skipLoggingErrors
if (skipLoggingErrorsResolver) {
shouldSkip = isBoolean(skipLoggingErrorsResolver)
? skipLoggingErrorsResolver
: skipLoggingErrorsResolver?.(res)
}
if (!isSubTokenMissingError && !shouldSkip) {
const gqlErrors: Array<GraphQLError> = isArray(res.graphQLErrors)
? res.graphQLErrors
@@ -484,7 +503,20 @@ const defaultConfigResolver: ApolloConfigResolver = () => {
cache: markRaw(createCache()),
link,
name: appName,
version: speckleServerVersion
version: speckleServerVersion,
defaultOptions: {
// We want to retain all data even if there are errors, cause there's often fields with special auth requirements that we don't want
// to be able to kill the entire query. Besides - in most cases partial data is better than no data at all.
query: {
errorPolicy: 'all'
},
mutate: {
errorPolicy: 'all'
},
watchQuery: {
errorPolicy: 'all'
}
}
}
}
@@ -121,12 +121,6 @@ export function useCreateProject() {
.mutate({
mutation: createProjectMutation,
variables: { input },
errorPolicy: 'all',
context: {
skipLoggingErrors: (err) =>
err.graphQLErrors?.length === 1 &&
err.graphQLErrors.some((e) => e.path?.includes('invitedTeam'))
},
update: (cache, { data }) => {
const newProject = data?.projectMutations.create
@@ -102,15 +102,7 @@ const { result: projectPageResult } = useQuery(
...(token.value?.length ? { token: token.value } : {})
}),
() => ({
fetchPolicy: pageFetchPolicy.value,
// Custom error policy so that a failing invitedTeam resolver (due to access rights)
// doesn't kill the entire query
errorPolicy: 'all'
// context: {
// skipLoggingErrors: (err) =>
// err.graphQLErrors?.length === 1 &&
// err.graphQLErrors.some((e) => !!e.path?.includes('invitedTeam'))
// }
fetchPolicy: pageFetchPolicy.value
})
)