Skip to content

Commit

Permalink
feat(validation): warn when failing to get metadata of an entity
Browse files Browse the repository at this point in the history
Closes #153
  • Loading branch information
B4nan committed Sep 22, 2019
1 parent 5a77a39 commit 33ce7d3
Show file tree
Hide file tree
Showing 11 changed files with 33 additions and 20 deletions.
4 changes: 2 additions & 2 deletions lib/EntityManager.ts
Expand Up @@ -153,7 +153,7 @@ export class EntityManager {
async nativeUpdate<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T>, data: EntityData<T>): Promise<number> {
entityName = Utils.className(entityName);
data = SmartQueryHelper.processParams(data);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName, this.metadata.get(entityName));
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName, this.metadata.get(entityName, false, false));
this.validator.validateParams(data, 'update data');
this.validator.validateParams(where, 'update condition');
const res = await this.driver.nativeUpdate(entityName, where, data, this.transactionContext);
Expand All @@ -163,7 +163,7 @@ export class EntityManager {

async nativeDelete<T extends IEntityType<T>>(entityName: EntityName<T>, where: FilterQuery<T> | string | any): Promise<number> {
entityName = Utils.className(entityName);
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName, this.metadata.get(entityName));
where = SmartQueryHelper.processWhere(where as FilterQuery<T>, entityName, this.metadata.get(entityName, false, false));
this.validator.validateParams(where, 'delete condition');
const res = await this.driver.nativeDelete(entityName, where, this.transactionContext);

Expand Down
2 changes: 1 addition & 1 deletion lib/connections/MongoConnection.ts
Expand Up @@ -198,7 +198,7 @@ export class MongoConnection extends Connection {

private getCollectionName(name: EntityName<IEntity>): string {
name = Utils.className(name);
const meta = this.metadata.get(name);
const meta = this.metadata.get(name, false, false);

return meta ? meta.collection : name;
}
Expand Down
6 changes: 3 additions & 3 deletions lib/drivers/AbstractSqlDriver.ts
Expand Up @@ -96,7 +96,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
res = await qb.update(data).where(where).execute('run', false);
}

await this.processManyToMany(entityName, Utils.extractPK(data[pk] || where, this.metadata.get(entityName))!, collections, ctx);
await this.processManyToMany(entityName, Utils.extractPK(data[pk] || where, this.metadata.get(entityName, false, false))!, collections, ctx);

return res;
}
Expand All @@ -115,7 +115,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
}

protected extractManyToMany<T extends IEntityType<T>>(entityName: string, data: EntityData<T>): EntityData<T> {
if (!this.metadata.get(entityName)) {
if (!this.metadata.get(entityName, false, false)) {
return {};
}

Expand All @@ -135,7 +135,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
}

protected async processManyToMany<T extends IEntityType<T>>(entityName: string, pk: IPrimaryKey, collections: EntityData<T>, ctx?: Transaction) {
if (!this.metadata.get(entityName)) {
if (!this.metadata.get(entityName, false, false)) {
return;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/drivers/DatabaseDriver.ts
Expand Up @@ -112,7 +112,7 @@ export abstract class DatabaseDriver<C extends Connection> implements IDatabaseD
}

protected getPrimaryKeyField(entityName: string): string {
const meta = this.metadata.get(entityName);
const meta = this.metadata.get(entityName, false, false);
return meta ? meta.primaryKey : this.config.getNamingStrategy().referenceColumnName();
}

Expand Down
2 changes: 1 addition & 1 deletion lib/drivers/MongoDriver.ts
Expand Up @@ -73,7 +73,7 @@ export class MongoDriver extends DatabaseDriver<MongoConnection> {
private renameFields<T>(entityName: string, data: T): T {
data = Object.assign({}, data); // copy first
Utils.renameKey(data, 'id', '_id');
const meta = this.metadata.get(entityName);
const meta = this.metadata.get(entityName, false, false);

Object.keys(data).forEach(k => {
if (meta && meta.properties[k]) {
Expand Down
8 changes: 6 additions & 2 deletions lib/metadata/MetadataStorage.ts
@@ -1,5 +1,5 @@
import { EntityMetadata, IEntityType } from '../decorators';
import { Utils } from '../utils';
import { Utils, ValidationError } from '../utils';
import { EntityManager } from '../EntityManager';
import { EntityHelper } from '../entity';

Expand Down Expand Up @@ -34,7 +34,11 @@ export class MetadataStorage {
return this.metadata;
}

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

if (!this.metadata[entity] && init) {
this.metadata[entity] = { properties: {} } as EntityMetadata;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/metadata/MetadataValidator.ts
Expand Up @@ -49,7 +49,7 @@ export class MetadataValidator {
}

// references do have type of known entity
if (!metadata.get(prop.type)) {
if (!metadata.get(prop.type, false, false)) {
throw ValidationError.fromWrongTypeDefinition(meta, prop);
}
}
Expand Down
8 changes: 4 additions & 4 deletions lib/query/QueryBuilder.ts
Expand Up @@ -109,7 +109,7 @@ export class QueryBuilder {
where(cond: Record<string, any>, operator?: keyof typeof QueryBuilderHelper.GROUP_OPERATORS): this; // tslint:disable-next-line:lines-between-class-members
where(cond: string, params?: any[], operator?: keyof typeof QueryBuilderHelper.GROUP_OPERATORS): this; // tslint:disable-next-line:lines-between-class-members
where(cond: Record<string, any> | string, params?: keyof typeof QueryBuilderHelper.GROUP_OPERATORS | any[], operator?: keyof typeof QueryBuilderHelper.GROUP_OPERATORS): this {
cond = SmartQueryHelper.processWhere(cond, this.entityName, this.metadata.get(this.entityName));
cond = SmartQueryHelper.processWhere(cond, this.entityName, this.metadata.get(this.entityName, false, false));

if (Utils.isString(cond)) {
cond = { [`(${cond})`]: Utils.asArray(params) };
Expand Down Expand Up @@ -221,7 +221,7 @@ export class QueryBuilder {
}

this.helper.getLockSQL(qb, this.lockMode);
this.helper.finalize(this.type, qb, this.metadata.get(this.entityName));
this.helper.finalize(this.type, qb, this.metadata.get(this.entityName, false, false));

return qb;
}
Expand All @@ -237,7 +237,7 @@ export class QueryBuilder {
async execute(method: 'all' | 'get' | 'run' = 'all', mapResults = true): Promise<any> {
const type = this.connectionType || (method === 'run' ? 'write' : 'read');
const res = await this.driver.getConnection(type).execute(this.getKnexQuery(), [], method);
const meta = this.metadata.get(this.entityName);
const meta = this.metadata.get(this.entityName, false, false);

if (!mapResults) {
return res;
Expand Down Expand Up @@ -288,7 +288,7 @@ export class QueryBuilder {

private processWhere(cond: any): any {
cond = Object.assign({}, cond); // copy first
const meta = this.metadata.get(this.entityName);
const meta = this.metadata.get(this.entityName, false, false);

Object.keys(cond).forEach(field => {
this.helper.replaceEmptyInConditions(cond, field);
Expand Down
10 changes: 5 additions & 5 deletions lib/query/QueryBuilderHelper.ts
Expand Up @@ -61,7 +61,7 @@ export class QueryBuilderHelper {

processData(data: any): any {
data = Object.assign({}, data); // copy first
const meta = this.metadata.get(this.entityName);
const meta = this.metadata.get(this.entityName, false, false);

Object.keys(data).forEach(k => {
if (meta && meta.properties[k]) {
Expand Down Expand Up @@ -195,7 +195,7 @@ export class QueryBuilderHelper {
}

getTableName(entityName: string): string {
const meta = this.metadata.get(entityName);
const meta = this.metadata.get(entityName, false, false);
return meta ? meta.collection : entityName;
}

Expand Down Expand Up @@ -412,15 +412,15 @@ export class QueryBuilderHelper {
return void qb.forUpdate();
}

const meta = this.metadata.get(this.entityName);
const meta = this.metadata.get(this.entityName, false, false);

if (lockMode === LockMode.OPTIMISTIC && meta && !meta.versionProperty) {
throw ValidationError.lockFailed(this.entityName);
}
}

updateVersionProperty(qb: KnexQueryBuilder): void {
const meta = this.metadata.get(this.entityName);
const meta = this.metadata.get(this.entityName, false, false);

if (!meta || !meta.versionProperty) {
return;
Expand Down Expand Up @@ -467,7 +467,7 @@ export class QueryBuilderHelper {

private fieldName(field: string, alias?: string): string {
const entityName = this.aliasMap[alias!] || this.entityName;
const meta = this.metadata.get(entityName);
const meta = this.metadata.get(entityName, false, false);
const prop = meta ? meta.properties[field] : false;

return prop ? prop.fieldName : field;
Expand Down
4 changes: 4 additions & 0 deletions lib/utils/ValidationError.ts
Expand Up @@ -120,6 +120,10 @@ export class ValidationError extends Error {
return new ValidationError(`${name} not found (${inspect(where)})`);
}

static missingMetadata(entity: string): ValidationError {
return new ValidationError(`Metadata for entity ${entity} not found`);
}

private static fromMessage(meta: EntityMetadata, prop: EntityProperty, message: string): ValidationError {
return new ValidationError(`${meta.name}.${prop.name} ${message}`);
}
Expand Down
5 changes: 5 additions & 0 deletions tests/MetadataValidator.test.ts
Expand Up @@ -103,4 +103,9 @@ describe('MetadataValidator', () => {
})).rejects.toThrowError(`Entity 'Author2' extends unknown base entity 'BaseEntity2', please make sure to provide it in 'entities' array when initializing the ORM`);
});

test('MetadataStorage.get throws when no metadata found', async () => {
const storage = new MetadataStorage({});
expect(() => storage.get('Test')).toThrowError('Metadata for entity Test not found');
});

});

0 comments on commit 33ce7d3

Please sign in to comment.