From c264d45927a33b4ff6e127adfb5e0cb7dbde53d5 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Sat, 5 Jun 2021 08:36:35 -0500 Subject: [PATCH] Add query executed event; forward model events to global event bus --- src/orm/connection/Connection.ts | 16 +++++ src/orm/connection/PostgresConnection.ts | 1 + .../connection/event/QueryExecutedEvent.ts | 67 +++++++++++++++++++ src/orm/model/Model.ts | 8 ++- 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/orm/connection/event/QueryExecutedEvent.ts diff --git a/src/orm/connection/Connection.ts b/src/orm/connection/Connection.ts index 5222343..a85568c 100644 --- a/src/orm/connection/Connection.ts +++ b/src/orm/connection/Connection.ts @@ -2,6 +2,9 @@ import {ErrorWithContext} from '../../util' import {QueryResult} from '../types' import {SQLDialect} from '../dialect/SQLDialect' 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. @@ -18,7 +21,10 @@ export class ConnectionNotReadyError extends ErrorWithContext { * Abstract base class for database connections. * @abstract */ +@Injectable() export abstract class Connection extends AppClass { + @Inject() + protected bus!: EventBus constructor( /** @@ -64,4 +70,14 @@ export abstract class Connection extends AppClass { // public abstract tables(database_name: string): Promise> // public abstract table(database_name: string, table_name: string): Promise + + /** + * Fire a QueryExecutedEvent for the given query string. + * @param query + * @protected + */ + protected async queryExecuted(query: string): Promise { + const event = new QueryExecutedEvent(this.name, this, query) + await this.bus.dispatch(event) + } } diff --git a/src/orm/connection/PostgresConnection.ts b/src/orm/connection/PostgresConnection.ts index 0088124..1d22086 100644 --- a/src/orm/connection/PostgresConnection.ts +++ b/src/orm/connection/PostgresConnection.ts @@ -54,6 +54,7 @@ export class PostgresConnection extends Connection { try { const result = await this.client.query(query) + await this.queryExecuted(query) return { rows: collect(result.rows), diff --git a/src/orm/connection/event/QueryExecutedEvent.ts b/src/orm/connection/event/QueryExecutedEvent.ts new file mode 100644 index 0000000..9f1f8c9 --- /dev/null +++ b/src/orm/connection/event/QueryExecutedEvent.ts @@ -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 { + 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) + } + +} diff --git a/src/orm/model/Model.ts b/src/orm/model/Model.ts index d627967..7e044dd 100644 --- a/src/orm/model/Model.ts +++ b/src/orm/model/Model.ts @@ -16,13 +16,17 @@ 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' /** * Base for classes that are mapped to tables in a database. */ export abstract class Model> extends AppClass implements Bus { @Inject() - protected readonly logging!: Logging; + protected readonly logging!: Logging + + @Inject() + protected readonly bus!: EventBus /** * The name of the connection this model should run through. @@ -819,6 +823,8 @@ export abstract class Model> extends AppClass implements Bus const eventClass: StaticClass = event.constructor as StaticClass await this.modelEventBusSubscribers.where('event', '=', eventClass) .promiseMap(entry => entry.subscriber(event)) + + await this.bus.dispatch(event) } /**