From 69badf2a663c32c1002c8daa90bea24b8404197e Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Mon, 26 May 2025 19:23:19 +0200 Subject: [PATCH] Capture FE GraphQL exceptions in sentry (#12286) We should capture graphQL exceptions thrown in the FE in Sentry. All the more so as we have just cleaned back-end errors in sentry, preventing 4xx errors from being wrongfully sent to sentry. Those 4xx errors should, except for `Unauthenticated` and `Forbidden` errors (for now - this list can evolve), trigger a sentry FE error, as we are not suppose to let users of the product interface trigger queries that will fail with 4xx errors (for instance a malformed input). We still miss an efficient way to group those errors together in sentry. It could be the message but the message may be different for each user if it contains user-specific data, and we don't always have control on the message. This can be done later as we iterate on improving sentry --- .../src/modules/apollo/services/apollo.factory.ts | 7 ++++++- .../relation/field-metadata-relation.service.ts | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts index d5966648b..3bad70273 100644 --- a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts +++ b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts @@ -17,13 +17,14 @@ import { AuthTokenPair } from '~/generated/graphql'; import { logDebug } from '~/utils/logDebug'; import { i18n } from '@lingui/core'; +import { captureException } from '@sentry/react'; import { GraphQLFormattedError } from 'graphql'; import { isDefined } from 'twenty-shared/utils'; import { cookieStorage } from '~/utils/cookie-storage'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; import { ApolloManager } from '../types/apolloManager.interface'; -import { loggerLink } from '../utils/loggerLink'; import { getTokenPair } from '../utils/getTokenPair'; +import { loggerLink } from '../utils/loggerLink'; const logger = loggerLink(() => 'Twenty'); @@ -133,6 +134,9 @@ export class ApolloFactory implements ApolloManager { }), ).flatMap(() => forward(operation)); } + case 'FORBIDDEN': { + return; + } default: if (isDebugMode === true) { logDebug( @@ -145,6 +149,7 @@ export class ApolloFactory implements ApolloManager { }, Path: ${graphQLError.path}`, ); } + captureException(graphQLError); } } } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/relation/field-metadata-relation.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/relation/field-metadata-relation.service.ts index 44a096f29..927439c54 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/relation/field-metadata-relation.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/relation/field-metadata-relation.service.ts @@ -88,9 +88,9 @@ export class FieldMetadataRelationService { const sourceObjectMetadata = objectMetadataMaps.byId[objectMetadataId]; const targetObjectMetadata = objectMetadataMaps.byId[relationTargetObjectMetadataId]; - const sourceFieldMetadata = sourceObjectMetadata.fieldsById[id]; + const sourceFieldMetadata = sourceObjectMetadata?.fieldsById[id]; const targetFieldMetadata = - targetObjectMetadata.fieldsById[relationTargetFieldMetadataId]; + targetObjectMetadata?.fieldsById[relationTargetFieldMetadataId]; if ( !sourceObjectMetadata ||