From 0d00e3d62db8fcc9657122e6df8fa0bcaaee8c90 Mon Sep 17 00:00:00 2001 From: Weiko Date: Thu, 21 Dec 2023 16:07:25 +0100 Subject: [PATCH] send pg graphql exception to sentry + fix missing nullable for relations (#3101) * Send pg_graphql errors to sentry * Send pg_graphql errors to sentry * fix * fix * fix * fix relation nullable --- .../utils/global-exception-handler.util.ts | 2 +- .../message-queue/drivers/sync.driver.ts | 40 ++- .../relation-metadata.service.ts | 4 + .../workspace-query-runner.service.ts | 239 ++++++++++++------ 4 files changed, 188 insertions(+), 97 deletions(-) diff --git a/packages/twenty-server/src/filters/utils/global-exception-handler.util.ts b/packages/twenty-server/src/filters/utils/global-exception-handler.util.ts index d7ec0f367..736f6c70d 100644 --- a/packages/twenty-server/src/filters/utils/global-exception-handler.util.ts +++ b/packages/twenty-server/src/filters/utils/global-exception-handler.util.ts @@ -49,7 +49,7 @@ export const httpExceptionHandler = ( ); } else { error = new BaseGraphQLError( - exception.message, + 'Internal Server Error', exception.getStatus().toString(), ); } diff --git a/packages/twenty-server/src/integrations/message-queue/drivers/sync.driver.ts b/packages/twenty-server/src/integrations/message-queue/drivers/sync.driver.ts index c81e47290..d3ddffc6c 100644 --- a/packages/twenty-server/src/integrations/message-queue/drivers/sync.driver.ts +++ b/packages/twenty-server/src/integrations/message-queue/drivers/sync.driver.ts @@ -1,21 +1,37 @@ -import { ModuleRef } from "@nestjs/core"; -import { QueueJobOptions } from "src/integrations/message-queue/drivers/interfaces/job-options.interface"; -import { MessageQueueDriver } from "src/integrations/message-queue/drivers/interfaces/message-queue-driver.interface"; -import { MessageQueueJob, MessageQueueJobData } from "src/integrations/message-queue/interfaces/message-queue-job.interface"; -import { MessageQueue } from "src/integrations/message-queue/message-queue.constants"; -import { MessageQueueModule } from "src/integrations/message-queue/message-queue.module"; -import { getJobClassName } from "src/integrations/message-queue/utils/get-job-class-name.util"; -import { QueueWorkerModule } from "src/queue-worker.module"; +import { ModuleRef } from '@nestjs/core'; + +import { QueueJobOptions } from 'src/integrations/message-queue/drivers/interfaces/job-options.interface'; +import { MessageQueueDriver } from 'src/integrations/message-queue/drivers/interfaces/message-queue-driver.interface'; +import { + MessageQueueJob, + MessageQueueJobData, +} from 'src/integrations/message-queue/interfaces/message-queue-job.interface'; + +import { MessageQueue } from 'src/integrations/message-queue/message-queue.constants'; +import { getJobClassName } from 'src/integrations/message-queue/utils/get-job-class-name.util'; export class SyncDriver implements MessageQueueDriver { constructor(private readonly jobsModuleRef: ModuleRef) {} - async add(_queueName: MessageQueue, jobName: string, data: T, _options?: QueueJobOptions | undefined): Promise { + + async add( + _queueName: MessageQueue, + jobName: string, + data: T, + _options?: QueueJobOptions | undefined, + ): Promise { const jobClassName = getJobClassName(jobName); - const job: MessageQueueJob = this.jobsModuleRef.get(jobClassName, { strict: true }); + const job: MessageQueueJob = this.jobsModuleRef.get( + jobClassName, + { strict: true }, + ); return await job.handle(data); } - work(queueName: MessageQueue, handler: ({ data, id }: { data: T; id: string; }) => void | Promise) { + + work( + queueName: MessageQueue, + handler: ({ data, id }: { data: T; id: string }) => void | Promise, + ) { return; } -} \ No newline at end of file +} diff --git a/packages/twenty-server/src/metadata/relation-metadata/relation-metadata.service.ts b/packages/twenty-server/src/metadata/relation-metadata/relation-metadata.service.ts index dc6b98a9a..0c42a8d9c 100644 --- a/packages/twenty-server/src/metadata/relation-metadata/relation-metadata.service.ts +++ b/packages/twenty-server/src/metadata/relation-metadata/relation-metadata.service.ts @@ -133,6 +133,7 @@ export class RelationMetadataService extends TypeOrmQueryService, options: WorkspaceQueryRunnerOptions, ): Promise | undefined> { - const { workspaceId, targetTableName } = options; - const query = await this.workspaceQueryBuilderFactory.findMany( - args, - options, - ); - const result = await this.execute(query, workspaceId); + try { + const { workspaceId, targetTableName } = options; + const query = await this.workspaceQueryBuilderFactory.findMany( + args, + options, + ); + const result = await this.execute(query, workspaceId); - return this.parseResult>(result, targetTableName, ''); + return this.parseResult>(result, targetTableName, ''); + } catch (exception) { + const error = globalExceptionHandler( + exception, + this.exceptionHandlerService, + ); + + return Promise.reject(error); + } } async findOne< @@ -61,40 +78,58 @@ export class WorkspaceQueryRunnerService { args: FindOneResolverArgs, options: WorkspaceQueryRunnerOptions, ): Promise { - if (!args.filter || Object.keys(args.filter).length === 0) { - throw new BadRequestException('Missing filter argument'); - } - const { workspaceId, targetTableName } = options; - const query = await this.workspaceQueryBuilderFactory.findOne( - args, - options, - ); - const result = await this.execute(query, workspaceId); - const parsedResult = this.parseResult>( - result, - targetTableName, - '', - ); + try { + if (!args.filter || Object.keys(args.filter).length === 0) { + throw new BadRequestException('Missing filter argument'); + } + const { workspaceId, targetTableName } = options; + const query = await this.workspaceQueryBuilderFactory.findOne( + args, + options, + ); + const result = await this.execute(query, workspaceId); + const parsedResult = this.parseResult>( + result, + targetTableName, + '', + ); - return parsedResult?.edges?.[0]?.node; + return parsedResult?.edges?.[0]?.node; + } catch (exception) { + const error = globalExceptionHandler( + exception, + this.exceptionHandlerService, + ); + + return Promise.reject(error); + } } async createMany( args: CreateManyResolverArgs, options: WorkspaceQueryRunnerOptions, ): Promise { - const { workspaceId, targetTableName } = options; - const query = await this.workspaceQueryBuilderFactory.createMany( - args, - options, - ); - const result = await this.execute(query, workspaceId); + try { + const { workspaceId, targetTableName } = options; + const query = await this.workspaceQueryBuilderFactory.createMany( + args, + options, + ); + const result = await this.execute(query, workspaceId); - return this.parseResult>( - result, - targetTableName, - 'insertInto', - )?.records; + return this.parseResult>( + result, + targetTableName, + 'insertInto', + )?.records; + } catch (exception) { + const error = globalExceptionHandler( + exception, + this.exceptionHandlerService, + ); + + return Promise.reject(error); + } } async createOne( @@ -110,54 +145,81 @@ export class WorkspaceQueryRunnerService { args: UpdateOneResolverArgs, options: WorkspaceQueryRunnerOptions, ): Promise { - const { workspaceId, targetTableName } = options; - const query = await this.workspaceQueryBuilderFactory.updateOne( - args, - options, - ); - const result = await this.execute(query, workspaceId); + try { + const { workspaceId, targetTableName } = options; + const query = await this.workspaceQueryBuilderFactory.updateOne( + args, + options, + ); + const result = await this.execute(query, workspaceId); - return this.parseResult>( - result, - targetTableName, - 'update', - )?.records?.[0]; + return this.parseResult>( + result, + targetTableName, + 'update', + )?.records?.[0]; + } catch (exception) { + const error = globalExceptionHandler( + exception, + this.exceptionHandlerService, + ); + + return Promise.reject(error); + } } async deleteOne( args: DeleteOneResolverArgs, options: WorkspaceQueryRunnerOptions, ): Promise { - const { workspaceId, targetTableName } = options; - const query = await this.workspaceQueryBuilderFactory.deleteOne( - args, - options, - ); - const result = await this.execute(query, workspaceId); + try { + const { workspaceId, targetTableName } = options; + const query = await this.workspaceQueryBuilderFactory.deleteOne( + args, + options, + ); + const result = await this.execute(query, workspaceId); - return this.parseResult>( - result, - targetTableName, - 'deleteFrom', - )?.records?.[0]; + return this.parseResult>( + result, + targetTableName, + 'deleteFrom', + )?.records?.[0]; + } catch (exception) { + const error = globalExceptionHandler( + exception, + this.exceptionHandlerService, + ); + + return Promise.reject(error); + } } async updateMany( args: UpdateManyResolverArgs, options: WorkspaceQueryRunnerOptions, ): Promise { - const { workspaceId, targetTableName } = options; - const query = await this.workspaceQueryBuilderFactory.updateMany( - args, - options, - ); - const result = await this.execute(query, workspaceId); + try { + const { workspaceId, targetTableName } = options; + const query = await this.workspaceQueryBuilderFactory.updateMany( + args, + options, + ); + const result = await this.execute(query, workspaceId); - return this.parseResult>( - result, - targetTableName, - 'update', - )?.records; + return this.parseResult>( + result, + targetTableName, + 'update', + )?.records; + } catch (exception) { + const error = globalExceptionHandler( + exception, + this.exceptionHandlerService, + ); + + return Promise.reject(error); + } } async deleteMany< @@ -167,18 +229,27 @@ export class WorkspaceQueryRunnerService { args: DeleteManyResolverArgs, options: WorkspaceQueryRunnerOptions, ): Promise { - const { workspaceId, targetTableName } = options; - const query = await this.workspaceQueryBuilderFactory.deleteMany( - args, - options, - ); - const result = await this.execute(query, workspaceId); + try { + const { workspaceId, targetTableName } = options; + const query = await this.workspaceQueryBuilderFactory.deleteMany( + args, + options, + ); + const result = await this.execute(query, workspaceId); - return this.parseResult>( - result, - targetTableName, - 'deleteFrom', - )?.records; + return this.parseResult>( + result, + targetTableName, + 'deleteFrom', + )?.records; + } catch (exception) { + const error = globalExceptionHandler( + exception, + this.exceptionHandlerService, + ); + + return Promise.reject(error); + } } async execute( @@ -214,12 +285,12 @@ export class WorkspaceQueryRunnerService { const result = graphqlResult?.[0]?.resolve?.data?.[entityKey]; const errors = graphqlResult?.[0]?.resolve?.errors; - if (Array.isArray(errors) && errors.length > 0) { - console.error(`GraphQL errors on ${command}${targetTableName}`, errors); - } - if (!result) { - throw new BadRequestException('Malformed result from GraphQL query'); + throw new InternalServerErrorException( + `GraphQL errors on ${command}${targetTableName}: ${JSON.stringify( + errors, + )}`, + ); } return parseResult(result);