Improve sentry grouping (#12007)
This PR attemps at improving sentry grouping and filtering by - Using the exceptionCode as the fingerprint when the error is a customException. For this to work in this PR we are now throwing customExceptions instead of internalServerError deprived of their code. They will still be converted to Internal server errors when sent back as response - Filtering 4xx issues where it was missing (for emailVerification because errors were not handled, for invalid captcha and billing errors because they are httpErrors and not graphqlErrors) --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
||||
@ -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<FieldMetadataEntit
|
||||
try {
|
||||
validateMetadataNameOrThrow(fieldMetadataInput.name);
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidMetadataNameException) {
|
||||
if (error instanceof InvalidMetadataException) {
|
||||
throw new FieldMetadataException(
|
||||
error.message,
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
@ -630,7 +630,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
||||
objectMetadata,
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidMetadataNameException) {
|
||||
if (error instanceof InvalidMetadataException) {
|
||||
throw new FieldMetadataException(
|
||||
`Name "${fieldMetadataInput.name}" is not available, check that it is not duplicating another field's name.`,
|
||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||
|
||||
@ -11,6 +11,14 @@ import {
|
||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { removeFieldMapsFromObjectMetadata } from 'src/engine/metadata-modules/utils/remove-field-maps-from-object-metadata.util';
|
||||
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 { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
|
||||
@Injectable()
|
||||
@ -43,9 +51,9 @@ export class FieldMetadataRelationService {
|
||||
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
||||
|
||||
if (!isDefined(metadataVersion)) {
|
||||
throw new FieldMetadataException(
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
`Metadata version not found for workspace ${workspaceId}`,
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
@ -56,9 +64,9 @@ export class FieldMetadataRelationService {
|
||||
);
|
||||
|
||||
if (!objectMetadataMaps) {
|
||||
throw new FieldMetadataException(
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
`Object metadata map not found for workspace ${workspaceId} and metadata version ${metadataVersion}`,
|
||||
FieldMetadataExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import {
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
@ -31,8 +30,12 @@ export const fieldMetadataGraphqlApiExceptionHandler = (error: Error) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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<RelationMetadat
|
||||
validateMetadataNameOrThrow(relationMetadataInput.fromName);
|
||||
validateMetadataNameOrThrow(relationMetadataInput.toName);
|
||||
} catch (error) {
|
||||
if (error instanceof InvalidMetadataNameException)
|
||||
if (error instanceof InvalidMetadataException)
|
||||
throw new RelationMetadataException(
|
||||
error.message,
|
||||
RelationMetadataExceptionCode.INVALID_RELATION_INPUT,
|
||||
@ -497,8 +505,9 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
||||
|
||||
if (!isDefined(metadataVersion)) {
|
||||
throw new NotFoundException(
|
||||
throw new WorkspaceMetadataVersionException(
|
||||
`Metadata version not found for workspace ${workspaceId}`,
|
||||
WorkspaceMetadataVersionExceptionCode.METADATA_VERSION_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
@ -509,8 +518,9 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
);
|
||||
|
||||
if (!objectMetadataMaps) {
|
||||
throw new NotFoundException(
|
||||
throw new WorkspaceMetadataCacheException(
|
||||
`Object metadata map not found for workspace ${workspaceId} and metadata version ${metadataVersion}`,
|
||||
WorkspaceMetadataCacheExceptionCode.OBJECT_METADATA_MAP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
import {
|
||||
ConflictError,
|
||||
InternalServerError,
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import {
|
||||
@ -17,15 +15,18 @@ export const relationMetadataGraphqlApiExceptionHandler = (error: Error) => {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
export class InvalidMetadataNameException extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -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',
|
||||
}
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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',
|
||||
}
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user