Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): pass entity as parameter in onCreate and onUpdate #564

Merged
merged 1 commit into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/core/src/decorators/Enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { ReferenceType } from '../entity';
import { PropertyOptions } from '.';
import { EntityProperty, AnyEntity, Dictionary } from '../typings';

export function Enum(options: EnumOptions | (() => Dictionary) = {}): Function {
export function Enum(options: EnumOptions<AnyEntity> | (() => Dictionary) = {}): Function {
return function (target: AnyEntity, propertyName: string) {
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
options = options instanceof Function ? { items: options } : options;
meta.properties[propertyName] = Object.assign({ name: propertyName, reference: ReferenceType.SCALAR, enum: true }, options) as EntityProperty;
};
}

export interface EnumOptions extends PropertyOptions {
export interface EnumOptions<T> extends PropertyOptions<T> {
items?: (number | string)[] | (() => Dictionary);
}
10 changes: 5 additions & 5 deletions packages/core/src/decorators/ManyToMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import { EntityValidator, ReferenceType } from '../entity';
import { EntityName, EntityProperty, AnyEntity } from '../typings';
import { QueryOrder } from '../enums';

export function ManyToMany<T extends AnyEntity<T>>(
entity?: ManyToManyOptions<T> | string | (() => EntityName<T>),
export function ManyToMany<T extends AnyEntity<T>, O extends AnyEntity<O>>(
entity?: ManyToManyOptions<T, O> | string | (() => EntityName<T>),
mappedBy?: (string & keyof T) | ((e: T) => any),
options: Partial<ManyToManyOptions<T>> = {},
options: Partial<ManyToManyOptions<T, O>> = {},
) {
return function (target: AnyEntity, propertyName: string) {
options = Utils.isObject<ManyToManyOptions<T>>(entity) ? entity : { ...options, entity, mappedBy };
options = Utils.isObject<ManyToManyOptions<T, O>>(entity) ? entity : { ...options, entity, mappedBy };
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
EntityValidator.validateSingleDecorator(meta, propertyName);
const property = { name: propertyName, reference: ReferenceType.MANY_TO_MANY } as EntityProperty<T>;
meta.properties[propertyName] = Object.assign(property, options);
};
}

export interface ManyToManyOptions<T extends AnyEntity<T>> extends ReferenceOptions<T> {
export interface ManyToManyOptions<T extends AnyEntity<T>, O extends AnyEntity<O>> extends ReferenceOptions<T, O> {
owner?: boolean;
inversedBy?: (string & keyof T) | ((e: T) => any);
mappedBy?: (string & keyof T) | ((e: T) => any);
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/decorators/ManyToOne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import { Utils } from '../utils';
import { EntityValidator, ReferenceType } from '../entity';
import { AnyEntity, EntityName, EntityProperty } from '../typings';

export function ManyToOne<T extends AnyEntity<T>>(
entity: ManyToOneOptions<T> | string | ((e?: any) => EntityName<T>) = {},
options: Partial<ManyToOneOptions<T>> = {},
export function ManyToOne<T extends AnyEntity<T>, O extends AnyEntity<O>>(
entity: ManyToOneOptions<T, O> | string | ((e?: any) => EntityName<T>) = {},
options: Partial<ManyToOneOptions<T, O>> = {},
) {
return function (target: AnyEntity, propertyName: string) {
options = Utils.isObject<ManyToOneOptions<T>>(entity) ? entity : { ...options, entity };
options = Utils.isObject<ManyToOneOptions<T, O>>(entity) ? entity : { ...options, entity };
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
EntityValidator.validateSingleDecorator(meta, propertyName);
const property = { name: propertyName, reference: ReferenceType.MANY_TO_ONE } as EntityProperty;
meta.properties[propertyName] = Object.assign(property, options);
};
}

export interface ManyToOneOptions<T extends AnyEntity<T>> extends ReferenceOptions<T> {
export interface ManyToOneOptions<T extends AnyEntity<T>, O extends AnyEntity<O>> extends ReferenceOptions<T, O> {
inversedBy?: (string & keyof T) | ((e: T) => any);
wrappedReference?: boolean;
primary?: boolean;
Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/decorators/OneToMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { EntityValidator, ReferenceType } from '../entity';
import { QueryOrder } from '../enums';
import { EntityName, EntityProperty, AnyEntity } from '../typings';

export function createOneToDecorator<T extends AnyEntity<T>>(
entity?: OneToManyOptions<T> | string | ((e?: any) => EntityName<T>),
export function createOneToDecorator<T extends AnyEntity<T>, O extends AnyEntity<O>>(
entity?: OneToManyOptions<T, O> | string | ((e?: any) => EntityName<T>),
mappedBy?: (string & keyof T) | ((e: T) => any),
options?: Partial<OneToManyOptions<T>>,
options?: Partial<OneToManyOptions<T, O>>,
reference?: ReferenceType,
) {
return function (target: AnyEntity, propertyName: string) {
options = Utils.isObject<OneToManyOptions<T>>(entity) ? entity : { ...options, entity, mappedBy };
options = Utils.isObject<OneToManyOptions<T, O>>(entity) ? entity : { ...options, entity, mappedBy };
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
EntityValidator.validateSingleDecorator(meta, propertyName);

Expand All @@ -22,15 +22,15 @@ export function createOneToDecorator<T extends AnyEntity<T>>(
};
}

export function OneToMany<T extends AnyEntity<T>>(
entity: OneToManyOptions<T> | string | ((e?: any) => EntityName<T>),
export function OneToMany<T extends AnyEntity<T>, O extends AnyEntity<O>>(
entity: OneToManyOptions<T, O> | string | ((e?: any) => EntityName<T>),
mappedBy?: (string & keyof T) | ((e: T) => any),
options: Partial<OneToManyOptions<T>> = {},
options: Partial<OneToManyOptions<T, O>> = {},
) {
return createOneToDecorator(entity, mappedBy, options, ReferenceType.ONE_TO_MANY);
}

export type OneToManyOptions<T extends AnyEntity<T>> = ReferenceOptions<T> & {
export type OneToManyOptions<T extends AnyEntity<T>, O extends AnyEntity<O>> = ReferenceOptions<T, O> & {
entity?: string | (() => EntityName<T>);
orphanRemoval?: boolean;
orderBy?: { [field: string]: QueryOrder };
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/decorators/OneToOne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { ReferenceType } from '../entity';
import { createOneToDecorator, OneToManyOptions } from './OneToMany';
import { EntityName, AnyEntity } from '../typings';

export function OneToOne<T extends AnyEntity<T>>(
entity?: OneToOneOptions<T> | string | ((e?: any) => EntityName<T>),
export function OneToOne<T extends AnyEntity<T>, O extends AnyEntity<O>>(
entity?: OneToOneOptions<T, O> | string | ((e?: any) => EntityName<T>),
mappedBy?: (string & keyof T) | ((e: T) => any),
options: Partial<OneToOneOptions<T>> = {},
options: Partial<OneToOneOptions<T, O>> = {},
) {
return createOneToDecorator<T>(entity as string, mappedBy, options, ReferenceType.ONE_TO_ONE);
return createOneToDecorator<T, O>(entity as string, mappedBy, options, ReferenceType.ONE_TO_ONE);
}

export interface OneToOneOptions<T extends AnyEntity<T>> extends Partial<Omit<OneToManyOptions<T>, 'orderBy'>> {
export interface OneToOneOptions<T extends AnyEntity<T>, O extends AnyEntity<O>> extends Partial<Omit<OneToManyOptions<T, O>, 'orderBy'>> {
owner?: boolean;
inversedBy?: (string & keyof T) | ((e: T) => any);
wrappedReference?: boolean;
Expand Down
10 changes: 5 additions & 5 deletions packages/core/src/decorators/PrimaryKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ReferenceType } from '../entity';
import { PropertyOptions } from '.';
import { AnyEntity, EntityProperty } from '../typings';

function createDecorator(options: PrimaryKeyOptions | SerializedPrimaryKeyOptions, serialized: boolean): Function {
function createDecorator<T extends AnyEntity<T>>(options: PrimaryKeyOptions<T> | SerializedPrimaryKeyOptions<T>, serialized: boolean): Function {
return function (target: AnyEntity, propertyName: string) {
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
const k = serialized ? 'serializedPrimaryKey' as const : 'primary' as const;
Expand All @@ -12,16 +12,16 @@ function createDecorator(options: PrimaryKeyOptions | SerializedPrimaryKeyOption
};
}

export function PrimaryKey(options: PrimaryKeyOptions = {}): Function {
export function PrimaryKey<T extends AnyEntity<T>>(options: PrimaryKeyOptions<T> = {}): Function {
return createDecorator(options, false);
}

export function SerializedPrimaryKey(options: SerializedPrimaryKeyOptions = {}): Function {
export function SerializedPrimaryKey<T extends AnyEntity<T>>(options: SerializedPrimaryKeyOptions<T> = {}): Function {
return createDecorator(options, true);
}

export interface PrimaryKeyOptions extends PropertyOptions { }
export interface PrimaryKeyOptions<T> extends PropertyOptions<T> { }

export interface SerializedPrimaryKeyOptions extends PropertyOptions {
export interface SerializedPrimaryKeyOptions<T> extends PropertyOptions<T> {
type?: any;
}
12 changes: 6 additions & 6 deletions packages/core/src/decorators/Property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Cascade, EntityValidator, ReferenceType } from '../entity';
import { EntityName, EntityProperty, AnyEntity, Constructor } from '../typings';
import { Type } from '../types';

export function Property(options: PropertyOptions = {}): Function {
export function Property<T extends AnyEntity<T>>(options: PropertyOptions<T> = {}): Function {
return function (target: AnyEntity, propertyName: string) {
const meta = MetadataStorage.getMetadataFromDecorator(target.constructor);
const desc = Object.getOwnPropertyDescriptor(target, propertyName) || {};
Expand Down Expand Up @@ -32,16 +32,16 @@ export function Property(options: PropertyOptions = {}): Function {
};
}

export type PropertyOptions = {
export type PropertyOptions<T extends AnyEntity<T>> = {
name?: string;
fieldName?: string;
fieldNames?: string[];
customType?: Type<any>;
columnType?: string;
type?: 'string' | 'number' | 'boolean' | 'bigint' | 'ObjectId' | string | object | bigint | Date | Constructor<Type<any>> | Type<any>;
length?: any;
onCreate?: () => any;
onUpdate?: () => any;
length?: number;
onCreate?: (entity: T) => any;
onUpdate?: (entity: T) => any;
default?: string | number | boolean | null;
defaultRaw?: string;
formula?: string | ((alias: string) => string);
Expand All @@ -56,7 +56,7 @@ export type PropertyOptions = {
serializedPrimaryKey?: boolean;
};

export interface ReferenceOptions<T extends AnyEntity<T>> extends PropertyOptions {
export interface ReferenceOptions<T extends AnyEntity<T>, O extends AnyEntity<O>> extends PropertyOptions<O> {
entity?: string | (() => EntityName<T>);
cascade?: Cascade[];
eager?: boolean;
Expand Down
50 changes: 25 additions & 25 deletions packages/core/src/metadata/EntitySchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ import { Utils } from '../utils';
type CollectionItem<T> = T extends Collection<infer K> ? K : T;
type TypeType = string | NumberConstructor | StringConstructor | BooleanConstructor | DateConstructor | ArrayConstructor | Constructor<Type<any>>;
type TypeDef<T> = { type: TypeType } | { customType: Type<any> } | { entity: string | (() => string | EntityName<T>) };
type Property<T> =
| ({ reference: ReferenceType.MANY_TO_ONE | 'm:1' } & TypeDef<T> & ManyToOneOptions<T>)
| ({ reference: ReferenceType.ONE_TO_ONE | '1:1' } & TypeDef<T> & OneToOneOptions<T>)
| ({ reference: ReferenceType.ONE_TO_MANY | '1:m' } & TypeDef<T> & OneToManyOptions<T>)
| ({ reference: ReferenceType.MANY_TO_MANY | 'm:n' } & TypeDef<T> & ManyToManyOptions<T>)
| ({ reference: ReferenceType.EMBEDDED | 'embedded' } & TypeDef<T> & EmbeddedOptions & PropertyOptions)
| ({ enum: true } & EnumOptions)
| (TypeDef<T> & PropertyOptions);
type Property<T, O> =
| ({ reference: ReferenceType.MANY_TO_ONE | 'm:1' } & TypeDef<T> & ManyToOneOptions<T, O>)
| ({ reference: ReferenceType.ONE_TO_ONE | '1:1' } & TypeDef<T> & OneToOneOptions<T, O>)
| ({ reference: ReferenceType.ONE_TO_MANY | '1:m' } & TypeDef<T> & OneToManyOptions<T, O>)
| ({ reference: ReferenceType.MANY_TO_MANY | 'm:n' } & TypeDef<T> & ManyToManyOptions<T, O>)
| ({ reference: ReferenceType.EMBEDDED | 'embedded' } & TypeDef<T> & EmbeddedOptions & PropertyOptions<O>)
| ({ enum: true } & EnumOptions<O>)
| (TypeDef<T> & PropertyOptions<O>);
type PropertyKey<T, U> = NonFunctionPropertyNames<Omit<T, keyof U>>;
type Metadata<T, U> =
& Omit<Partial<EntityMetadata<T>>, 'name' | 'properties'>
& ({ name: string } | { class: Constructor<T>; name?: string })
& { properties?: { [K in PropertyKey<T, U> & string]-?: Property<CollectionItem<NonNullable<T[K]>>> } };
& { properties?: { [K in PropertyKey<T, U> & string]-?: Property<CollectionItem<NonNullable<T[K]>>, T> } };

export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntity<T> | undefined = undefined> {

Expand All @@ -48,7 +48,7 @@ export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntit
return schema;
}

addProperty(name: string & keyof T, type?: TypeType, options: PropertyOptions | EntityProperty = {}): void {
addProperty(name: string & keyof T, type?: TypeType, options: PropertyOptions<T> | EntityProperty = {}): void {
const rename = <U> (data: U, from: string, to: string): void => {
if (options[from] && !options[to]) {
options[to] = [options[from]];
Expand Down Expand Up @@ -80,7 +80,7 @@ export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntit
this._meta.properties[name] = prop;
}

addEnum(name: string & keyof T, type?: TypeType, options: EnumOptions = {}): void {
addEnum(name: string & keyof T, type?: TypeType, options: EnumOptions<T> = {}): void {
if (options.items instanceof Function) {
options.items = Utils.extractEnumValues(options.items());
}
Expand All @@ -89,15 +89,15 @@ export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntit
this.addProperty(name, this.internal ? type : type || 'enum', prop);
}

addVersion(name: string & keyof T, type: TypeType, options: PropertyOptions = {}): void {
addVersion(name: string & keyof T, type: TypeType, options: PropertyOptions<T> = {}): void {
this.addProperty(name, type, { version: true, ...options });
}

addPrimaryKey(name: string & keyof T, type: TypeType, options: PrimaryKeyOptions = {}): void {
addPrimaryKey(name: string & keyof T, type: TypeType, options: PrimaryKeyOptions<T> = {}): void {
this.addProperty(name, type, { primary: true, ...options });
}

addSerializedPrimaryKey(name: string & keyof T, type: TypeType, options: SerializedPrimaryKeyOptions = {}): void {
addSerializedPrimaryKey(name: string & keyof T, type: TypeType, options: SerializedPrimaryKeyOptions<T> = {}): void {
this._meta.serializedPrimaryKey = name;
this.addProperty(name, type, options);
}
Expand All @@ -112,13 +112,13 @@ export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntit
} as EntityProperty<T>;
}

addManyToOne<K = object>(name: string & keyof T, type: TypeType, options: ManyToOneOptions<K>): void {
const prop = { reference: ReferenceType.MANY_TO_ONE, cascade: [Cascade.PERSIST, Cascade.MERGE], ...options };
addManyToOne<K = object>(name: string & keyof T, type: TypeType, options: ManyToOneOptions<K, T>): void {
const prop = { reference: ReferenceType.MANY_TO_ONE, cascade: [Cascade.PERSIST, Cascade.MERGE], ...options } as unknown as EntityProperty<T>;
Utils.defaultValue(prop, 'nullable', prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL));
this.addProperty(name, type, prop);
}

addManyToMany<K = object>(name: string & keyof T, type: TypeType, options: ManyToManyOptions<K>): void {
addManyToMany<K = object>(name: string & keyof T, type: TypeType, options: ManyToManyOptions<K, T>): void {
options.fixedOrder = options.fixedOrder || !!options.fixedOrderColumn;

if (!options.owner && !options.mappedBy) {
Expand All @@ -129,17 +129,17 @@ export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntit
Utils.renameKey(options, 'mappedBy', 'inversedBy');
}

const prop = { reference: ReferenceType.MANY_TO_MANY, cascade: [Cascade.PERSIST, Cascade.MERGE], ...options };
const prop = { reference: ReferenceType.MANY_TO_MANY, cascade: [Cascade.PERSIST, Cascade.MERGE], ...options } as PropertyOptions<T>;
this.addProperty(name, type, prop);
}

addOneToMany<K = object>(name: string & keyof T, type: TypeType, options: OneToManyOptions<K>): void {
const prop = { reference: ReferenceType.ONE_TO_MANY, cascade: [Cascade.PERSIST, Cascade.MERGE], ...options };
addOneToMany<K = object>(name: string & keyof T, type: TypeType, options: OneToManyOptions<K, T>): void {
const prop = { reference: ReferenceType.ONE_TO_MANY, cascade: [Cascade.PERSIST, Cascade.MERGE], ...options } as PropertyOptions<T>;
this.addProperty(name, type, prop);
}

addOneToOne<K = object>(name: string & keyof T, type: TypeType, options: OneToOneOptions<K>): void {
const prop = { reference: ReferenceType.ONE_TO_ONE, cascade: [Cascade.PERSIST, Cascade.MERGE], ...options };
addOneToOne<K = object>(name: string & keyof T, type: TypeType, options: OneToOneOptions<K, T>): void {
const prop = { reference: ReferenceType.ONE_TO_ONE, cascade: [Cascade.PERSIST, Cascade.MERGE], ...options } as unknown as EntityProperty<T>;
Utils.defaultValue(prop, 'nullable', prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL));
Utils.defaultValue(prop, 'owner', !!prop.inversedBy || !prop.mappedBy);
Utils.defaultValue(prop, 'unique', prop.owner);
Expand Down Expand Up @@ -213,15 +213,15 @@ export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntit
}

private initProperties(): void {
Object.entries<Property<T[keyof T]>>(this._meta.properties).forEach(([name, options]) => {
Object.entries<Property<T, unknown>>(this._meta.properties as Dictionary).forEach(([name, options]) => {
options.type = 'customType' in options ? options.customType!.constructor.name : options.type;

switch ((options as EntityProperty).reference) {
case ReferenceType.ONE_TO_ONE:
this.addOneToOne(name as keyof T & string, options.type as string, options);
break;
case ReferenceType.ONE_TO_MANY:
this.addOneToMany(name as keyof T & string, options.type as string, options as OneToManyOptions<T>);
this.addOneToMany(name as keyof T & string, options.type as string, options as OneToManyOptions<T, AnyEntity>);
break;
case ReferenceType.MANY_TO_ONE:
this.addManyToOne(name as keyof T & string, options.type as string, options);
Expand Down Expand Up @@ -268,7 +268,7 @@ export class EntitySchema<T extends AnyEntity<T> = AnyEntity, U extends AnyEntit
}
}

private normalizeType(options: PropertyOptions | EntityProperty, type?: string | any | Constructor<Type>) {
private normalizeType(options: PropertyOptions<T> | EntityProperty, type?: string | any | Constructor<Type>) {
if ('entity' in options) {
if (Utils.isString(options.entity)) {
type = options.type = options.entity;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ export interface EntityProperty<T extends AnyEntity<T> = any> {
getterName?: keyof T;
cascade: Cascade[];
orphanRemoval?: boolean;
onCreate?: () => any;
onUpdate?: () => any;
onCreate?: (entity: T) => any;
onUpdate?: (entity: T) => any;
onDelete?: 'cascade' | 'no action' | 'set null' | 'set default' | string;
onUpdateIntegrity?: 'cascade' | 'no action' | 'set null' | 'set default' | string;
owner: boolean;
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/unit-of-work/ChangeSetPersister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ export class ChangeSetPersister {
}

if (prop.onCreate && changeSet.type === ChangeSetType.CREATE) {
changeSet.entity[prop.name] = changeSet.payload[prop.name] = prop.onCreate();
changeSet.entity[prop.name] = changeSet.payload[prop.name] = prop.onCreate(changeSet.entity);
}

if (prop.onUpdate && changeSet.type === ChangeSetType.UPDATE) {
changeSet.entity[prop.name] = changeSet.payload[prop.name] = prop.onUpdate();
changeSet.entity[prop.name] = changeSet.payload[prop.name] = prop.onUpdate(changeSet.entity);
}
}

Expand Down