Add query executed event; forward model events to global event bus
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Garrett Mills 2021-06-05 08:36:35 -05:00
parent 61731c4ebd
commit c264d45927
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246
4 changed files with 91 additions and 1 deletions

View File

@ -2,6 +2,9 @@ import {ErrorWithContext} from '../../util'
import {QueryResult} from '../types' import {QueryResult} from '../types'
import {SQLDialect} from '../dialect/SQLDialect' import {SQLDialect} from '../dialect/SQLDialect'
import {AppClass} from '../../lifecycle/AppClass' import {AppClass} from '../../lifecycle/AppClass'
import {Inject, Injectable} from '../../di'
import {EventBus} from '../../event/EventBus'
import {QueryExecutedEvent} from './event/QueryExecutedEvent'
/** /**
* Error thrown when a connection is used before it is ready. * Error thrown when a connection is used before it is ready.
@ -18,7 +21,10 @@ export class ConnectionNotReadyError extends ErrorWithContext {
* Abstract base class for database connections. * Abstract base class for database connections.
* @abstract * @abstract
*/ */
@Injectable()
export abstract class Connection extends AppClass { export abstract class Connection extends AppClass {
@Inject()
protected bus!: EventBus
constructor( constructor(
/** /**
@ -64,4 +70,14 @@ export abstract class Connection extends AppClass {
// public abstract tables(database_name: string): Promise<Collection<Table>> // public abstract tables(database_name: string): Promise<Collection<Table>>
// public abstract table(database_name: string, table_name: string): Promise<Table | undefined> // public abstract table(database_name: string, table_name: string): Promise<Table | undefined>
/**
* Fire a QueryExecutedEvent for the given query string.
* @param query
* @protected
*/
protected async queryExecuted(query: string): Promise<void> {
const event = new QueryExecutedEvent(this.name, this, query)
await this.bus.dispatch(event)
}
} }

View File

@ -54,6 +54,7 @@ export class PostgresConnection extends Connection {
try { try {
const result = await this.client.query(query) const result = await this.client.query(query)
await this.queryExecuted(query)
return { return {
rows: collect(result.rows), rows: collect(result.rows),

View File

@ -0,0 +1,67 @@
import {Event} from '../../../event/Event'
import {Inject, Injectable} from '../../../di'
import {InvalidJSONStateError, JSONState} from '../../../util'
import {Connection} from '../Connection'
import {DatabaseService} from '../../DatabaseService'
/**
* Event fired when a query is executed.
*/
@Injectable()
export class QueryExecutedEvent extends Event {
@Inject()
protected database!: DatabaseService
/**
* The name of the connection where the query was executed.
* @protected
*/
public connectionName!: string
/**
* The connection where the query was executed.
*/
public connection!: Connection
/**
* The query that was executed.
*/
public query!: string
constructor(
connectionName?: string,
connection?: Connection,
query?: string,
) {
super()
if ( connectionName ) {
this.connectionName = connectionName
}
if ( connection ) {
this.connection = connection
}
if ( query ) {
this.query = query
}
}
async dehydrate(): Promise<JSONState> {
return {
connectionName: this.connectionName,
query: this.query,
}
}
rehydrate(state: JSONState): void {
if ( !state.connectionName || !state.query ) {
throw new InvalidJSONStateError('Missing connectionName or query from QueryExecutedEvent state.')
}
this.query = String(state.query)
this.connectionName = String(state.connectionName)
this.connection = this.database.get(this.connectionName)
}
}

View File

@ -16,13 +16,17 @@ import {ModelUpdatingEvent} from './events/ModelUpdatingEvent'
import {ModelUpdatedEvent} from './events/ModelUpdatedEvent' import {ModelUpdatedEvent} from './events/ModelUpdatedEvent'
import {ModelCreatingEvent} from './events/ModelCreatingEvent' import {ModelCreatingEvent} from './events/ModelCreatingEvent'
import {ModelCreatedEvent} from './events/ModelCreatedEvent' import {ModelCreatedEvent} from './events/ModelCreatedEvent'
import {EventBus} from '../../event/EventBus'
/** /**
* 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 AppClass implements Bus { export abstract class Model<T extends Model<T>> extends AppClass implements Bus {
@Inject() @Inject()
protected readonly logging!: Logging; protected readonly logging!: Logging
@Inject()
protected readonly bus!: EventBus
/** /**
* The name of the connection this model should run through. * The name of the connection this model should run through.
@ -819,6 +823,8 @@ export abstract class Model<T extends Model<T>> extends AppClass implements Bus
const eventClass: StaticClass<typeof event, typeof event> = event.constructor as StaticClass<Dispatchable, Dispatchable> const eventClass: StaticClass<typeof event, typeof event> = event.constructor as StaticClass<Dispatchable, Dispatchable>
await this.modelEventBusSubscribers.where('event', '=', eventClass) await this.modelEventBusSubscribers.where('event', '=', eventClass)
.promiseMap(entry => entry.subscriber(event)) .promiseMap(entry => entry.subscriber(event))
await this.bus.dispatch(event)
} }
/** /**