@rapidrest/service-core 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +16 -0
- package/dist/lib/ApiErrors.js +41 -0
- package/dist/lib/ApiErrors.js.map +1 -0
- package/dist/lib/BackgroundService.js +16 -0
- package/dist/lib/BackgroundService.js.map +1 -0
- package/dist/lib/BackgroundServiceManager.js +135 -0
- package/dist/lib/BackgroundServiceManager.js.map +1 -0
- package/dist/lib/BulkError.js +24 -0
- package/dist/lib/BulkError.js.map +1 -0
- package/dist/lib/EventListenerManager.js +161 -0
- package/dist/lib/EventListenerManager.js.map +1 -0
- package/dist/lib/NetUtils.js +103 -0
- package/dist/lib/NetUtils.js.map +1 -0
- package/dist/lib/NotificationUtils.js +34 -0
- package/dist/lib/NotificationUtils.js.map +1 -0
- package/dist/lib/ObjectFactory.js +90 -0
- package/dist/lib/ObjectFactory.js.map +1 -0
- package/dist/lib/OpenApiSpec.js +748 -0
- package/dist/lib/OpenApiSpec.js.map +1 -0
- package/dist/lib/Server.js +549 -0
- package/dist/lib/Server.js.map +1 -0
- package/dist/lib/Types.js +2 -0
- package/dist/lib/Types.js.map +1 -0
- package/dist/lib/auth/AuthMiddleware.js +229 -0
- package/dist/lib/auth/AuthMiddleware.js.map +1 -0
- package/dist/lib/auth/AuthStrategy.js +2 -0
- package/dist/lib/auth/AuthStrategy.js.map +1 -0
- package/dist/lib/auth/BasicStrategy.js +106 -0
- package/dist/lib/auth/BasicStrategy.js.map +1 -0
- package/dist/lib/auth/JWTStrategy.js +161 -0
- package/dist/lib/auth/JWTStrategy.js.map +1 -0
- package/dist/lib/auth/index.js +5 -0
- package/dist/lib/auth/index.js.map +1 -0
- package/dist/lib/database/ConnectionKinds.js +14 -0
- package/dist/lib/database/ConnectionKinds.js.map +1 -0
- package/dist/lib/database/ConnectionManager.js +161 -0
- package/dist/lib/database/ConnectionManager.js.map +1 -0
- package/dist/lib/database/MongoConnection.js +86 -0
- package/dist/lib/database/MongoConnection.js.map +1 -0
- package/dist/lib/database/MongoRepository.js +136 -0
- package/dist/lib/database/MongoRepository.js.map +1 -0
- package/dist/lib/database/MongoSchemaSync.js +136 -0
- package/dist/lib/database/MongoSchemaSync.js.map +1 -0
- package/dist/lib/database/NamingUtils.js +52 -0
- package/dist/lib/database/NamingUtils.js.map +1 -0
- package/dist/lib/database/TypeOrmSupport.js +146 -0
- package/dist/lib/database/TypeOrmSupport.js.map +1 -0
- package/dist/lib/database/index.js +7 -0
- package/dist/lib/database/index.js.map +1 -0
- package/dist/lib/decorators/DatabaseDecorators.js +52 -0
- package/dist/lib/decorators/DatabaseDecorators.js.map +1 -0
- package/dist/lib/decorators/DocDecorators.js +120 -0
- package/dist/lib/decorators/DocDecorators.js.map +1 -0
- package/dist/lib/decorators/EventDecorators.js +24 -0
- package/dist/lib/decorators/EventDecorators.js.map +1 -0
- package/dist/lib/decorators/ModelDecorators.js +173 -0
- package/dist/lib/decorators/ModelDecorators.js.map +1 -0
- package/dist/lib/decorators/PersistenceDecorators.js +177 -0
- package/dist/lib/decorators/PersistenceDecorators.js.map +1 -0
- package/dist/lib/decorators/RouteDecorators.js +324 -0
- package/dist/lib/decorators/RouteDecorators.js.map +1 -0
- package/dist/lib/decorators/index.js +7 -0
- package/dist/lib/decorators/index.js.map +1 -0
- package/dist/lib/http/Adapters.js +230 -0
- package/dist/lib/http/Adapters.js.map +1 -0
- package/dist/lib/http/Router.js +403 -0
- package/dist/lib/http/Router.js.map +1 -0
- package/dist/lib/http/WebSocket.js +82 -0
- package/dist/lib/http/WebSocket.js.map +1 -0
- package/dist/lib/http/index.js +4 -0
- package/dist/lib/http/index.js.map +1 -0
- package/dist/lib/http/types.js +5 -0
- package/dist/lib/http/types.js.map +1 -0
- package/dist/lib/index.js +18 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/models/BaseEntity.js +83 -0
- package/dist/lib/models/BaseEntity.js.map +1 -0
- package/dist/lib/models/BaseMongoEntity.js +40 -0
- package/dist/lib/models/BaseMongoEntity.js.map +1 -0
- package/dist/lib/models/ModelUtils.js +645 -0
- package/dist/lib/models/ModelUtils.js.map +1 -0
- package/dist/lib/models/RecoverableBaseEntity.js +39 -0
- package/dist/lib/models/RecoverableBaseEntity.js.map +1 -0
- package/dist/lib/models/RecoverableBaseMongoEntity.js +40 -0
- package/dist/lib/models/RecoverableBaseMongoEntity.js.map +1 -0
- package/dist/lib/models/RepoUtils.js +717 -0
- package/dist/lib/models/RepoUtils.js.map +1 -0
- package/dist/lib/models/SimpleEntity.js +42 -0
- package/dist/lib/models/SimpleEntity.js.map +1 -0
- package/dist/lib/models/SimpleMongoEntity.js +38 -0
- package/dist/lib/models/SimpleMongoEntity.js.map +1 -0
- package/dist/lib/models/StatusExtraData.js +15 -0
- package/dist/lib/models/StatusExtraData.js.map +1 -0
- package/dist/lib/models/index.js +10 -0
- package/dist/lib/models/index.js.map +1 -0
- package/dist/lib/routes/AdminRoute.js +268 -0
- package/dist/lib/routes/AdminRoute.js.map +1 -0
- package/dist/lib/routes/MetricsRoute.js +86 -0
- package/dist/lib/routes/MetricsRoute.js.map +1 -0
- package/dist/lib/routes/ModelRoute.js +481 -0
- package/dist/lib/routes/ModelRoute.js.map +1 -0
- package/dist/lib/routes/OpenAPIRoute.js +115 -0
- package/dist/lib/routes/OpenAPIRoute.js.map +1 -0
- package/dist/lib/routes/RouteUtils.js +445 -0
- package/dist/lib/routes/RouteUtils.js.map +1 -0
- package/dist/lib/routes/StatusRoute.js +55 -0
- package/dist/lib/routes/StatusRoute.js.map +1 -0
- package/dist/lib/routes/index.js +7 -0
- package/dist/lib/routes/index.js.map +1 -0
- package/dist/lib/security/ACLRouteMongo.js +194 -0
- package/dist/lib/security/ACLRouteMongo.js.map +1 -0
- package/dist/lib/security/ACLRouteSQL.js +193 -0
- package/dist/lib/security/ACLRouteSQL.js.map +1 -0
- package/dist/lib/security/ACLUtils.js +457 -0
- package/dist/lib/security/ACLUtils.js.map +1 -0
- package/dist/lib/security/AccessControlList.js +18 -0
- package/dist/lib/security/AccessControlList.js.map +1 -0
- package/dist/lib/security/AccessControlListMongo.js +155 -0
- package/dist/lib/security/AccessControlListMongo.js.map +1 -0
- package/dist/lib/security/AccessControlListSQL.js +149 -0
- package/dist/lib/security/AccessControlListSQL.js.map +1 -0
- package/dist/lib/security/index.js +3 -0
- package/dist/lib/security/index.js.map +1 -0
- package/dist/lib/test/index.js +3 -0
- package/dist/lib/test/index.js.map +1 -0
- package/dist/lib/test/request.js +99 -0
- package/dist/lib/test/request.js.map +1 -0
- package/dist/lib/test/requestws.js +173 -0
- package/dist/lib/test/requestws.js.map +1 -0
- package/dist/types/ApiErrors.d.ts +38 -0
- package/dist/types/BackgroundService.d.ts +30 -0
- package/dist/types/BackgroundServiceManager.d.ts +66 -0
- package/dist/types/BulkError.d.ts +11 -0
- package/dist/types/EventListenerManager.d.ts +36 -0
- package/dist/types/NetUtils.d.ts +29 -0
- package/dist/types/NotificationUtils.d.ts +25 -0
- package/dist/types/ObjectFactory.d.ts +17 -0
- package/dist/types/OpenApiSpec.d.ts +114 -0
- package/dist/types/Server.d.ts +180 -0
- package/dist/types/Types.d.ts +8 -0
- package/dist/types/auth/AuthMiddleware.d.ts +42 -0
- package/dist/types/auth/AuthStrategy.d.ts +32 -0
- package/dist/types/auth/BasicStrategy.d.ts +33 -0
- package/dist/types/auth/JWTStrategy.d.ts +61 -0
- package/dist/types/auth/index.d.ts +4 -0
- package/dist/types/database/ConnectionKinds.d.ts +7 -0
- package/dist/types/database/ConnectionManager.d.ts +35 -0
- package/dist/types/database/MongoConnection.d.ts +54 -0
- package/dist/types/database/MongoRepository.d.ts +92 -0
- package/dist/types/database/MongoSchemaSync.d.ts +41 -0
- package/dist/types/database/NamingUtils.d.ts +24 -0
- package/dist/types/database/TypeOrmSupport.d.ts +20 -0
- package/dist/types/database/index.d.ts +6 -0
- package/dist/types/decorators/DatabaseDecorators.d.ts +18 -0
- package/dist/types/decorators/DocDecorators.d.ts +69 -0
- package/dist/types/decorators/EventDecorators.d.ts +12 -0
- package/dist/types/decorators/ModelDecorators.d.ts +80 -0
- package/dist/types/decorators/PersistenceDecorators.d.ts +117 -0
- package/dist/types/decorators/RouteDecorators.d.ts +172 -0
- package/dist/types/decorators/index.d.ts +6 -0
- package/dist/types/http/Adapters.d.ts +68 -0
- package/dist/types/http/Router.d.ts +99 -0
- package/dist/types/http/WebSocket.d.ts +56 -0
- package/dist/types/http/index.d.ts +6 -0
- package/dist/types/http/types.d.ts +54 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/models/BaseEntity.d.ts +29 -0
- package/dist/types/models/BaseMongoEntity.d.ts +13 -0
- package/dist/types/models/ModelUtils.d.ts +166 -0
- package/dist/types/models/RecoverableBaseEntity.d.ts +16 -0
- package/dist/types/models/RecoverableBaseMongoEntity.d.ts +16 -0
- package/dist/types/models/RepoUtils.d.ts +154 -0
- package/dist/types/models/SimpleEntity.d.ts +14 -0
- package/dist/types/models/SimpleMongoEntity.d.ts +15 -0
- package/dist/types/models/StatusExtraData.d.ts +6 -0
- package/dist/types/models/index.d.ts +9 -0
- package/dist/types/routes/AdminRoute.d.ts +47 -0
- package/dist/types/routes/MetricsRoute.d.ts +15 -0
- package/dist/types/routes/ModelRoute.d.ts +226 -0
- package/dist/types/routes/OpenAPIRoute.d.ts +17 -0
- package/dist/types/routes/RouteUtils.d.ts +55 -0
- package/dist/types/routes/StatusRoute.d.ts +11 -0
- package/dist/types/routes/index.d.ts +6 -0
- package/dist/types/security/ACLRouteMongo.d.ts +19 -0
- package/dist/types/security/ACLRouteSQL.d.ts +19 -0
- package/dist/types/security/ACLUtils.d.ts +94 -0
- package/dist/types/security/AccessControlList.d.ts +103 -0
- package/dist/types/security/AccessControlListMongo.d.ts +24 -0
- package/dist/types/security/AccessControlListSQL.d.ts +24 -0
- package/dist/types/security/index.d.ts +2 -0
- package/dist/types/test/index.d.ts +2 -0
- package/dist/types/test/request.d.ts +24 -0
- package/dist/types/test/requestws.d.ts +21 -0
- package/docs/Makefile +20 -0
- package/docs/conf.py +58 -0
- package/docs/index.rst +17 -0
- package/docs/make.bat +35 -0
- package/docs/reference/@rapidrest/namespaces/DatabaseDecorators/README.md +13 -0
- package/docs/reference/@rapidrest/namespaces/DatabaseDecorators/functions/MongoRepository.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DatabaseDecorators/functions/RedisConnection.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DatabaseDecorators/functions/Repository.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/README.md +23 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Default.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Description.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Document.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Example.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Format.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Returns.md +28 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Summary.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Tags.md +25 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/TypeInfo.md +28 -0
- package/docs/reference/@rapidrest/namespaces/DocDecorators/interfaces/DocumentsData.md +57 -0
- package/docs/reference/@rapidrest/namespaces/EventDecorators/README.md +12 -0
- package/docs/reference/@rapidrest/namespaces/EventDecorators/functions/EventListener.md +17 -0
- package/docs/reference/@rapidrest/namespaces/EventDecorators/functions/OnEvent.md +26 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/README.md +26 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Cache.md +25 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/ChildEntity.md +18 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/DataStore.md +25 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Identifier.md +27 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Protect.md +35 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Reference.md +25 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Shard.md +27 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/TrackChanges.md +26 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/interfaces/PendingTypeOrmColumn.md +45 -0
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/variables/pendingTypeOrmColumns.md +14 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/README.md +25 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/Column.md +25 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/Entity.md +26 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/Index.md +119 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/PrimaryColumn.md +25 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/Unique.md +68 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/getColumnMetadata.md +26 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/getEntityName.md +26 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/getIndexMetadata.md +27 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/ColumnInfo.md +41 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/ColumnOptions.md +51 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/IndexInfo.md +41 -0
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/IndexOptions.md +51 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/README.md +36 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/After.md +26 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Auth.md +33 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/AuthResult.md +31 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Before.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/ContentType.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Delete.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Get.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Head.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Header.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Method.md +31 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Model.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Options.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Param.md +26 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Patch.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Post.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Protect.md +31 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Put.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Query.md +26 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Request.md +31 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/RequiresRole.md +26 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Response.md +31 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Route.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Socket.md +33 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/User.md +31 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Validate.md +25 -0
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/WebSocket.md +25 -0
- package/docs/reference/README.md +20 -0
- package/docs/reference/classes/ACLUtils.md +251 -0
- package/docs/reference/classes/AdminRoute.md +51 -0
- package/docs/reference/classes/AuthMiddleware.md +131 -0
- package/docs/reference/classes/BackgroundService.md +117 -0
- package/docs/reference/classes/BackgroundServiceManager.md +172 -0
- package/docs/reference/classes/BaseEntity.md +82 -0
- package/docs/reference/classes/BaseMongoEntity.md +107 -0
- package/docs/reference/classes/BasicStrategy.md +89 -0
- package/docs/reference/classes/BasicStrategyOptions.md +91 -0
- package/docs/reference/classes/BulkError.md +271 -0
- package/docs/reference/classes/ConnectionManager.md +75 -0
- package/docs/reference/classes/EventListenerManager.md +88 -0
- package/docs/reference/classes/JWTStrategy.md +77 -0
- package/docs/reference/classes/JWTStrategyOptions.md +97 -0
- package/docs/reference/classes/MetricsRoute.md +27 -0
- package/docs/reference/classes/ModelRoute.md +527 -0
- package/docs/reference/classes/ModelUtils.md +448 -0
- package/docs/reference/classes/MongoConnection.md +218 -0
- package/docs/reference/classes/MongoRepository.md +354 -0
- package/docs/reference/classes/MongoSchemaSync.md +67 -0
- package/docs/reference/classes/NetUtils.md +90 -0
- package/docs/reference/classes/NotificationUtils.md +77 -0
- package/docs/reference/classes/ObjectFactory.md +336 -0
- package/docs/reference/classes/OpenAPIRoute.md +77 -0
- package/docs/reference/classes/OpenApiSpec.md +892 -0
- package/docs/reference/classes/RecoverableBaseEntity.md +114 -0
- package/docs/reference/classes/RecoverableBaseMongoEntity.md +124 -0
- package/docs/reference/classes/RedisTransport.md +2202 -0
- package/docs/reference/classes/RepoUtils.md +486 -0
- package/docs/reference/classes/RouteUtils.md +191 -0
- package/docs/reference/classes/Server.md +408 -0
- package/docs/reference/classes/SimpleEntity.md +48 -0
- package/docs/reference/classes/SimpleMongoEntity.md +66 -0
- package/docs/reference/classes/StatusExtraData.md +57 -0
- package/docs/reference/classes/StatusRoute.md +26 -0
- package/docs/reference/enumerations/ACLAction.md +63 -0
- package/docs/reference/enumerations/ApiErrorMessages.md +123 -0
- package/docs/reference/enumerations/ApiErrors.md +123 -0
- package/docs/reference/functions/isSqlDataSource.md +26 -0
- package/docs/reference/functions/resolveCollectionName.md +33 -0
- package/docs/reference/functions/snakeCase.md +28 -0
- package/docs/reference/globals.md +91 -0
- package/docs/reference/interfaces/ACLRecord.md +96 -0
- package/docs/reference/interfaces/AccessControlList.md +76 -0
- package/docs/reference/interfaces/AuthResult.md +55 -0
- package/docs/reference/interfaces/AuthStrategy.md +57 -0
- package/docs/reference/interfaces/CreateRequestOptions.md +121 -0
- package/docs/reference/interfaces/DeleteRequestOptions.md +147 -0
- package/docs/reference/interfaces/FindRequestOptions.md +133 -0
- package/docs/reference/interfaces/JWTAuthResult.md +84 -0
- package/docs/reference/interfaces/RepoCreateOptions.md +95 -0
- package/docs/reference/interfaces/RepoDeleteOptions.md +115 -0
- package/docs/reference/interfaces/RepoFindOptions.md +135 -0
- package/docs/reference/interfaces/RepoOperationOptions.md +69 -0
- package/docs/reference/interfaces/RepoUpdateOptions.md +111 -0
- package/docs/reference/interfaces/RequestOptions.md +112 -0
- package/docs/reference/interfaces/TruncateRequestOptions.md +175 -0
- package/docs/reference/interfaces/UpdateRequestOptions.md +149 -0
- package/docs/reference/type-aliases/OneOrMany.md +19 -0
- package/docs/reference/type-aliases/OneOrNull.md +19 -0
- package/docs/reference/type-aliases/PartialBaseEntity.md +17 -0
- package/docs/reference/type-aliases/PartialSimpleEntity.md +17 -0
- package/docs/reference/type-aliases/UpdateObject.md +19 -0
- package/package.json +125 -0
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2020-2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
13
|
+
import * as crypto from "crypto";
|
|
14
|
+
import { MongoRepository } from "../database/MongoRepository.js";
|
|
15
|
+
import { MongoConnection } from "../database/MongoConnection.js";
|
|
16
|
+
import { isSqlDataSource } from "../database/ConnectionKinds.js";
|
|
17
|
+
import { resolveCollectionName } from "../database/NamingUtils.js";
|
|
18
|
+
import { ModelUtils } from "../models/ModelUtils.js";
|
|
19
|
+
import { BaseEntity } from "../models/BaseEntity.js";
|
|
20
|
+
import { BaseMongoEntity } from "../models/BaseMongoEntity.js";
|
|
21
|
+
import { ApiErrorMessages, ApiErrors } from "../ApiErrors.js";
|
|
22
|
+
import { ApiError, ObjectDecorators, ObjectUtils, UserUtils } from "@rapidrest/core";
|
|
23
|
+
import { DatabaseDecorators } from "../decorators/index.js";
|
|
24
|
+
import { Redis } from "ioredis";
|
|
25
|
+
import { ObjectFactory } from "../ObjectFactory.js";
|
|
26
|
+
import { NotificationUtils } from "../NotificationUtils.js";
|
|
27
|
+
import { RecoverableBaseEntity } from "./RecoverableBaseEntity.js";
|
|
28
|
+
import { ACLAction, ACLUtils } from "../security/index.js";
|
|
29
|
+
import { ConnectionManager } from "../database/index.js";
|
|
30
|
+
const { Config, Init, Inject, Logger } = ObjectDecorators;
|
|
31
|
+
const { RedisConnection } = DatabaseDecorators;
|
|
32
|
+
/**
|
|
33
|
+
* @author Jean-Philippe Steinmetz
|
|
34
|
+
*/
|
|
35
|
+
export class RepoUtils {
|
|
36
|
+
constructor(modelClass, repo) {
|
|
37
|
+
/** The unique identifier of the default ACL for the model type. */
|
|
38
|
+
this.defaultACLUid = "";
|
|
39
|
+
this.trustedRoles = ["admin"];
|
|
40
|
+
this.modelClass = modelClass;
|
|
41
|
+
this.repo = repo;
|
|
42
|
+
}
|
|
43
|
+
async init() {
|
|
44
|
+
// Retrieve the repository based on the modelClass that was passed in to the constructor
|
|
45
|
+
if (!this.repo) {
|
|
46
|
+
if (!this.modelClass.datastore) {
|
|
47
|
+
throw new Error(`Cannot initialize RepoUtils. Did you forget to add @DataStore() to ${this.modelClass.name}?`);
|
|
48
|
+
}
|
|
49
|
+
if (!this.connectionManager) {
|
|
50
|
+
throw new Error("Cannot initialize RepoUtils. Failed to retrieve ConnectionManager.");
|
|
51
|
+
}
|
|
52
|
+
const ds = this.connectionManager.connections.get(this.modelClass.datastore);
|
|
53
|
+
if (!ds) {
|
|
54
|
+
throw new Error(`Cannot initialize RepoUtils. No connection found for datastore '${this.modelClass.datastore}'`);
|
|
55
|
+
}
|
|
56
|
+
this.repo = ds.getRepository(this.modelClass);
|
|
57
|
+
}
|
|
58
|
+
if (!this.repo) {
|
|
59
|
+
throw new Error(`Cannot initialize RepoUtils. No repository found for class ${this.modelClass.name}.`);
|
|
60
|
+
}
|
|
61
|
+
let defaultAcl = this.getDefaultACL();
|
|
62
|
+
if (defaultAcl) {
|
|
63
|
+
this.defaultACLUid = defaultAcl.uid;
|
|
64
|
+
await this.aclUtils?.saveDefaultACL(defaultAcl);
|
|
65
|
+
}
|
|
66
|
+
// Does the model specify a MongoDB shard configuration?
|
|
67
|
+
const shardConfig = Reflect.getMetadata("rrst:shardConfig", this.modelClass);
|
|
68
|
+
if (shardConfig && this.repo instanceof MongoRepository) {
|
|
69
|
+
const conn = this.connectionManager?.connections.get(this.modelClass.datastore);
|
|
70
|
+
const admin = conn?.admin();
|
|
71
|
+
if (admin) {
|
|
72
|
+
const collectionName = resolveCollectionName(this.modelClass);
|
|
73
|
+
const dbName = this.config.get(`datastores:${this.modelClass.datastore}:database`);
|
|
74
|
+
try {
|
|
75
|
+
this.logger.info(`Configuring sharding for: collection=${dbName}.${collectionName}, key=${JSON.stringify(shardConfig.key)}, unique=${shardConfig.unique}, options=${JSON.stringify(shardConfig.options)})`);
|
|
76
|
+
const result = await admin.command({
|
|
77
|
+
shardCollection: `${dbName}.${collectionName}`,
|
|
78
|
+
key: shardConfig.key,
|
|
79
|
+
unique: shardConfig.unique,
|
|
80
|
+
...shardConfig.options,
|
|
81
|
+
});
|
|
82
|
+
this.logger.debug(`Result: ${JSON.stringify(result)}`);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
this.logger.warn(`There was a problem trying to configure MongoDB sharding for collection '${collectionName}'. Error=${e.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
this.logger.debug("Failed to get mongodb admin interface or sharding not supported.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The base key used to get or set data in the cache.
|
|
95
|
+
*/
|
|
96
|
+
get baseCacheKey() {
|
|
97
|
+
return "db.cache." + this.modelClass.name;
|
|
98
|
+
}
|
|
99
|
+
async count(query, options) {
|
|
100
|
+
if (!this.repo) {
|
|
101
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
102
|
+
}
|
|
103
|
+
let count = 0;
|
|
104
|
+
// Check user permissions
|
|
105
|
+
if (this.aclUtils && !options?.ignoreACL) {
|
|
106
|
+
if (!(await this.aclUtils.hasPermission(options?.user, this.defaultACLUid, ACLAction.READ))) {
|
|
107
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (this.repo instanceof MongoRepository && Array.isArray(query)) {
|
|
111
|
+
query.push({ $count: "count" });
|
|
112
|
+
const result = await this.repo.aggregate(query).next();
|
|
113
|
+
count = result ? result.count : count;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
count = await this.repo.count(query);
|
|
117
|
+
}
|
|
118
|
+
return count;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Stores a new record of the provided object in the datastore. Performs pre-processing, permission checks against
|
|
122
|
+
* the class ACL, cache seeding, telemetry recording and push notifications.
|
|
123
|
+
*
|
|
124
|
+
* @param obj The object to store.
|
|
125
|
+
* @param acl The ACL to use
|
|
126
|
+
*/
|
|
127
|
+
async create(obj, options) {
|
|
128
|
+
if (!this.repo) {
|
|
129
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
130
|
+
}
|
|
131
|
+
// Verify the user's permission to create objects
|
|
132
|
+
if (this.aclUtils &&
|
|
133
|
+
!options?.ignoreACL &&
|
|
134
|
+
!(await this.aclUtils.hasPermission(options?.user, this.defaultACLUid, ACLAction.CREATE))) {
|
|
135
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
136
|
+
}
|
|
137
|
+
// Instantiate the object if not already done
|
|
138
|
+
const clazz = this.getClassType(obj);
|
|
139
|
+
const newObj = obj instanceof clazz ? obj : this.instantiateObject(obj, clazz);
|
|
140
|
+
// Make sure an existing object doesn't already exist with the same identifiers
|
|
141
|
+
const ids = [];
|
|
142
|
+
const idProps = ModelUtils.getIdPropertyNames(clazz);
|
|
143
|
+
for (const prop of idProps) {
|
|
144
|
+
// Skip `productUid` as it is considered a compound key
|
|
145
|
+
if (prop === "productUid")
|
|
146
|
+
continue;
|
|
147
|
+
const val = newObj[prop];
|
|
148
|
+
if (val) {
|
|
149
|
+
ids.push(val);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const query = ModelUtils.buildIdSearchQuery(this.repo, clazz, ids, undefined, newObj.productUid);
|
|
153
|
+
const count = await this.repo.count(query);
|
|
154
|
+
if (!this.modelClass.trackChanges && count > 0) {
|
|
155
|
+
throw new ApiError(ApiErrors.IDENTIFIER_EXISTS, 400, ApiErrorMessages.IDENTIFIER_EXISTS);
|
|
156
|
+
}
|
|
157
|
+
// Override the date and version fields with their defaults
|
|
158
|
+
if (newObj instanceof BaseEntity) {
|
|
159
|
+
newObj.dateCreated = new Date();
|
|
160
|
+
newObj.dateModified = new Date();
|
|
161
|
+
newObj.version = count;
|
|
162
|
+
}
|
|
163
|
+
// Are we tracking multiple versions for this object?
|
|
164
|
+
if (newObj instanceof BaseEntity && this.modelClass.trackChanges === 0) {
|
|
165
|
+
newObj.version = 0;
|
|
166
|
+
}
|
|
167
|
+
// HAX We shouldn't be casting obj to any here but this is the only way to get it to compile since T
|
|
168
|
+
// extends BaseEntity.
|
|
169
|
+
const result = this.instantiateObject(await this.repo.save(newObj));
|
|
170
|
+
if (this.cacheClient && this.modelClass.cacheTTL) {
|
|
171
|
+
// Cache the object for faster retrieval
|
|
172
|
+
const query = this.searchIdQuery(newObj.uid);
|
|
173
|
+
const cacheKey = `${this.baseCacheKey}.${this.hashQuery(query)}`;
|
|
174
|
+
void this.cacheClient.setex(cacheKey, this.modelClass.cacheTTL, JSON.stringify(result));
|
|
175
|
+
}
|
|
176
|
+
if (this.aclUtils && this.modelClass.recordACL) {
|
|
177
|
+
// If ACLs are enabled but no ACL was given create one
|
|
178
|
+
const acl = {
|
|
179
|
+
uid: result.uid,
|
|
180
|
+
parentUid: options?.acl?.parentUid || this.defaultACLUid,
|
|
181
|
+
records: options?.acl?.records || [],
|
|
182
|
+
};
|
|
183
|
+
// Look for an existing record for the creator
|
|
184
|
+
let found = !!this.aclUtils.getRecord(acl, options?.user);
|
|
185
|
+
// Always grant the creator CRUD access, unless the user is a superuser.
|
|
186
|
+
if (!found && options?.user && !UserUtils.hasRoles(options?.user, this.trustedRoles)) {
|
|
187
|
+
acl.records.push({
|
|
188
|
+
userOrRoleId: options.user.uid,
|
|
189
|
+
create: true,
|
|
190
|
+
read: true,
|
|
191
|
+
update: true,
|
|
192
|
+
delete: true,
|
|
193
|
+
special: false,
|
|
194
|
+
full: false,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
await this.aclUtils?.saveACL(acl);
|
|
198
|
+
}
|
|
199
|
+
if (!options?.skipPush) {
|
|
200
|
+
let channels = [result.uid].concat(options?.pushChannels || []);
|
|
201
|
+
this.notificationUtils?.sendMessage(channels, this.modelClass.name, "create", result);
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
async delete(uid, options) {
|
|
206
|
+
if (!this.repo) {
|
|
207
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
208
|
+
}
|
|
209
|
+
if (this.aclUtils && !options.ignoreACL) {
|
|
210
|
+
const acl = await this.aclUtils.findACL(uid);
|
|
211
|
+
if (!(await this.aclUtils.hasPermission(options.user, acl ? acl : this.defaultACLUid, ACLAction.DELETE))) {
|
|
212
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const isRecoverable = this.instantiateObject({}) instanceof RecoverableBaseEntity;
|
|
216
|
+
const isPurge = isRecoverable ? options.purge || false : true;
|
|
217
|
+
const query = ModelUtils.buildIdSearchQuery(this.repo, this.modelClass, uid, options.version ? Number(options.version) : undefined, options.productUid);
|
|
218
|
+
// If the object(s) are being permenantly removed from the database do so and then clear the accompanying
|
|
219
|
+
// ACL(s). If the class type is recoverable and purge isn't desired, simply mark the object(s) as deleted.
|
|
220
|
+
if (isPurge) {
|
|
221
|
+
if (this.repo instanceof MongoRepository) {
|
|
222
|
+
await this.repo.deleteMany(query);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
await this.repo.delete(query.where);
|
|
226
|
+
}
|
|
227
|
+
if (this.aclUtils && this.modelClass.recordACL) {
|
|
228
|
+
await this.aclUtils.removeACL(uid);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
if (this.repo instanceof MongoRepository) {
|
|
233
|
+
await this.repo.updateMany(query, {
|
|
234
|
+
$set: {
|
|
235
|
+
deleted: true,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
await this.repo.update(query.where, {
|
|
241
|
+
deleted: true,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (this.cacheClient && this.modelClass.cacheTTL) {
|
|
246
|
+
// Delete the object from cache
|
|
247
|
+
void this.cacheClient.del(`${this.baseCacheKey}.${this.hashQuery(query)}`);
|
|
248
|
+
void this.cacheClient.del(`${this.baseCacheKey}.${this.hashQuery(this.searchIdQuery(uid))}`);
|
|
249
|
+
}
|
|
250
|
+
if (!options?.skipPush) {
|
|
251
|
+
let channels = [uid].concat(options?.pushChannels || []);
|
|
252
|
+
this.notificationUtils?.sendMessage(channels, this.modelClass.name, "delete", {
|
|
253
|
+
uid,
|
|
254
|
+
productUid: options.productUid,
|
|
255
|
+
version: options.version,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Retrieves an array of objects from the datastore matching the given search query. This function will first
|
|
261
|
+
* attempt to look up the results in the cache. Also checks ACLs for READ permission.
|
|
262
|
+
*
|
|
263
|
+
* @param query The constructed search query to run.
|
|
264
|
+
* @param options The additional options to consider during the search.
|
|
265
|
+
*/
|
|
266
|
+
async find(query, options) {
|
|
267
|
+
if (!this.repo) {
|
|
268
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
269
|
+
}
|
|
270
|
+
let results = [];
|
|
271
|
+
// Check user permissions
|
|
272
|
+
if (this.aclUtils && !options?.ignoreACL) {
|
|
273
|
+
if (!(await this.aclUtils.hasPermission(options?.user, this.defaultACLUid, ACLAction.READ))) {
|
|
274
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const limit = options?.limit ? Math.min(options?.limit, 1000) : 100;
|
|
278
|
+
const page = options?.page ? Number(options?.page) : 0;
|
|
279
|
+
// When we hash the seach query we need to ensure we're including the pagination information to preserve
|
|
280
|
+
// like queries and results.
|
|
281
|
+
const searchQueryHash = this.hashQuery({
|
|
282
|
+
...query,
|
|
283
|
+
limit,
|
|
284
|
+
page,
|
|
285
|
+
});
|
|
286
|
+
// Pull from the cache if available
|
|
287
|
+
if (this.cacheClient && this.modelClass.cacheTTL) {
|
|
288
|
+
const json = await this.cacheClient.get(`${this.baseCacheKey}.${searchQueryHash}`);
|
|
289
|
+
if (json) {
|
|
290
|
+
try {
|
|
291
|
+
const uids = JSON.parse(json);
|
|
292
|
+
for (const uid of uids) {
|
|
293
|
+
// Retrieve the object from the cache or from database if not available
|
|
294
|
+
const obj = await this.findOne(uid, options);
|
|
295
|
+
if (obj) {
|
|
296
|
+
results.push(obj);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
// It doesn't matter if this fails
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// If the query wasn't cached retrieve from the database
|
|
306
|
+
if (results.length === 0) {
|
|
307
|
+
if (this.repo instanceof MongoRepository && Array.isArray(query)) {
|
|
308
|
+
const skip = page * limit;
|
|
309
|
+
results = await this.repo.aggregate(query).skip(skip).limit(limit).toArray();
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
results = await this.repo.find(query);
|
|
313
|
+
}
|
|
314
|
+
// Cache the results for future requests
|
|
315
|
+
if (this.cacheClient && this.modelClass.cacheTTL) {
|
|
316
|
+
const uids = [];
|
|
317
|
+
for (const result of results) {
|
|
318
|
+
uids.push(result.uid);
|
|
319
|
+
}
|
|
320
|
+
void this.cacheClient.setex(`${this.baseCacheKey}.${searchQueryHash}`, this.modelClass.cacheTTL, JSON.stringify(uids));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return results;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Retrieves the object with the given id from either the cache or the database. If retrieving from the database
|
|
327
|
+
* the cache is populated to speed up subsequent requests.
|
|
328
|
+
*
|
|
329
|
+
* @param id The unique identifier of the object to retrieve.
|
|
330
|
+
* @param options The additional options to consider during the search.
|
|
331
|
+
*/
|
|
332
|
+
async findOne(id, options) {
|
|
333
|
+
if (!this.repo) {
|
|
334
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
335
|
+
}
|
|
336
|
+
const query = this.searchIdQuery(id, options?.version, options?.productUid);
|
|
337
|
+
if (!options?.skipCache && this.cacheClient && this.modelClass.cacheTTL) {
|
|
338
|
+
// First attempt to retrieve the object from the cache
|
|
339
|
+
const json = await this.cacheClient.get(`${this.baseCacheKey}.${this.hashQuery(query)}`);
|
|
340
|
+
if (json) {
|
|
341
|
+
try {
|
|
342
|
+
const existing = JSON.parse(json);
|
|
343
|
+
if (existing) {
|
|
344
|
+
return existing;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
// It doesn't matter if this fails
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
let existing = null;
|
|
353
|
+
if (this.repo instanceof MongoRepository) {
|
|
354
|
+
existing = await this.repo
|
|
355
|
+
.aggregate([
|
|
356
|
+
{
|
|
357
|
+
$match: query,
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
$sort: { version: -1 },
|
|
361
|
+
},
|
|
362
|
+
])
|
|
363
|
+
.limit(1)
|
|
364
|
+
.next();
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
existing = await this.repo.findOne(query);
|
|
368
|
+
}
|
|
369
|
+
if (existing && this.cacheClient && this.modelClass.cacheTTL) {
|
|
370
|
+
// Cache the object for faster retrieval
|
|
371
|
+
void this.cacheClient.setex(`${this.baseCacheKey}.${this.hashQuery(query)}`, this.modelClass.cacheTTL, JSON.stringify(existing));
|
|
372
|
+
}
|
|
373
|
+
// Make sure we return the correct data type
|
|
374
|
+
return existing ? this.instantiateObject(existing) : undefined;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Returns the default access control list governing the model type. Returning a value of `undefined` will grant
|
|
378
|
+
* full acccess to any user (including unauthenticated anonymous users).
|
|
379
|
+
*/
|
|
380
|
+
getDefaultACL() {
|
|
381
|
+
let result = undefined;
|
|
382
|
+
// Check if the model has the Protect decorator
|
|
383
|
+
if (this.modelClass.classACL) {
|
|
384
|
+
result = this.modelClass.classACL;
|
|
385
|
+
if (result) {
|
|
386
|
+
// Override the specified uid with the actual class name if the value is `<ClassName>`
|
|
387
|
+
result.uid = result.uid === "<ClassName>" ? this.modelClass.name : result.uid;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Hashes the given query object to a unique string.
|
|
394
|
+
* @param query The query object to hash.
|
|
395
|
+
*/
|
|
396
|
+
hashQuery(query) {
|
|
397
|
+
const queryStr = JSON.stringify(query);
|
|
398
|
+
return crypto.createHash("sha512").update(queryStr).digest("hex");
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Returns the class type (constructor) for the given object. This uses the `_fqn` or `_type` property of `obj` to
|
|
402
|
+
* identify the class. If neither property is defined `modelClass` is assumed.
|
|
403
|
+
*
|
|
404
|
+
* @param obj The object whose class type to look up.
|
|
405
|
+
* @returns The class type (constructor) associated with the given object.
|
|
406
|
+
*/
|
|
407
|
+
getClassType(obj) {
|
|
408
|
+
const className = obj._fqn || obj._type;
|
|
409
|
+
if (this.objectFactory) {
|
|
410
|
+
if (className && typeof className === "string") {
|
|
411
|
+
const clazz = this.objectFactory.classes.get(className) || this.objectFactory.classes.get(`models.${className}`);
|
|
412
|
+
return clazz;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return this.modelClass;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Creates a new instance of obj scoped to the correct model class or sub-class.
|
|
419
|
+
*/
|
|
420
|
+
instantiateObject(obj, clazz) {
|
|
421
|
+
if (!clazz) {
|
|
422
|
+
clazz = this.getClassType(obj);
|
|
423
|
+
}
|
|
424
|
+
if (this.objectFactory) {
|
|
425
|
+
return this.objectFactory.newInstance(clazz, { initialize: false, args: [obj] });
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
return new clazz(obj);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Search for existing object based on passed in id and version and product uid.
|
|
433
|
+
*
|
|
434
|
+
* The result of this function is compatible with all `Repository.find()` functions.
|
|
435
|
+
*/
|
|
436
|
+
searchIdQuery(id, version, productUid) {
|
|
437
|
+
return ModelUtils.buildIdSearchQuery(this.repo, this.modelClass, id, typeof version === "string" ? parseInt(version, 10) : version, productUid);
|
|
438
|
+
}
|
|
439
|
+
async truncate(query, options) {
|
|
440
|
+
if (!this.repo) {
|
|
441
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
442
|
+
}
|
|
443
|
+
// Check user permissions. Don't check if record-level ACLs are used as this will be done
|
|
444
|
+
// per record later.
|
|
445
|
+
if (this.aclUtils && !options.ignoreACL && !this.modelClass.recordACL) {
|
|
446
|
+
if (!(await this.aclUtils.hasPermission(options.user, this.defaultACLUid, ACLAction.DELETE))) {
|
|
447
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
try {
|
|
451
|
+
let uids = [];
|
|
452
|
+
if (this.repo instanceof MongoRepository) {
|
|
453
|
+
if (Array.isArray(query)) {
|
|
454
|
+
uids = await this.repo.distinct("uid", query[0].$match);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
uids = await this.repo.distinct("uid", query);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
(await this.repo.find(query)).forEach((obj) => uids.push(obj.uid));
|
|
462
|
+
}
|
|
463
|
+
if (uids.length > 0) {
|
|
464
|
+
let finalUids = uids;
|
|
465
|
+
// Check if this class uses record level ACLs. If so, we need to check the perms of
|
|
466
|
+
// each one. We will remove any from our list that the user does not have permission to
|
|
467
|
+
// delete.
|
|
468
|
+
if (this.modelClass.recordACL) {
|
|
469
|
+
finalUids = [];
|
|
470
|
+
for (const uid of uids) {
|
|
471
|
+
if (this.aclUtils && !options.ignoreACL) {
|
|
472
|
+
if (await this.aclUtils.hasPermission(options.user, uid, ACLAction.DELETE)) {
|
|
473
|
+
finalUids.push(uid);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// Now delete all records that were found
|
|
479
|
+
if (this.repo instanceof MongoRepository) {
|
|
480
|
+
await this.repo.deleteMany({ uid: { $in: finalUids } });
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
await this.repo.delete(finalUids);
|
|
484
|
+
}
|
|
485
|
+
if (!options?.skipPush) {
|
|
486
|
+
let channels = options?.pushChannels || [];
|
|
487
|
+
for (const uid of finalUids) {
|
|
488
|
+
const finalChannels = channels.concat([uid]);
|
|
489
|
+
this.notificationUtils?.sendMessage(finalChannels, this.modelClass.name, "delete", {
|
|
490
|
+
uid,
|
|
491
|
+
productUid: options.productUid,
|
|
492
|
+
version: options.version,
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
catch (err) {
|
|
499
|
+
// The error "ns not found" occurs when the collection doesn't exist yet. We can ignore this error.
|
|
500
|
+
if (err.message !== "ns not found") {
|
|
501
|
+
throw err;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
async update(obj, existing, options) {
|
|
506
|
+
if (!this.repo) {
|
|
507
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
508
|
+
}
|
|
509
|
+
if (this.aclUtils && !options?.ignoreACL) {
|
|
510
|
+
const acl = await this.aclUtils.findACL(existing.uid);
|
|
511
|
+
if (!(await this.aclUtils.hasPermission(options?.user, acl ? acl : this.defaultACLUid, ACLAction.UPDATE))) {
|
|
512
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// Enforce optimistic locking when applicable
|
|
516
|
+
if (existing instanceof BaseEntity) {
|
|
517
|
+
if (existing.version !== obj.version) {
|
|
518
|
+
throw new ApiError(ApiErrors.INVALID_OBJECT_VERSION, 409, ApiErrorMessages.INVALID_OBJECT_VERSION);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// Make sure the object provided actually matches the id given
|
|
522
|
+
if (existing.uid !== obj.uid) {
|
|
523
|
+
throw new ApiError(ApiErrors.OBJECT_ID_MISMATCH, 400, ApiErrorMessages.OBJECT_ID_MISMATCH);
|
|
524
|
+
}
|
|
525
|
+
// When using MongoDB we need to copy the _id property in order to prevent duplicate entries
|
|
526
|
+
if (existing instanceof BaseMongoEntity) {
|
|
527
|
+
obj._id = existing._id;
|
|
528
|
+
}
|
|
529
|
+
const keepPrevious = !!this.modelClass.trackChanges;
|
|
530
|
+
let query = this.searchIdQuery(existing.uid, options?.version || obj.version, options?.productUid || obj.productUid);
|
|
531
|
+
let result = null;
|
|
532
|
+
if (this.repo instanceof MongoRepository) {
|
|
533
|
+
if (existing instanceof BaseEntity) {
|
|
534
|
+
if (keepPrevious) {
|
|
535
|
+
result = this.instantiateObject(await this.repo.save({
|
|
536
|
+
...obj,
|
|
537
|
+
_id: undefined, // Ensure we save a new document
|
|
538
|
+
dateModified: new Date(),
|
|
539
|
+
version: obj.version + 1,
|
|
540
|
+
}));
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
await this.repo.updateOne({ uid: obj.uid, version: obj.version }, {
|
|
544
|
+
$set: {
|
|
545
|
+
...obj,
|
|
546
|
+
dateModified: new Date(),
|
|
547
|
+
version: obj.version + 1,
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else if (obj.uid) {
|
|
553
|
+
if (keepPrevious) {
|
|
554
|
+
result = this.instantiateObject(await this.repo.save({
|
|
555
|
+
...obj,
|
|
556
|
+
version: obj.version + 1,
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
await this.repo.updateOne({ uid: obj.uid }, {
|
|
561
|
+
$set: {
|
|
562
|
+
...obj,
|
|
563
|
+
},
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
const toSave = obj;
|
|
569
|
+
if (keepPrevious) {
|
|
570
|
+
toSave.version += 1;
|
|
571
|
+
}
|
|
572
|
+
result = await this.repo.save(toSave);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
if (existing instanceof BaseEntity) {
|
|
577
|
+
if (keepPrevious) {
|
|
578
|
+
await this.repo.insert({
|
|
579
|
+
...obj,
|
|
580
|
+
dateModified: new Date(),
|
|
581
|
+
version: obj.version + 1,
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
await this.repo.update(query.where, {
|
|
586
|
+
...obj,
|
|
587
|
+
dateModified: new Date(),
|
|
588
|
+
version: obj.version + 1,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
const toSave = obj;
|
|
594
|
+
if (keepPrevious) {
|
|
595
|
+
toSave.version += 1;
|
|
596
|
+
result = await this.repo.save(toSave);
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
await this.repo.update(query.where, toSave);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
query = this.searchIdQuery(existing.uid, obj instanceof BaseEntity ? obj.version + 1 : undefined);
|
|
604
|
+
if (!result) {
|
|
605
|
+
if (this.repo instanceof MongoRepository) {
|
|
606
|
+
result = await this.repo
|
|
607
|
+
.aggregate([
|
|
608
|
+
{
|
|
609
|
+
$match: query,
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
$sort: { version: -1 },
|
|
613
|
+
},
|
|
614
|
+
])
|
|
615
|
+
.limit(1)
|
|
616
|
+
.next();
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
result = await this.repo.findOne(query);
|
|
620
|
+
}
|
|
621
|
+
if (!result) {
|
|
622
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
result = this.instantiateObject(result);
|
|
626
|
+
if (result && this.cacheClient && this.modelClass.cacheTTL) {
|
|
627
|
+
// Cache the object for faster retrieval
|
|
628
|
+
void this.cacheClient.setex(`${this.baseCacheKey}.${this.hashQuery(query)}`, this.modelClass.cacheTTL, JSON.stringify(result));
|
|
629
|
+
void this.cacheClient.setex(`${this.baseCacheKey}.${this.hashQuery(this.searchIdQuery(result.uid))}`, this.modelClass.cacheTTL, JSON.stringify(result));
|
|
630
|
+
}
|
|
631
|
+
if (!options?.skipPush) {
|
|
632
|
+
let channels = [result.uid].concat(options?.pushChannels || []);
|
|
633
|
+
this.notificationUtils?.sendMessage(channels, this.modelClass.name, "update", result);
|
|
634
|
+
}
|
|
635
|
+
return result;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Performs validation on the object(s) provided. This function first calls `ObjectUtils.validate()` to check
|
|
639
|
+
* any class level defined validation functions. Second, it scans for any properties with the `@Reference`
|
|
640
|
+
* decorator and attempts to verify that an existing object for the given reference ID is valid.
|
|
641
|
+
*
|
|
642
|
+
* @param objs The object(s) to validate.
|
|
643
|
+
* @param options The optional set of arguments that can be passed to perform additonal checks.
|
|
644
|
+
*/
|
|
645
|
+
async validate(objs, options) {
|
|
646
|
+
objs = Array.isArray(objs) ? objs : [objs];
|
|
647
|
+
try {
|
|
648
|
+
for (let obj of objs) {
|
|
649
|
+
// Instantiate the correct object type so that we can perform validation correctly. If we don't do this
|
|
650
|
+
// then the provided object will be missing all decorators and validation won't work as desired.
|
|
651
|
+
const metadataObj = this.instantiateObject(obj);
|
|
652
|
+
ObjectUtils.validate(obj, metadataObj.constructor);
|
|
653
|
+
// Iterate through all properties and look for `@Reference`
|
|
654
|
+
for (const member of Object.getOwnPropertyNames(obj)) {
|
|
655
|
+
const clazz = Reflect.getMetadata("rrst:reference", metadataObj, member);
|
|
656
|
+
if (clazz && clazz.datastore && obj[member]) {
|
|
657
|
+
// Attempt to grab the repository for this reference type
|
|
658
|
+
const conn = this.connectionManager?.connections.get(clazz.datastore);
|
|
659
|
+
const repo = (conn instanceof MongoConnection || isSqlDataSource(conn)) ? conn.getRepository(clazz) : undefined;
|
|
660
|
+
if (repo) {
|
|
661
|
+
// Check to see if there are any objects with this UID in the datastore. If the value is an array
|
|
662
|
+
// let's make sure that every uid is valid.
|
|
663
|
+
const uids = Array.isArray(obj[member]) ? obj[member] : [obj[member]];
|
|
664
|
+
const query = ModelUtils.buildIdSearchQuery(repo, clazz, uids);
|
|
665
|
+
const count = await repo.count(query);
|
|
666
|
+
if (count !== uids.length) {
|
|
667
|
+
throw new ApiError(ApiErrorMessages.INVALID_REQUEST, 400, `Property ${member} is invalid. No resource found with the given identifier.`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
catch (err) {
|
|
675
|
+
throw new ApiError(ApiErrorMessages.INVALID_REQUEST, 400, err.message);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
__decorate([
|
|
680
|
+
Inject("ACLUtils"),
|
|
681
|
+
__metadata("design:type", ACLUtils)
|
|
682
|
+
], RepoUtils.prototype, "aclUtils", void 0);
|
|
683
|
+
__decorate([
|
|
684
|
+
RedisConnection("cache"),
|
|
685
|
+
__metadata("design:type", Redis)
|
|
686
|
+
], RepoUtils.prototype, "cacheClient", void 0);
|
|
687
|
+
__decorate([
|
|
688
|
+
Config(),
|
|
689
|
+
__metadata("design:type", Object)
|
|
690
|
+
], RepoUtils.prototype, "config", void 0);
|
|
691
|
+
__decorate([
|
|
692
|
+
Inject(ConnectionManager),
|
|
693
|
+
__metadata("design:type", ConnectionManager)
|
|
694
|
+
], RepoUtils.prototype, "connectionManager", void 0);
|
|
695
|
+
__decorate([
|
|
696
|
+
Logger,
|
|
697
|
+
__metadata("design:type", Object)
|
|
698
|
+
], RepoUtils.prototype, "logger", void 0);
|
|
699
|
+
__decorate([
|
|
700
|
+
Inject(ObjectFactory),
|
|
701
|
+
__metadata("design:type", ObjectFactory)
|
|
702
|
+
], RepoUtils.prototype, "objectFactory", void 0);
|
|
703
|
+
__decorate([
|
|
704
|
+
Inject(NotificationUtils),
|
|
705
|
+
__metadata("design:type", NotificationUtils)
|
|
706
|
+
], RepoUtils.prototype, "notificationUtils", void 0);
|
|
707
|
+
__decorate([
|
|
708
|
+
Config("trusted_roles", ["admin"]),
|
|
709
|
+
__metadata("design:type", Array)
|
|
710
|
+
], RepoUtils.prototype, "trustedRoles", void 0);
|
|
711
|
+
__decorate([
|
|
712
|
+
Init,
|
|
713
|
+
__metadata("design:type", Function),
|
|
714
|
+
__metadata("design:paramtypes", []),
|
|
715
|
+
__metadata("design:returntype", Promise)
|
|
716
|
+
], RepoUtils.prototype, "init", null);
|
|
717
|
+
//# sourceMappingURL=RepoUtils.js.map
|