Handle query runner errors (#6424)
- Throw service error from query runner - Catch in resolver factories - Map to graphql errors --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+8
-2
@@ -1,4 +1,7 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import {
|
||||
WorkspaceQueryRunnerException,
|
||||
WorkspaceQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
|
||||
|
||||
export const assertIsValidUuid = (value: string) => {
|
||||
const isValid =
|
||||
@@ -7,6 +10,9 @@ export const assertIsValidUuid = (value: string) => {
|
||||
);
|
||||
|
||||
if (!isValid) {
|
||||
throw new BadRequestException(`Value "${value}" is not a valid UUID`);
|
||||
throw new WorkspaceQueryRunnerException(
|
||||
`Value "${value}" is not a valid UUID`,
|
||||
WorkspaceQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
+20
-10
@@ -1,8 +1,7 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
HttpException,
|
||||
InternalServerErrorException,
|
||||
} from '@nestjs/common';
|
||||
WorkspaceQueryRunnerException,
|
||||
WorkspaceQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
|
||||
|
||||
export type PgGraphQLConfig = {
|
||||
atMost: number;
|
||||
@@ -13,7 +12,7 @@ interface PgGraphQLErrorMapping {
|
||||
command: string,
|
||||
objectName: string,
|
||||
pgGraphqlConfig: PgGraphQLConfig,
|
||||
) => HttpException;
|
||||
) => WorkspaceQueryRunnerException;
|
||||
}
|
||||
|
||||
const pgGraphQLCommandMapping = {
|
||||
@@ -24,18 +23,28 @@ const pgGraphQLCommandMapping = {
|
||||
|
||||
const pgGraphQLErrorMapping: PgGraphQLErrorMapping = {
|
||||
'delete impacts too many records': (_, objectName, pgGraphqlConfig) =>
|
||||
new BadRequestException(
|
||||
new WorkspaceQueryRunnerException(
|
||||
`Cannot delete ${objectName} because it impacts too many records (more than ${pgGraphqlConfig?.atMost}).`,
|
||||
WorkspaceQueryRunnerExceptionCode.TOO_MANY_ROWS_AFFECTED,
|
||||
),
|
||||
'update impacts too many records': (_, objectName, pgGraphqlConfig) =>
|
||||
new BadRequestException(
|
||||
new WorkspaceQueryRunnerException(
|
||||
`Cannot update ${objectName} because it impacts too many records (more than ${pgGraphqlConfig?.atMost}).`,
|
||||
WorkspaceQueryRunnerExceptionCode.TOO_MANY_ROWS_AFFECTED,
|
||||
),
|
||||
'duplicate key value violates unique constraint': (command, objectName, _) =>
|
||||
new BadRequestException(
|
||||
new WorkspaceQueryRunnerException(
|
||||
`Cannot ${
|
||||
pgGraphQLCommandMapping[command] ?? command
|
||||
} ${objectName} because it violates a uniqueness constraint.`,
|
||||
WorkspaceQueryRunnerExceptionCode.QUERY_VIOLATES_UNIQUE_CONSTRAINT,
|
||||
),
|
||||
'violates foreign key constraint': (command, objectName, _) =>
|
||||
new WorkspaceQueryRunnerException(
|
||||
`Cannot ${
|
||||
pgGraphQLCommandMapping[command] ?? command
|
||||
} ${objectName} because it violates a foreign key constraint.`,
|
||||
WorkspaceQueryRunnerExceptionCode.QUERY_VIOLATES_FOREIGN_KEY_CONSTRAINT,
|
||||
),
|
||||
};
|
||||
|
||||
@@ -49,7 +58,7 @@ export const computePgGraphQLError = (
|
||||
const errorMessage = error?.message;
|
||||
|
||||
const mappedErrorKey = Object.keys(pgGraphQLErrorMapping).find(
|
||||
(key) => errorMessage?.startsWith(key),
|
||||
(key) => errorMessage?.includes(key),
|
||||
);
|
||||
|
||||
const mappedError = mappedErrorKey
|
||||
@@ -60,7 +69,8 @@ export const computePgGraphQLError = (
|
||||
return mappedError(command, objectName, pgGraphqlConfig);
|
||||
}
|
||||
|
||||
return new InternalServerErrorException(
|
||||
return new WorkspaceQueryRunnerException(
|
||||
`GraphQL errors on ${command}${objectName}: ${JSON.stringify(error)}`,
|
||||
WorkspaceQueryRunnerExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
};
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
WorkspaceQueryRunnerException,
|
||||
WorkspaceQueryRunnerExceptionCode,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.exception';
|
||||
import {
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
TimeoutError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
|
||||
export const workspaceQueryRunnerGraphqlApiExceptionHandler = (
|
||||
error: Error,
|
||||
) => {
|
||||
if (error instanceof WorkspaceQueryRunnerException) {
|
||||
switch (error.code) {
|
||||
case WorkspaceQueryRunnerExceptionCode.DATA_NOT_FOUND:
|
||||
throw new NotFoundError(error.message);
|
||||
case WorkspaceQueryRunnerExceptionCode.INVALID_QUERY_INPUT:
|
||||
throw new UserInputError(error.message);
|
||||
case WorkspaceQueryRunnerExceptionCode.QUERY_VIOLATES_UNIQUE_CONSTRAINT:
|
||||
case WorkspaceQueryRunnerExceptionCode.QUERY_VIOLATES_FOREIGN_KEY_CONSTRAINT:
|
||||
case WorkspaceQueryRunnerExceptionCode.TOO_MANY_ROWS_AFFECTED:
|
||||
case WorkspaceQueryRunnerExceptionCode.NO_ROWS_AFFECTED:
|
||||
throw new ForbiddenError(error.message);
|
||||
case WorkspaceQueryRunnerExceptionCode.QUERY_TIMEOUT:
|
||||
throw new TimeoutError(error.message);
|
||||
case WorkspaceQueryRunnerExceptionCode.INTERNAL_SERVER_ERROR:
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
};
|
||||
Reference in New Issue
Block a user