ORM type refactor
This commit is contained in:
parent
f6a7cac05c
commit
395e8e4d1c
@ -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'
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 ) {
|
||||||
|
@ -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}>()
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
) {
|
) {
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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>,
|
||||||
) {
|
) {
|
||||||
|
@ -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)
|
||||||
|
@ -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.`)
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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'
|
||||||
|
Loading…
Reference in New Issue
Block a user