Skip to content

Commit

Permalink
feat(core): use custom errors for failHandler and metadata
Browse files Browse the repository at this point in the history
Closes #611
  • Loading branch information
B4nan committed Aug 9, 2020
1 parent 13fe6e5 commit 6db22af
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 116 deletions.
6 changes: 3 additions & 3 deletions packages/core/src/decorators/ManyToMany.ts
@@ -1,7 +1,7 @@
import { ReferenceOptions } from './Property';
import { MetadataStorage } from '../metadata';
import { MetadataStorage, MetadataValidator } from '../metadata';
import { Utils } from '../utils';
import { EntityValidator, ReferenceType } from '../entity';
import { ReferenceType } from '../entity';
import { EntityName, EntityProperty, AnyEntity } from '../typings';
import { QueryOrder } from '../enums';

Expand All @@ -13,7 +13,7 @@ export function ManyToMany<T, O>(
return function (target: AnyEntity, propertyName: string) {
options = Utils.isObject<ManyToManyOptions<T, O>>(entity) ? entity : { ...options, entity, mappedBy };
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
EntityValidator.validateSingleDecorator(meta, propertyName);
MetadataValidator.validateSingleDecorator(meta, propertyName);
const property = { name: propertyName, reference: ReferenceType.MANY_TO_MANY } as EntityProperty<T>;
meta.properties[propertyName] = Object.assign(property, options);
};
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/decorators/ManyToOne.ts
@@ -1,7 +1,7 @@
import { ReferenceOptions } from './Property';
import { MetadataStorage } from '../metadata';
import { MetadataStorage, MetadataValidator } from '../metadata';
import { Utils } from '../utils';
import { EntityValidator, ReferenceType } from '../entity';
import { ReferenceType } from '../entity';
import { AnyEntity, EntityName, EntityProperty } from '../typings';

export function ManyToOne<T, O>(
Expand All @@ -11,7 +11,7 @@ export function ManyToOne<T, O>(
return function (target: AnyEntity, propertyName: string) {
options = Utils.isObject<ManyToOneOptions<T, O>>(entity) ? entity : { ...options, entity };
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
EntityValidator.validateSingleDecorator(meta, propertyName);
MetadataValidator.validateSingleDecorator(meta, propertyName);
const property = { name: propertyName, reference: ReferenceType.MANY_TO_ONE } as EntityProperty;
meta.properties[propertyName] = Object.assign(property, options);
};
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/decorators/OneToMany.ts
@@ -1,7 +1,7 @@
import { ReferenceOptions } from './Property';
import { MetadataStorage } from '../metadata';
import { MetadataStorage, MetadataValidator } from '../metadata';
import { Utils } from '../utils';
import { EntityValidator, ReferenceType } from '../entity';
import { ReferenceType } from '../entity';
import { QueryOrder } from '../enums';
import { EntityName, EntityProperty, AnyEntity } from '../typings';

Expand All @@ -14,7 +14,7 @@ export function createOneToDecorator<T, O>(
return function (target: AnyEntity, propertyName: string) {
options = Utils.isObject<OneToManyOptions<T, O>>(entity) ? entity : { ...options, entity, mappedBy };
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
EntityValidator.validateSingleDecorator(meta, propertyName);
MetadataValidator.validateSingleDecorator(meta, propertyName);

const prop = { name: propertyName, reference } as EntityProperty<T>;
Object.assign(prop, options);
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/decorators/Property.ts
@@ -1,14 +1,14 @@
import { MetadataStorage } from '../metadata';
import { MetadataStorage, MetadataValidator } from '../metadata';
import { Utils } from '../utils';
import { Cascade, EntityValidator, ReferenceType, LoadStrategy } from '../entity';
import { Cascade, ReferenceType, LoadStrategy } from '../entity';
import { EntityName, EntityProperty, AnyEntity, Constructor } from '../typings';
import { Type } from '../types';

export function Property<T>(options: PropertyOptions<T> = {}) {
return function (target: AnyEntity, propertyName: string) {
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
const desc = Object.getOwnPropertyDescriptor(target, propertyName) || {};
EntityValidator.validateSingleDecorator(meta, propertyName);
MetadataValidator.validateSingleDecorator(meta, propertyName);
const name = options.name || propertyName;

if (propertyName !== name && !(desc.value instanceof Function)) {
Expand Down
6 changes: 0 additions & 6 deletions packages/core/src/entity/EntityValidator.ts
Expand Up @@ -8,12 +8,6 @@ export class EntityValidator {

constructor(private strict: boolean) { }

static validateSingleDecorator(meta: EntityMetadata, propertyName: string): void {
if (meta.properties[propertyName]?.reference) {
throw ValidationError.multipleDecorators(meta.className, propertyName);
}
}

validate<T extends AnyEntity<T>>(entity: T, payload: any, meta: EntityMetadata): void {
Object.values(meta.properties).forEach(prop => {
if ([ReferenceType.ONE_TO_MANY, ReferenceType.MANY_TO_MANY].includes(prop.reference)) {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/metadata/MetadataDiscovery.ts
Expand Up @@ -3,7 +3,7 @@ import globby from 'globby';
import chalk from 'chalk';

import { AnyEntity, Constructor, Dictionary, EntityClass, EntityClassGroup, EntityMetadata, EntityProperty } from '../typings';
import { Configuration, Utils, ValidationError } from '../utils';
import { Configuration, Utils, MetadataError } from '../utils';
import { MetadataValidator } from './MetadataValidator';
import { MetadataStorage } from './MetadataStorage';
import { Cascade, ReferenceType } from '../entity';
Expand Down Expand Up @@ -723,7 +723,7 @@ export class MetadataDiscovery {
}

if (!target) {
throw ValidationError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
throw MetadataError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
}

return target;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/metadata/MetadataStorage.ts
@@ -1,5 +1,5 @@
import { EntityMetadata, AnyEntity, Dictionary } from '../typings';
import { Utils, ValidationError } from '../utils';
import { MetadataError, Utils } from '../utils';
import { EntityManager } from '../EntityManager';
import { EntityHelper } from '../entity';
import { EventSubscriber } from '../events';
Expand Down Expand Up @@ -52,7 +52,7 @@ export class MetadataStorage {

get<T extends AnyEntity<T> = any>(entity: string, init = false, validate = true): EntityMetadata<T> {
if (entity && !this.metadata[entity] && validate && !init) {
throw ValidationError.missingMetadata(entity);
throw MetadataError.missingMetadata(entity);
}

if (!this.metadata[entity] && init) {
Expand Down
40 changes: 23 additions & 17 deletions packages/core/src/metadata/MetadataValidator.ts
@@ -1,16 +1,22 @@
import { EntityMetadata, EntityProperty } from '../typings';
import { Utils, ValidationError } from '../utils';
import { Utils, MetadataError } from '../utils';
import { ReferenceType } from '../entity';
import { MetadataStorage } from './MetadataStorage';

export class MetadataValidator {

static validateSingleDecorator(meta: EntityMetadata, propertyName: string): void {
if (meta.properties[propertyName]?.reference) {
throw MetadataError.multipleDecorators(meta.className, propertyName);
}
}

validateEntityDefinition(metadata: MetadataStorage, name: string): void {
const meta = metadata.get(name);

// entities have PK
if (!meta.embeddable && (!meta.primaryKeys || meta.primaryKeys.length === 0)) {
throw ValidationError.fromMissingPrimaryKey(meta);
throw MetadataError.fromMissingPrimaryKey(meta);
}

this.validateVersionField(meta);
Expand All @@ -24,42 +30,42 @@ export class MetadataValidator {

validateDiscovered(discovered: EntityMetadata[], warnWhenNoEntities: boolean): void {
if (discovered.length === 0 && warnWhenNoEntities) {
throw ValidationError.noEntityDiscovered();
throw MetadataError.noEntityDiscovered();
}

const duplicates = Utils.findDuplicates(discovered.map(meta => meta.className));

if (duplicates.length > 0) {
throw ValidationError.duplicateEntityDiscovered(duplicates);
throw MetadataError.duplicateEntityDiscovered(duplicates);
}

// validate base entities
discovered
.filter(meta => meta.extends && !discovered.find(m => m.className === meta.extends))
.forEach(meta => { throw ValidationError.fromUnknownBaseEntity(meta); });
.forEach(meta => { throw MetadataError.fromUnknownBaseEntity(meta); });

// validate we found at least one entity (not just abstract/base entities)
if (discovered.filter(meta => meta.name).length === 0 && warnWhenNoEntities) {
throw ValidationError.onlyAbstractEntitiesDiscovered();
throw MetadataError.onlyAbstractEntitiesDiscovered();
}

// check for not discovered entities
discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
if (prop.reference !== ReferenceType.SCALAR && !discovered.find(m => m.className === prop.type)) {
throw ValidationError.fromUnknownEntity(prop.type, `${meta.className}.${prop.name}`);
throw MetadataError.fromUnknownEntity(prop.type, `${meta.className}.${prop.name}`);
}
}));
}

private validateReference(meta: EntityMetadata, prop: EntityProperty, metadata: MetadataStorage): void {
// references do have types
if (!prop.type) {
throw ValidationError.fromWrongTypeDefinition(meta, prop);
throw MetadataError.fromWrongTypeDefinition(meta, prop);
}

// references do have type of known entity
if (!metadata.get(prop.type, false, false)) {
throw ValidationError.fromWrongTypeDefinition(meta, prop);
throw MetadataError.fromWrongTypeDefinition(meta, prop);
}
}

Expand All @@ -76,34 +82,34 @@ export class MetadataValidator {
private validateOwningSide(meta: EntityMetadata, prop: EntityProperty, inverse: EntityProperty): void {
// has correct `inversedBy` on owning side
if (!inverse) {
throw ValidationError.fromWrongReference(meta, prop, 'inversedBy');
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy');
}

// has correct `inversedBy` reference type
if (inverse.type !== meta.name) {
throw ValidationError.fromWrongReference(meta, prop, 'inversedBy', inverse);
throw MetadataError.fromWrongReference(meta, prop, 'inversedBy', inverse);
}

// inversed side is not defined as owner
if (inverse.inversedBy) {
throw ValidationError.fromWrongOwnership(meta, prop, 'inversedBy');
throw MetadataError.fromWrongOwnership(meta, prop, 'inversedBy');
}
}

private validateInverseSide(meta: EntityMetadata, prop: EntityProperty, owner: EntityProperty): void {
// has correct `mappedBy` on inverse side
if (prop.mappedBy && !owner) {
throw ValidationError.fromWrongReference(meta, prop, 'mappedBy');
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy');
}

// has correct `mappedBy` reference type
if (owner.type !== meta.name) {
throw ValidationError.fromWrongReference(meta, prop, 'mappedBy', owner);
throw MetadataError.fromWrongReference(meta, prop, 'mappedBy', owner);
}

// owning side is not defined as inverse
if (owner.mappedBy) {
throw ValidationError.fromWrongOwnership(meta, prop, 'mappedBy');
throw MetadataError.fromWrongOwnership(meta, prop, 'mappedBy');
}
}

Expand All @@ -115,14 +121,14 @@ export class MetadataValidator {
const props = Object.values(meta.properties).filter(p => p.version);

if (props.length > 1) {
throw ValidationError.multipleVersionFields(meta, props.map(p => p.name));
throw MetadataError.multipleVersionFields(meta, props.map(p => p.name));
}

const prop = meta.properties[meta.versionProperty];
const type = prop.type.toLowerCase();

if (type !== 'number' && type !== 'date' && !type.startsWith('timestamp') && !type.startsWith('datetime')) {
throw ValidationError.invalidVersionFieldType(meta);
throw MetadataError.invalidVersionFieldType(meta);
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/utils/Configuration.ts
Expand Up @@ -6,7 +6,7 @@ import { CacheAdapter, FileCacheAdapter, NullCacheAdapter } from '../cache';
import { EntityFactory, EntityRepository } from '../entity';
import { AnyEntity, Constructor, Dictionary, EntityClass, EntityClassGroup, IPrimaryKey } from '../typings';
import { Hydrator, ObjectHydrator } from '../hydration';
import { Logger, LoggerNamespace, Utils, ValidationError } from '../utils';
import { Logger, LoggerNamespace, NotFoundError, Utils } from '../utils';
import { EntityManager } from '../EntityManager';
import { EntityOptions, EntitySchema, IDatabaseDriver, MetadataStorage } from '..';
import { Platform } from '../platforms';
Expand All @@ -31,7 +31,7 @@ export class Configuration<D extends IDatabaseDriver = IDatabaseDriver> {
strict: false,
// eslint-disable-next-line no-console
logger: console.log.bind(console),
findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => ValidationError.findOneFailed(entityName, where),
findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => NotFoundError.findOneFailed(entityName, where),
baseDir: process.cwd(),
hydrator: ObjectHydrator,
autoJoinOneToOneOwner: true,
Expand Down

0 comments on commit 6db22af

Please sign in to comment.