diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx index 627f0654d..e9bc21fde 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx @@ -19,7 +19,7 @@ const StyledTbody = styled.tbody` tr:not(:last-child) td:nth-of-type(3) { // Last row is aggregate footer position: sticky; - left: 43px; + left: 39px; z-index: ${TABLE_Z_INDEX.cell.sticky}; transition: transform 0.3s ease; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx index e9b0e74ee..9e8c024b1 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeader.tsx @@ -40,7 +40,7 @@ const StyledTableHead = styled.thead` th:nth-of-type(3) { position: sticky; - left: 43px; + left: 39px; z-index: ${TABLE_Z_INDEX.header.default}; transition: 0.3s ease; diff --git a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.resolver.ts b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.resolver.ts index 7d935019e..fa5c0aef0 100644 --- a/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/admin-panel/admin-panel.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import GraphQLJSON from 'graphql-type-json'; @@ -18,6 +18,8 @@ import { QueueMetricsTimeRange } from 'src/engine/core-modules/admin-panel/enums import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter'; import { FeatureFlagException } from 'src/engine/core-modules/feature-flag/feature-flag.exception'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { HealthIndicatorId } from 'src/engine/core-modules/health/enums/health-indicator-id.enum'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; @@ -32,9 +34,11 @@ import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { AdminPanelHealthServiceData } from './dtos/admin-panel-health-service-data.dto'; import { QueueMetricsData } from './dtos/queue-metrics-data.dto'; +@UsePipes(ResolverValidationPipe) @Resolver() @UseFilters( AuthGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, ConfigVariableGraphqlApiExceptionFilter, ) export class AdminPanelResolver { diff --git a/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts b/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts index feb2298d7..af738446a 100644 --- a/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/approved-access-domain/approved-access-domain.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { ApprovedAccessDomainExceptionFilter } from 'src/engine/core-modules/approved-access-domain/approved-access-domain-exception-filter'; @@ -7,16 +7,22 @@ import { CreateApprovedAccessDomainInput } from 'src/engine/core-modules/approve import { DeleteApprovedAccessDomainInput } from 'src/engine/core-modules/approved-access-domain/dtos/delete-approved-access-domain.input'; import { ValidateApprovedAccessDomainInput } from 'src/engine/core-modules/approved-access-domain/dtos/validate-approved-access-domain.input'; import { ApprovedAccessDomainService } from 'src/engine/core-modules/approved-access-domain/services/approved-access-domain.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @UseGuards(WorkspaceAuthGuard) -@UseFilters(ApprovedAccessDomainExceptionFilter) +@UsePipes(ResolverValidationPipe) +@UseFilters( + ApprovedAccessDomainExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) @Resolver() export class ApprovedAccessDomainResolver { constructor( diff --git a/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts b/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts index 25d83da0c..3b7c6b7ae 100644 --- a/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/audit/audit.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { AuditExceptionFilter } from 'src/engine/core-modules/audit/audit-exception-filter'; @@ -7,6 +7,8 @@ import { AuditExceptionCode, } from 'src/engine/core-modules/audit/audit.exception'; import { CreateObjectEventInput } from 'src/engine/core-modules/audit/dtos/create-object-event.input'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; @@ -23,7 +25,8 @@ import { Analytics } from './entities/analytics.entity'; import { AuditService } from './services/audit.service'; @Resolver(() => Analytics) -@UseFilters(AuditExceptionFilter) +@UsePipes(ResolverValidationPipe) +@UseFilters(AuditExceptionFilter, PreventNestToAutoLogGraphqlErrorsFilter) export class AuditResolver { constructor(private readonly auditService: AuditService) {} diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts index 81d767c03..f534c8d5c 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Context, Mutation, Query, Resolver } from '@nestjs/graphql'; import { InjectRepository } from '@nestjs/typeorm'; @@ -44,6 +44,8 @@ import { CaptchaGuard } from 'src/engine/core-modules/captcha/captcha.guard'; import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; import { EmailVerificationExceptionFilter } from 'src/engine/core-modules/email-verification/email-verification-exception-filter.util'; import { EmailVerificationService } from 'src/engine/core-modules/email-verification/services/email-verification.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type'; import { SSOService } from 'src/engine/core-modules/sso/services/sso.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; @@ -73,11 +75,13 @@ import { WorkspaceInviteHashValid } from './dto/workspace-invite-hash-valid.enti import { WorkspaceInviteHashValidInput } from './dto/workspace-invite-hash.input'; import { AuthService } from './services/auth.service'; +@UsePipes(ResolverValidationPipe) @Resolver() @UseFilters( AuthGraphqlApiExceptionFilter, PermissionsGraphqlApiExceptionFilter, EmailVerificationExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, ) export class AuthResolver { constructor( diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts index bb3635051..4a6adaca0 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.resolver.ts @@ -1,6 +1,6 @@ /* @license Enterprise */ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { isDefined } from 'twenty-shared/utils'; @@ -21,6 +21,8 @@ import { BillingUsageService } from 'src/engine/core-modules/billing/services/bi import { BillingService } from 'src/engine/core-modules/billing/services/billing.service'; import { BillingPortalCheckoutSessionParameters } from 'src/engine/core-modules/billing/types/billing-portal-checkout-session-parameters.type'; import { formatBillingDatabaseProductToGraphqlDTO } from 'src/engine/core-modules/billing/utils/format-database-product-to-graphql-dto.util'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthApiKey } from 'src/engine/decorators/auth/auth-api-key.decorator'; @@ -40,7 +42,11 @@ import { PermissionsService } from 'src/engine/metadata-modules/permissions/perm import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; @Resolver() -@UseFilters(PermissionsGraphqlApiExceptionFilter) +@UsePipes(ResolverValidationPipe) +@UseFilters( + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) export class BillingResolver { constructor( private readonly billingSubscriptionService: BillingSubscriptionService, diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.module.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.module.ts index 63f7d0d8b..a3c408214 100644 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.module.ts +++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.module.ts @@ -3,13 +3,12 @@ import { Module } from '@nestjs/common'; import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module'; import { ClientConfigController } from './client-config.controller'; -import { ClientConfigResolver } from './client-config.resolver'; import { ClientConfigService } from './services/client-config.service'; @Module({ imports: [DomainManagerModule], controllers: [ClientConfigController], - providers: [ClientConfigResolver, ClientConfigService], + providers: [ClientConfigService], }) export class ClientConfigModule {} diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.spec.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.spec.ts deleted file mode 100644 index ae1774cac..000000000 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; - -import { ClientConfigService } from 'src/engine/core-modules/client-config/services/client-config.service'; - -import { ClientConfigResolver } from './client-config.resolver'; - -describe('ClientConfigResolver', () => { - let resolver: ClientConfigResolver; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - ClientConfigResolver, - { - provide: ClientConfigService, - useValue: { - getClientConfig: jest.fn(), - }, - }, - ], - }).compile(); - - resolver = module.get(ClientConfigResolver); - }); - - it('should be defined', () => { - expect(resolver).toBeDefined(); - }); -}); diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts deleted file mode 100644 index c2fd86b08..000000000 --- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { UseGuards } from '@nestjs/common'; -import { Query, Resolver } from '@nestjs/graphql'; - -import { ClientConfigService } from 'src/engine/core-modules/client-config/services/client-config.service'; -import { PublicEndpointGuard } from 'src/engine/guards/public-endpoint.guard'; - -import { ClientConfig } from './client-config.entity'; - -@Resolver() -export class ClientConfigResolver { - constructor(private clientConfigService: ClientConfigService) {} - - @Query(() => ClientConfig) - @UseGuards(PublicEndpointGuard) - async clientConfig(): Promise { - return this.clientConfigService.getClientConfig(); - } -} diff --git a/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.resolver.ts b/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.resolver.ts index e9b0ae0bc..1394170ff 100644 --- a/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Context, Mutation, Resolver } from '@nestjs/graphql'; import { SOURCE_LOCALE } from 'twenty-shared/translations'; @@ -8,11 +8,17 @@ import { ResendEmailVerificationTokenInput } from 'src/engine/core-modules/email import { ResendEmailVerificationTokenOutput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.output'; import { EmailVerificationExceptionFilter } from 'src/engine/core-modules/email-verification/email-verification-exception-filter.util'; import { EmailVerificationService } from 'src/engine/core-modules/email-verification/services/email-verification.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type'; import { PublicEndpointGuard } from 'src/engine/guards/public-endpoint.guard'; @Resolver() -@UseFilters(EmailVerificationExceptionFilter) +@UsePipes(ResolverValidationPipe) +@UseFilters( + EmailVerificationExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) export class EmailVerificationResolver { constructor( private readonly emailVerificationService: EmailVerificationService, diff --git a/packages/twenty-server/src/engine/core-modules/file/file-upload/resolvers/file-upload.resolver.ts b/packages/twenty-server/src/engine/core-modules/file/file-upload/resolvers/file-upload.resolver.ts index 0c35e78d4..67e2f7b11 100644 --- a/packages/twenty-server/src/engine/core-modules/file/file-upload/resolvers/file-upload.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/file/file-upload/resolvers/file-upload.resolver.ts @@ -1,18 +1,22 @@ -import { UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { FileUpload, GraphQLUpload } from 'graphql-upload'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; +import { SignedFileDTO } from 'src/engine/core-modules/file/file-upload/dtos/signed-file.dto'; import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { streamToBuffer } from 'src/utils/stream-to-buffer'; -import { SignedFileDTO } from 'src/engine/core-modules/file/file-upload/dtos/signed-file.dto'; @UseGuards(WorkspaceAuthGuard) +@UsePipes(ResolverValidationPipe) +@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) @Resolver() export class FileUploadResolver { constructor(private readonly fileUploadService: FileUploadService) {} diff --git a/packages/twenty-server/src/engine/core-modules/graphql/engine-graphql.module.ts b/packages/twenty-server/src/engine/core-modules/graphql/engine-graphql.module.ts index 0df672199..522d7f0a8 100644 --- a/packages/twenty-server/src/engine/core-modules/graphql/engine-graphql.module.ts +++ b/packages/twenty-server/src/engine/core-modules/graphql/engine-graphql.module.ts @@ -1,11 +1,12 @@ import { Module } from '@nestjs/common'; -import { useGraphQLErrorHandlerHook } from 'src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook'; import { ExceptionHandlerModule } from 'src/engine/core-modules/exception-handler/exception-handler.module'; +import { useGraphQLErrorHandlerHook } from 'src/engine/core-modules/graphql/hooks/use-graphql-error-handler.hook'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; @Module({ imports: [ExceptionHandlerModule], - exports: [useGraphQLErrorHandlerHook], - providers: [], + exports: [useGraphQLErrorHandlerHook, ResolverValidationPipe], + providers: [ResolverValidationPipe], }) export class EngineGraphQLModule {} diff --git a/packages/twenty-server/src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter.ts b/packages/twenty-server/src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter.ts new file mode 100644 index 000000000..d01da137e --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter.ts @@ -0,0 +1,17 @@ +import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common'; + +import { GraphQLError } from 'graphql'; + +/** + * In NestJS, if an exception is not handled, it will shown in the logs + * This filter is used to prevent NestJS from auto-logging GraphQL errors + * and leave it to the GraphQL layer to handle the error. + */ +@Catch(GraphQLError) +export class PreventNestToAutoLogGraphqlErrorsFilter + implements ExceptionFilter +{ + catch(exception: GraphQLError, _host: ArgumentsHost) { + return exception; + } +} diff --git a/packages/twenty-server/src/engine/core-modules/graphql/pipes/resolver-validation.pipe.ts b/packages/twenty-server/src/engine/core-modules/graphql/pipes/resolver-validation.pipe.ts new file mode 100644 index 000000000..295fc3867 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/graphql/pipes/resolver-validation.pipe.ts @@ -0,0 +1,58 @@ +import { + ArgumentMetadata, + Injectable, + PipeTransform, + Type, +} from '@nestjs/common'; + +import { plainToInstance } from 'class-transformer'; +import { ValidationError, validate } from 'class-validator'; + +import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; + +@Injectable() +export class ResolverValidationPipe implements PipeTransform { + async transform(value: unknown, metadata: ArgumentMetadata) { + const { metatype } = metadata; + + if (!metatype || !this.toValidate(metatype)) { + return value; + } + + const object = plainToInstance(metatype, value); + + try { + const errors = await validate(object); + + if (errors.length > 0) { + const errorMessage = this.formatErrorMessage(errors); + + throw new UserInputError(errorMessage); + } + } catch (error) { + // If the element is not a class, we can't validate it + return value; + } + + return value; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private toValidate(metatype: Type): boolean { + const types: unknown[] = [String, Boolean, Number, Array, Object]; + + return !types.includes(metatype); + } + + private formatErrorMessage(errors: ValidationError[]): string { + const messages = errors.flatMap((error) => { + if (error.constraints) { + return Object.values(error.constraints); + } + + return []; + }); + + return messages.join(', '); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/lab/lab.resolver.ts b/packages/twenty-server/src/engine/core-modules/lab/lab.resolver.ts index 6ffcbab2d..e1ac254ca 100644 --- a/packages/twenty-server/src/engine/core-modules/lab/lab.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/lab/lab.resolver.ts @@ -1,10 +1,12 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter'; import { FeatureFlagDTO } from 'src/engine/core-modules/feature-flag/dtos/feature-flag-dto'; import { FeatureFlagException } from 'src/engine/core-modules/feature-flag/feature-flag.exception'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { UpdateLabPublicFeatureFlagInput } from 'src/engine/core-modules/lab/dtos/update-lab-public-feature-flag.input'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -15,7 +17,12 @@ import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/c import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; @Resolver() -@UseFilters(AuthGraphqlApiExceptionFilter, PermissionsGraphqlApiExceptionFilter) +@UsePipes(ResolverValidationPipe) +@UseFilters( + AuthGraphqlApiExceptionFilter, + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) @UseGuards(SettingsPermissionsGuard(SettingPermissionType.WORKSPACE)) export class LabResolver { constructor(private featureFlagService: FeatureFlagService) {} diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.resolver.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.resolver.ts index 46c4856d5..3fa32f41e 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.resolver.ts @@ -1,6 +1,8 @@ -import { UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Mutation, Resolver } from '@nestjs/graphql'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { OnboardingStepSuccess } from 'src/engine/core-modules/onboarding/dtos/onboarding-step-success.dto'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; import { User } from 'src/engine/core-modules/user/user.entity'; @@ -11,6 +13,8 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @UseGuards(WorkspaceAuthGuard, UserAuthGuard) +@UsePipes(ResolverValidationPipe) +@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) @Resolver() export class OnboardingResolver { constructor(private readonly onboardingService: OnboardingService) {} diff --git a/packages/twenty-server/src/engine/core-modules/search/search.resolver.ts b/packages/twenty-server/src/engine/core-modules/search/search.resolver.ts index 0e49fbade..035ef05dc 100644 --- a/packages/twenty-server/src/engine/core-modules/search/search.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/search/search.resolver.ts @@ -1,6 +1,8 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Query, Resolver } from '@nestjs/graphql'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { SearchArgs } from 'src/engine/core-modules/search/dtos/search-args'; import { SearchResultConnectionDTO } from 'src/engine/core-modules/search/dtos/search-result-connection.dto'; import { SearchApiExceptionFilter } from 'src/engine/core-modules/search/filters/search-api-exception.filter'; @@ -10,7 +12,8 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @Resolver() -@UseFilters(SearchApiExceptionFilter) +@UseFilters(SearchApiExceptionFilter, PreventNestToAutoLogGraphqlErrorsFilter) +@UsePipes(ResolverValidationPipe) export class SearchResolver { constructor(private readonly searchService: SearchService) {} diff --git a/packages/twenty-server/src/engine/core-modules/sso/sso.resolver.ts b/packages/twenty-server/src/engine/core-modules/sso/sso.resolver.ts index c450aa43e..08760a7c9 100644 --- a/packages/twenty-server/src/engine/core-modules/sso/sso.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/sso/sso.resolver.ts @@ -1,9 +1,11 @@ /* @license Enterprise */ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { EnterpriseFeaturesEnabledGuard } from 'src/engine/core-modules/auth/guards/enterprise-features-enabled.guard'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { DeleteSsoInput } from 'src/engine/core-modules/sso/dtos/delete-sso.input'; import { DeleteSsoOutput } from 'src/engine/core-modules/sso/dtos/delete-sso.output'; import { EditSsoInput } from 'src/engine/core-modules/sso/dtos/edit-sso.input'; @@ -24,7 +26,11 @@ import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/c import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; @Resolver() -@UseFilters(PermissionsGraphqlApiExceptionFilter) +@UseFilters( + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) +@UsePipes(ResolverValidationPipe) @UseGuards(SettingsPermissionsGuard(SettingPermissionType.SECURITY)) export class SSOResolver { constructor(private readonly sSOService: SSOService) {} diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.resolver.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.resolver.ts index 4abba0ee6..e119aba98 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.resolver.ts @@ -1,17 +1,21 @@ -import { UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Resolver } from '@nestjs/graphql'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; +import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; -import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service'; @UseGuards(WorkspaceAuthGuard) @Resolver(() => UserWorkspace) +@UsePipes(ResolverValidationPipe) +@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) export class UserWorkspaceResolver { constructor( @InjectRepository(Workspace, 'core') diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-builder.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-builder.resolver.ts index ac9766493..7dbe1d99c 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-builder.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-builder.resolver.ts @@ -1,8 +1,10 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import graphqlTypeJson from 'graphql-type-json'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { ComputeStepOutputSchemaInput } from 'src/engine/core-modules/workflow/dtos/compute-step-output-schema-input.dto'; import { WorkflowTriggerGraphqlApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -21,9 +23,11 @@ import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-bu UserAuthGuard, SettingsPermissionsGuard(SettingPermissionType.WORKFLOWS), ) +@UsePipes(ResolverValidationPipe) @UseFilters( WorkflowTriggerGraphqlApiExceptionFilter, PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, ) export class WorkflowBuilderResolver { constructor( diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts index 9b005cbf0..2cf2708e5 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts @@ -1,8 +1,10 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-step-input.dto'; import { DeleteWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/delete-workflow-version-step-input.dto'; import { SubmitFormStepInput } from 'src/engine/core-modules/workflow/dtos/submit-form-step-input.dto'; @@ -21,12 +23,16 @@ import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workf import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; @Resolver() +@UsePipes(ResolverValidationPipe) @UseGuards( WorkspaceAuthGuard, UserAuthGuard, SettingsPermissionsGuard(SettingPermissionType.WORKFLOWS), ) -@UseFilters(PermissionsGraphqlApiExceptionFilter) +@UseFilters( + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) export class WorkflowStepResolver { constructor( private readonly workflowVersionStepWorkspaceService: WorkflowVersionStepWorkspaceService, diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts index d8b69a44d..9825f6669 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver.ts @@ -1,22 +1,24 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { buildCreatedByFromFullNameMetadata } from 'src/engine/core-modules/actor/utils/build-created-by-from-full-name-metadata.util'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { User } from 'src/engine/core-modules/user/user.entity'; import { RunWorkflowVersionInput } from 'src/engine/core-modules/workflow/dtos/run-workflow-version-input.dto'; import { WorkflowRunDTO } from 'src/engine/core-modules/workflow/dtos/workflow-run.dto'; import { WorkflowTriggerGraphqlApiExceptionFilter } from 'src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; +import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard'; import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants'; import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { WorkflowTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/workspace-services/workflow-trigger.workspace-service'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; @Resolver() @UseGuards( @@ -24,9 +26,11 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat UserAuthGuard, SettingsPermissionsGuard(SettingPermissionType.WORKFLOWS), ) +@UsePipes(ResolverValidationPipe) @UseFilters( WorkflowTriggerGraphqlApiExceptionFilter, PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, ) export class WorkflowTriggerResolver { constructor( diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version.resolver.ts index c35d3fff9..e6e304d4d 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version.resolver.ts @@ -1,6 +1,8 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Resolver } from '@nestjs/graphql'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { CreateDraftFromWorkflowVersionInput } from 'src/engine/core-modules/workflow/dtos/create-draft-from-workflow-version-input'; import { WorkflowVersionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version.dto'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -13,12 +15,16 @@ import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-module import { WorkflowVersionWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.workspace-service'; @Resolver() +@UsePipes(ResolverValidationPipe) @UseGuards( WorkspaceAuthGuard, UserAuthGuard, SettingsPermissionsGuard(SettingPermissionType.WORKFLOWS), ) -@UseFilters(PermissionsGraphqlApiExceptionFilter) +@UseFilters( + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) export class WorkflowVersionResolver { constructor( private readonly workflowVersionWorkspaceService: WorkflowVersionWorkspaceService, diff --git a/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts index 3a4c3dfcd..5958767ea 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace-invitation/workspace-invitation.resolver.ts @@ -1,7 +1,9 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { User } from 'src/engine/core-modules/user/user.entity'; import { SendInvitationsOutput } from 'src/engine/core-modules/workspace-invitation/dtos/send-invitations.output'; import { WorkspaceInvitation } from 'src/engine/core-modules/workspace-invitation/dtos/workspace-invitation.dto'; @@ -14,8 +16,8 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants'; import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; -import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { SendInvitationsInput } from './dtos/send-invitations.input'; @@ -23,7 +25,11 @@ import { SendInvitationsInput } from './dtos/send-invitations.input'; WorkspaceAuthGuard, SettingsPermissionsGuard(SettingPermissionType.WORKSPACE_MEMBERS), ) -@UseFilters(PermissionsGraphqlApiExceptionFilter) +@UsePipes(ResolverValidationPipe) +@UseFilters( + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) @Resolver() export class WorkspaceInvitationResolver { constructor( diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts index 9150dce0d..df6c6cadc 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts @@ -2,6 +2,7 @@ import { ExecutionContext, UseFilters, UseGuards, + UsePipes, createParamDecorator, } from '@nestjs/common'; import { @@ -32,6 +33,8 @@ import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/service import { SignedFileDTO } from 'src/engine/core-modules/file/file-upload/dtos/signed-file.dto'; import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { User } from 'src/engine/core-modules/user/user.entity'; @@ -57,7 +60,6 @@ import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/c import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto'; import { RoleService } from 'src/engine/metadata-modules/role/role.service'; -import { GraphqlValidationExceptionFilter } from 'src/filters/graphql-validation-exception.filter'; import { getRequest } from 'src/utils/extract-request'; import { streamToBuffer } from 'src/utils/stream-to-buffer'; @@ -74,8 +76,9 @@ const OriginHeader = createParamDecorator( ); @Resolver(() => Workspace) +@UsePipes(ResolverValidationPipe) @UseFilters( - GraphqlValidationExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, PermissionsGraphqlApiExceptionFilter, ) export class WorkspaceResolver { diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto.ts index 9679454df..ccb7ce73e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/field-metadata.dto.ts @@ -21,7 +21,6 @@ import { IsOptional, IsString, IsUUID, - Validate, } from 'class-validator'; import { GraphQLJSON } from 'graphql-type-json'; import { FieldMetadataType } from 'twenty-shared/types'; @@ -34,8 +33,6 @@ import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/ import { IsValidMetadataName } from 'src/engine/decorators/metadata/is-valid-metadata-name.decorator'; import { FieldStandardOverridesDTO } from 'src/engine/metadata-modules/field-metadata/dtos/field-standard-overrides.dto'; import { FieldMetadataDefaultOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input'; -import { IsFieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/validators/is-field-metadata-default-value.validator'; -import { IsFieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata/validators/is-field-metadata-options.validator'; import { ObjectMetadataDTO } from 'src/engine/metadata-modules/object-metadata/dtos/object-metadata.dto'; import { transformEnumValue } from 'src/engine/utils/transform-enum-value'; @@ -120,7 +117,9 @@ export class FieldMetadataDTO { @Field({ nullable: true }) isUnique?: boolean; - @Validate(IsFieldMetadataDefaultValue) + // TODO: This validator was not used anymore, and it is since graphql error hadling refactoring + // it is adding extra load on the database, we are still validing inputs on field update and create + // @Validate(IsFieldMetadataDefaultValue) @IsOptional() @Field(() => GraphQLJSON, { nullable: true }) defaultValue?: FieldMetadataDefaultValue; @@ -128,7 +127,9 @@ export class FieldMetadataDTO { @Transform(({ value }) => transformEnumValue(value as FieldMetadataDefaultOption[]), ) - @Validate(IsFieldMetadataOptions) + // TODO: This validator was not used anymore, and it is since graphql error hadling refactoring + // it is adding extra load on the database, we are still validing inputs on field update and create + // @Validate(IsFieldMetadataOptions) @IsOptional() @Field(() => GraphQLJSON, { nullable: true }) options?: FieldMetadataOptions; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts index 2be2a448b..63158e972 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Context, @@ -10,6 +10,8 @@ import { import { FieldMetadataType } from 'twenty-shared/types'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { ForbiddenError, ValidationError, @@ -41,8 +43,12 @@ import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-module import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util'; @UseGuards(WorkspaceAuthGuard) +@UsePipes(ResolverValidationPipe) @Resolver(() => FieldMetadataDTO) -@UseFilters(PermissionsGraphqlApiExceptionFilter) +@UseFilters( + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) export class FieldMetadataResolver { constructor( private readonly fieldMetadataService: FieldMetadataService, diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts index 53b104d7b..797c90a19 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Context, @@ -8,6 +8,8 @@ import { Resolver, } from '@nestjs/graphql'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { IDataloaders } from 'src/engine/dataloaders/dataloader.interface'; @@ -29,7 +31,11 @@ import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-module @UseGuards(WorkspaceAuthGuard) @Resolver(() => ObjectMetadataDTO) -@UseFilters(PermissionsGraphqlApiExceptionFilter) +@UsePipes(ResolverValidationPipe) +@UseFilters( + PreventNestToAutoLogGraphqlErrorsFilter, + PermissionsGraphqlApiExceptionFilter, +) export class ObjectMetadataResolver { constructor( private readonly objectMetadataService: ObjectMetadataService, diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.resolver.ts index 8bb6f6042..f1128ec0e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.resolver.ts @@ -1,6 +1,8 @@ -import { UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @@ -14,6 +16,8 @@ import { RemoteServerService } from 'src/engine/metadata-modules/remote-server/r import { remoteServerGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/remote-server/utils/remote-server-graphql-api-exception-handler.util'; @UseGuards(WorkspaceAuthGuard) +@UsePipes(ResolverValidationPipe) +@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) @Resolver() export class RemoteServerResolver { constructor( diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.resolver.ts index c3be47ba7..2c6386502 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.resolver.ts @@ -1,6 +1,8 @@ -import { UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; @@ -11,6 +13,8 @@ import { RemoteTableService } from 'src/engine/metadata-modules/remote-server/re import { remoteTableGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/remote-server/remote-table/utils/remote-table-graphql-api-exception-handler.util'; @UseGuards(WorkspaceAuthGuard) +@UsePipes(ResolverValidationPipe) +@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) @Resolver() export class RemoteTableResolver { constructor(private readonly remoteTableService: RemoteTableService) {} diff --git a/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts index 013916d3f..25b5541a2 100644 --- a/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/role/role.resolver.ts @@ -1,4 +1,4 @@ -import { UseFilters, UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, @@ -11,6 +11,8 @@ import { import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service'; import { FileService } from 'src/engine/core-modules/file/services/file.service'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -40,9 +42,15 @@ import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; @Resolver(() => RoleDTO) -@UseGuards(WorkspaceAuthGuard) -@UseGuards(SettingsPermissionsGuard(SettingPermissionType.ROLES)) -@UseFilters(PermissionsGraphqlApiExceptionFilter) +@UsePipes(ResolverValidationPipe) +@UseGuards( + WorkspaceAuthGuard, + SettingsPermissionsGuard(SettingPermissionType.ROLES), +) +@UseFilters( + PermissionsGraphqlApiExceptionFilter, + PreventNestToAutoLogGraphqlErrorsFilter, +) export class RoleResolver { constructor( private readonly userRoleService: UserRoleService, diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts index f1628e8ac..353aa99b9 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.resolver.ts @@ -1,10 +1,12 @@ -import { UseGuards } from '@nestjs/common'; +import { UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { InjectRepository } from '@nestjs/typeorm'; import graphqlTypeJson from 'graphql-type-json'; import { Repository } from 'typeorm'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { FeatureFlagGuard } from 'src/engine/guards/feature-flag.guard'; @@ -23,6 +25,8 @@ import { serverlessFunctionGraphQLApiExceptionHandler } from 'src/engine/metadat @UseGuards(WorkspaceAuthGuard, FeatureFlagGuard) @Resolver() +@UsePipes(ResolverValidationPipe) +@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) export class ServerlessFunctionResolver { constructor( private readonly serverlessFunctionService: ServerlessFunctionService, diff --git a/packages/twenty-server/src/engine/subscriptions/subscriptions.resolver.ts b/packages/twenty-server/src/engine/subscriptions/subscriptions.resolver.ts index c663be28d..d300d6639 100644 --- a/packages/twenty-server/src/engine/subscriptions/subscriptions.resolver.ts +++ b/packages/twenty-server/src/engine/subscriptions/subscriptions.resolver.ts @@ -1,16 +1,20 @@ +import { Inject, UseFilters, UseGuards, UsePipes } from '@nestjs/common'; import { Args, Resolver, Subscription } from '@nestjs/graphql'; -import { Inject, UseGuards } from '@nestjs/common'; import { RedisPubSub } from 'graphql-redis-subscriptions'; import { isDefined } from 'twenty-shared/utils'; +import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; +import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; +import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; +import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { OnDbEventDTO } from 'src/engine/subscriptions/dtos/on-db-event.dto'; import { OnDbEventInput } from 'src/engine/subscriptions/dtos/on-db-event.input'; -import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; -import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; @Resolver() @UseGuards(WorkspaceAuthGuard, UserAuthGuard) +@UsePipes(ResolverValidationPipe) +@UseFilters(PreventNestToAutoLogGraphqlErrorsFilter) export class SubscriptionsResolver { constructor(@Inject('PUB_SUB') private readonly pubSub: RedisPubSub) {} diff --git a/packages/twenty-server/src/filters/graphql-validation-exception.filter.ts b/packages/twenty-server/src/filters/graphql-validation-exception.filter.ts deleted file mode 100644 index 37f0ae1a6..000000000 --- a/packages/twenty-server/src/filters/graphql-validation-exception.filter.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common'; - -import { ValidationError } from 'class-validator'; - -import { UserInputError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; - -@Catch(ValidationError) -export class GraphqlValidationExceptionFilter implements ExceptionFilter { - catch(exception: ValidationError, _host: ArgumentsHost) { - const errors = Object.values(exception.constraints || {}).map((error) => ({ - message: error, - path: exception.property, - })); - - return new UserInputError(errors.map((error) => error.message).join(', ')); - } -} diff --git a/packages/twenty-server/src/main.ts b/packages/twenty-server/src/main.ts index 3cb2fa907..b4a8b8716 100644 --- a/packages/twenty-server/src/main.ts +++ b/packages/twenty-server/src/main.ts @@ -1,11 +1,10 @@ -import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; import fs from 'fs'; import bytes from 'bytes'; -import { useContainer, ValidationError } from 'class-validator'; +import { useContainer } from 'class-validator'; import session from 'express-session'; import { graphqlUploadExpress } from 'graphql-upload'; @@ -50,22 +49,6 @@ const bootstrap = async () => { app.useGlobalFilters(new UnhandledExceptionFilter()); - // Apply validation pipes globally - app.useGlobalPipes( - new ValidationPipe({ - transform: true, - exceptionFactory: (errors) => { - const error = new ValidationError(); - - error.constraints = Object.assign( - {}, - ...errors.map((error) => error.constraints), - ); - - return error; - }, - }), - ); app.useBodyParser('json', { limit: settings.storage.maxFileSize }); app.useBodyParser('urlencoded', { limit: settings.storage.maxFileSize,