多租户

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 { InstanceChecker } from "../util/InstanceChecker";
import { escapeRegExp } from "../util/escapeRegExp";
import { Broadcaster } from "../../subscriber/Broadcaster";
// todo: completely cover query builder with tests
// 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?)
@ -42,6 +43,7 @@ export class QueryBuilder {
this.queryRunner = connectionOrQueryBuilder.queryRunner;
this.expressionMap = connectionOrQueryBuilder.expressionMap.clone();
}
this.broadcaster = new Broadcaster(this.queryRunner);
}
static registerQueryBuilderClass(name, factory) {
QueryBuilder.queryBuilderRegistry[name] = factory;
@ -75,7 +77,9 @@ export class QueryBuilder {
}
if (InstanceChecker.isSelectQueryBuilder(this))
return this;
return QueryBuilder.queryBuilderRegistry["SelectQueryBuilder"](this);
const selectQueryBuilder = QueryBuilder.queryBuilderRegistry["SelectQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(selectQueryBuilder, "select");
return selectQueryBuilder;
}
/**
* Creates INSERT query.
@ -84,7 +88,9 @@ export class QueryBuilder {
this.expressionMap.queryType = "insert";
if (InstanceChecker.isInsertQueryBuilder(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.
@ -105,7 +111,9 @@ export class QueryBuilder {
this.expressionMap.valuesSet = updateSet;
if (InstanceChecker.isUpdateQueryBuilder(this))
return this;
return QueryBuilder.queryBuilderRegistry["UpdateQueryBuilder"](this);
const updateQueryBuilder = QueryBuilder.queryBuilderRegistry["UpdateQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(updateQueryBuilder, "update");
return updateQueryBuilder;
}
/**
* Creates DELETE query.
@ -114,19 +122,25 @@ export class QueryBuilder {
this.expressionMap.queryType = "delete";
if (InstanceChecker.isDeleteQueryBuilder(this))
return this;
return QueryBuilder.queryBuilderRegistry["DeleteQueryBuilder"](this);
const deleteQueryBuilder = QueryBuilder.queryBuilderRegistry["DeleteQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(deleteQueryBuilder, "delete");
return deleteQueryBuilder;
}
softDelete() {
this.expressionMap.queryType = "soft-delete";
if (InstanceChecker.isSoftDeleteQueryBuilder(this))
return this;
return QueryBuilder.queryBuilderRegistry["SoftDeleteQueryBuilder"](this);
const softDeleteQueryBuilder = QueryBuilder.queryBuilderRegistry["SoftDeleteQueryBuilder"](this);
this.broadcaster.broadcastAfterQueryBuilder(softDeleteQueryBuilder, "soft-delete");
return softDeleteQueryBuilder;
}
restore() {
this.expressionMap.queryType = "restore";
if (InstanceChecker.isSoftDeleteQueryBuilder(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.
@ -144,7 +158,9 @@ export class QueryBuilder {
}
if (InstanceChecker.isRelationQueryBuilder(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.

View File

@ -5,6 +5,8 @@ const QueryBuilder_1 = require("./QueryBuilder");
const DeleteResult_1 = require("./result/DeleteResult");
const ReturningStatementNotSupportedError_1 = require("../error/ReturningStatementNotSupportedError");
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.
*/
@ -16,6 +18,8 @@ class DeleteQueryBuilder extends QueryBuilder_1.QueryBuilder {
super(connectionOrQueryBuilder, queryRunner);
this["@instanceof"] = Symbol.for("DeleteQueryBuilder");
this.expressionMap.aliasNamePrefixingEnabled = false;
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "delete");
}
// -------------------------------------------------------------------------
// Public Implemented Methods

View File

@ -12,6 +12,7 @@ const ObjectUtils_1 = require("../util/ObjectUtils");
const QueryBuilder_1 = require("./QueryBuilder");
const InsertResult_1 = require("./result/InsertResult");
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.
*/
@ -19,6 +20,8 @@ class InsertQueryBuilder extends QueryBuilder_1.QueryBuilder {
constructor() {
super(...arguments);
this["@instanceof"] = Symbol.for("InsertQueryBuilder");
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "insert");
}
// -------------------------------------------------------------------------
// Public Implemented Methods

View File

@ -27,6 +27,7 @@ const EntityPropertyNotFoundError_1 = require("../error/EntityPropertyNotFoundEr
const InstanceChecker_1 = require("../util/InstanceChecker");
const FindOperator_1 = require("../find-options/FindOperator");
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.
*/
@ -40,7 +41,10 @@ class SelectQueryBuilder extends QueryBuilder_1.QueryBuilder {
this.conditions = "";
this.orderBys = [];
this.relationMetadatas = [];
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "select");
}
// -------------------------------------------------------------------------
// Public Implemented Methods
// -------------------------------------------------------------------------

View File

@ -11,6 +11,7 @@ const UpdateValuesMissingError_1 = require("../error/UpdateValuesMissingError");
const error_1 = require("../error");
const DriverUtils_1 = require("../driver/DriverUtils");
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.
*/
@ -22,6 +23,8 @@ class SoftDeleteQueryBuilder extends QueryBuilder_1.QueryBuilder {
super(connectionOrQueryBuilder, queryRunner);
this["@instanceof"] = Symbol.for("SoftDeleteQueryBuilder");
this.expressionMap.aliasNamePrefixingEnabled = false;
this.broadcaster = new Broadcaster_1.Broadcaster(this.queryRunner);
this.broadcaster.broadcastAfterQueryBuilder(this, "soft-delete");
}
// -------------------------------------------------------------------------
// Public Implemented Methods

View File

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

View File

@ -1,6 +1,10 @@
import { EntitySubscriberInterface } from "./EntitySubscriberInterface";
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 { BroadcasterResult } from "./BroadcasterResult";
import { ColumnMetadata } from "../metadata/ColumnMetadata";
@ -32,7 +36,26 @@ interface BroadcasterEvents {
export declare class Broadcaster {
private 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.
* 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();
}
/**
* 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.
* 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 { RecoverEvent } from "./event/RecoverEvent";
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.
*/
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.
* If this method is omitted, then subscriber will listen to events of all entities.