ORM type refactor

This commit is contained in:
Garrett Mills 2022-04-05 09:38:58 -05:00
parent f6a7cac05c
commit 395e8e4d1c
30 changed files with 86 additions and 71 deletions

View File

@ -8,7 +8,7 @@ import {Awaitable, JSONState} from '../../../util'
* A basic ORM-driven user class. * A basic ORM-driven user class.
*/ */
@Injectable() @Injectable()
export class ORMUser extends Model<ORMUser> implements Authenticatable { export class ORMUser extends Model implements Authenticatable {
protected static table = 'users' protected static table = 'users'

View File

@ -18,7 +18,7 @@ export class ORMUserRepository extends AuthenticatableRepository {
/** Look up the user by their username. */ /** Look up the user by their username. */
getByIdentifier(id: AuthenticatableIdentifier): Awaitable<Maybe<Authenticatable>> { getByIdentifier(id: AuthenticatableIdentifier): Awaitable<Maybe<Authenticatable>> {
return (this.injector.getStaticOverride(ORMUser) as typeof ORMUser).query<ORMUser>() return (this.injector.getStaticOverride(ORMUser) as typeof ORMUser).query()
.where('username', '=', id) .where('username', '=', id)
.first() .first()
} }

View File

@ -1,7 +1,7 @@
import {Field, FieldType, Model} from '../../../orm' import {Field, FieldType, Model} from '../../../orm'
import {OAuth2Token} from '../types' import {OAuth2Token} from '../types'
export class OAuth2TokenModel extends Model<OAuth2TokenModel> implements OAuth2Token { export class OAuth2TokenModel extends Model implements OAuth2Token {
public static table = 'oauth2_tokens' public static table = 'oauth2_tokens'
public static key = 'oauth2_token_id' public static key = 'oauth2_token_id'

View File

@ -14,7 +14,7 @@ export class ORMTokenRepository extends TokenRepository {
async find(id: string): Promise<Maybe<OAuth2Token>> { async find(id: string): Promise<Maybe<OAuth2Token>> {
const idNum = parseInt(id, 10) const idNum = parseInt(id, 10)
if ( !isNaN(idNum) ) { if ( !isNaN(idNum) ) {
return OAuth2TokenModel.query<OAuth2TokenModel>() return OAuth2TokenModel.query()
.whereKey(idNum) .whereKey(idNum)
.first() .first()
} }

View File

@ -36,7 +36,13 @@ export function isInstantiableOf<T>(what: unknown, type: StaticClass<T, any>): w
/** /**
* Type that identifies a value as a static class, even if it is not instantiable. * Type that identifies a value as a static class, even if it is not instantiable.
*/ */
export type StaticClass<T, T2, TCtorParams extends any[] = any[]> = Function & {prototype: T} & { new (...args: TCtorParams) : T } & T2 // eslint-disable-line @typescript-eslint/ban-types export type StaticClass<T, T2, TCtorParams extends any[] = any[]> = T2 & StaticThis<T, TCtorParams> // eslint-disable-line @typescript-eslint/ban-types
/**
* Quasi-reference to a `this` type w/in a static member.
* @see https://github.com/microsoft/TypeScript/issues/5863#issuecomment-302861175
*/
export type StaticThis<T, TCtorParams extends any[]> = { new (...args: TCtorParams): T }
/** /**
* Type that identifies a value as a static class that instantiates to itself * Type that identifies a value as a static class that instantiates to itself

View File

@ -1,5 +1,5 @@
import {ModelKey, QueryRow, QuerySource} from '../types' import {ModelKey, QueryRow, QuerySource} from '../types'
import {Container, Inject, Instantiable, isInstantiable} from '../../di' import {Container, Inject, Instantiable, isInstantiable, StaticClass, StaticThis} from '../../di'
import {DatabaseService} from '../DatabaseService' import {DatabaseService} from '../DatabaseService'
import {ModelBuilder} from './ModelBuilder' import {ModelBuilder} from './ModelBuilder'
import {getFieldsMeta, ModelField} from './Field' import {getFieldsMeta, ModelField} from './Field'
@ -19,13 +19,13 @@ import {HasOne} from './relation/HasOne'
import {HasMany} from './relation/HasMany' import {HasMany} from './relation/HasMany'
import {HasOneOrMany} from './relation/HasOneOrMany' import {HasOneOrMany} from './relation/HasOneOrMany'
import {Scope, ScopeClosure} from './scope/Scope' import {Scope, ScopeClosure} from './scope/Scope'
import {LocalBus} from '../../support/bus/LocalBus' import {LocalBus} from '../../support/bus'
import {ModelEvent} from './events/ModelEvent' import {ModelEvent} from './events/ModelEvent'
/** /**
* Base for classes that are mapped to tables in a database. * Base for classes that are mapped to tables in a database.
*/ */
export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>> { export abstract class Model extends LocalBus<ModelEvent> {
@Inject() @Inject()
protected readonly logging!: Logging protected readonly logging!: Logging
@ -88,7 +88,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* Relations that should be eager-loaded by default. * Relations that should be eager-loaded by default.
* @protected * @protected
*/ */
protected with: (keyof T)[] = [] protected with: (keyof this)[] = []
/** /**
* The original row fetched from the database. * The original row fetched from the database.
@ -100,7 +100,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* Cache of relation instances by property accessor. * Cache of relation instances by property accessor.
* This is used by the `@Relation()` decorator to cache Relation instances. * This is used by the `@Relation()` decorator to cache Relation instances.
*/ */
public relationCache: Collection<{ accessor: string | symbol, relation: Relation<T, any, any> }> = new Collection<{accessor: string | symbol; relation: Relation<T, any, any>}>() public relationCache: Collection<{ accessor: string | symbol, relation: Relation<any, any, any> }> = new Collection<{accessor: string | symbol; relation: Relation<any, any, any>}>()
protected scopes: Collection<{ accessor: string | Instantiable<Scope>, scope: ScopeClosure }> = new Collection<{accessor: string | Instantiable<Scope>; scope: ScopeClosure}>() protected scopes: Collection<{ accessor: string | Instantiable<Scope>, scope: ScopeClosure }> = new Collection<{accessor: string | Instantiable<Scope>; scope: ScopeClosure}>()
@ -148,7 +148,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* const user = await UserModel.query<UserModel>().where('name', 'LIKE', 'John Doe').first() * const user = await UserModel.query<UserModel>().where('name', 'LIKE', 'John Doe').first()
* ``` * ```
*/ */
public static query<T2 extends Model<T2>>(): ModelBuilder<T2> { public static query<T2 extends Model>(this: StaticThis<T2, any[]> & typeof Model): ModelBuilder<T2> {
const builder = <ModelBuilder<T2>> Container.getContainer().make<ModelBuilder<T2>>(ModelBuilder, this) const builder = <ModelBuilder<T2>> Container.getContainer().make<ModelBuilder<T2>>(ModelBuilder, this)
const source: QuerySource = this.querySource() const source: QuerySource = this.querySource()
@ -174,7 +174,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
} else if ( this.constructor.length < 1 ) { } else if ( this.constructor.length < 1 ) {
// Otherwise, if we can instantiate the model without any arguments, // Otherwise, if we can instantiate the model without any arguments,
// do that and get the eager-loaded relations directly. // do that and get the eager-loaded relations directly.
const inst = Container.getContainer().make<Model<any>>(this) const inst = Container.getContainer().make<Model>(this)
if ( Array.isArray(inst.with) ) { if ( Array.isArray(inst.with) ) {
for (const relation of inst.with) { for (const relation of inst.with) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -189,7 +189,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
builder.withScopes(this.prototype.scopes) builder.withScopes(this.prototype.scopes)
} else if ( this.constructor.length < 1 ) { } else if ( this.constructor.length < 1 ) {
// Otherwise, try to instantiate the model if possible and load the scopes that way // Otherwise, try to instantiate the model if possible and load the scopes that way
const inst = Container.getContainer().make<Model<any>>(this) const inst = Container.getContainer().make<Model>(this)
builder.withScopes(inst.scopes) builder.withScopes(inst.scopes)
} }
@ -247,7 +247,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
this.setFieldFromObject(field.modelKey, field.databaseKey, row) this.setFieldFromObject(field.modelKey, field.databaseKey, row)
}) })
await this.push(new ModelRetrievedEvent<T>(this as any)) await this.push(new ModelRetrievedEvent<this>(this as any))
return this return this
} }
@ -329,9 +329,9 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* .update({ username: 'jdoe' }) * .update({ username: 'jdoe' })
* ``` * ```
*/ */
public query(): ModelBuilder<T> { public query(): ModelBuilder<this> {
const ModelClass = this.constructor as typeof Model const ModelClass = this.constructor as typeof Model
const builder = <ModelBuilder<T>> this.app().make<ModelBuilder<T>>(ModelBuilder, ModelClass) const builder = <ModelBuilder<this>> this.app().make<ModelBuilder<this>>(ModelBuilder, ModelClass)
const source: QuerySource = ModelClass.querySource() const source: QuerySource = ModelClass.querySource()
builder.connection(ModelClass.getConnection()) builder.connection(ModelClass.getConnection())
@ -352,6 +352,15 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
builder.withScopes(this.scopes) builder.withScopes(this.scopes)
return this.newBuilderInstance(builder)
}
/**
* Configure ModelBuilder instances that query this model.
* @param builder
* @protected
*/
protected newBuilderInstance(builder: ModelBuilder<this>): ModelBuilder<this> {
return builder return builder
} }
@ -365,7 +374,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* *
* @param key * @param key
*/ */
public static async findByKey<T2 extends Model<T2>>(key: ModelKey): Promise<undefined | T2> { public static async findByKey<T2 extends Model>(this: StaticThis<T2, any[]> & typeof Model, key: ModelKey): Promise<undefined | T2> {
return this.query<T2>() return this.query<T2>()
.where(this.qualifyKey(), '=', key) .where(this.qualifyKey(), '=', key)
.limit(1) .limit(1)
@ -376,7 +385,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
/** /**
* Get an array of all instances of this model. * Get an array of all instances of this model.
*/ */
public async all(): Promise<T[]> { public async all(): Promise<this[]> {
return this.query().get() return this.query().get()
.all() .all()
} }
@ -616,12 +625,12 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* *
* @param withoutTimestamps * @param withoutTimestamps
*/ */
public async save({ withoutTimestamps = false } = {}): Promise<Model<T>> { public async save({ withoutTimestamps = false } = {}): Promise<this> {
await this.push(new ModelSavingEvent<T>(this as any)) await this.push(new ModelSavingEvent<this>(this))
const ctor = this.constructor as typeof Model const ctor = this.constructor as typeof Model
if ( this.exists() && this.isDirty() ) { if ( this.exists() && this.isDirty() ) {
await this.push(new ModelUpdatingEvent<T>(this as any)) await this.push(new ModelUpdatingEvent<this>(this))
if ( !withoutTimestamps && ctor.timestamps && ctor.UPDATED_AT ) { if ( !withoutTimestamps && ctor.timestamps && ctor.UPDATED_AT ) {
(this as any)[ctor.UPDATED_AT] = new Date() (this as any)[ctor.UPDATED_AT] = new Date()
@ -642,9 +651,9 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
await this.assumeFromSource(data) await this.assumeFromSource(data)
} }
await this.push(new ModelUpdatedEvent<T>(this as any)) await this.push(new ModelUpdatedEvent<this>(this))
} else if ( !this.exists() ) { } else if ( !this.exists() ) {
await this.push(new ModelCreatingEvent<T>(this as any)) await this.push(new ModelCreatingEvent<this>(this))
if ( !withoutTimestamps ) { if ( !withoutTimestamps ) {
if ( ctor.timestamps && ctor.CREATED_AT ) { if ( ctor.timestamps && ctor.CREATED_AT ) {
@ -675,10 +684,10 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
await this.assumeFromSource(data) await this.assumeFromSource(data)
} }
await this.push(new ModelCreatedEvent<T>(this as any)) await this.push(new ModelCreatedEvent<this>(this))
} }
await this.push(new ModelSavedEvent<T>(this as any)) await this.push(new ModelSavedEvent<this>(this))
return this return this
} }
@ -745,7 +754,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* This returns a NEW instance of the SAME record by matching on * This returns a NEW instance of the SAME record by matching on
* the primary key. It does NOT change the current instance of the record. * the primary key. It does NOT change the current instance of the record.
*/ */
public async fresh(): Promise<Model<T> | undefined> { public async fresh(): Promise<this | undefined> {
return this.query() return this.query()
.where(this.qualifyKey(), '=', this.key()) .where(this.qualifyKey(), '=', this.key())
.limit(1) .limit(1)
@ -789,7 +798,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* *
* @param model * @param model
*/ */
public async populate(model: T): Promise<T> { public async populate(model: this): Promise<this> {
const row = this.toQueryRow() const row = this.toQueryRow()
delete row[this.keyName()] delete row[this.keyName()]
await model.assumeFromSource(row) await model.assumeFromSource(row)
@ -803,7 +812,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* *
* @param other * @param other
*/ */
public is(other: Model<any>): boolean { public is(other: Model): boolean {
return this.key() === other.key() && this.qualifyKey() === other.qualifyKey() return this.key() === other.key() && this.qualifyKey() === other.qualifyKey()
} }
@ -811,7 +820,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* Inverse of `is()`. * Inverse of `is()`.
* @param other * @param other
*/ */
public isNot(other: Model<any>): boolean { public isNot(other: Model): boolean {
return !this.is(other) return !this.is(other)
} }
@ -886,8 +895,8 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* @param foreignKeyOverride * @param foreignKeyOverride
* @param localKeyOverride * @param localKeyOverride
*/ */
public hasOne<T2 extends Model<T2>>(related: Instantiable<T2>, foreignKeyOverride?: keyof T & string, localKeyOverride?: keyof T2 & string): HasOne<T, T2> { public hasOne<T2 extends Model>(related: Instantiable<T2>, foreignKeyOverride?: keyof this & string, localKeyOverride?: keyof T2 & string): HasOne<this, T2> {
return new HasOne<T, T2>(this as unknown as T, this.make(related), foreignKeyOverride, localKeyOverride) return new HasOne<this, T2>(this, this.make(related), foreignKeyOverride, localKeyOverride)
} }
@ -908,8 +917,8 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* @param foreignKeyOverride * @param foreignKeyOverride
* @param localKeyOverride * @param localKeyOverride
*/ */
public hasMany<T2 extends Model<T2>>(related: Instantiable<T2>, foreignKeyOverride?: keyof T & string, localKeyOverride?: keyof T2 & string): HasMany<T, T2> { public hasMany<T2 extends Model>(related: Instantiable<T2>, foreignKeyOverride?: keyof this & string, localKeyOverride?: keyof T2 & string): HasMany<this, T2> {
return new HasMany<T, T2>(this as unknown as T, this.make(related), foreignKeyOverride, localKeyOverride) return new HasMany<this, T2>(this, this.make(related), foreignKeyOverride, localKeyOverride)
} }
/** /**
@ -935,7 +944,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* @param related * @param related
* @param relationName * @param relationName
*/ */
public belongsToOne<T2 extends Model<T2>>(related: Instantiable<T>, relationName: keyof T2): HasOne<T, T2> { public belongsToOne<T2 extends Model>(related: Instantiable<T2>, relationName: keyof T2): HasOne<this, T2> {
const relatedInst = this.make(related) as T2 const relatedInst = this.make(related) as T2
const relation = relatedInst.getRelation(relationName) const relation = relatedInst.getRelation(relationName)
@ -946,11 +955,11 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
const localKey = relation.localKey const localKey = relation.localKey
const foreignKey = relation.foreignKey const foreignKey = relation.foreignKey
if ( !isKeyof(localKey, this as unknown as T) || !isKeyof(foreignKey, relatedInst) ) { if ( !isKeyof(localKey, this) || !isKeyof(foreignKey, relatedInst) ) {
throw new TypeError('Local or foreign keys do not exist on the base model.') throw new TypeError('Local or foreign keys do not exist on the base model.')
} }
return new HasOne<T, T2>(this as unknown as T, relatedInst, localKey, foreignKey) return new HasOne(this, relatedInst, localKey, foreignKey)
} }
@ -977,7 +986,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* @param related * @param related
* @param relationName * @param relationName
*/ */
public belongsToMany<T2 extends Model<T2>>(related: Instantiable<T>, relationName: keyof T2): HasMany<T, T2> { public belongsToMany<T2 extends Model>(related: Instantiable<T2>, relationName: keyof T2): HasMany<this, T2> {
const relatedInst = this.make(related) as T2 const relatedInst = this.make(related) as T2
const relation = relatedInst.getRelation(relationName) const relation = relatedInst.getRelation(relationName)
@ -988,11 +997,11 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
const localKey = relation.localKey const localKey = relation.localKey
const foreignKey = relation.foreignKey const foreignKey = relation.foreignKey
if ( !isKeyof(localKey, this as unknown as T) || !isKeyof(foreignKey, relatedInst) ) { if ( !isKeyof(localKey, this) || !isKeyof(foreignKey, relatedInst) ) {
throw new TypeError('Local or foreign keys do not exist on the base model.') throw new TypeError('Local or foreign keys do not exist on the base model.')
} }
return new HasMany<T, T2>(this as unknown as T, relatedInst, localKey, foreignKey) return new HasMany(this, relatedInst, localKey, foreignKey)
} }
/** /**
@ -1000,7 +1009,7 @@ export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>>
* @param name * @param name
* @protected * @protected
*/ */
public getRelation<T2 extends Model<T2>>(name: keyof this): Relation<T, T2, RelationValue<T2>> { public getRelation<T2 extends Model>(name: keyof this): Relation<this, T2, RelationValue<T2>> {
const relFn = this[name] const relFn = this[name]
if ( relFn instanceof Relation ) { if ( relFn instanceof Relation ) {

View File

@ -11,7 +11,7 @@ import {Scope, ScopeClosure} from './scope/Scope'
/** /**
* Implementation of the abstract builder whose results yield instances of a given Model, `T`. * Implementation of the abstract builder whose results yield instances of a given Model, `T`.
*/ */
export class ModelBuilder<T extends Model<T>> extends AbstractBuilder<T> { export class ModelBuilder<T extends Model> extends AbstractBuilder<T> {
protected eagerLoadRelations: (keyof T)[] = [] protected eagerLoadRelations: (keyof T)[] = []
protected appliedScopes: Collection<{ accessor: string | Instantiable<Scope>, scope: ScopeClosure }> = new Collection<{accessor: string | Instantiable<Scope>; scope: ScopeClosure}>() protected appliedScopes: Collection<{ accessor: string | Instantiable<Scope>, scope: ScopeClosure }> = new Collection<{accessor: string | Instantiable<Scope>; scope: ScopeClosure}>()

View File

@ -9,7 +9,7 @@ import {collect, Collection} from '../../util'
/** /**
* Implementation of the result iterable that returns query results as instances of the defined model. * Implementation of the result iterable that returns query results as instances of the defined model.
*/ */
export class ModelResultIterable<T extends Model<T>> extends AbstractResultIterable<T> { export class ModelResultIterable<T extends Model> extends AbstractResultIterable<T> {
constructor( constructor(
public readonly builder: ModelBuilder<T>, public readonly builder: ModelBuilder<T>,
public readonly connection: Connection, public readonly connection: Connection,

View File

@ -13,19 +13,19 @@ export interface ModelSerialPayload extends JSONState {
@ObjectSerializer() @ObjectSerializer()
@Injectable() @Injectable()
export class ModelSerializer extends BaseSerializer<Model<any>, ModelSerialPayload> { export class ModelSerializer extends BaseSerializer<Model, ModelSerialPayload> {
@Inject() @Inject()
protected readonly canon!: Canon protected readonly canon!: Canon
protected async decodeSerial(serial: ModelSerialPayload): Promise<Model<any>> { protected async decodeSerial(serial: ModelSerialPayload): Promise<Model> {
const ModelClass = this.canon.getFromFullyQualified(serial.canonicalResolver) as typeof Model const ModelClass = this.canon.getFromFullyQualified(serial.canonicalResolver) as typeof Model
if ( !ModelClass || !(ModelClass.prototype instanceof Model) || !isInstantiable<Model<any>>(ModelClass) ) { if ( !ModelClass || !(ModelClass.prototype instanceof Model) || !isInstantiable<Model>(ModelClass) ) {
throw new ErrorWithContext('Cannot decode serialized model as canonical resolver is invalid', { throw new ErrorWithContext('Cannot decode serialized model as canonical resolver is invalid', {
serial, serial,
}) })
} }
let inst: Maybe<Model<any>> = this.make<Model<any>>(ModelClass) let inst: Maybe<Model> = this.make<Model>(ModelClass)
if ( serial.primaryKey ) { if ( serial.primaryKey ) {
inst = await ModelClass.query() inst = await ModelClass.query()
.whereKey(serial.primaryKey) .whereKey(serial.primaryKey)
@ -42,7 +42,7 @@ export class ModelSerializer extends BaseSerializer<Model<any>, ModelSerialPaylo
return inst return inst
} }
protected encodeActual(actual: Model<any>): Awaitable<ModelSerialPayload> { protected encodeActual(actual: Model): Awaitable<ModelSerialPayload> {
const ctor = actual.constructor as typeof Model const ctor = actual.constructor as typeof Model
const canonicalResolver = ctor.getFullyQualifiedCanonicalResolver() const canonicalResolver = ctor.getFullyQualifiedCanonicalResolver()
if ( !canonicalResolver ) { if ( !canonicalResolver ) {
@ -62,7 +62,7 @@ export class ModelSerializer extends BaseSerializer<Model<any>, ModelSerialPaylo
return '@extollo/lib.ModelSerializer' return '@extollo/lib.ModelSerializer'
} }
matchActual(some: Model<any>): boolean { matchActual(some: Model): boolean {
return some instanceof Model return some instanceof Model
} }
} }

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right after a model is inserted. * Event fired right after a model is inserted.
*/ */
export class ModelCreatedEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelCreatedEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelCreatedEvent' eventName = '@extollo/lib.ModelCreatedEvent'
} }

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right before a model is inserted. * Event fired right before a model is inserted.
*/ */
export class ModelCreatingEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelCreatingEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelCreatingEvent' eventName = '@extollo/lib.ModelCreatingEvent'
} }

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right after a model is deleted. * Event fired right after a model is deleted.
*/ */
export class ModelDeletedEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelDeletedEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelDeletedEvent' eventName = '@extollo/lib.ModelDeletedEvent'
} }

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right before a model is deleted. * Event fired right before a model is deleted.
*/ */
export class ModelDeletingEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelDeletingEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelDeletingEvent' eventName = '@extollo/lib.ModelDeletingEvent'
} }

View File

@ -6,7 +6,7 @@ import {Awaitable} from '../../../util'
* Base class for events that concern an instance of a model. * Base class for events that concern an instance of a model.
* @fixme support serialization * @fixme support serialization
*/ */
export abstract class ModelEvent<T extends Model<T>> extends BaseEvent { export abstract class ModelEvent<T extends Model = Model> extends BaseEvent {
constructor( constructor(
public readonly instance: T, public readonly instance: T,
) { ) {

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right after a model's data is loaded from the source. * Event fired right after a model's data is loaded from the source.
*/ */
export class ModelRetrievedEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelRetrievedEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelRetrievedEvent' eventName = '@extollo/lib.ModelRetrievedEvent'
} }

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right after a model is persisted to the source. * Event fired right after a model is persisted to the source.
*/ */
export class ModelSavedEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelSavedEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelSavedEvent' eventName = '@extollo/lib.ModelSavedEvent'
} }

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right before a model is persisted to the source. * Event fired right before a model is persisted to the source.
*/ */
export class ModelSavingEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelSavingEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelSavingEvent' eventName = '@extollo/lib.ModelSavingEvent'
} }

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right after a model's data is updated. * Event fired right after a model's data is updated.
*/ */
export class ModelUpdatedEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelUpdatedEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelUpdatedEvent' eventName = '@extollo/lib.ModelUpdatedEvent'
} }

View File

@ -4,6 +4,6 @@ import {ModelEvent} from './ModelEvent'
/** /**
* Event fired right before a model's data is updated. * Event fired right before a model's data is updated.
*/ */
export class ModelUpdatingEvent<T extends Model<T>> extends ModelEvent<T> { export class ModelUpdatingEvent<T extends Model> extends ModelEvent<T> {
eventName = '@extollo/lib.ModelUpdatingEvent' eventName = '@extollo/lib.ModelUpdatingEvent'
} }

View File

@ -6,7 +6,7 @@ import {RelationNotLoadedError} from './Relation'
/** /**
* One-to-many relation implementation. * One-to-many relation implementation.
*/ */
export class HasMany<T extends Model<T>, T2 extends Model<T2>> extends HasOneOrMany<T, T2, Collection<T2>> { export class HasMany<T extends Model, T2 extends Model> extends HasOneOrMany<T, T2, Collection<T2>> {
protected cachedValue?: Collection<T2> protected cachedValue?: Collection<T2>
protected cachedLoaded = false protected cachedLoaded = false

View File

@ -6,7 +6,7 @@ import {Maybe} from '../../../util'
/** /**
* One-to-one relation implementation. * One-to-one relation implementation.
*/ */
export class HasOne<T extends Model<T>, T2 extends Model<T2>> extends HasOneOrMany<T, T2, Maybe<T2>> { export class HasOne<T extends Model, T2 extends Model> extends HasOneOrMany<T, T2, Maybe<T2>> {
protected cachedValue?: T2 protected cachedValue?: T2
protected cachedLoaded = false protected cachedLoaded = false

View File

@ -9,7 +9,7 @@ import {Collection, toString} from '../../../util'
/** /**
* Base class for 1:1 and 1:M relations. * Base class for 1:1 and 1:M relations.
*/ */
export abstract class HasOneOrMany<T extends Model<T>, T2 extends Model<T2>, V extends RelationValue<T2>> extends Relation<T, T2, V> { export abstract class HasOneOrMany<T extends Model, T2 extends Model, V extends RelationValue<T2>> extends Relation<T, T2, V> {
protected constructor( protected constructor(
parent: T, parent: T,
related: T2, related: T2,

View File

@ -22,7 +22,7 @@ export class RelationNotLoadedError extends ErrorWithContext {
/** /**
* Base class for inter-model relation implementations. * Base class for inter-model relation implementations.
*/ */
export abstract class Relation<T extends Model<T>, T2 extends Model<T2>, V extends RelationValue<T2>> extends InjectionAware { export abstract class Relation<T extends Model, T2 extends Model, V extends RelationValue<T2>> extends InjectionAware {
protected constructor( protected constructor(
/** The model related from. */ /** The model related from. */
protected parent: T, protected parent: T,

View File

@ -5,7 +5,7 @@ import {Relation} from './Relation'
/** /**
* ModelBuilder instance that queries the related model in a relation. * ModelBuilder instance that queries the related model in a relation.
*/ */
export class RelationBuilder<T extends Model<T>> extends ModelBuilder<T> { export class RelationBuilder<T extends Model> extends ModelBuilder<T> {
constructor( constructor(
protected relation: Relation<any, T, any>, protected relation: Relation<any, T, any>,
) { ) {

View File

@ -14,7 +14,7 @@ export function Related(): MethodDecorator {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
descriptor.value = function(...args) { descriptor.value = function(...args) {
const model = this as Model<any> const model = this as Model
const cache = model.relationCache const cache = model.relationCache
const existing = cache.firstWhere('accessor', '=', propertyKey) const existing = cache.firstWhere('accessor', '=', propertyKey)

View File

@ -9,7 +9,7 @@ import {CanonicalDefinition} from '../../service/Canonical'
* Canonical unit responsible for loading the model classes defined by the application. * Canonical unit responsible for loading the model classes defined by the application.
*/ */
@Singleton() @Singleton()
export class Models extends CanonicalStatic<Model<any>, Instantiable<Model<any>>> { export class Models extends CanonicalStatic<Model, Instantiable<Model>> {
@Inject() @Inject()
protected readonly cli!: CommandLine protected readonly cli!: CommandLine
@ -24,7 +24,7 @@ export class Models extends CanonicalStatic<Model<any>, Instantiable<Model<any>>
this.cli.registerTemplate(templateModel) this.cli.registerTemplate(templateModel)
} }
public async initCanonicalItem(definition: CanonicalDefinition): Promise<Instantiable<Model<any>>> { public async initCanonicalItem(definition: CanonicalDefinition): Promise<Instantiable<Model>> {
const item = await super.initCanonicalItem(definition) const item = await super.initCanonicalItem(definition)
if ( !(item.prototype instanceof Model) ) { if ( !(item.prototype instanceof Model) ) {
throw new TypeError(`Invalid controller definition: ${definition.originalName}. Models must extend from @extollo/orm.Model.`) throw new TypeError(`Invalid controller definition: ${definition.originalName}. Models must extend from @extollo/orm.Model.`)

View File

@ -7,7 +7,7 @@ import {ModelBuilder} from '../model/ModelBuilder'
/** /**
* A model instance which stores records from the ORMCache driver. * A model instance which stores records from the ORMCache driver.
*/ */
export class CacheModel extends Model<CacheModel> { export class CacheModel extends Model {
protected static table = 'caches'; // FIXME allow configuring protected static table = 'caches'; // FIXME allow configuring
protected static key = 'cache_key'; protected static key = 'cache_key';
@ -22,7 +22,7 @@ export class CacheModel extends Model<CacheModel> {
public cacheExpires?: Date; public cacheExpires?: Date;
public static withCacheKey(key: string): ModelBuilder<CacheModel> { public static withCacheKey(key: string): ModelBuilder<CacheModel> {
return this.query<CacheModel>() return this.query()
.whereKey(key) .whereKey(key)
.whereProperty('cacheExpires', '>', new Date()) .whereProperty('cacheExpires', '>', new Date())
} }

View File

@ -11,7 +11,7 @@ export class ORMCache extends Cache {
} }
public async put(key: string, value: string, expires?: Date): Promise<void> { public async put(key: string, value: string, expires?: Date): Promise<void> {
let model = await CacheModel.findByKey<CacheModel>(key) let model = await CacheModel.findByKey(key)
if ( !model ) { if ( !model ) {
model = <CacheModel> Container.getContainer().make(CacheModel) model = <CacheModel> Container.getContainer().make(CacheModel)
} }

View File

@ -29,7 +29,7 @@ export class ORMSession extends Session {
throw new NoSessionKeyError() throw new NoSessionKeyError()
} }
const session = <SessionModel> await SessionModel.findByKey(this.key) const session = await SessionModel.findByKey(this.key)
if ( session ) { if ( session ) {
this.session = session this.session = session
this.data = this.session.json this.data = this.session.json

View File

@ -5,7 +5,7 @@ import {FieldType} from '../types'
/** /**
* Model used to fetch & store sessions from the ORMSession driver. * Model used to fetch & store sessions from the ORMSession driver.
*/ */
export class SessionModel extends Model<SessionModel> { export class SessionModel extends Model {
protected static table = 'sessions' // FIXME allow configuring protected static table = 'sessions' // FIXME allow configuring
protected static key = 'session_uuid' protected static key = 'session_uuid'