Add exceptions for metadata modules (#6070)
Class exception for each metadata module + handler to map on graphql error TODO left : - find a way to call handler on auto-resolvers nestjs query (probably interceptors) - discuss what should be done for pre-hooks errors - discuss what should be done for Unauthorized exception
This commit is contained in:
@ -0,0 +1,15 @@
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export class ObjectMetadataException extends CustomException {
|
||||
code: ObjectMetadataExceptionCode;
|
||||
constructor(message: string, code: ObjectMetadataExceptionCode) {
|
||||
super(message, code);
|
||||
}
|
||||
}
|
||||
|
||||
export enum ObjectMetadataExceptionCode {
|
||||
OBJECT_METADATA_NOT_FOUND = 'OBJECT_METADATA_NOT_FOUND',
|
||||
INVALID_OBJECT_INPUT = 'INVALID_OBJECT_INPUT',
|
||||
OBJECT_MUTATION_NOT_ALLOWED = 'OBJECT_MUTATION_NOT_ALLOWED',
|
||||
OBJECT_ALREADY_EXISTS = 'OBJECT_ALREADY_EXISTS',
|
||||
}
|
||||
@ -12,6 +12,7 @@ import {
|
||||
UpdateOneObjectInput,
|
||||
} from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import { BeforeUpdateOneObject } from 'src/engine/metadata-modules/object-metadata/hooks/before-update-one-object.hook';
|
||||
import { objectMetadataGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/object-metadata/utils/object-metadata-graphql-api-exception-handler.util';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => ObjectMetadataDTO)
|
||||
@ -26,7 +27,11 @@ export class ObjectMetadataResolver {
|
||||
@Args('input') input: DeleteOneObjectInput,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
) {
|
||||
return this.objectMetadataService.deleteOneObject(input, workspaceId);
|
||||
try {
|
||||
return this.objectMetadataService.deleteOneObject(input, workspaceId);
|
||||
} catch (error) {
|
||||
objectMetadataGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
|
||||
@Mutation(() => ObjectMetadataDTO)
|
||||
@ -34,8 +39,12 @@ export class ObjectMetadataResolver {
|
||||
@Args('input') input: UpdateOneObjectInput,
|
||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||
) {
|
||||
await this.beforeUpdateOneObject.run(input, workspaceId);
|
||||
try {
|
||||
await this.beforeUpdateOneObject.run(input, workspaceId);
|
||||
|
||||
return this.objectMetadataService.updateOneObject(input, workspaceId);
|
||||
return this.objectMetadataService.updateOneObject(input, workspaceId);
|
||||
} catch (error) {
|
||||
objectMetadataGraphqlApiExceptionHandler(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
ConflictException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import console from 'console';
|
||||
@ -56,6 +51,10 @@ import { mapUdtNameToFieldType } from 'src/engine/metadata-modules/remote-server
|
||||
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
|
||||
import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import { RemoteTableRelationsService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service';
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
|
||||
import { ObjectMetadataEntity } from './object-metadata.entity';
|
||||
|
||||
@ -121,7 +120,10 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
});
|
||||
|
||||
if (!objectMetadata) {
|
||||
throw new NotFoundException('Object does not exist');
|
||||
throw new ObjectMetadataException(
|
||||
'Object does not exist',
|
||||
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
// DELETE RELATIONS
|
||||
@ -159,8 +161,9 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectMetadataInput.nameSingular.toLowerCase() ===
|
||||
objectMetadataInput.namePlural.toLowerCase()
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
throw new ObjectMetadataException(
|
||||
'The singular and plural name cannot be the same for an object',
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
@ -186,7 +189,10 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
});
|
||||
|
||||
if (objectAlreadyExists) {
|
||||
throw new ConflictException('Object already exists');
|
||||
throw new ObjectMetadataException(
|
||||
'Object already exists',
|
||||
ObjectMetadataExceptionCode.OBJECT_ALREADY_EXISTS,
|
||||
);
|
||||
}
|
||||
|
||||
const isCustom = !objectMetadataInput.isRemote;
|
||||
@ -372,18 +378,25 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
workspaceId: string,
|
||||
options: FindOneOptions<ObjectMetadataEntity>,
|
||||
): Promise<ObjectMetadataEntity> {
|
||||
return this.objectMetadataRepository.findOneOrFail({
|
||||
relations: [
|
||||
'fields',
|
||||
'fields.fromRelationMetadata',
|
||||
'fields.toRelationMetadata',
|
||||
],
|
||||
...options,
|
||||
where: {
|
||||
...options.where,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
try {
|
||||
return this.objectMetadataRepository.findOneOrFail({
|
||||
relations: [
|
||||
'fields',
|
||||
'fields.fromRelationMetadata',
|
||||
'fields.toRelationMetadata',
|
||||
],
|
||||
...options,
|
||||
where: {
|
||||
...options.where,
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new ObjectMetadataException(
|
||||
'Object does not exist',
|
||||
ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async findManyWithinWorkspace(
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
|
||||
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
|
||||
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
|
||||
export const assertMutationNotOnRemoteObject = (
|
||||
objectMetadataItem: ObjectMetadataInterface,
|
||||
) => {
|
||||
if (objectMetadataItem.isRemote) {
|
||||
throw new BadRequestException('Remote objects are read-only');
|
||||
throw new ObjectMetadataException(
|
||||
'Remote objects are read-only',
|
||||
ObjectMetadataExceptionCode.OBJECT_MUTATION_NOT_ALLOWED,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import {
|
||||
UserInputError,
|
||||
ForbiddenError,
|
||||
ConflictError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
} from 'src/engine/utils/graphql-errors.util';
|
||||
|
||||
export const objectMetadataGraphqlApiExceptionHandler = (error: Error) => {
|
||||
if (error instanceof ObjectMetadataException) {
|
||||
switch (error.code) {
|
||||
case ObjectMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND:
|
||||
throw new NotFoundError(error.message);
|
||||
case ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT:
|
||||
throw new UserInputError(error.message);
|
||||
case ObjectMetadataExceptionCode.OBJECT_MUTATION_NOT_ALLOWED:
|
||||
throw new ForbiddenError(error.message);
|
||||
case ObjectMetadataExceptionCode.OBJECT_ALREADY_EXISTS:
|
||||
throw new ConflictError(error.message);
|
||||
default:
|
||||
throw new InternalServerError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
};
|
||||
@ -1,9 +1,13 @@
|
||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
|
||||
|
||||
import { InvalidStringException } from 'src/engine/metadata-modules/errors/InvalidStringException';
|
||||
import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input';
|
||||
import { UpdateObjectPayload } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||
import { validateMetadataName } from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
|
||||
import {
|
||||
ObjectMetadataException,
|
||||
ObjectMetadataExceptionCode,
|
||||
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
|
||||
import {
|
||||
validateMetadataName,
|
||||
InvalidStringException,
|
||||
} from 'src/engine/metadata-modules/utils/validate-metadata-name.utils';
|
||||
import { camelCase } from 'src/utils/camel-case';
|
||||
|
||||
const coreObjectNames = [
|
||||
@ -55,7 +59,10 @@ export const validateObjectMetadataInputOrThrow = <
|
||||
const validateNameIsNotReservedKeywordOrThrow = (name?: string) => {
|
||||
if (name) {
|
||||
if (reservedKeywords.includes(name)) {
|
||||
throw new ForbiddenException(`The name "${name}" is not available`);
|
||||
throw new ObjectMetadataException(
|
||||
`The name "${name}" is not available`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -63,7 +70,10 @@ const validateNameIsNotReservedKeywordOrThrow = (name?: string) => {
|
||||
const validateNameCamelCasedOrThrow = (name?: string) => {
|
||||
if (name) {
|
||||
if (name !== camelCase(name)) {
|
||||
throw new ForbiddenException(`Name should be in camelCase: ${name}`);
|
||||
throw new ObjectMetadataException(
|
||||
`Name should be in camelCase: ${name}`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -75,8 +85,9 @@ const validateNameCharactersOrThrow = (name?: string) => {
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidStringException) {
|
||||
throw new BadRequestException(
|
||||
throw new ObjectMetadataException(
|
||||
`Characters used in name "${name}" are not supported`,
|
||||
ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
|
||||
Reference in New Issue
Block a user