Refactor event bus and queue system; detect cycles in DI realization and make
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
@@ -1,14 +1,12 @@
|
||||
import {ModelKey, QueryRow, QuerySource} from '../types'
|
||||
import {Container, Inject, Instantiable, isInstantiable, StaticClass} from '../../di'
|
||||
import {Container, Inject, Instantiable, isInstantiable} from '../../di'
|
||||
import {DatabaseService} from '../DatabaseService'
|
||||
import {ModelBuilder} from './ModelBuilder'
|
||||
import {getFieldsMeta, ModelField} from './Field'
|
||||
import {deepCopy, Collection, Awaitable, uuid4, isKeyof, Pipeline} from '../../util'
|
||||
import {deepCopy, Collection, uuid4, isKeyof, Pipeline} from '../../util'
|
||||
import {EscapeValueObject} from '../dialect/SQLDialect'
|
||||
import {AppClass} from '../../lifecycle/AppClass'
|
||||
import {Logging} from '../../service/Logging'
|
||||
import {Connection} from '../connection/Connection'
|
||||
import {Bus, Dispatchable, EventSubscriber, EventSubscriberEntry, EventSubscription} from '../../event/types'
|
||||
import {ModelRetrievedEvent} from './events/ModelRetrievedEvent'
|
||||
import {ModelSavingEvent} from './events/ModelSavingEvent'
|
||||
import {ModelSavedEvent} from './events/ModelSavedEvent'
|
||||
@@ -16,23 +14,21 @@ import {ModelUpdatingEvent} from './events/ModelUpdatingEvent'
|
||||
import {ModelUpdatedEvent} from './events/ModelUpdatedEvent'
|
||||
import {ModelCreatingEvent} from './events/ModelCreatingEvent'
|
||||
import {ModelCreatedEvent} from './events/ModelCreatedEvent'
|
||||
import {EventBus} from '../../event/EventBus'
|
||||
import {Relation, RelationValue} from './relation/Relation'
|
||||
import {HasOne} from './relation/HasOne'
|
||||
import {HasMany} from './relation/HasMany'
|
||||
import {HasOneOrMany} from './relation/HasOneOrMany'
|
||||
import {Scope, ScopeClosure} from './scope/Scope'
|
||||
import {LocalBus} from '../../support/bus/LocalBus'
|
||||
import {ModelEvent} from './events/ModelEvent'
|
||||
|
||||
/**
|
||||
* Base for classes that are mapped to tables in a database.
|
||||
*/
|
||||
export abstract class Model<T extends Model<T>> extends AppClass implements Bus {
|
||||
export abstract class Model<T extends Model<T>> extends LocalBus<ModelEvent<T>> {
|
||||
@Inject()
|
||||
protected readonly logging!: Logging
|
||||
|
||||
@Inject()
|
||||
protected readonly bus!: EventBus
|
||||
|
||||
/**
|
||||
* The name of the connection this model should run through.
|
||||
* @type string
|
||||
@@ -100,12 +96,6 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
||||
*/
|
||||
protected originalSourceRow?: QueryRow
|
||||
|
||||
/**
|
||||
* Collection of event subscribers, by their events.
|
||||
* @protected
|
||||
*/
|
||||
protected modelEventBusSubscribers: Collection<EventSubscriberEntry<any>> = new Collection<EventSubscriberEntry<any>>()
|
||||
|
||||
/**
|
||||
* Cache of relation instances by property accessor.
|
||||
* This is used by the `@Relation()` decorator to cache Relation instances.
|
||||
@@ -257,7 +247,7 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
||||
this.setFieldFromObject(field.modelKey, field.databaseKey, row)
|
||||
})
|
||||
|
||||
await this.dispatch(new ModelRetrievedEvent<T>(this as any))
|
||||
await this.push(new ModelRetrievedEvent<T>(this as any))
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -627,11 +617,11 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
||||
* @param withoutTimestamps
|
||||
*/
|
||||
public async save({ withoutTimestamps = false } = {}): Promise<Model<T>> {
|
||||
await this.dispatch(new ModelSavingEvent<T>(this as any))
|
||||
await this.push(new ModelSavingEvent<T>(this as any))
|
||||
const ctor = this.constructor as typeof Model
|
||||
|
||||
if ( this.exists() && this.isDirty() ) {
|
||||
await this.dispatch(new ModelUpdatingEvent<T>(this as any))
|
||||
await this.push(new ModelUpdatingEvent<T>(this as any))
|
||||
|
||||
if ( !withoutTimestamps && ctor.timestamps && ctor.UPDATED_AT ) {
|
||||
(this as any)[ctor.UPDATED_AT] = new Date()
|
||||
@@ -652,9 +642,9 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
||||
await this.assumeFromSource(data)
|
||||
}
|
||||
|
||||
await this.dispatch(new ModelUpdatedEvent<T>(this as any))
|
||||
await this.push(new ModelUpdatedEvent<T>(this as any))
|
||||
} else if ( !this.exists() ) {
|
||||
await this.dispatch(new ModelCreatingEvent<T>(this as any))
|
||||
await this.push(new ModelCreatingEvent<T>(this as any))
|
||||
|
||||
if ( !withoutTimestamps ) {
|
||||
if ( ctor.timestamps && ctor.CREATED_AT ) {
|
||||
@@ -685,10 +675,10 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
||||
await this.assumeFromSource(data)
|
||||
}
|
||||
|
||||
await this.dispatch(new ModelCreatedEvent<T>(this as any))
|
||||
await this.push(new ModelCreatedEvent<T>(this as any))
|
||||
}
|
||||
|
||||
await this.dispatch(new ModelSavedEvent<T>(this as any))
|
||||
await this.push(new ModelSavedEvent<T>(this as any))
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -825,13 +815,6 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
||||
return !this.is(other)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Pipe instance containing this model instance.
|
||||
*/
|
||||
public pipe<TOut>(pipeline: Pipeline<this, TOut>): TOut {
|
||||
return pipeline.apply(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a wrapped function that compares whether the given model field
|
||||
* on the current instance differs from the originally fetched value.
|
||||
@@ -886,46 +869,6 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
|
||||
(this as any)[thisFieldName] = object[objectFieldName]
|
||||
}
|
||||
|
||||
subscribe<EventT extends Dispatchable>(event: StaticClass<EventT, Instantiable<EventT>>, subscriber: EventSubscriber<EventT>): Awaitable<EventSubscription> {
|
||||
const entry: EventSubscriberEntry<EventT> = {
|
||||
id: uuid4(),
|
||||
event,
|
||||
subscriber,
|
||||
}
|
||||
|
||||
this.modelEventBusSubscribers.push(entry)
|
||||
return this.buildSubscription(entry.id)
|
||||
}
|
||||
|
||||
unsubscribe<EventT extends Dispatchable>(subscriber: EventSubscriber<EventT>): Awaitable<void> {
|
||||
this.modelEventBusSubscribers = this.modelEventBusSubscribers.where('subscriber', '!=', subscriber)
|
||||
}
|
||||
|
||||
async dispatch(event: Dispatchable): Promise<void> {
|
||||
const eventClass: StaticClass<typeof event, typeof event> = event.constructor as StaticClass<Dispatchable, Dispatchable>
|
||||
await this.modelEventBusSubscribers.where('event', '=', eventClass)
|
||||
.promiseMap(entry => entry.subscriber(event))
|
||||
|
||||
await this.bus.dispatch(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an EventSubscription object for the subscriber of the given ID.
|
||||
* @param id
|
||||
* @protected
|
||||
*/
|
||||
protected buildSubscription(id: string): EventSubscription {
|
||||
let subscribed = true
|
||||
return {
|
||||
unsubscribe: (): Awaitable<void> => {
|
||||
if ( subscribed ) {
|
||||
this.modelEventBusSubscribers = this.modelEventBusSubscribers.where('id', '!=', id)
|
||||
subscribed = false
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new one-to-one relation instance. Should be called from a method on the model:
|
||||
*
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right after a model is inserted.
|
||||
*/
|
||||
export class ModelCreatedEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelCreatedEvent'
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right before a model is inserted.
|
||||
*/
|
||||
export class ModelCreatingEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelCreatingEvent'
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right after a model is deleted.
|
||||
*/
|
||||
export class ModelDeletedEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelDeletedEvent'
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right before a model is deleted.
|
||||
*/
|
||||
export class ModelDeletingEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelDeletingEvent'
|
||||
}
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
import {Model} from '../Model'
|
||||
import {Event} from '../../../event/Event'
|
||||
import {JSONState} from '../../../util'
|
||||
import {BaseEvent} from '../../../support/bus'
|
||||
import {Awaitable} from '../../../util'
|
||||
|
||||
/**
|
||||
* Base class for events that concern an instance of a model.
|
||||
* @fixme support serialization
|
||||
*/
|
||||
export abstract class ModelEvent<T extends Model<T>> extends Event {
|
||||
/**
|
||||
* The instance of the model.
|
||||
*/
|
||||
public instance!: T
|
||||
|
||||
export abstract class ModelEvent<T extends Model<T>> extends BaseEvent {
|
||||
constructor(
|
||||
instance?: T,
|
||||
public readonly instance: T,
|
||||
) {
|
||||
super()
|
||||
if ( instance ) {
|
||||
this.instance = instance
|
||||
}
|
||||
}
|
||||
|
||||
// TODO implement serialization here
|
||||
dehydrate(): Promise<JSONState> {
|
||||
return Promise.resolve({})
|
||||
}
|
||||
|
||||
rehydrate(/* state: JSONState */): void | Promise<void> {
|
||||
return undefined
|
||||
shouldBroadcast(): Awaitable<boolean> {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right after a model's data is loaded from the source.
|
||||
*/
|
||||
export class ModelRetrievedEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelRetrievedEvent'
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right after a model is persisted to the source.
|
||||
*/
|
||||
export class ModelSavedEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelSavedEvent'
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right before a model is persisted to the source.
|
||||
*/
|
||||
export class ModelSavingEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelSavingEvent'
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right after a model's data is updated.
|
||||
*/
|
||||
export class ModelUpdatedEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelUpdatedEvent'
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ import {ModelEvent} from './ModelEvent'
|
||||
* Event fired right before a model's data is updated.
|
||||
*/
|
||||
export class ModelUpdatingEvent<T extends Model<T>> extends ModelEvent<T> {
|
||||
|
||||
eventName = '@extollo/lib.ModelUpdatingEvent'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user