Skip to content

Commit

Permalink
feat(core): require mappedBy option for 1:m properties
Browse files Browse the repository at this point in the history
It was already required on type level, now it is also validated during init phase.
  • Loading branch information
B4nan committed Nov 5, 2023
1 parent 32d7c5f commit 716aa76
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 2 deletions.
4 changes: 4 additions & 0 deletions packages/core/src/errors.ts
Expand Up @@ -256,6 +256,10 @@ export class MetadataError<T extends AnyEntity = AnyEntity> extends ValidationEr
return this.fromMessage(meta, prop, `is defined as scalar @Property(), but its type is a discovered entity ${target.className}. Maybe you want to use @${suggestion}() decorator instead?`);
}

static fromMissingOption(meta: EntityMetadata, prop: EntityProperty, option: string) {
return this.fromMessage(meta, prop, `is missing '${option}' option`);
}

private static fromMessage(meta: EntityMetadata, prop: EntityProperty, message: string): MetadataError {
return new MetadataError(`${meta.className}.${prop.name} ${message}`);
}
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/metadata/MetadataValidator.ts
@@ -1,4 +1,4 @@
import type { EntityName, EntityMetadata, EntityProperty } from '../typings';
import type { EntityMetadata, EntityName, EntityProperty } from '../typings';
import { Utils } from '../utils';
import { MetadataError } from '../errors';
import { ReferenceKind } from '../enums';
Expand Down Expand Up @@ -141,6 +141,11 @@ export class MetadataValidator {
} else if (prop.mappedBy) {
const inverse = metadata.get(prop.type).properties[prop.mappedBy];
this.validateInverseSide(meta, prop, inverse, metadata);
} else {
// 1:m property has `mappedBy`
if (prop.kind === ReferenceKind.ONE_TO_MANY && !prop.mappedBy) {
throw MetadataError.fromMissingOption(meta, prop, 'mappedBy');
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion tests/MetadataValidator.test.ts
Expand Up @@ -21,7 +21,9 @@ describe('MetadataValidator', () => {
// one to many
meta.Test = { name: 'Test', className: 'Test', properties: {} };
meta.Test.root = meta.Test;
meta.Author.properties.tests = { name: 'tests', kind: ReferenceKind.ONE_TO_MANY, type: 'Test', mappedBy: 'foo' };
meta.Author.properties.tests = { name: 'tests', kind: ReferenceKind.ONE_TO_MANY, type: 'Test' };
expect(() => validator.validateEntityDefinition(new MetadataStorage(meta as any), 'Author')).toThrowError(`Author.tests is missing 'mappedBy' option`);
meta.Author.properties.tests.mappedBy = 'foo';
expect(() => validator.validateEntityDefinition(new MetadataStorage(meta as any), 'Author')).toThrowError(`Author.tests has unknown 'mappedBy' reference: Test.foo`);

meta.Test.properties.foo = { name: 'foo', kind: ReferenceKind.ONE_TO_ONE, type: 'Author' };
Expand Down

0 comments on commit 716aa76

Please sign in to comment.