多租户

This commit is contained in:
COOL 2025-01-18 21:44:04 +08:00
parent 4ccb08cb49
commit caf79c6f2c
9 changed files with 113 additions and 9 deletions

View File

@ -6,6 +6,7 @@ import { TypeORMError } from "../error";
import { EntityPropertyNotFoundError } from "../error/EntityPropertyNotFoundError"; import { EntityPropertyNotFoundError } from "../error/EntityPropertyNotFoundError";
import { InstanceChecker } from "../util/InstanceChecker"; import { InstanceChecker } from "../util/InstanceChecker";
import { escapeRegExp } from "../util/escapeRegExp"; import { escapeRegExp } from "../util/escapeRegExp";
import { Broadcaster } from "../../subscriber/Broadcaster";
// todo: completely cover query builder with tests // todo: completely cover query builder with tests
// todo: entityOrProperty can be target name. implement proper behaviour if it is. // todo: entityOrProperty can be target name. implement proper behaviour if it is.
// todo: check in persistment if id exist on object and throw exception (can be in partial selection?) // todo: check in persistment if id exist on object and throw exception (can be in partial selection?)
@ -42,6 +43,7 @@ export class QueryBuilder {
this.queryRunner = connectionOrQueryBuilder.queryRunner; this.queryRunner = connectionOrQueryBuilder.queryRunner;
this.expressionMap = connectionOrQueryBuilder.expressionMap.clone(); this.expressionMap = connectionOrQueryBuilder.expressionMap.clone();
} }
this.broadcaster = new Broadcaster(this.queryRunner);
} }
static registerQueryBuilderClass(name, factory) { static registerQueryBuilderClass(name, factory) {
QueryBuilder.queryBuilderRegistry[name] = factory; QueryBuilder.queryBuilderRegistry[name] = factory;
@ -75,7 +77,9 @@ export class QueryBuilder {
} }
if (InstanceChecker.isSelectQueryBuilder(this)) if (InstanceChecker.isSelectQueryBuilder(this))
return this; return this;
return QueryBuilder.queryBuilderRegistry["SelectQueryBuilder"](this); const selectQueryBuilder = QueryBuilder.queryBuilderRegistry["SelectQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(selectQueryBuilder, "select");
return selectQueryBuilder;
} }
/** /**
* Creates INSERT query. * Creates INSERT query.
@ -84,7 +88,9 @@ export class QueryBuilder {
this.expressionMap.queryType = "insert"; this.expressionMap.queryType = "insert";
if (InstanceChecker.isInsertQueryBuilder(this)) if (InstanceChecker.isInsertQueryBuilder(this))
return this; return this;
return QueryBuilder.queryBuilderRegistry["InsertQueryBuilder"](this); const insertQueryBuilder = QueryBuilder.queryBuilderRegistry["InsertQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(insertQueryBuilder, "insert");
return insertQueryBuilder;
} }
/** /**
* Creates UPDATE query and applies given update values. * Creates UPDATE query and applies given update values.
@ -105,7 +111,9 @@ export class QueryBuilder {
this.expressionMap.valuesSet = updateSet; this.expressionMap.valuesSet = updateSet;
if (InstanceChecker.isUpdateQueryBuilder(this)) if (InstanceChecker.isUpdateQueryBuilder(this))
return this; return this;
return QueryBuilder.queryBuilderRegistry["UpdateQueryBuilder"](this); const updateQueryBuilder = QueryBuilder.queryBuilderRegistry["UpdateQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(updateQueryBuilder, "update");
return updateQueryBuilder;
} }
/** /**
* Creates DELETE query. * Creates DELETE query.
@ -114,19 +122,25 @@ export class QueryBuilder {
this.expressionMap.queryType = "delete"; this.expressionMap.queryType = "delete";
if (InstanceChecker.isDeleteQueryBuilder(this)) if (InstanceChecker.isDeleteQueryBuilder(this))
return this; return this;
return QueryBuilder.queryBuilderRegistry["DeleteQueryBuilder"](this); const deleteQueryBuilder = QueryBuilder.queryBuilderRegistry["DeleteQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(deleteQueryBuilder, "delete");
return deleteQueryBuilder;
} }
softDelete() { softDelete() {
this.expressionMap.queryType = "soft-delete"; this.expressionMap.queryType = "soft-delete";
if (InstanceChecker.isSoftDeleteQueryBuilder(this)) if (InstanceChecker.isSoftDeleteQueryBuilder(this))
return this; return this;
return QueryBuilder.queryBuilderRegistry["SoftDeleteQueryBuilder"](this); const softDeleteQueryBuilder = QueryBuilder.queryBuilderRegistry["SoftDeleteQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(softDeleteQueryBuilder, "soft-delete");
return softDeleteQueryBuilder;
} }
restore() { restore() {
this.expressionMap.queryType = "restore"; this.expressionMap.queryType = "restore";
if (InstanceChecker.isSoftDeleteQueryBuilder(this)) if (InstanceChecker.isSoftDeleteQueryBuilder(this))
return this; return this;
return QueryBuilder.queryBuilderRegistry["SoftDeleteQueryBuilder"](this); const restoreQueryBuilder = QueryBuilder.queryBuilderRegistry["RestoreQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(restoreQueryBuilder, "restore");
return restoreQueryBuilder;
} }
/** /**
* Sets entity's relation with which this query builder gonna work. * Sets entity's relation with which this query builder gonna work.
@ -144,7 +158,9 @@ export class QueryBuilder {
} }
if (InstanceChecker.isRelationQueryBuilder(this)) if (InstanceChecker.isRelationQueryBuilder(this))
return this; return this;
return QueryBuilder.queryBuilderRegistry["RelationQueryBuilder"](this); const relationQueryBuilder = QueryBuilder.queryBuilderRegistry["RelationQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(relationQueryBuilder, "relation");
return relationQueryBuilder;
} }
/** /**
* Checks if given relation or relations exist in the entity. * Checks if given relation or relations exist in the entity.

View File

@ -5,6 +5,8 @@ const QueryBuilder_1 = require("./QueryBuilder");
const DeleteResult_1 = require("./result/DeleteResult"); const DeleteResult_1 = require("./result/DeleteResult");
const ReturningStatementNotSupportedError_1 = require("../error/ReturningStatementNotSupportedError"); const ReturningStatementNotSupportedError_1 = require("../error/ReturningStatementNotSupportedError");
const InstanceChecker_1 = require("../util/InstanceChecker"); const InstanceChecker_1 = require("../util/InstanceChecker");
const Broadcaster_1 = require("../subscriber/Broadcaster");
/** /**
* Allows to build complex sql queries in a fashion way and execute those queries. * Allows to build complex sql queries in a fashion way and execute those queries.
*/ */
@ -16,6 +18,8 @@ class DeleteQueryBuilder extends QueryBuilder_1.QueryBuilder {
super(connectionOrQueryBuilder, queryRunner); super(connectionOrQueryBuilder, queryRunner);
this["@instanceof"] = Symbol.for("DeleteQueryBuilder"); this["@instanceof"] = Symbol.for("DeleteQueryBuilder");
this.expressionMap.aliasNamePrefixingEnabled = false; this.expressionMap.aliasNamePrefixingEnabled = false;
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "delete");
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Public Implemented Methods // Public Implemented Methods

View File

@ -12,6 +12,7 @@ const ObjectUtils_1 = require("../util/ObjectUtils");
const QueryBuilder_1 = require("./QueryBuilder"); const QueryBuilder_1 = require("./QueryBuilder");
const InsertResult_1 = require("./result/InsertResult"); const InsertResult_1 = require("./result/InsertResult");
const ReturningResultsEntityUpdator_1 = require("./ReturningResultsEntityUpdator"); const ReturningResultsEntityUpdator_1 = require("./ReturningResultsEntityUpdator");
const Broadcaster_1 = require("../subscriber/Broadcaster");
/** /**
* Allows to build complex sql queries in a fashion way and execute those queries. * Allows to build complex sql queries in a fashion way and execute those queries.
*/ */
@ -19,6 +20,8 @@ class InsertQueryBuilder extends QueryBuilder_1.QueryBuilder {
constructor() { constructor() {
super(...arguments); super(...arguments);
this["@instanceof"] = Symbol.for("InsertQueryBuilder"); this["@instanceof"] = Symbol.for("InsertQueryBuilder");
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "insert");
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Public Implemented Methods // Public Implemented Methods

View File

@ -27,6 +27,7 @@ const EntityPropertyNotFoundError_1 = require("../error/EntityPropertyNotFoundEr
const InstanceChecker_1 = require("../util/InstanceChecker"); const InstanceChecker_1 = require("../util/InstanceChecker");
const FindOperator_1 = require("../find-options/FindOperator"); const FindOperator_1 = require("../find-options/FindOperator");
const ApplyValueTransformers_1 = require("../util/ApplyValueTransformers"); const ApplyValueTransformers_1 = require("../util/ApplyValueTransformers");
const Broadcaster_1 = require("../subscriber/Broadcaster");
/** /**
* Allows to build complex sql queries in a fashion way and execute those queries. * Allows to build complex sql queries in a fashion way and execute those queries.
*/ */
@ -40,7 +41,10 @@ class SelectQueryBuilder extends QueryBuilder_1.QueryBuilder {
this.conditions = ""; this.conditions = "";
this.orderBys = []; this.orderBys = [];
this.relationMetadatas = []; this.relationMetadatas = [];
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "select");
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Public Implemented Methods // Public Implemented Methods
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------

View File

@ -11,6 +11,7 @@ const UpdateValuesMissingError_1 = require("../error/UpdateValuesMissingError");
const error_1 = require("../error"); const error_1 = require("../error");
const DriverUtils_1 = require("../driver/DriverUtils"); const DriverUtils_1 = require("../driver/DriverUtils");
const InstanceChecker_1 = require("../util/InstanceChecker"); const InstanceChecker_1 = require("../util/InstanceChecker");
const Broadcaster_1 = require("../subscriber/Broadcaster");
/** /**
* Allows to build complex sql queries in a fashion way and execute those queries. * Allows to build complex sql queries in a fashion way and execute those queries.
*/ */
@ -22,6 +23,8 @@ class SoftDeleteQueryBuilder extends QueryBuilder_1.QueryBuilder {
super(connectionOrQueryBuilder, queryRunner); super(connectionOrQueryBuilder, queryRunner);
this["@instanceof"] = Symbol.for("SoftDeleteQueryBuilder"); this["@instanceof"] = Symbol.for("SoftDeleteQueryBuilder");
this.expressionMap.aliasNamePrefixingEnabled = false; this.expressionMap.aliasNamePrefixingEnabled = false;
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "soft-delete");
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Public Implemented Methods // Public Implemented Methods

View File

@ -21,6 +21,8 @@ class UpdateQueryBuilder extends QueryBuilder_1.QueryBuilder {
super(connectionOrQueryBuilder, queryRunner); super(connectionOrQueryBuilder, queryRunner);
this["@instanceof"] = Symbol.for("UpdateQueryBuilder"); this["@instanceof"] = Symbol.for("UpdateQueryBuilder");
this.expressionMap.aliasNamePrefixingEnabled = false; this.expressionMap.aliasNamePrefixingEnabled = false;
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "update");
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Public Implemented Methods // Public Implemented Methods

View File

@ -1,6 +1,10 @@
import { EntitySubscriberInterface } from "./EntitySubscriberInterface"; import { EntitySubscriberInterface } from "./EntitySubscriberInterface";
import { ObjectLiteral } from "../common/ObjectLiteral"; import { ObjectLiteral } from "../common/ObjectLiteral";
import { QueryRunner } from "../query-runner/QueryRunner"; import { SelectQueryBuilder } from "../query-builder/SelectQueryBuilder";
import { InsertQueryBuilder } from "../query-builder/InsertQueryBuilder";
import { UpdateQueryBuilder } from "../query-builder/UpdateQueryBuilder";
import { DeleteQueryBuilder } from "../query-builder/DeleteQueryBuilder";
import { SoftDeleteQueryBuilder } from "../query-builder/SoftDeleteQueryBuilder";
import { EntityMetadata } from "../metadata/EntityMetadata"; import { EntityMetadata } from "../metadata/EntityMetadata";
import { BroadcasterResult } from "./BroadcasterResult"; import { BroadcasterResult } from "./BroadcasterResult";
import { ColumnMetadata } from "../metadata/ColumnMetadata"; import { ColumnMetadata } from "../metadata/ColumnMetadata";
@ -32,7 +36,26 @@ interface BroadcasterEvents {
export declare class Broadcaster { export declare class Broadcaster {
private queryRunner; private queryRunner;
constructor(queryRunner: QueryRunner); constructor(queryRunner: QueryRunner);
broadcast<U extends keyof BroadcasterEvents>(event: U, ...args: Parameters<BroadcasterEvents[U]>): Promise<void>; /**
* Broadcasts "AFTER_QUERY_BUILDER" event.
*/
broadcastAfterQueryBuilder(
queryBuilder:
| SelectQueryBuilder<any>
| InsertQueryBuilder<any>
| UpdateQueryBuilder<any>
| DeleteQueryBuilder<any>
| SoftDeleteQueryBuilder<any>,
type: "select" | "insert" | "update" | "delete" | "soft-delete"
): void;
/**
* Broadcasts "BEFORE_QUERY" event.
*/
broadcastBeforeQuery(query: string, parameters: undefined | any[]): Promise<void>;
/**
* Broadcasts "AFTER_QUERY" event.
*/
broadcastAfterQuery(query: string, parameters: undefined | any[], success: boolean, executionTime: undefined | number, rawResults: undefined | any, error: undefined | any): Promise<void>;
/** /**
* Broadcasts "BEFORE_INSERT" event. * Broadcasts "BEFORE_INSERT" event.
* Before insert event is executed before entity is being inserted to the database for the first time. * Before insert event is executed before entity is being inserted to the database for the first time.

View File

@ -25,6 +25,34 @@ class Broadcaster {
} }
await result.wait(); await result.wait();
} }
/**
* Broadcasts "AFTER_QUERY_BUILDER" event.
*/
broadcastAfterQueryBuilder(queryBuilder, type) {
const subscribers = queryBuilder?.connection?.subscribers;
if (!subscribers || !subscribers.length) return;
for (const subscriber of subscribers) {
try{
if (type == "select" && subscriber.afterSelectQueryBuilder) {
subscriber.afterSelectQueryBuilder(queryBuilder);
}
if (type == "insert" && subscriber.afterInsertQueryBuilder) {
subscriber.afterInsertQueryBuilder(queryBuilder);
}
if (type == "update" && subscriber.afterUpdateQueryBuilder) {
subscriber.afterUpdateQueryBuilder(queryBuilder);
}
if (type == "delete" && subscriber.afterDeleteQueryBuilder) {
subscriber.afterDeleteQueryBuilder(queryBuilder);
}
if (type == "soft-delete" && subscriber.afterDeleteQueryBuilder) {
subscriber.afterDeleteQueryBuilder(queryBuilder);
}
}catch(e){
continue;
}
}
}
/** /**
* Broadcasts "BEFORE_INSERT" event. * Broadcasts "BEFORE_INSERT" event.
* Before insert event is executed before entity is being inserted to the database for the first time. * Before insert event is executed before entity is being inserted to the database for the first time.

View File

@ -8,10 +8,31 @@ import { LoadEvent } from "./event/LoadEvent";
import { SoftRemoveEvent } from "./event/SoftRemoveEvent"; import { SoftRemoveEvent } from "./event/SoftRemoveEvent";
import { RecoverEvent } from "./event/RecoverEvent"; import { RecoverEvent } from "./event/RecoverEvent";
import { AfterQueryEvent, BeforeQueryEvent } from "./event/QueryEvent"; import { AfterQueryEvent, BeforeQueryEvent } from "./event/QueryEvent";
import { SelectQueryBuilder } from "../query-builder/SelectQueryBuilder";
import { InsertQueryBuilder } from "../query-builder/InsertQueryBuilder";
import { UpdateQueryBuilder } from "../query-builder/UpdateQueryBuilder";
import { DeleteQueryBuilder } from "../query-builder/DeleteQueryBuilder";
import { SoftDeleteQueryBuilder } from "../query-builder/SoftDeleteQueryBuilder";
/** /**
* Classes that implement this interface are subscribers that subscribe for the specific events in the ORM. * Classes that implement this interface are subscribers that subscribe for the specific events in the ORM.
*/ */
export interface EntitySubscriberInterface<Entity = any> { export interface EntitySubscriberInterface<Entity = any> {
/**
* Called after SelectQueryBuilder is built.
*/
afterSelectQueryBuilder?(queryBuilder: SelectQueryBuilder<any>): void;
/**
* Called after InsertQueryBuilder is built.
*/
afterInsertQueryBuilder?(queryBuilder: InsertQueryBuilder<any>): void;
/**
* Called after UpdateQueryBuilder is built.
*/
afterUpdateQueryBuilder?(queryBuilder: UpdateQueryBuilder<any>): void;
/**
* Called after DeleteQueryBuilder is built.
*/
afterDeleteQueryBuilder?(queryBuilder: DeleteQueryBuilder<any>): void;
/** /**
* Returns the class of the entity to which events will listen. * Returns the class of the entity to which events will listen.
* If this method is omitted, then subscriber will listen to events of all entities. * If this method is omitted, then subscriber will listen to events of all entities.