diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts index 438e0aa92..b841b01c0 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts @@ -194,7 +194,7 @@ export const useAuth = () => { // TODO: Get intellisense for graphql error extensions code (codegen?) if ( error instanceof ApolloError && - error.graphQLErrors[0]?.extensions?.code === 'EMAIL_NOT_VERIFIED' + error.graphQLErrors[0]?.extensions?.subCode === 'EMAIL_NOT_VERIFIED' ) { setSearchParams({ email }); setSignInUpStep(SignInUpStep.EmailVerification); diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception.ts index 24e736c73..ab5869b52 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class GraphqlQueryRunnerException extends CustomException { + declare code: GraphqlQueryRunnerExceptionCode; constructor(message: string, code: GraphqlQueryRunnerExceptionCode) { super(message, code); } @@ -18,8 +19,6 @@ export enum GraphqlQueryRunnerExceptionCode { RECORD_NOT_FOUND = 'RECORD_NOT_FOUND', INVALID_ARGS_FIRST = 'INVALID_ARGS_FIRST', INVALID_ARGS_LAST = 'INVALID_ARGS_LAST', - METADATA_CACHE_VERSION_NOT_FOUND = 'METADATA_CACHE_VERSION_NOT_FOUND', - METADATA_CACHE_FEATURE_FLAG_RECOMPUTATION_REQUIRED = 'METADATA_CACHE_FEATURE_FLAG_RECOMPUTATION_REQUIRED', RELATION_SETTINGS_NOT_FOUND = 'RELATION_SETTINGS_NOT_FOUND', RELATION_TARGET_OBJECT_METADATA_NOT_FOUND = 'RELATION_TARGET_OBJECT_METADATA_NOT_FOUND', NOT_IMPLEMENTED = 'NOT_IMPLEMENTED', diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/graphql-query-runner-exception-handler.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/graphql-query-runner-exception-handler.util.ts index 8a2799084..c861941b2 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/graphql-query-runner-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/graphql-query-runner-exception-handler.util.ts @@ -3,7 +3,6 @@ import { GraphqlQueryRunnerExceptionCode, } from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception'; import { - InternalServerError, NotFoundError, UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -26,7 +25,15 @@ export const graphqlQueryRunnerExceptionHandler = ( throw new UserInputError(error.message); case GraphqlQueryRunnerExceptionCode.RECORD_NOT_FOUND: throw new NotFoundError(error.message); - default: - throw new InternalServerError(error.message); + case GraphqlQueryRunnerExceptionCode.RELATION_SETTINGS_NOT_FOUND: + case GraphqlQueryRunnerExceptionCode.RELATION_TARGET_OBJECT_METADATA_NOT_FOUND: + case GraphqlQueryRunnerExceptionCode.OBJECT_METADATA_COLLECTION_NOT_FOUND: + case GraphqlQueryRunnerExceptionCode.INVALID_POST_HOOK_PAYLOAD: + throw error; + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } }; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-exception-handler.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-exception-handler.util.ts index 69b9b3fa1..04eab266f 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/workspace-exception-handler.util.ts @@ -4,7 +4,6 @@ import { } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception'; import { ForbiddenError, - InternalServerError, NotFoundError, TimeoutError, UserInputError, @@ -26,7 +25,11 @@ export const workspaceExceptionHandler = ( case WorkspaceQueryRunnerExceptionCode.QUERY_TIMEOUT: throw new TimeoutError(error.message); case WorkspaceQueryRunnerExceptionCode.INTERNAL_SERVER_ERROR: - default: - throw new InternalServerError(error.message); + throw error; + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } }; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception.ts index 0f33bdd65..9a7ca365f 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception.ts @@ -1,8 +1,10 @@ import { CustomException } from 'src/utils/custom-exception'; export class WorkspaceQueryRunnerException extends CustomException { + code: WorkspaceQueryRunnerExceptionCode; constructor(message: string, code: WorkspaceQueryRunnerExceptionCode) { super(message, code); + this.code = code; } } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts index 4a9afd308..30054bdf8 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema.factory.ts @@ -19,6 +19,10 @@ import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twent import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; +import { + WorkspaceMetadataVersionException, + WorkspaceMetadataVersionExceptionCode, +} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; @Injectable() @@ -89,9 +93,9 @@ export class WorkspaceSchemaFactory { } if (!currentCacheVersion) { - throw new GraphqlQueryRunnerException( + throw new WorkspaceMetadataVersionException( 'Metadata cache version not found', - GraphqlQueryRunnerExceptionCode.METADATA_CACHE_VERSION_NOT_FOUND, + WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND, ); } diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.exception.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.exception.ts index ef6d2f3e9..777259d48 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.exception.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class AuthException extends CustomException { + declare code: AuthExceptionCode; constructor(message: string, code: AuthExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts b/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts index 818fc399f..c3af4bb73 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts @@ -6,9 +6,7 @@ import { } from 'src/engine/core-modules/auth/auth.exception'; import { AuthenticationError, - EmailNotVerifiedError, ForbiddenError, - InternalServerError, NotFoundError, UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -21,18 +19,33 @@ export class AuthGraphqlApiExceptionFilter implements ExceptionFilter { throw new NotFoundError(exception.message); case AuthExceptionCode.INVALID_INPUT: throw new UserInputError(exception.message); - case AuthExceptionCode.EMAIL_NOT_VERIFIED: - throw new EmailNotVerifiedError(exception.message); case AuthExceptionCode.FORBIDDEN_EXCEPTION: throw new ForbiddenError(exception.message); + case AuthExceptionCode.EMAIL_NOT_VERIFIED: + throw new ForbiddenError(exception.message, { + subCode: AuthExceptionCode.EMAIL_NOT_VERIFIED, + }); case AuthExceptionCode.UNAUTHENTICATED: case AuthExceptionCode.USER_NOT_FOUND: case AuthExceptionCode.WORKSPACE_NOT_FOUND: throw new AuthenticationError(exception.message); case AuthExceptionCode.INVALID_DATA: case AuthExceptionCode.INTERNAL_SERVER_ERROR: - default: - throw new InternalServerError(exception.message); + case AuthExceptionCode.USER_WORKSPACE_NOT_FOUND: + case AuthExceptionCode.INSUFFICIENT_SCOPES: + case AuthExceptionCode.OAUTH_ACCESS_DENIED: + case AuthExceptionCode.SSO_AUTH_FAILED: + case AuthExceptionCode.USE_SSO_AUTH: + case AuthExceptionCode.SIGNUP_DISABLED: + case AuthExceptionCode.GOOGLE_API_AUTH_DISABLED: + case AuthExceptionCode.MICROSOFT_API_AUTH_DISABLED: + case AuthExceptionCode.MISSING_ENVIRONMENT_VARIABLE: + throw exception; + default: { + const _exhaustiveCheck: never = exception.code; + + throw exception; + } } } } diff --git a/packages/twenty-server/src/engine/core-modules/email-verification/email-verification-exception-handler.util.ts b/packages/twenty-server/src/engine/core-modules/email-verification/email-verification-exception-handler.util.ts new file mode 100644 index 000000000..367071dbe --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/email-verification/email-verification-exception-handler.util.ts @@ -0,0 +1,29 @@ +import { + EmailVerificationException, + EmailVerificationExceptionCode, +} from 'src/engine/core-modules/email-verification/email-verification.exception'; +import { + ForbiddenError, + UserInputError, +} from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; + +export const emailVerificationGraphqlApiExceptionHandler = ( + error: EmailVerificationException, +) => { + switch (error.code) { + case EmailVerificationExceptionCode.INVALID_TOKEN: + case EmailVerificationExceptionCode.INVALID_APP_TOKEN_TYPE: + case EmailVerificationExceptionCode.TOKEN_EXPIRED: + case EmailVerificationExceptionCode.RATE_LIMIT_EXCEEDED: + throw new ForbiddenError(error.message); + case EmailVerificationExceptionCode.EMAIL_MISSING: + case EmailVerificationExceptionCode.EMAIL_ALREADY_VERIFIED: + case EmailVerificationExceptionCode.EMAIL_VERIFICATION_NOT_REQUIRED: + throw new UserInputError(error.message); + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } + } +}; diff --git a/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.exception.ts b/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.exception.ts index cd3c49d96..8cc641e8d 100644 --- a/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.exception.ts +++ b/packages/twenty-server/src/engine/core-modules/email-verification/email-verification.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class EmailVerificationException extends CustomException { + declare code: EmailVerificationExceptionCode; constructor(message: string, code: EmailVerificationExceptionCode) { super(message, code); } 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 4018e1892..db286c95c 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 @@ -5,6 +5,8 @@ import { SOURCE_LOCALE } from 'twenty-shared/translations'; import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service'; import { ResendEmailVerificationTokenInput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.input'; import { ResendEmailVerificationTokenOutput } from 'src/engine/core-modules/email-verification/dtos/resend-email-verification-token.output'; +import { emailVerificationGraphqlApiExceptionHandler } from 'src/engine/core-modules/email-verification/email-verification-exception-handler.util'; +import { EmailVerificationException } from 'src/engine/core-modules/email-verification/email-verification.exception'; import { EmailVerificationService } from 'src/engine/core-modules/email-verification/services/email-verification.service'; import { I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type'; import { OriginHeader } from 'src/engine/decorators/auth/origin-header.decorator'; @@ -23,15 +25,22 @@ export class EmailVerificationResolver { @OriginHeader() origin: string, @Context() context: I18nContext, ): Promise { - const workspace = - await this.domainManagerService.getWorkspaceByOriginOrDefaultWorkspace( - origin, - ); + try { + const workspace = + await this.domainManagerService.getWorkspaceByOriginOrDefaultWorkspace( + origin, + ); - return await this.emailVerificationService.resendEmailVerificationToken( - resendEmailVerificationTokenInput.email, - workspace, - context.req.headers['x-locale'] ?? SOURCE_LOCALE, - ); + return await this.emailVerificationService.resendEmailVerificationToken( + resendEmailVerificationTokenInput.email, + workspace, + context.req.headers['x-locale'] ?? SOURCE_LOCALE, + ); + } catch (error) { + if (error instanceof EmailVerificationException) { + return emailVerificationGraphqlApiExceptionHandler(error); + } + throw error; + } } } diff --git a/packages/twenty-server/src/engine/core-modules/exception-handler/drivers/sentry.driver.ts b/packages/twenty-server/src/engine/core-modules/exception-handler/drivers/sentry.driver.ts index 28d6ab047..95adbb126 100644 --- a/packages/twenty-server/src/engine/core-modules/exception-handler/drivers/sentry.driver.ts +++ b/packages/twenty-server/src/engine/core-modules/exception-handler/drivers/sentry.driver.ts @@ -3,6 +3,7 @@ import * as Sentry from '@sentry/node'; import { ExceptionHandlerOptions } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-options.interface'; import { ExceptionHandlerDriverInterface } from 'src/engine/core-modules/exception-handler/interfaces'; +import { CustomException } from 'src/utils/custom-exception'; export class ExceptionHandlerSentryDriver implements ExceptionHandlerDriverInterface @@ -15,8 +16,7 @@ export class ExceptionHandlerSentryDriver Sentry.withScope((scope) => { if (options?.operation) { - scope.setTag('operation', options.operation.name); - scope.setTag('operationName', options.operation.name); + scope.setExtra('operation', options.operation.name); } if (options?.document) { @@ -49,6 +49,11 @@ export class ExceptionHandlerSentryDriver }); } + if (exception instanceof CustomException) { + scope.setTag('customExceptionCode', exception.code); + scope.setFingerprint([exception.code]); + } + const eventId = Sentry.captureException(exception, { contexts: { GraphQL: { diff --git a/packages/twenty-server/src/engine/core-modules/graphql/utils/graphql-errors.util.ts b/packages/twenty-server/src/engine/core-modules/graphql/utils/graphql-errors.util.ts index d91ef60ad..701159c0f 100644 --- a/packages/twenty-server/src/engine/core-modules/graphql/utils/graphql-errors.util.ts +++ b/packages/twenty-server/src/engine/core-modules/graphql/utils/graphql-errors.util.ts @@ -24,7 +24,6 @@ export enum ErrorCode { PERSISTED_QUERY_NOT_SUPPORTED = 'PERSISTED_QUERY_NOT_SUPPORTED', BAD_USER_INPUT = 'BAD_USER_INPUT', NOT_FOUND = 'NOT_FOUND', - EMAIL_NOT_VERIFIED = 'EMAIL_NOT_VERIFIED', METHOD_NOT_ALLOWED = 'METHOD_NOT_ALLOWED', CONFLICT = 'CONFLICT', TIMEOUT = 'TIMEOUT', @@ -107,16 +106,16 @@ export class ValidationError extends BaseGraphQLError { } export class AuthenticationError extends BaseGraphQLError { - constructor(message: string) { - super(message, ErrorCode.UNAUTHENTICATED); + constructor(message: string, extensions?: Record) { + super(message, ErrorCode.UNAUTHENTICATED, extensions); Object.defineProperty(this, 'name', { value: 'AuthenticationError' }); } } export class ForbiddenError extends BaseGraphQLError { - constructor(message: string) { - super(message, ErrorCode.FORBIDDEN); + constructor(message: string, extensions?: Record) { + super(message, ErrorCode.FORBIDDEN, extensions); Object.defineProperty(this, 'name', { value: 'ForbiddenError' }); } @@ -161,14 +160,6 @@ export class NotFoundError extends BaseGraphQLError { } } -export class EmailNotVerifiedError extends BaseGraphQLError { - constructor(message: string) { - super(message, ErrorCode.EMAIL_NOT_VERIFIED); - - Object.defineProperty(this, 'name', { value: 'EmailNotVerifiedError' }); - } -} - export class MethodNotAllowedError extends BaseGraphQLError { constructor(message: string) { super(message, ErrorCode.METHOD_NOT_ALLOWED); diff --git a/packages/twenty-server/src/engine/core-modules/graphql/utils/should-capture-exception.util.ts b/packages/twenty-server/src/engine/core-modules/graphql/utils/should-capture-exception.util.ts index 817f6d76e..2af14c8e7 100644 --- a/packages/twenty-server/src/engine/core-modules/graphql/utils/should-capture-exception.util.ts +++ b/packages/twenty-server/src/engine/core-modules/graphql/utils/should-capture-exception.util.ts @@ -1,3 +1,5 @@ +import { HttpException } from '@nestjs/common'; + import { BaseGraphQLError, ErrorCode, @@ -12,7 +14,6 @@ export const graphQLErrorCodesToFilterOut = [ ErrorCode.TIMEOUT, ErrorCode.CONFLICT, ErrorCode.BAD_USER_INPUT, - ErrorCode.EMAIL_NOT_VERIFIED, ]; export const shouldCaptureException = (exception: Error): boolean => { @@ -23,5 +24,13 @@ export const shouldCaptureException = (exception: Error): boolean => { return false; } + if ( + exception instanceof HttpException && + exception.getStatus() >= 400 && + exception.getStatus() < 500 + ) { + return false; + } + return true; }; diff --git a/packages/twenty-server/src/engine/core-modules/search/exceptions/search.exception.ts b/packages/twenty-server/src/engine/core-modules/search/exceptions/search.exception.ts index 72b88d200..97603519b 100644 --- a/packages/twenty-server/src/engine/core-modules/search/exceptions/search.exception.ts +++ b/packages/twenty-server/src/engine/core-modules/search/exceptions/search.exception.ts @@ -1,13 +1,12 @@ import { CustomException } from 'src/utils/custom-exception'; export class SearchException extends CustomException { + declare code: SearchExceptionCode; constructor(message: string, code: SearchExceptionCode) { super(message, code); } } export enum SearchExceptionCode { - METADATA_CACHE_VERSION_NOT_FOUND = 'METADATA_CACHE_VERSION_NOT_FOUND', LABEL_IDENTIFIER_FIELD_NOT_FOUND = 'LABEL_IDENTIFIER_FIELD_NOT_FOUND', - OBJECT_METADATA_MAP_NOT_FOUND = 'OBJECT_METADATA_MAP_NOT_FOUND', } diff --git a/packages/twenty-server/src/engine/core-modules/search/filters/search-api-exception.filter.ts b/packages/twenty-server/src/engine/core-modules/search/filters/search-api-exception.filter.ts index d8f0c13c8..43f901cf6 100644 --- a/packages/twenty-server/src/engine/core-modules/search/filters/search-api-exception.filter.ts +++ b/packages/twenty-server/src/engine/core-modules/search/filters/search-api-exception.filter.ts @@ -1,7 +1,9 @@ import { Catch, ExceptionFilter } from '@nestjs/common'; -import { InternalServerError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; -import { SearchException } from 'src/engine/core-modules/search/exceptions/search.exception'; +import { + SearchException, + SearchExceptionCode, +} from 'src/engine/core-modules/search/exceptions/search.exception'; @Catch(SearchException) export class SearchApiExceptionFilter implements ExceptionFilter { @@ -9,8 +11,13 @@ export class SearchApiExceptionFilter implements ExceptionFilter { catch(exception: SearchException) { switch (exception.code) { - default: - throw new InternalServerError(exception.message); + case SearchExceptionCode.LABEL_IDENTIFIER_FIELD_NOT_FOUND: + throw exception; + default: { + const _exhaustiveCheck: never = exception.code; + + throw exception; + } } } } 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 88a19dd50..7bce44e97 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 @@ -7,16 +7,20 @@ import { ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-build import { SearchArgs } from 'src/engine/core-modules/search/dtos/search-args'; import { SearchRecordDTO } from 'src/engine/core-modules/search/dtos/search-record-dto'; -import { - SearchException, - SearchExceptionCode, -} from 'src/engine/core-modules/search/exceptions/search.exception'; import { SearchApiExceptionFilter } from 'src/engine/core-modules/search/filters/search-api-exception.filter'; import { SearchService } from 'src/engine/core-modules/search/services/search.service'; import { RecordsWithObjectMetadataItem } from 'src/engine/core-modules/search/types/records-with-object-metadata-item'; import { formatSearchTerms } from 'src/engine/core-modules/search/utils/format-search-terms'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; +import { + WorkspaceMetadataCacheException, + WorkspaceMetadataCacheExceptionCode, +} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception'; +import { + WorkspaceMetadataVersionException, + WorkspaceMetadataVersionExceptionCode, +} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception'; import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; @@ -47,9 +51,9 @@ export class SearchResolver { await this.workspaceCacheStorageService.getMetadataVersion(workspace.id); if (currentCacheVersion === undefined) { - throw new SearchException( - 'Metadata cache version not found', - SearchExceptionCode.METADATA_CACHE_VERSION_NOT_FOUND, + throw new WorkspaceMetadataVersionException( + `Metadata version not found for workspace ${workspace.id}`, + WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND, ); } @@ -60,9 +64,9 @@ export class SearchResolver { ); if (!objectMetadataMaps) { - throw new SearchException( + throw new WorkspaceMetadataCacheException( `Object metadata map not found for workspace ${workspace.id} and metadata version ${currentCacheVersion}`, - SearchExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND, + WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND, ); } diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/filters/config-variable-graphql-api-exception.filter.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/filters/config-variable-graphql-api-exception.filter.ts index 636541d57..1387a30e7 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/filters/config-variable-graphql-api-exception.filter.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/filters/config-variable-graphql-api-exception.filter.ts @@ -2,7 +2,6 @@ import { Catch, ExceptionFilter } from '@nestjs/common'; import { ForbiddenError, - InternalServerError, NotFoundError, UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -25,8 +24,13 @@ export class ConfigVariableGraphqlApiExceptionFilter case ConfigVariableExceptionCode.VALIDATION_FAILED: throw new UserInputError(exception.message); case ConfigVariableExceptionCode.INTERNAL_ERROR: - default: - throw new InternalServerError(exception.message); + case ConfigVariableExceptionCode.UNSUPPORTED_CONFIG_TYPE: + throw exception; + default: { + const _exhaustiveCheck: never = exception.code; + + throw exception; + } } } } diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/twenty-config.exception.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/twenty-config.exception.ts index 9d7c17c53..dd24b7da7 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/twenty-config.exception.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/twenty-config.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class ConfigVariableException extends CustomException { + declare code: ConfigVariableExceptionCode; constructor(message: string, code: ConfigVariableExceptionCode) { super(message, code); } @@ -12,6 +13,5 @@ export enum ConfigVariableExceptionCode { VARIABLE_NOT_FOUND = 'VARIABLE_NOT_FOUND', VALIDATION_FAILED = 'VALIDATION_FAILED', UNSUPPORTED_CONFIG_TYPE = 'UNSUPPORTED_CONFIG_TYPE', - METADATA_NOT_FOUND = 'METADATA_NOT_FOUND', INTERNAL_ERROR = 'INTERNAL_ERROR', } diff --git a/packages/twenty-server/src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter.ts b/packages/twenty-server/src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter.ts index df46ad401..508aff9fe 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/filters/workflow-trigger-graphql-api-exception.filter.ts @@ -1,7 +1,6 @@ import { Catch, ExceptionFilter } from '@nestjs/common'; import { - InternalServerError, NotFoundError, UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -25,8 +24,13 @@ export class WorkflowTriggerGraphqlApiExceptionFilter throw new UserInputError(exception.message); case WorkflowTriggerExceptionCode.NOT_FOUND: throw new NotFoundError(exception.message); - default: - throw new InternalServerError(exception.message); + case WorkflowTriggerExceptionCode.INTERNAL_ERROR: + throw exception; + default: { + const _exhaustiveCheck: never = exception.code; + + throw exception; + } } } } diff --git a/packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/workspace-graphql-api-exception-handler.util.spec.ts b/packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/workspace-graphql-api-exception-handler.util.spec.ts index f5ae36bf0..26e94f054 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/workspace-graphql-api-exception-handler.util.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/utils/__tests__/workspace-graphql-api-exception-handler.util.spec.ts @@ -1,6 +1,5 @@ import { ConflictError, - InternalServerError, NotFoundError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util'; @@ -8,6 +7,7 @@ import { WorkspaceException, WorkspaceExceptionCode, } from 'src/engine/core-modules/workspace/workspace.exception'; +import { CustomException } from 'src/utils/custom-exception'; describe('workspaceGraphqlApiExceptionHandler', () => { it('should throw NotFoundError when WorkspaceExceptionCode is SUBDOMAIN_NOT_FOUND', () => { @@ -48,7 +48,7 @@ describe('workspaceGraphqlApiExceptionHandler', () => { const error = new WorkspaceException('Unknown error', 'UNKNOWN_CODE'); expect(() => workspaceGraphqlApiExceptionHandler(error)).toThrow( - InternalServerError, + CustomException, ); }); diff --git a/packages/twenty-server/src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util.ts index 10f349b40..2f424dfdf 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util.ts @@ -1,7 +1,6 @@ import { ConflictError, ForbiddenError, - InternalServerError, NotFoundError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { @@ -15,12 +14,17 @@ export const workspaceGraphqlApiExceptionHandler = (error: Error) => { case WorkspaceExceptionCode.SUBDOMAIN_NOT_FOUND: case WorkspaceExceptionCode.WORKSPACE_NOT_FOUND: throw new NotFoundError(error.message); + case WorkspaceExceptionCode.DOMAIN_ALREADY_TAKEN: case WorkspaceExceptionCode.SUBDOMAIN_ALREADY_TAKEN: throw new ConflictError(error.message); case WorkspaceExceptionCode.ENVIRONMENT_VAR_NOT_ENABLED: + case WorkspaceExceptionCode.WORKSPACE_CUSTOM_DOMAIN_DISABLED: throw new ForbiddenError(error.message); - default: - throw new InternalServerError(error.message); + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } } diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.exception.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.exception.ts index 7e301dd4f..29f6c421b 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.exception.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class WorkspaceException extends CustomException { + declare code: WorkspaceExceptionCode; constructor(message: string, code: WorkspaceExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.exception.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.exception.ts index f0ac60c8c..a544b183b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class FieldMetadataException extends CustomException { + declare code: FieldMetadataExceptionCode; constructor(message: string, code: FieldMetadataExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts index 5f34cc407..0402ac7df 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts @@ -46,7 +46,7 @@ import { RelationMetadataEntity, RelationMetadataType, } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; import { exceedsDatabaseIdentifierMaximumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils'; import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils'; import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils'; @@ -614,7 +614,7 @@ export class FieldMetadataService extends TypeOrmQueryService { case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_NOT_ENABLED: case FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED: case FieldMetadataExceptionCode.LABEL_IDENTIFIER_FIELD_METADATA_ID_NOT_FOUND: - default: - throw new InternalServerError(error.message); + throw error; + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } } diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.exception.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.exception.ts index 7bef57773..4a843ac23 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class ObjectMetadataException extends CustomException { + declare code: ObjectMetadataExceptionCode; constructor(message: string, code: ObjectMetadataExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util.ts index c4eca8e8c..f130b1ee0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util.ts @@ -1,7 +1,6 @@ import { ConflictError, ForbiddenError, - InternalServerError, NotFoundError, UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -26,8 +25,13 @@ export const objectMetadataGraphqlApiExceptionHandler = (error: Error) => { throw new ForbiddenError(error.message); case ObjectMetadataExceptionCode.OBJECT_ALREADY_EXISTS: throw new ConflictError(error.message); - default: - throw new InternalServerError(error.message); + case ObjectMetadataExceptionCode.MISSING_CUSTOM_OBJECT_DEFAULT_LABEL_IDENTIFIER_FIELD: + throw error; + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } } diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util.ts index eba464d06..6cda29ee1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util.ts @@ -6,7 +6,7 @@ import { ObjectMetadataException, ObjectMetadataExceptionCode, } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception'; -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; import { validateMetadataNameIsNotTooLongOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-long.utils'; import { validateMetadataNameIsNotTooShortOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-short.utils'; import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils'; @@ -28,7 +28,7 @@ export const validateObjectMetadataInputNameOrThrow = (name: string): void => { try { validateMetadataNameOrThrow(name); } catch (error) { - if (error instanceof InvalidMetadataNameException) { + if (error instanceof InvalidMetadataException) { throw new ObjectMetadataException( error.message, ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT, @@ -58,7 +58,7 @@ const validateObjectMetadataInputLabelOrThrow = (name: string): void => { try { validators.forEach((validator) => validator(name.trim())); } catch (error) { - if (error instanceof InvalidMetadataNameException) { + if (error instanceof InvalidMetadataException) { throw new ObjectMetadataException( error.message, ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT, diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts index 3b84e765a..10fc01352 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class PermissionsException extends CustomException { + declare code: PermissionsExceptionCode; constructor(message: string, code: PermissionsExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permission-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permission-graphql-api-exception-handler.util.ts index a04436025..ae8c00ed4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permission-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/utils/permission-graphql-api-exception-handler.util.ts @@ -1,6 +1,5 @@ import { ForbiddenError, - InternalServerError, NotFoundError, UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -29,7 +28,20 @@ export const permissionGraphqlApiExceptionHandler = ( case PermissionsExceptionCode.OBJECT_METADATA_NOT_FOUND: throw new NotFoundError(error.message); case PermissionsExceptionCode.DEFAULT_ROLE_NOT_FOUND: - default: - throw new InternalServerError(error.message); + case PermissionsExceptionCode.WORKSPACE_ID_ROLE_USER_WORKSPACE_MISMATCH: + case PermissionsExceptionCode.TOO_MANY_ADMIN_CANDIDATES: + case PermissionsExceptionCode.USER_WORKSPACE_ALREADY_HAS_ROLE: + case PermissionsExceptionCode.ADMIN_ROLE_NOT_FOUND: + case PermissionsExceptionCode.DEFAULT_ROLE_CANNOT_BE_DELETED: + case PermissionsExceptionCode.WORKSPACE_MEMBER_NOT_FOUND: + case PermissionsExceptionCode.UNKNOWN_OPERATION_NAME: + case PermissionsExceptionCode.UNKNOWN_REQUIRED_PERMISSION: + case PermissionsExceptionCode.NO_ROLE_FOUND_FOR_USER_WORKSPACE: + throw error; + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } }; diff --git a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.exception.ts b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.exception.ts index 69ae12902..abe61f3f7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class RelationMetadataException extends CustomException { + declare code: RelationMetadataExceptionCode; constructor(message: string, code: RelationMetadataExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts index 26bb87d3d..49e05d44e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/relation-metadata/relation-metadata.service.ts @@ -22,9 +22,17 @@ import { RelationMetadataException, RelationMetadataExceptionCode, } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.exception'; -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; import { validateFieldNameAvailabilityOrThrow } from 'src/engine/metadata-modules/utils/validate-field-name-availability.utils'; import { validateMetadataNameOrThrow } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils'; +import { + WorkspaceMetadataCacheException, + WorkspaceMetadataCacheExceptionCode, +} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception'; +import { + WorkspaceMetadataVersionException, + WorkspaceMetadataVersionExceptionCode, +} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception'; import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; import { @@ -73,7 +81,7 @@ export class RelationMetadataService extends TypeOrmQueryService { if (error instanceof RelationMetadataException) { switch (error.code) { - case RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND: - throw new NotFoundError(error.message); case RelationMetadataExceptionCode.INVALID_RELATION_INPUT: throw new UserInputError(error.message); case RelationMetadataExceptionCode.RELATION_ALREADY_EXISTS: throw new ConflictError(error.message); case RelationMetadataExceptionCode.FOREIGN_KEY_NOT_FOUND: - default: - throw new InternalServerError(error.message); + case RelationMetadataExceptionCode.RELATION_METADATA_NOT_FOUND: + throw error; + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } } diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.exception.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.exception.ts index 7e9e41b9a..ab95b6e4d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class RemoteServerException extends CustomException { + declare code: RemoteServerExceptionCode; constructor(message: string, code: RemoteServerExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.exception.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.exception.ts index e1bb768c9..37fd452c8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class RemoteTableException extends CustomException { + declare code: RemoteTableExceptionCode; constructor(message: string, code: RemoteTableExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/remote-table-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/remote-table-graphql-api-exception-handler.util.ts index 13e2c976a..1ea1f6fc8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/remote-table-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/remote-table-graphql-api-exception-handler.util.ts @@ -1,6 +1,5 @@ import { ConflictError, - InternalServerError, NotFoundError, UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -21,8 +20,11 @@ export const remoteTableGraphqlApiExceptionHandler = (error: Error) => { throw new UserInputError(error.message); case RemoteTableExceptionCode.REMOTE_TABLE_ALREADY_EXISTS: throw new ConflictError(error.message); - default: - throw new InternalServerError(error.message); + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } } diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/utils/remote-server-graphql-api-exception-handler.util.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/utils/remote-server-graphql-api-exception-handler.util.ts index e289d02af..7cb6e331d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/utils/remote-server-graphql-api-exception-handler.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/utils/remote-server-graphql-api-exception-handler.util.ts @@ -1,7 +1,6 @@ import { ConflictError, ForbiddenError, - InternalServerError, NotFoundError, UserInputError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @@ -21,8 +20,13 @@ export const remoteServerGraphqlApiExceptionHandler = (error: any) => { throw new ForbiddenError(error.message); case RemoteServerExceptionCode.REMOTE_SERVER_ALREADY_EXISTS: throw new ConflictError(error.message); - default: - throw new InternalServerError(error.message); + case RemoteServerExceptionCode.REMOTE_SERVER_CONNECTION_ERROR: + throw error; + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } } diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.exception.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.exception.ts index 0a0137dd5..191fa2c92 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/serverless-function.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class ServerlessFunctionException extends CustomException { + declare code: ServerlessFunctionExceptionCode; constructor(message: string, code: ServerlessFunctionExceptionCode) { super(message, code); } diff --git a/packages/twenty-server/src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils.ts b/packages/twenty-server/src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils.ts index 5518e7dac..68ccc4dad 100644 --- a/packages/twenty-server/src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils.ts +++ b/packages/twenty-server/src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils.ts @@ -1,7 +1,6 @@ import { ConflictError, ForbiddenError, - InternalServerError, NotFoundError, } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { @@ -20,9 +19,15 @@ export const serverlessFunctionGraphQLApiExceptionHandler = (error: any) => { case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_READY: case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_BUILDING: case ServerlessFunctionExceptionCode.FEATURE_FLAG_INVALID: + case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_EXECUTION_LIMIT_REACHED: throw new ForbiddenError(error.message); - default: - throw new InternalServerError(error.message); + case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_CODE_UNCHANGED: + throw error; + default: { + const _exhaustiveCheck: never = error.code; + + throw error; + } } } throw error; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/compute-metadata-name-from-label.util.ts b/packages/twenty-server/src/engine/metadata-modules/utils/compute-metadata-name-from-label.util.ts index 9b5f8133d..c301092a9 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/compute-metadata-name-from-label.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/compute-metadata-name-from-label.util.ts @@ -2,11 +2,17 @@ import camelCase from 'lodash.camelcase'; import { slugify } from 'transliteration'; import { isDefined } from 'twenty-shared/utils'; -import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; +import { + InvalidMetadataException, + InvalidMetadataExceptionCode, +} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; export const computeMetadataNameFromLabel = (label: string): string => { if (!isDefined(label)) { - throw new InvalidMetadataException('Label is required'); + throw new InvalidMetadataException( + 'Label is required', + InvalidMetadataExceptionCode.LABEL_REQUIRED, + ); } const prefixedLabel = /^\d/.test(label) ? `n${label}` : label; @@ -22,7 +28,10 @@ export const computeMetadataNameFromLabel = (label: string): string => { }); if (formattedString === '') { - throw new InvalidMetadataException(`Invalid label: "${label}"`); + throw new InvalidMetadataException( + `Invalid label: "${label}"`, + InvalidMetadataExceptionCode.INVALID_LABEL, + ); } return camelCase(formattedString); diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception.ts b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception.ts deleted file mode 100644 index dd985672d..000000000 --- a/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class InvalidMetadataNameException extends Error { - constructor(message: string) { - super(message); - } -} diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception.ts b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception.ts index 8f3e59644..49b451eed 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception.ts @@ -1,5 +1,19 @@ -export class InvalidMetadataException extends Error { - constructor(message: string) { - super(message); +import { CustomException } from 'src/utils/custom-exception'; + +export class InvalidMetadataException extends CustomException { + constructor(message: string, code: InvalidMetadataExceptionCode) { + super(message, code); } } + +export enum InvalidMetadataExceptionCode { + LABEL_REQUIRED = 'Label required', + INPUT_TOO_SHORT = 'Input too short', + EXCEEDS_MAX_LENGTH = 'Exceeds max length', + RESERVED_KEYWORD = 'Reserved keyword', + NOT_CAMEL_CASE = 'Not camel case', + INVALID_LABEL = 'Invalid label', + NAME_NOT_SYNCED_WITH_LABEL = 'Name not synced with label', + INVALID_STRING = 'Invalid string', + NOT_AVAILABLE = 'Name not available', +} diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-field-name-availability.utils.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-field-name-availability.utils.ts index adbd0948f..c728eef3b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/validate-field-name-availability.utils.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-field-name-availability.utils.ts @@ -2,7 +2,10 @@ import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-meta import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { + InvalidMetadataException, + InvalidMetadataExceptionCode, +} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; const getReservedCompositeFieldNames = ( objectMetadata: ObjectMetadataEntity, @@ -33,10 +36,16 @@ export const validateFieldNameAvailabilityOrThrow = ( getReservedCompositeFieldNames(objectMetadata); if (objectMetadata.fields.some((field) => field.name === name)) { - throw new InvalidMetadataNameException(`Name "${name}" is not available`); + throw new InvalidMetadataException( + `Name "${name}" is not available`, + InvalidMetadataExceptionCode.NOT_AVAILABLE, + ); } if (reservedCompositeFieldsNames.includes(name)) { - throw new InvalidMetadataNameException(`Name "${name}" is not available`); + throw new InvalidMetadataException( + `Name "${name}" is not available`, + InvalidMetadataExceptionCode.RESERVED_KEYWORD, + ); } }; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-camel-case.utils.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-camel-case.utils.ts index ef89e0d76..bdfb99b43 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-camel-case.utils.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-camel-case.utils.ts @@ -1,11 +1,15 @@ import camelCase from 'lodash.camelcase'; -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { + InvalidMetadataException, + InvalidMetadataExceptionCode, +} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; export const validateMetadataNameIsCamelCaseOrThrow = (name: string) => { if (name !== camelCase(name)) { - throw new InvalidMetadataNameException( + throw new InvalidMetadataException( `Name should be in camelCase: ${name}`, + InvalidMetadataExceptionCode.NOT_CAMEL_CASE, ); } }; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-reserved-keyword.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-reserved-keyword.ts index c8073ae7e..1d0da0a19 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-reserved-keyword.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-reserved-keyword.ts @@ -1,4 +1,7 @@ -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { + InvalidMetadataException, + InvalidMetadataExceptionCode, +} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; const coreObjectNames = [ 'approvedAccessDomain', @@ -64,8 +67,9 @@ export const validateMetadataNameIsNotReservedKeywordOrThrow = ( name: string, ) => { if (reservedKeywords.includes(name)) { - throw new InvalidMetadataNameException( + throw new InvalidMetadataException( `The name "${name}" is not available`, + InvalidMetadataExceptionCode.RESERVED_KEYWORD, ); } }; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-long.utils.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-long.utils.ts index ae4cd293d..afd9a20e7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-long.utils.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-long.utils.ts @@ -1,10 +1,14 @@ -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { + InvalidMetadataException, + InvalidMetadataExceptionCode, +} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; import { exceedsDatabaseIdentifierMaximumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils'; export const validateMetadataNameIsNotTooLongOrThrow = (name: string) => { if (exceedsDatabaseIdentifierMaximumLength(name)) { - throw new InvalidMetadataNameException( + throw new InvalidMetadataException( `String "${name}" exceeds 63 characters limit`, + InvalidMetadataExceptionCode.EXCEEDS_MAX_LENGTH, ); } }; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-short.utils.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-short.utils.ts index 52077b577..db59fe063 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-short.utils.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-is-not-too-short.utils.ts @@ -1,8 +1,14 @@ -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { + InvalidMetadataException, + InvalidMetadataExceptionCode, +} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; import { beneathDatabaseIdentifierMinimumLength } from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils'; export const validateMetadataNameIsNotTooShortOrThrow = (name: string) => { if (beneathDatabaseIdentifierMinimumLength(name)) { - throw new InvalidMetadataNameException(`Input is too short: "${name}"`); + throw new InvalidMetadataException( + `Input is too short: "${name}"`, + InvalidMetadataExceptionCode.INPUT_TOO_SHORT, + ); } }; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-start-with-lowercase-letter-and-contain-digits-nor-letters.utils.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-start-with-lowercase-letter-and-contain-digits-nor-letters.utils.ts index fd80f678e..cc6d93eab 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-start-with-lowercase-letter-and-contain-digits-nor-letters.utils.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-metadata-name-start-with-lowercase-letter-and-contain-digits-nor-letters.utils.ts @@ -1,4 +1,7 @@ -import { InvalidMetadataNameException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata-name.exception'; +import { + InvalidMetadataException, + InvalidMetadataExceptionCode, +} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; const STARTS_WITH_LOWER_CASE_AND_CONTAINS_ONLY_CAPS_AND_LOWER_LETTERS_AND_NUMBER_STRING_REGEX = /^[a-z][a-zA-Z0-9]*$/; @@ -10,8 +13,9 @@ export const validateMetadataNameStartWithLowercaseLetterAndContainDigitsNorLett STARTS_WITH_LOWER_CASE_AND_CONTAINS_ONLY_CAPS_AND_LOWER_LETTERS_AND_NUMBER_STRING_REGEX, ) ) { - throw new InvalidMetadataNameException( + throw new InvalidMetadataException( `String "${name}" is not valid: must start with lowercase letter and contain only alphanumeric letters`, + InvalidMetadataExceptionCode.INVALID_STRING, ); } }; diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util.ts index d619b4b6d..6f7968d89 100644 --- a/packages/twenty-server/src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util.ts @@ -2,7 +2,10 @@ import camelCase from 'lodash.camelcase'; import { slugify } from 'transliteration'; import { isDefined } from 'twenty-shared/utils'; -import { InvalidMetadataException } from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; +import { + InvalidMetadataException, + InvalidMetadataExceptionCode, +} from 'src/engine/metadata-modules/utils/exceptions/invalid-metadata.exception'; export const validateNameAndLabelAreSyncOrThrow = ( label: string, @@ -13,13 +16,17 @@ export const validateNameAndLabelAreSyncOrThrow = ( if (name !== computedName) { throw new InvalidMetadataException( `Name is not synced with label. Expected name: "${computedName}", got ${name}`, + InvalidMetadataExceptionCode.NAME_NOT_SYNCED_WITH_LABEL, ); } }; export const computeMetadataNameFromLabel = (label: string): string => { if (!isDefined(label)) { - throw new InvalidMetadataException('Label is required'); + throw new InvalidMetadataException( + 'Label is required', + InvalidMetadataExceptionCode.LABEL_REQUIRED, + ); } const prefixedLabel = /^\d/.test(label) ? `n${label}` : label; @@ -35,7 +42,10 @@ export const computeMetadataNameFromLabel = (label: string): string => { }); if (formattedString === '') { - throw new InvalidMetadataException(`Invalid label: "${label}"`); + throw new InvalidMetadataException( + `Invalid label: "${label}"`, + InvalidMetadataExceptionCode.INVALID_LABEL, + ); } return camelCase(formattedString); diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception.ts index b8b344583..43445bc27 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception.ts @@ -7,5 +7,5 @@ export class WorkspaceMetadataCacheException extends CustomException { } export enum WorkspaceMetadataCacheExceptionCode { - METADATA_VERSION_NOT_FOUND = 'METADATA_VERSION_NOT_FOUND', + OBJECT_METADATA_MAP_NOT_FOUND = 'Object Metadata map not found', } diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service.ts index acc620c7b..ff373aa5b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service.ts @@ -1,17 +1,17 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; import { isDefined } from 'twenty-shared/utils'; +import { Repository } from 'typeorm'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps'; import { generateObjectMetadataMaps } from 'src/engine/metadata-modules/utils/generate-object-metadata-maps.util'; import { - WorkspaceMetadataCacheException, - WorkspaceMetadataCacheExceptionCode, -} from 'src/engine/metadata-modules/workspace-metadata-cache/exceptions/workspace-metadata-cache.exception'; + WorkspaceMetadataVersionException, + WorkspaceMetadataVersionExceptionCode, +} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception'; import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; @Injectable() @@ -46,9 +46,9 @@ export class WorkspaceMetadataCacheService { await this.getMetadataVersionFromDatabase(workspaceId); if (!isDefined(currentDatabaseVersion)) { - throw new WorkspaceMetadataCacheException( + throw new WorkspaceMetadataVersionException( 'Metadata version not found in the database', - WorkspaceMetadataCacheExceptionCode.METADATA_VERSION_NOT_FOUND, + WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND, ); } diff --git a/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts b/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts index fb639b433..9a75a5f5b 100644 --- a/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts +++ b/packages/twenty-server/src/engine/twenty-orm/factories/workspace-datasource.factory.ts @@ -11,6 +11,10 @@ import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twent import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service'; import { WorkspaceMetadataCacheService } from 'src/engine/metadata-modules/workspace-metadata-cache/services/workspace-metadata-cache.service'; +import { + WorkspaceMetadataVersionException, + WorkspaceMetadataVersionExceptionCode, +} from 'src/engine/metadata-modules/workspace-metadata-version/exceptions/workspace-metadata-version.exception'; import { WorkspacePermissionsCacheStorageService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache-storage.service'; import { ROLES_PERMISSIONS, @@ -312,9 +316,9 @@ export class WorkspaceDatasourceFactory { if (!isDefined(latestWorkspaceMetadataVersion)) { if (shouldFailIfMetadataNotFound) { - throw new TwentyORMException( + throw new WorkspaceMetadataVersionException( `Metadata version not found for workspace ${workspaceId}`, - TwentyORMExceptionCode.METADATA_VERSION_NOT_FOUND, + WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND, ); } else { await this.workspaceMetadataCacheService.recomputeMetadataCache({ @@ -330,7 +334,7 @@ export class WorkspaceDatasourceFactory { if (!isDefined(latestWorkspaceMetadataVersion)) { throw new TwentyORMException( - `Metadata version not found after recompute for workspace ${workspaceId}`, + `Metadata version not found after recompute`, TwentyORMExceptionCode.METADATA_VERSION_NOT_FOUND, ); } diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service.ts index 5baa32038..7a5f915c8 100644 --- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service.ts +++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-event-import-exception-handler.service.ts @@ -148,16 +148,21 @@ export class CalendarEventImportErrorHandlerService { workspaceId, ); - this.exceptionHandlerService.captureExceptions([exception], { - workspace: { - id: workspaceId, - }, - }); - - throw new CalendarEventImportException( + const calendarEventImportException = new CalendarEventImportException( `Unknown error importing calendar events for calendar channel ${calendarChannel.id} in workspace ${workspaceId}: ${exception.message}`, CalendarEventImportExceptionCode.UNKNOWN, ); + + this.exceptionHandlerService.captureExceptions( + [calendarEventImportException], + { + workspace: { + id: workspaceId, + }, + }, + ); + + throw calendarEventImportException; } private async handleNotFoundException( diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts index 497896808..0870da04f 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-import-exception-handler.service.ts @@ -162,7 +162,10 @@ export class MessageImportExceptionHandlerService { ); this.exceptionHandlerService.captureExceptions([ - `Unknown error importing messages for message channel ${messageChannel.id} in workspace ${workspaceId}: ${exception.message}`, + new MessageImportException( + `Unknown error importing messages for message channel ${messageChannel.id} in workspace ${workspaceId}: ${exception.message}`, + MessageImportExceptionCode.UNKNOWN, + ), ]); throw new MessageImportException( diff --git a/packages/twenty-server/src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception.ts b/packages/twenty-server/src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception.ts index 0d9f2dc21..549d25b75 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception.ts @@ -1,6 +1,7 @@ import { CustomException } from 'src/utils/custom-exception'; export class WorkflowTriggerException extends CustomException { + declare code: WorkflowTriggerExceptionCode; constructor(message: string, code: WorkflowTriggerExceptionCode) { super(message, code); }