@rapidrest/service-core 1.0.0-rc.9 → 1.0.0
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 +382 -23
- package/README.md +79 -5
- package/dist/lib/ApiErrors.js +10 -0
- package/dist/lib/ApiErrors.js.map +1 -1
- package/dist/lib/BackgroundService.js +1 -4
- package/dist/lib/BackgroundService.js.map +1 -1
- package/dist/lib/BackgroundServiceManager.js +47 -6
- package/dist/lib/BackgroundServiceManager.js.map +1 -1
- package/dist/lib/BulkError.js +4 -0
- package/dist/lib/BulkError.js.map +1 -1
- package/dist/lib/EventListenerManager.js +53 -18
- package/dist/lib/EventListenerManager.js.map +1 -1
- package/dist/lib/NetUtils.js +1 -0
- package/dist/lib/NetUtils.js.map +1 -1
- package/dist/lib/NotificationUtils.js +27 -8
- package/dist/lib/NotificationUtils.js.map +1 -1
- package/dist/lib/ObjectFactory.js +25 -44
- package/dist/lib/ObjectFactory.js.map +1 -1
- package/dist/lib/OpenApiSpec.js +31 -11
- package/dist/lib/OpenApiSpec.js.map +1 -1
- package/dist/lib/Server.js +74 -110
- package/dist/lib/Server.js.map +1 -1
- package/dist/lib/auth/AuthMiddleware.js +11 -16
- package/dist/lib/auth/AuthMiddleware.js.map +1 -1
- package/dist/lib/auth/JWTStrategy.js +68 -17
- package/dist/lib/auth/JWTStrategy.js.map +1 -1
- package/dist/lib/auth/index.js +0 -1
- package/dist/lib/auth/index.js.map +1 -1
- package/dist/lib/database/ConnectionKinds.js +15 -0
- package/dist/lib/database/ConnectionKinds.js.map +1 -1
- package/dist/lib/database/ConnectionManager.js +140 -61
- package/dist/lib/database/ConnectionManager.js.map +1 -1
- package/dist/lib/database/MongoConnection.js +13 -3
- package/dist/lib/database/MongoConnection.js.map +1 -1
- package/dist/lib/database/MongoRepository.js +61 -15
- package/dist/lib/database/MongoRepository.js.map +1 -1
- package/dist/lib/database/MongoSchemaSync.js +3 -3
- package/dist/lib/database/MongoSchemaSync.js.map +1 -1
- package/dist/lib/database/NamingUtils.js +3 -2
- package/dist/lib/database/NamingUtils.js.map +1 -1
- package/dist/lib/database/RedisCache.js +64 -0
- package/dist/lib/database/RedisCache.js.map +1 -0
- package/dist/lib/database/TypeOrmSupport.js +53 -13
- package/dist/lib/database/TypeOrmSupport.js.map +1 -1
- package/dist/lib/database/index.js +1 -0
- package/dist/lib/database/index.js.map +1 -1
- package/dist/lib/decorators/DatabaseDecorators.js +264 -20
- package/dist/lib/decorators/DatabaseDecorators.js.map +1 -1
- package/dist/lib/decorators/DocDecorators.js +30 -9
- package/dist/lib/decorators/DocDecorators.js.map +1 -1
- package/dist/lib/decorators/EventDecorators.js +1 -0
- package/dist/lib/decorators/EventDecorators.js.map +1 -1
- package/dist/lib/decorators/ModelDecorators.js +39 -23
- package/dist/lib/decorators/ModelDecorators.js.map +1 -1
- package/dist/lib/decorators/PersistenceDecorators.js +4 -3
- package/dist/lib/decorators/PersistenceDecorators.js.map +1 -1
- package/dist/lib/decorators/RouteDecorators.js +139 -36
- package/dist/lib/decorators/RouteDecorators.js.map +1 -1
- package/dist/lib/http/IWebSocketShim.js +2 -0
- package/dist/lib/http/IWebSocketShim.js.map +1 -0
- package/dist/lib/http/MiddlewareChain.js +167 -0
- package/dist/lib/http/MiddlewareChain.js.map +1 -0
- package/dist/lib/http/RuntimeDetect.js +9 -0
- package/dist/lib/http/RuntimeDetect.js.map +1 -0
- package/dist/lib/http/bun/BunAdapters.js +326 -0
- package/dist/lib/http/bun/BunAdapters.js.map +1 -0
- package/dist/lib/http/bun/BunRouter.js +391 -0
- package/dist/lib/http/bun/BunRouter.js.map +1 -0
- package/dist/lib/http/bun/BunWebSocket.js +49 -0
- package/dist/lib/http/bun/BunWebSocket.js.map +1 -0
- package/dist/lib/http/index.js +16 -3
- package/dist/lib/http/index.js.map +1 -1
- package/dist/lib/http/session/RedisSessionStore.js +30 -0
- package/dist/lib/http/session/RedisSessionStore.js.map +1 -0
- package/dist/lib/http/session/SessionManager.js +113 -0
- package/dist/lib/http/session/SessionManager.js.map +1 -0
- package/dist/lib/http/session/SessionStore.js +6 -0
- package/dist/lib/http/session/SessionStore.js.map +1 -0
- package/dist/lib/http/session/sessionMiddleware.js +42 -0
- package/dist/lib/http/session/sessionMiddleware.js.map +1 -0
- package/dist/lib/http/types.js +0 -3
- package/dist/lib/http/types.js.map +1 -1
- package/dist/lib/http/uWS/Adapters.js +421 -0
- package/dist/lib/http/uWS/Adapters.js.map +1 -0
- package/dist/lib/http/{Router.js → uWS/Router.js} +73 -173
- package/dist/lib/http/uWS/Router.js.map +1 -0
- package/dist/lib/http/{WebSocket.js → uWS/WebSocket.js} +4 -1
- package/dist/lib/http/uWS/WebSocket.js.map +1 -0
- package/dist/lib/models/BaseEntity.js +2 -1
- package/dist/lib/models/BaseEntity.js.map +1 -1
- package/dist/lib/models/BaseMongoEntity.js +2 -2
- package/dist/lib/models/BaseMongoEntity.js.map +1 -1
- package/dist/lib/models/ModelUtils.js +248 -101
- package/dist/lib/models/ModelUtils.js.map +1 -1
- package/dist/lib/models/RecoverableBaseEntity.js +6 -1
- package/dist/lib/models/RecoverableBaseEntity.js.map +1 -1
- package/dist/lib/models/RecoverableBaseMongoEntity.js +1 -0
- package/dist/lib/models/RecoverableBaseMongoEntity.js.map +1 -1
- package/dist/lib/models/RepoUtils.js +641 -213
- package/dist/lib/models/RepoUtils.js.map +1 -1
- package/dist/lib/models/SimpleEntity.js +1 -0
- package/dist/lib/models/SimpleEntity.js.map +1 -1
- package/dist/lib/models/SimpleMongoEntity.js +2 -1
- package/dist/lib/models/SimpleMongoEntity.js.map +1 -1
- package/dist/lib/models/StatusExtraData.js +4 -0
- package/dist/lib/models/StatusExtraData.js.map +1 -1
- package/dist/lib/routes/{AdminRoute.js → BaseAdminRoute.js} +98 -80
- package/dist/lib/routes/BaseAdminRoute.js.map +1 -0
- package/dist/lib/routes/{MetricsRoute.js → BaseMetricsRoute.js} +29 -15
- package/dist/lib/routes/BaseMetricsRoute.js.map +1 -0
- package/dist/lib/routes/{OpenAPIRoute.js → BaseOpenAPIRoute.js} +32 -32
- package/dist/lib/routes/BaseOpenAPIRoute.js.map +1 -0
- package/dist/lib/routes/BasePushRoute.js +305 -0
- package/dist/lib/routes/BasePushRoute.js.map +1 -0
- package/dist/lib/routes/BaseStaticRoute.js +153 -0
- package/dist/lib/routes/BaseStaticRoute.js.map +1 -0
- package/dist/lib/routes/BaseStatusRoute.js +80 -0
- package/dist/lib/routes/BaseStatusRoute.js.map +1 -0
- package/dist/lib/routes/CRUDRoute.js +284 -0
- package/dist/lib/routes/CRUDRoute.js.map +1 -0
- package/dist/lib/routes/ModelRoute.js +84 -57
- package/dist/lib/routes/ModelRoute.js.map +1 -1
- package/dist/lib/routes/RouteUtils.js +108 -20
- package/dist/lib/routes/RouteUtils.js.map +1 -1
- package/dist/lib/routes/index.js +7 -4
- package/dist/lib/routes/index.js.map +1 -1
- package/dist/lib/security/ACLUtils.js +297 -170
- package/dist/lib/security/ACLUtils.js.map +1 -1
- package/dist/lib/security/AccessControlList.js +18 -10
- package/dist/lib/security/AccessControlList.js.map +1 -1
- package/dist/lib/security/AccessControlListMongo.js +19 -68
- package/dist/lib/security/AccessControlListMongo.js.map +1 -1
- package/dist/lib/security/AccessControlListSQL.js +21 -70
- package/dist/lib/security/AccessControlListSQL.js.map +1 -1
- package/dist/lib/security/BaseACLRoute.js +239 -0
- package/dist/lib/security/BaseACLRoute.js.map +1 -0
- package/dist/lib/security/index.js +3 -0
- package/dist/lib/security/index.js.map +1 -1
- package/dist/lib/test/request.js +62 -5
- package/dist/lib/test/request.js.map +1 -1
- package/dist/lib/test/requestws.js +34 -10
- package/dist/lib/test/requestws.js.map +1 -1
- package/dist/types/ApiErrors.d.ts +8 -2
- package/dist/types/BackgroundService.d.ts +0 -5
- package/dist/types/BackgroundServiceManager.d.ts +2 -1
- package/dist/types/EventListenerManager.d.ts +7 -6
- package/dist/types/NotificationUtils.d.ts +5 -3
- package/dist/types/OpenApiSpec.d.ts +2 -2
- package/dist/types/Server.d.ts +28 -10
- package/dist/types/auth/AuthMiddleware.d.ts +3 -3
- package/dist/types/auth/AuthStrategy.d.ts +10 -7
- package/dist/types/auth/JWTStrategy.d.ts +6 -4
- package/dist/types/auth/index.d.ts +0 -1
- package/dist/types/database/ConnectionKinds.d.ts +7 -0
- package/dist/types/database/ConnectionManager.d.ts +29 -4
- package/dist/types/database/MongoConnection.d.ts +16 -4
- package/dist/types/database/MongoRepository.d.ts +32 -11
- package/dist/types/database/RedisCache.d.ts +23 -0
- package/dist/types/database/TypeOrmSupport.d.ts +4 -4
- package/dist/types/database/index.d.ts +1 -0
- package/dist/types/decorators/DatabaseDecorators.d.ts +139 -8
- package/dist/types/decorators/DocDecorators.d.ts +11 -2
- package/dist/types/decorators/ModelDecorators.d.ts +12 -5
- package/dist/types/decorators/PersistenceDecorators.d.ts +11 -4
- package/dist/types/decorators/RouteDecorators.d.ts +36 -6
- package/dist/types/http/IWebSocketShim.d.ts +11 -0
- package/dist/types/http/MiddlewareChain.d.ts +47 -0
- package/dist/types/http/RuntimeDetect.d.ts +2 -0
- package/dist/types/http/bun/BunAdapters.d.ts +139 -0
- package/dist/types/http/bun/BunRouter.d.ts +79 -0
- package/dist/types/http/bun/BunWebSocket.d.ts +35 -0
- package/dist/types/http/index.d.ts +16 -6
- package/dist/types/http/session/RedisSessionStore.d.ts +15 -0
- package/dist/types/http/session/SessionManager.d.ts +35 -0
- package/dist/types/http/session/SessionStore.d.ts +12 -0
- package/dist/types/http/session/sessionMiddleware.d.ts +10 -0
- package/dist/types/http/types.d.ts +42 -3
- package/dist/types/http/uWS/Adapters.d.ts +129 -0
- package/dist/types/http/{Router.d.ts → uWS/Router.d.ts} +14 -38
- package/dist/types/http/{WebSocket.d.ts → uWS/WebSocket.d.ts} +6 -5
- package/dist/types/models/BaseEntity.d.ts +1 -1
- package/dist/types/models/ModelUtils.d.ts +47 -14
- package/dist/types/models/RecoverableBaseEntity.d.ts +3 -1
- package/dist/types/models/RepoUtils.d.ts +84 -14
- package/dist/types/routes/BaseAdminRoute.d.ts +65 -0
- package/dist/types/routes/BaseMetricsRoute.d.ts +37 -0
- package/dist/types/routes/BaseOpenAPIRoute.d.ts +33 -0
- package/dist/types/routes/BasePushRoute.d.ts +53 -0
- package/dist/types/routes/BaseStaticRoute.d.ts +44 -0
- package/dist/types/routes/BaseStatusRoute.d.ts +37 -0
- package/dist/types/routes/CRUDRoute.d.ts +57 -0
- package/dist/types/routes/ModelRoute.d.ts +39 -36
- package/dist/types/routes/RouteUtils.d.ts +19 -0
- package/dist/types/routes/index.d.ts +7 -4
- package/dist/types/security/ACLUtils.d.ts +94 -18
- package/dist/types/security/AccessControlList.d.ts +37 -59
- package/dist/types/security/AccessControlListMongo.d.ts +3 -8
- package/dist/types/security/AccessControlListSQL.d.ts +3 -8
- package/dist/types/security/BaseACLRoute.d.ts +73 -0
- package/dist/types/security/index.d.ts +3 -0
- package/dist/types/test/request.d.ts +18 -1
- package/package.json +135 -129
- package/tsconfig.export.json +17 -0
- package/dist/lib/auth/BasicStrategy.js +0 -113
- package/dist/lib/auth/BasicStrategy.js.map +0 -1
- package/dist/lib/http/Adapters.js +0 -241
- package/dist/lib/http/Adapters.js.map +0 -1
- package/dist/lib/http/Router.js.map +0 -1
- package/dist/lib/http/WebSocket.js.map +0 -1
- package/dist/lib/routes/AdminRoute.js.map +0 -1
- package/dist/lib/routes/MetricsRoute.js.map +0 -1
- package/dist/lib/routes/OpenAPIRoute.js.map +0 -1
- package/dist/lib/routes/StatusRoute.js +0 -55
- package/dist/lib/routes/StatusRoute.js.map +0 -1
- package/dist/lib/security/ACLRouteMongo.js +0 -194
- package/dist/lib/security/ACLRouteMongo.js.map +0 -1
- package/dist/lib/security/ACLRouteSQL.js +0 -193
- package/dist/lib/security/ACLRouteSQL.js.map +0 -1
- package/dist/types/auth/BasicStrategy.d.ts +0 -37
- package/dist/types/http/Adapters.d.ts +0 -68
- package/dist/types/routes/AdminRoute.d.ts +0 -47
- package/dist/types/routes/MetricsRoute.d.ts +0 -15
- package/dist/types/routes/OpenAPIRoute.d.ts +0 -17
- package/dist/types/routes/StatusRoute.d.ts +0 -11
- package/dist/types/security/ACLRouteMongo.d.ts +0 -19
- package/dist/types/security/ACLRouteSQL.d.ts +0 -19
- package/docs/Makefile +0 -20
- package/docs/conf.py +0 -58
- package/docs/index.rst +0 -17
- package/docs/make.bat +0 -35
- package/docs/reference/@rapidrest/namespaces/DatabaseDecorators/README.md +0 -13
- package/docs/reference/@rapidrest/namespaces/DatabaseDecorators/functions/MongoRepository.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DatabaseDecorators/functions/RedisConnection.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DatabaseDecorators/functions/Repository.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DocDecorators/README.md +0 -23
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Default.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Description.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Document.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Example.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Format.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Returns.md +0 -28
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Summary.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/Tags.md +0 -25
- package/docs/reference/@rapidrest/namespaces/DocDecorators/functions/TypeInfo.md +0 -28
- package/docs/reference/@rapidrest/namespaces/DocDecorators/interfaces/DocumentsData.md +0 -57
- package/docs/reference/@rapidrest/namespaces/EventDecorators/README.md +0 -12
- package/docs/reference/@rapidrest/namespaces/EventDecorators/functions/EventListener.md +0 -17
- package/docs/reference/@rapidrest/namespaces/EventDecorators/functions/OnEvent.md +0 -26
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/README.md +0 -26
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Cache.md +0 -25
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/ChildEntity.md +0 -18
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/DataStore.md +0 -25
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Identifier.md +0 -27
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Protect.md +0 -35
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Reference.md +0 -25
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/Shard.md +0 -27
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/functions/TrackChanges.md +0 -26
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/interfaces/PendingTypeOrmColumn.md +0 -45
- package/docs/reference/@rapidrest/namespaces/ModelDecorators/variables/pendingTypeOrmColumns.md +0 -14
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/README.md +0 -27
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/Column.md +0 -25
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/Entity.md +0 -25
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/Index.md +0 -119
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/PrimaryColumn.md +0 -25
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/Unique.md +0 -68
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/getColumnMetadata.md +0 -26
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/getEntityName.md +0 -26
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/functions/getIndexMetadata.md +0 -27
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/CollationOptions.md +0 -79
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/ColumnInfo.md +0 -41
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/ColumnOptions.md +0 -51
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/EntityOptions.md +0 -32
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/IndexInfo.md +0 -41
- package/docs/reference/@rapidrest/namespaces/PersistenceDecorators/interfaces/IndexOptions.md +0 -61
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/README.md +0 -36
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/After.md +0 -26
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Auth.md +0 -33
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/AuthResult.md +0 -31
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Before.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/ContentType.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Delete.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Get.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Head.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Header.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Method.md +0 -31
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Model.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Options.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Param.md +0 -26
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Patch.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Post.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Protect.md +0 -31
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Put.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Query.md +0 -26
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Request.md +0 -31
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/RequiresRole.md +0 -26
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Response.md +0 -31
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Route.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Socket.md +0 -33
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/User.md +0 -31
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/Validate.md +0 -25
- package/docs/reference/@rapidrest/namespaces/RouteDecorators/functions/WebSocket.md +0 -25
- package/docs/reference/README.md +0 -20
- package/docs/reference/classes/ACLUtils.md +0 -263
- package/docs/reference/classes/AdminRoute.md +0 -51
- package/docs/reference/classes/AuthMiddleware.md +0 -173
- package/docs/reference/classes/BackgroundService.md +0 -117
- package/docs/reference/classes/BackgroundServiceManager.md +0 -172
- package/docs/reference/classes/BaseEntity.md +0 -82
- package/docs/reference/classes/BaseMongoEntity.md +0 -107
- package/docs/reference/classes/BasicStrategy.md +0 -131
- package/docs/reference/classes/BasicStrategyOptions.md +0 -115
- package/docs/reference/classes/BulkError.md +0 -271
- package/docs/reference/classes/ConnectionManager.md +0 -75
- package/docs/reference/classes/EventListenerManager.md +0 -88
- package/docs/reference/classes/HttpRouter.md +0 -343
- package/docs/reference/classes/JWTStrategy.md +0 -100
- package/docs/reference/classes/JWTStrategyOptions.md +0 -97
- package/docs/reference/classes/MetricsRoute.md +0 -27
- package/docs/reference/classes/ModelRoute.md +0 -527
- package/docs/reference/classes/ModelUtils.md +0 -446
- package/docs/reference/classes/MongoConnection.md +0 -218
- package/docs/reference/classes/MongoRepository.md +0 -390
- package/docs/reference/classes/MongoSchemaSync.md +0 -97
- package/docs/reference/classes/NetUtils.md +0 -90
- package/docs/reference/classes/NotificationUtils.md +0 -77
- package/docs/reference/classes/ObjectFactory.md +0 -336
- package/docs/reference/classes/OpenAPIRoute.md +0 -77
- package/docs/reference/classes/OpenApiSpec.md +0 -892
- package/docs/reference/classes/RecoverableBaseEntity.md +0 -114
- package/docs/reference/classes/RecoverableBaseMongoEntity.md +0 -124
- package/docs/reference/classes/RedisTransport.md +0 -2202
- package/docs/reference/classes/RepoUtils.md +0 -482
- package/docs/reference/classes/RouteUtils.md +0 -191
- package/docs/reference/classes/Server.md +0 -408
- package/docs/reference/classes/SimpleEntity.md +0 -48
- package/docs/reference/classes/SimpleMongoEntity.md +0 -66
- package/docs/reference/classes/StatusExtraData.md +0 -57
- package/docs/reference/classes/StatusRoute.md +0 -26
- package/docs/reference/classes/UWSRequest.md +0 -226
- package/docs/reference/classes/UWSResponse.md +0 -257
- package/docs/reference/classes/UWSWebSocketShim.md +0 -958
- package/docs/reference/enumerations/ACLAction.md +0 -63
- package/docs/reference/enumerations/ApiErrorMessages.md +0 -123
- package/docs/reference/enumerations/ApiErrors.md +0 -123
- package/docs/reference/functions/createWebSocketStream.md +0 -27
- package/docs/reference/functions/isSqlDataSource.md +0 -26
- package/docs/reference/functions/readBody.md +0 -29
- package/docs/reference/functions/resolveCollectionName.md +0 -33
- package/docs/reference/functions/runChain.md +0 -40
- package/docs/reference/functions/snakeCase.md +0 -28
- package/docs/reference/globals.md +0 -106
- package/docs/reference/interfaces/ACLRecord.md +0 -96
- package/docs/reference/interfaces/AccessControlList.md +0 -76
- package/docs/reference/interfaces/AuthResult.md +0 -55
- package/docs/reference/interfaces/AuthStrategy.md +0 -95
- package/docs/reference/interfaces/CreateRequestOptions.md +0 -121
- package/docs/reference/interfaces/DeleteRequestOptions.md +0 -137
- package/docs/reference/interfaces/FindRequestOptions.md +0 -133
- package/docs/reference/interfaces/HttpRequest.md +0 -147
- package/docs/reference/interfaces/HttpResponse.md +0 -164
- package/docs/reference/interfaces/JWTAuthResult.md +0 -84
- package/docs/reference/interfaces/RepoCreateOptions.md +0 -95
- package/docs/reference/interfaces/RepoDeleteOptions.md +0 -105
- package/docs/reference/interfaces/RepoFindOptions.md +0 -125
- package/docs/reference/interfaces/RepoOperationOptions.md +0 -69
- package/docs/reference/interfaces/RepoUpdateOptions.md +0 -101
- package/docs/reference/interfaces/RequestOptions.md +0 -112
- package/docs/reference/interfaces/RequestWS.md +0 -225
- package/docs/reference/interfaces/TruncateRequestOptions.md +0 -161
- package/docs/reference/interfaces/UpdateRequestOptions.md +0 -139
- package/docs/reference/type-aliases/ErrorHandler.md +0 -35
- package/docs/reference/type-aliases/NextFunction.md +0 -23
- package/docs/reference/type-aliases/OneOrMany.md +0 -19
- package/docs/reference/type-aliases/OneOrNull.md +0 -19
- package/docs/reference/type-aliases/PartialBaseEntity.md +0 -17
- package/docs/reference/type-aliases/PartialSimpleEntity.md +0 -17
- package/docs/reference/type-aliases/RequestHandler.md +0 -31
- package/docs/reference/type-aliases/UpdateObject.md +0 -19
- package/docs/reference/type-aliases/WsUpgradeAuth.md +0 -27
- package/docs/reference/type-aliases/WsUpgradeAuthResult.md +0 -47
|
@@ -6,8 +6,10 @@ import { RecoverableBaseEntity } from "./RecoverableBaseEntity.js";
|
|
|
6
6
|
import { ApiErrorMessages, ApiErrors } from "../ApiErrors.js";
|
|
7
7
|
const logger = Logger();
|
|
8
8
|
const REGEX_QUERY_PARAM_VALUE = new RegExp(/^([a-zA-Z]+)\((.*)\)$/, "i");
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// Anchored at the start so these only match the intended reserved parameter names/prefixes and not any
|
|
10
|
+
// field that merely contains one as a substring (e.g. "sortOrder", "rateLimit", "packageId", "homepage").
|
|
11
|
+
const REGEX_RESERVED_QUERY_PARAMS = new RegExp("^(jwt_|oauth_|auth_|cache).*", "i");
|
|
12
|
+
const REGEX_QUERY_LIMITS = new RegExp("^(limit|page|sort)$", "i");
|
|
11
13
|
const REGEX_QUERY_SORT_STRING = new RegExp(/^\{.*\}$/, "i");
|
|
12
14
|
// Apparently calling JSON.stringify on RegExp returns an empty set. So the recommended way to
|
|
13
15
|
// overcome this is by adding a `toJSON` method that uses the `toString` instead which will
|
|
@@ -21,7 +23,7 @@ RegExp.prototype.toJSON = RegExp.prototype.toString;
|
|
|
21
23
|
export class ModelUtils {
|
|
22
24
|
/**
|
|
23
25
|
* Provides the `typeorm` module to use when building SQL queries. This is called automatically when a SQL
|
|
24
|
-
*
|
|
26
|
+
* datasource connection is established.
|
|
25
27
|
*
|
|
26
28
|
* @param module The `typeorm` module.
|
|
27
29
|
*/
|
|
@@ -33,7 +35,7 @@ export class ModelUtils {
|
|
|
33
35
|
*/
|
|
34
36
|
static get orm() {
|
|
35
37
|
if (!ModelUtils.typeOrm) {
|
|
36
|
-
throw new Error("SQL query construction requires the optional peer dependency 'typeorm' but no SQL
|
|
38
|
+
throw new Error("SQL query construction requires the optional peer dependency 'typeorm' but no SQL datasource has been initialized.");
|
|
37
39
|
}
|
|
38
40
|
return ModelUtils.typeOrm;
|
|
39
41
|
}
|
|
@@ -65,6 +67,34 @@ export class ModelUtils {
|
|
|
65
67
|
ModelUtils.idPropertyCache.set(modelClass, results);
|
|
66
68
|
return results;
|
|
67
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Retrieves a list of all of the specified class's properties that have the @ReadOnly decorator applied.
|
|
72
|
+
*
|
|
73
|
+
* @param modelClass The class definition to search for read-only properties from.
|
|
74
|
+
* @returns The list of all property names that have the @ReadOnly decorator applied.
|
|
75
|
+
*/
|
|
76
|
+
static getReadOnlyPropertyNames(modelClass) {
|
|
77
|
+
const results = [];
|
|
78
|
+
if (ModelUtils.readOnlyPropertyCache.has(modelClass)) {
|
|
79
|
+
return ModelUtils.readOnlyPropertyCache.get(modelClass);
|
|
80
|
+
}
|
|
81
|
+
// The props don't show up correctly on the class def. So instantiate a dummy object that we can read the props
|
|
82
|
+
// from and look for read-only fields.
|
|
83
|
+
let proto = Object.getPrototypeOf(new modelClass());
|
|
84
|
+
while (proto) {
|
|
85
|
+
const props = Object.getOwnPropertyNames(proto);
|
|
86
|
+
for (const prop of props) {
|
|
87
|
+
const isReadOnly = Reflect.getMetadata("rrst:readOnly", proto, prop);
|
|
88
|
+
if (isReadOnly) {
|
|
89
|
+
results.push(prop);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
proto = Object.getPrototypeOf(proto);
|
|
93
|
+
}
|
|
94
|
+
// Cache the results so we're not always having to walk the class structure
|
|
95
|
+
ModelUtils.readOnlyPropertyCache.set(modelClass, results);
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
68
98
|
/**
|
|
69
99
|
* Builds a query object for use with `find` functions of the given repository for retrieving objects matching the
|
|
70
100
|
* specified unique identifier.
|
|
@@ -73,14 +103,19 @@ export class ModelUtils {
|
|
|
73
103
|
* @param modelClass The class definition of the data model to build a search query for.
|
|
74
104
|
* @param id The unique identifier to search for.
|
|
75
105
|
* @param version The version number of the document to search for.
|
|
106
|
+
* @param includeDeleted Set to false to exclude soft-deleted `RecoverableBaseEntity` records from matching.
|
|
107
|
+
* Defaults to true (matches regardless of deleted state) to preserve existing lookup/validation behavior;
|
|
108
|
+
* callers that expose a record by id to an API client (e.g. `findOne`, `exists`) should pass false so a
|
|
109
|
+
* soft-deleted record doesn't remain fully readable/resurrectable, the same way `buildSearchQuery` already
|
|
110
|
+
* excludes deleted records by default for list-style queries.
|
|
76
111
|
* @returns An object that can be passed to a TypeORM `find` function.
|
|
77
112
|
*/
|
|
78
|
-
static buildIdSearchQuery(repo, modelClass, id, version) {
|
|
113
|
+
static buildIdSearchQuery(repo, modelClass, id, version, includeDeleted = true) {
|
|
79
114
|
if (repo instanceof MongoRepository) {
|
|
80
|
-
return ModelUtils.buildIdSearchQueryMongo(modelClass, id, version);
|
|
115
|
+
return ModelUtils.buildIdSearchQueryMongo(modelClass, id, version, includeDeleted);
|
|
81
116
|
}
|
|
82
117
|
else {
|
|
83
|
-
return ModelUtils.buildIdSearchQuerySQL(modelClass, id, version);
|
|
118
|
+
return ModelUtils.buildIdSearchQuerySQL(modelClass, id, version, includeDeleted);
|
|
84
119
|
}
|
|
85
120
|
}
|
|
86
121
|
/**
|
|
@@ -92,8 +127,9 @@ export class ModelUtils {
|
|
|
92
127
|
* @param version The version number of the document to search for.
|
|
93
128
|
* @returns An object that can be passed to a TypeORM `find` function.
|
|
94
129
|
*/
|
|
95
|
-
static buildIdSearchQuerySQL(modelClass, id, version) {
|
|
130
|
+
static buildIdSearchQuerySQL(modelClass, id, version, includeDeleted = true) {
|
|
96
131
|
const props = ModelUtils.getIdPropertyNames(modelClass);
|
|
132
|
+
const isRecoverable = new modelClass() instanceof RecoverableBaseEntity;
|
|
97
133
|
// Create the where in SQL syntax. We only care about one of the identifier field's matching.
|
|
98
134
|
// e.g. WHERE idField1 = :idField1 OR idField2 = :idField2 ...
|
|
99
135
|
const where = [];
|
|
@@ -102,6 +138,11 @@ export class ModelUtils {
|
|
|
102
138
|
if (version !== undefined) {
|
|
103
139
|
q.version = version;
|
|
104
140
|
}
|
|
141
|
+
// By default we don't want an id-based lookup to match a soft-deleted recoverable object,
|
|
142
|
+
// matching the same default `buildSearchQuery` applies to list-style queries.
|
|
143
|
+
if (isRecoverable && !includeDeleted) {
|
|
144
|
+
q.deleted = false;
|
|
145
|
+
}
|
|
105
146
|
where.push(q);
|
|
106
147
|
}
|
|
107
148
|
return { where };
|
|
@@ -113,10 +154,12 @@ export class ModelUtils {
|
|
|
113
154
|
* @param modelClass The class definition of the data model to build a search query for.
|
|
114
155
|
* @param id The unique identifier to search for.
|
|
115
156
|
* @param version The version number of the document to search for.
|
|
157
|
+
* @param includeDeleted Set to true to also match soft-deleted `RecoverableBaseEntity` records.
|
|
116
158
|
* @returns An object that can be passed to a MongoDB `find` function.
|
|
117
159
|
*/
|
|
118
|
-
static buildIdSearchQueryMongo(modelClass, id, version) {
|
|
160
|
+
static buildIdSearchQueryMongo(modelClass, id, version, includeDeleted = true) {
|
|
119
161
|
const props = ModelUtils.getIdPropertyNames(modelClass);
|
|
162
|
+
const isRecoverable = new modelClass() instanceof RecoverableBaseEntity;
|
|
120
163
|
// We want to performa case-insensitive search. We used to convert strings to RegEx but this is _very_
|
|
121
164
|
// slow. Instead, we use case-insenstive indexes that are configured using the `collation` option. Now
|
|
122
165
|
// we only need to pass in the raw string. So we skip this section but we retain the code for historical
|
|
@@ -138,6 +181,11 @@ export class ModelUtils {
|
|
|
138
181
|
if (version !== undefined) {
|
|
139
182
|
q.version = version;
|
|
140
183
|
}
|
|
184
|
+
// By default we don't want an id-based lookup to match a soft-deleted recoverable object,
|
|
185
|
+
// matching the same default `buildSearchQuery` applies to list-style queries.
|
|
186
|
+
if (isRecoverable && !includeDeleted) {
|
|
187
|
+
q.deleted = false;
|
|
188
|
+
}
|
|
141
189
|
query.push(q);
|
|
142
190
|
}
|
|
143
191
|
return { $or: query };
|
|
@@ -179,7 +227,9 @@ export class ModelUtils {
|
|
|
179
227
|
case "gte":
|
|
180
228
|
return MoreThanOrEqual(value);
|
|
181
229
|
case "in": {
|
|
182
|
-
|
|
230
|
+
// Split the raw match text, not `value`: a comma-joined list like "1,5" can itself parse
|
|
231
|
+
// as a valid `Date` above (e.g. "1,5" -> Jan 5), leaving `value` a Date with no `.split()`.
|
|
232
|
+
const args = matches[2].split(",");
|
|
183
233
|
return In(args);
|
|
184
234
|
}
|
|
185
235
|
case "like":
|
|
@@ -191,8 +241,14 @@ export class ModelUtils {
|
|
|
191
241
|
case "ne":
|
|
192
242
|
case "not":
|
|
193
243
|
return Not(value);
|
|
244
|
+
case "nin": {
|
|
245
|
+
// See "in" above for why this splits `matches[2]` instead of `value`.
|
|
246
|
+
const args = matches[2].split(",");
|
|
247
|
+
return Not(In(args));
|
|
248
|
+
}
|
|
194
249
|
case "range": {
|
|
195
|
-
|
|
250
|
+
// See "in" above for why this splits `matches[2]` instead of `value`.
|
|
251
|
+
const args = matches[2].split(",");
|
|
196
252
|
if (args.length !== 2) {
|
|
197
253
|
const msg = StringUtils.findAndReplace(ApiErrorMessages.SEARCH_INVALID_RANGE, {
|
|
198
254
|
value,
|
|
@@ -228,6 +284,45 @@ export class ModelUtils {
|
|
|
228
284
|
return param;
|
|
229
285
|
}
|
|
230
286
|
}
|
|
287
|
+
/**
|
|
288
|
+
* Recursively verifies that no key in the given value (at any depth, including keys of objects nested inside
|
|
289
|
+
* arrays) is a MongoDB operator (starts with `$`) or uses dot-notation field addressing (contains `.`). Client
|
|
290
|
+
* input is only ever meant to supply plain field values/comparison operands — never raw Mongo query operators —
|
|
291
|
+
* so any such key indicates an attempt to inject arbitrary query behavior (e.g. `$where`, `$expr`, or reaching
|
|
292
|
+
* into a field the API doesn't expose via dot-notation).
|
|
293
|
+
*
|
|
294
|
+
* @param value The value to check, typically a parsed query parameter.
|
|
295
|
+
* @throws {ApiError} If an operator-like or dotted key is found anywhere in `value`.
|
|
296
|
+
*/
|
|
297
|
+
static assertNoOperatorInjection(value) {
|
|
298
|
+
if (Array.isArray(value)) {
|
|
299
|
+
for (const item of value) {
|
|
300
|
+
ModelUtils.assertNoOperatorInjection(item);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else if (value && typeof value === "object") {
|
|
304
|
+
for (const key of Object.keys(value)) {
|
|
305
|
+
if (key.startsWith("$") || key.includes(".")) {
|
|
306
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
|
|
307
|
+
}
|
|
308
|
+
ModelUtils.assertNoOperatorInjection(value[key]);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Best-effort check for regex patterns vulnerable to catastrophic backtracking (ReDoS). Rejects patterns that
|
|
314
|
+
* are unreasonably long, or that contain a quantified group whose contents are themselves quantified (e.g.
|
|
315
|
+
* `(a+)+`, `(a*)*`) — the classic shape that causes exponential backtracking in JS's regex engine. This is not
|
|
316
|
+
* an exhaustive defense; it catches the common cases a client would realistically send.
|
|
317
|
+
*
|
|
318
|
+
* @param pattern The user-supplied `like()` pattern.
|
|
319
|
+
*/
|
|
320
|
+
static isUnsafeRegexPattern(pattern) {
|
|
321
|
+
if (pattern.length > ModelUtils.MAX_LIKE_PATTERN_LENGTH) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
return /\([^()]*[+*]\)[+*{]/.test(pattern);
|
|
325
|
+
}
|
|
231
326
|
/**
|
|
232
327
|
* Given a string containing a parameter value and/or a comparison operation return a MongoDB compatible find value.
|
|
233
328
|
* e.g.
|
|
@@ -256,6 +351,11 @@ export class ModelUtils {
|
|
|
256
351
|
value = matches[2];
|
|
257
352
|
}
|
|
258
353
|
}
|
|
354
|
+
// `value` is the client-supplied comparison operand — reject any Mongo operator/dotted key hidden
|
|
355
|
+
// inside it, regardless of which (trusted, framework-constructed) operator wrapper it ends up
|
|
356
|
+
// under below. Must run *after* the parse fallback above, not inside its try/catch, so a
|
|
357
|
+
// rejection here isn't silently swallowed as a "not valid JSON" case.
|
|
358
|
+
ModelUtils.assertNoOperatorInjection(value);
|
|
259
359
|
switch (opName) {
|
|
260
360
|
case "eq":
|
|
261
361
|
return value;
|
|
@@ -264,15 +364,23 @@ export class ModelUtils {
|
|
|
264
364
|
case "gte":
|
|
265
365
|
return { $gte: value };
|
|
266
366
|
case "in": {
|
|
267
|
-
|
|
367
|
+
// Split the raw match text, not `value`: a comma-joined list like "1,5" can itself parse
|
|
368
|
+
// as a valid `Date` above (e.g. "1,5" -> Jan 5), leaving `value` a Date with no `.split()`.
|
|
369
|
+
const args = matches[2].split(",");
|
|
268
370
|
return { $in: args };
|
|
269
371
|
}
|
|
270
372
|
case "nin": {
|
|
271
|
-
|
|
373
|
+
// See "in" above for why this splits `matches[2]` instead of `value`.
|
|
374
|
+
const args = matches[2].split(",");
|
|
272
375
|
return { $nin: args };
|
|
273
376
|
}
|
|
274
|
-
case "like":
|
|
275
|
-
|
|
377
|
+
case "like": {
|
|
378
|
+
const pattern = String(value);
|
|
379
|
+
if (ModelUtils.isUnsafeRegexPattern(pattern)) {
|
|
380
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
|
|
381
|
+
}
|
|
382
|
+
return { $regex: pattern, $options: "i" };
|
|
383
|
+
}
|
|
276
384
|
case "lt":
|
|
277
385
|
return { $lt: value };
|
|
278
386
|
case "lte":
|
|
@@ -282,7 +390,8 @@ export class ModelUtils {
|
|
|
282
390
|
case "not":
|
|
283
391
|
return { $not: value };
|
|
284
392
|
case "range": {
|
|
285
|
-
|
|
393
|
+
// See "in" above for why this splits `matches[2]` instead of `value`.
|
|
394
|
+
const args = matches[2].split(",");
|
|
286
395
|
if (args.length !== 2) {
|
|
287
396
|
const msg = StringUtils.findAndReplace(ApiErrorMessages.SEARCH_INVALID_RANGE, {
|
|
288
397
|
value,
|
|
@@ -290,31 +399,45 @@ export class ModelUtils {
|
|
|
290
399
|
});
|
|
291
400
|
throw new ApiError(ApiErrors.SEARCH_INVALID_RANGE, 400, msg);
|
|
292
401
|
}
|
|
402
|
+
// Attempt to parse the range values to native types, falling back to the raw strings
|
|
403
|
+
let gte = args[0];
|
|
404
|
+
let lte = args[1];
|
|
293
405
|
try {
|
|
294
|
-
|
|
295
|
-
|
|
406
|
+
gte = JSON.parse(args[0]);
|
|
407
|
+
lte = JSON.parse(args[1]);
|
|
296
408
|
}
|
|
297
409
|
catch (err) {
|
|
298
|
-
|
|
410
|
+
// Not valid JSON — fall back to the raw strings assigned above.
|
|
299
411
|
}
|
|
412
|
+
ModelUtils.assertNoOperatorInjection(gte);
|
|
413
|
+
ModelUtils.assertNoOperatorInjection(lte);
|
|
414
|
+
return { $gte: gte, $lte: lte };
|
|
300
415
|
}
|
|
301
416
|
default:
|
|
302
417
|
return value;
|
|
303
418
|
}
|
|
304
419
|
}
|
|
305
420
|
else {
|
|
421
|
+
// Attempt to parse the value to a native type, falling back to a date/string if it's not valid JSON
|
|
422
|
+
let parsed;
|
|
306
423
|
try {
|
|
307
|
-
|
|
308
|
-
return JSON.parse(param);
|
|
424
|
+
parsed = JSON.parse(param);
|
|
309
425
|
}
|
|
310
426
|
catch (err) {
|
|
311
|
-
// If an error occurred it's because the value is a string or date, not another type.
|
|
312
427
|
const date = new Date(param);
|
|
313
428
|
return !isNaN(date.valueOf()) ? date : param;
|
|
314
429
|
}
|
|
430
|
+
ModelUtils.assertNoOperatorInjection(parsed);
|
|
431
|
+
return parsed;
|
|
315
432
|
}
|
|
316
433
|
}
|
|
317
434
|
else {
|
|
435
|
+
// A non-string value only reaches here when the caller already parsed the raw query into native
|
|
436
|
+
// types itself (e.g. the `q` base64-encoded JSON query parameter in RouteUtils.wrapMiddleware) —
|
|
437
|
+
// it never passed through the string-only checks above, so it must be validated here instead.
|
|
438
|
+
// Without this, a client could smuggle a raw Mongo operator (e.g. `{"$ne": null}`) straight into
|
|
439
|
+
// the query by supplying it as a JSON object rather than an `op(value)`-encoded string.
|
|
440
|
+
ModelUtils.assertNoOperatorInjection(param);
|
|
318
441
|
return param;
|
|
319
442
|
}
|
|
320
443
|
}
|
|
@@ -338,25 +461,24 @@ export class ModelUtils {
|
|
|
338
461
|
*
|
|
339
462
|
* @param modelClass The class definition of the data model to build a search query for.
|
|
340
463
|
* @param repo The repository to build a search query for.
|
|
341
|
-
* @param {any}
|
|
342
|
-
* @param {any} queryParams The URI query parameters that were included in the request.
|
|
464
|
+
* @param {any} query The search query parameters to include.
|
|
343
465
|
* @param {bool} exactMatch Set to true to create a query where parameters are to be matched exactly, otherwise set to false to use a 'contains' search.
|
|
344
466
|
* @param {any} user The user that is performing the request.
|
|
345
467
|
* @returns {object} The TypeORM compatible query object.
|
|
346
468
|
*/
|
|
347
|
-
static buildSearchQuery(modelClass, repo,
|
|
469
|
+
static buildSearchQuery(modelClass, repo, query = {}, exactMatch = false, user) {
|
|
348
470
|
// By default we don't want to return deleted recoverable objects unless explicitly requested
|
|
349
471
|
if (new modelClass() instanceof RecoverableBaseEntity) {
|
|
350
|
-
|
|
351
|
-
...
|
|
352
|
-
deleted:
|
|
472
|
+
query = {
|
|
473
|
+
...query,
|
|
474
|
+
deleted: query && "deleted" in query ? query.deleted : false,
|
|
353
475
|
};
|
|
354
476
|
}
|
|
355
477
|
if (repo instanceof MongoRepository) {
|
|
356
|
-
return ModelUtils.buildSearchQueryMongo(modelClass,
|
|
478
|
+
return ModelUtils.buildSearchQueryMongo(modelClass, query, exactMatch, user);
|
|
357
479
|
}
|
|
358
480
|
else {
|
|
359
|
-
return ModelUtils.buildSearchQuerySQL(modelClass,
|
|
481
|
+
return ModelUtils.buildSearchQuerySQL(modelClass, query, exactMatch, user);
|
|
360
482
|
}
|
|
361
483
|
}
|
|
362
484
|
/**
|
|
@@ -376,26 +498,22 @@ export class ModelUtils {
|
|
|
376
498
|
* When no operator is provided the comparison will always be evaluated as `eq`.
|
|
377
499
|
*
|
|
378
500
|
* @param modelClass The class definition of the data model to build a search query for.
|
|
379
|
-
* @param {any}
|
|
380
|
-
* @param {any} queryParams The URI query parameters that were included in the request.
|
|
501
|
+
* @param {any} query The search query parameters to include.
|
|
381
502
|
* @param {bool} exactMatch Set to true to create a query where parameters are to be matched exactly, otherwise set to false to use a 'contains' search.
|
|
382
503
|
* @param {any} user The user that is performing the request.
|
|
383
504
|
* @returns {object} The TypeORM compatible query object.
|
|
384
505
|
*/
|
|
385
|
-
static buildSearchQuerySQL(modelClass,
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
//
|
|
389
|
-
for (const key in
|
|
506
|
+
static buildSearchQuerySQL(modelClass, query = {}, exactMatch = false, user) {
|
|
507
|
+
const result = {};
|
|
508
|
+
result.where = [];
|
|
509
|
+
// Pre-process any query values (e.g. the `me` identifier)
|
|
510
|
+
for (const key in query) {
|
|
390
511
|
// If the value is 'me' that's a special keyword to reference the user ID.
|
|
391
|
-
if (
|
|
512
|
+
if (query[key] === "me") {
|
|
392
513
|
if (!user) {
|
|
393
514
|
throw new ApiError(ApiErrors.SEARCH_INVALID_ME_REFERENCE, 403, ApiErrorMessages.SEARCH_INVALID_ME_REFERENCE);
|
|
394
515
|
}
|
|
395
|
-
query
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
query.where[key] = params[key];
|
|
516
|
+
query[key] = user.uid;
|
|
399
517
|
}
|
|
400
518
|
}
|
|
401
519
|
// Query parameters can be a single value or multiple. In the case of multiple we want to perform an OR
|
|
@@ -403,8 +521,8 @@ export class ModelUtils {
|
|
|
403
521
|
// the parameters as well.
|
|
404
522
|
// So first let's find out how many queries in total we are going to need.
|
|
405
523
|
let numQueries = 1;
|
|
406
|
-
for (const key in
|
|
407
|
-
const value =
|
|
524
|
+
for (const key in query) {
|
|
525
|
+
const value = query[key];
|
|
408
526
|
if (Array.isArray(value)) {
|
|
409
527
|
if (value.length > numQueries) {
|
|
410
528
|
numQueries = value.length;
|
|
@@ -414,20 +532,20 @@ export class ModelUtils {
|
|
|
414
532
|
// logger?.debug(`Query params: ${JSON.stringify(queryParams)}`);
|
|
415
533
|
// Now go through each query paramater. If the parameter is a single value, add it to each query object. If it's an array,
|
|
416
534
|
// add only one value to each query object.
|
|
417
|
-
for (let key in
|
|
535
|
+
for (let key in query) {
|
|
418
536
|
// Ignore reserved query parameters
|
|
419
537
|
if (key.match(REGEX_RESERVED_QUERY_PARAMS)) {
|
|
420
538
|
continue;
|
|
421
539
|
}
|
|
422
540
|
// Limit, page and sort are reserved for specifying query limits
|
|
423
541
|
if (key.match(REGEX_QUERY_LIMITS)) {
|
|
424
|
-
let value =
|
|
542
|
+
let value = query[key];
|
|
425
543
|
if (key === "limit") {
|
|
426
544
|
key = "take";
|
|
427
|
-
|
|
545
|
+
result[key] = Number(value);
|
|
428
546
|
}
|
|
429
547
|
else if (key === "page") {
|
|
430
|
-
|
|
548
|
+
result[key] = Number(value);
|
|
431
549
|
}
|
|
432
550
|
else if (key === "sort") {
|
|
433
551
|
key = "order";
|
|
@@ -442,42 +560,45 @@ export class ModelUtils {
|
|
|
442
560
|
value = newValue;
|
|
443
561
|
}
|
|
444
562
|
}
|
|
445
|
-
|
|
563
|
+
result[key] = value;
|
|
446
564
|
}
|
|
447
565
|
continue;
|
|
448
566
|
}
|
|
449
|
-
if (Array.isArray(
|
|
450
|
-
// Add each value in the array to each corresponding query
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
567
|
+
if (Array.isArray(query[key])) {
|
|
568
|
+
// Add each value in the array to each corresponding query. Multi-valued keys are "zipped"
|
|
569
|
+
// together via `numQueries` above; if this key's array is shorter than another key's, pad the
|
|
570
|
+
// remaining branches by repeating this key's own last value rather than leaving the key unset —
|
|
571
|
+
// an unset key would match ANY value, silently dropping this filter from those branches.
|
|
572
|
+
const values = query[key];
|
|
573
|
+
for (let i = 0; i < numQueries; i++) {
|
|
574
|
+
if (!result.where[i]) {
|
|
575
|
+
result.where[i] = {};
|
|
455
576
|
}
|
|
456
|
-
|
|
457
|
-
i
|
|
577
|
+
const value = i < values.length ? values[i] : values[values.length - 1];
|
|
578
|
+
result.where[i][key] = ModelUtils.getQueryParamValue(value);
|
|
458
579
|
}
|
|
459
580
|
}
|
|
460
581
|
else {
|
|
461
582
|
// Add the parameter to every query
|
|
462
583
|
for (let i = 0; i < numQueries; i++) {
|
|
463
|
-
if (!
|
|
464
|
-
|
|
584
|
+
if (!result.where[i]) {
|
|
585
|
+
result.where[i] = {};
|
|
465
586
|
}
|
|
466
|
-
|
|
587
|
+
result.where[i][key] = ModelUtils.getQueryParamValue(query[key]);
|
|
467
588
|
}
|
|
468
589
|
}
|
|
469
590
|
}
|
|
470
|
-
if (
|
|
471
|
-
delete
|
|
591
|
+
if (result.where.length === 0) {
|
|
592
|
+
delete result.where;
|
|
472
593
|
}
|
|
473
|
-
if (
|
|
474
|
-
|
|
594
|
+
if (result.take) {
|
|
595
|
+
result.take = Math.min(result.take, 1000);
|
|
475
596
|
}
|
|
476
597
|
else {
|
|
477
|
-
|
|
598
|
+
result.take = 100;
|
|
478
599
|
}
|
|
479
|
-
|
|
480
|
-
return
|
|
600
|
+
result.page = result.page ? result.page : 0;
|
|
601
|
+
return result;
|
|
481
602
|
}
|
|
482
603
|
/**
|
|
483
604
|
* Builds a MongoDB compatible query object for the given criteria. Query params can have a value containing a
|
|
@@ -498,37 +619,47 @@ export class ModelUtils {
|
|
|
498
619
|
* NOTE: The result of this function is only compatible with the `aggregate()` function.
|
|
499
620
|
*
|
|
500
621
|
* @param modelClass The class definition of the data model to build a search query for.
|
|
501
|
-
* @param {any}
|
|
502
|
-
* @param {any} queryParams The URI query parameters that were included in the request.
|
|
622
|
+
* @param {any} query The search query parameters to include.
|
|
503
623
|
* @param {bool} exactMatch Set to true to create a query where parameters are to be matched exactly, otherwise set to false to use a 'contains' search.
|
|
504
624
|
* @param {any} user The user that is performing the request.
|
|
505
|
-
* @returns {object} The
|
|
625
|
+
* @returns {object} The MongoDB compatible query object.
|
|
506
626
|
*/
|
|
507
|
-
static buildSearchQueryMongo(modelClass,
|
|
627
|
+
static buildSearchQueryMongo(modelClass, query = {}, exactMatch = false, user) {
|
|
508
628
|
const queries = [{}];
|
|
509
629
|
let sort = undefined;
|
|
510
|
-
//
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
630
|
+
// logger?.debug(`Query params: ${JSON.stringify(queryParams)}`);
|
|
631
|
+
// Query parameters can be a single value or multiple. In the case of multiple we want to perform an OR
|
|
632
|
+
// operation for each value, "zipped" together with any other multi-valued parameters (see the equivalent
|
|
633
|
+
// two-pass logic in buildSearchQuerySQL) — so first determine how many OR branches we need in total and
|
|
634
|
+
// pre-allocate them, before any key gets applied to a subset of branches.
|
|
635
|
+
let numQueries = 1;
|
|
636
|
+
for (const key in query) {
|
|
637
|
+
if (key === "$or" || key.match(REGEX_RESERVED_QUERY_PARAMS) || key.match(REGEX_QUERY_LIMITS)) {
|
|
638
|
+
continue;
|
|
518
639
|
}
|
|
519
|
-
|
|
520
|
-
|
|
640
|
+
const value = query[key];
|
|
641
|
+
if (Array.isArray(value) && value.length > numQueries) {
|
|
642
|
+
numQueries = value.length;
|
|
521
643
|
}
|
|
522
644
|
}
|
|
523
|
-
|
|
524
|
-
|
|
645
|
+
for (let i = 1; i < numQueries; i++) {
|
|
646
|
+
queries[i] = {};
|
|
647
|
+
}
|
|
648
|
+
for (const key in query) {
|
|
525
649
|
// Ignore reserved query parameters
|
|
526
650
|
if (key.match(REGEX_RESERVED_QUERY_PARAMS)) {
|
|
527
651
|
continue;
|
|
528
652
|
}
|
|
653
|
+
// If the value is 'me' that's a special keyword to reference the user ID.
|
|
654
|
+
if (query[key] === "me") {
|
|
655
|
+
if (!user) {
|
|
656
|
+
throw new ApiError(ApiErrors.SEARCH_INVALID_ME_REFERENCE, 403, ApiErrorMessages.SEARCH_INVALID_ME_REFERENCE);
|
|
657
|
+
}
|
|
658
|
+
query[key] = user.uid;
|
|
659
|
+
}
|
|
529
660
|
// Limit, page and sort are reserved for specifying query limits
|
|
530
661
|
if (key.match(REGEX_QUERY_LIMITS)) {
|
|
531
|
-
let value =
|
|
662
|
+
let value = query[key];
|
|
532
663
|
if (key === "sort") {
|
|
533
664
|
if (typeof value === "string") {
|
|
534
665
|
if (value.match(REGEX_QUERY_SORT_STRING)) {
|
|
@@ -554,6 +685,9 @@ export class ModelUtils {
|
|
|
554
685
|
if (typeof value === "number") {
|
|
555
686
|
sort[key] = value;
|
|
556
687
|
}
|
|
688
|
+
else if (typeof value !== "string") {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
557
691
|
else if (value.toUpperCase() === "ASC") {
|
|
558
692
|
sort[key] = 1;
|
|
559
693
|
}
|
|
@@ -564,35 +698,45 @@ export class ModelUtils {
|
|
|
564
698
|
}
|
|
565
699
|
continue;
|
|
566
700
|
}
|
|
701
|
+
// Reject any other Mongo operator key or dot-notation field path outright — client input may only
|
|
702
|
+
// supply plain field names, never raw query operators (`$or` is the one deliberate exception, handled
|
|
703
|
+
// just below; its sub-queries are validated recursively when they're built).
|
|
704
|
+
if (key !== "$or" && (key.startsWith("$") || key.includes("."))) {
|
|
705
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
|
|
706
|
+
}
|
|
567
707
|
if (key === "$or") {
|
|
568
708
|
// Array of OR queries
|
|
569
709
|
let orResults = [];
|
|
570
|
-
for (const
|
|
571
|
-
const subQueryOrResult = this.buildSearchQueryMongo(modelClass,
|
|
710
|
+
for (const q of query[key]) {
|
|
711
|
+
const subQueryOrResult = this.buildSearchQueryMongo(modelClass, q, exactMatch, user);
|
|
572
712
|
const validSubQueryResult = Array.isArray(subQueryOrResult) && subQueryOrResult.length > 0
|
|
573
713
|
? subQueryOrResult[0]["$match"]
|
|
574
714
|
: subQueryOrResult["$match"];
|
|
575
715
|
validSubQueryResult && orResults.push(validSubQueryResult);
|
|
576
716
|
}
|
|
577
|
-
|
|
717
|
+
// Merge into whatever conditions earlier keys (including the injected soft-delete filter)
|
|
718
|
+
// already placed on each branch — replacing outright would silently discard them, and merging
|
|
719
|
+
// only into queries[0] would silently drop the $or constraint from any other zipped branch.
|
|
720
|
+
for (let i = 0; i < numQueries; i++) {
|
|
721
|
+
queries[i] = { ...queries[i], $or: orResults };
|
|
722
|
+
}
|
|
578
723
|
continue;
|
|
579
724
|
}
|
|
580
|
-
if (Array.isArray(
|
|
581
|
-
// Add each value in the array to each corresponding query
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
queries[i][key] = value;
|
|
725
|
+
if (Array.isArray(query[key])) {
|
|
726
|
+
// Add each value in the array to each corresponding query, zipped per `numQueries` above. If
|
|
727
|
+
// this key's array is shorter than another key's, pad the remaining branches by repeating this
|
|
728
|
+
// key's own last value rather than leaving the key unset — an unset key would match ANY value,
|
|
729
|
+
// silently dropping this filter from those branches. Injection safety is already enforced
|
|
730
|
+
// inside getQueryParamValueMongo() itself, at the point the client-supplied value is parsed.
|
|
731
|
+
const values = query[key];
|
|
732
|
+
for (let i = 0; i < numQueries; i++) {
|
|
733
|
+
const raw = i < values.length ? values[i] : values[values.length - 1];
|
|
734
|
+
queries[i][key] = ModelUtils.getQueryParamValueMongo(raw);
|
|
591
735
|
}
|
|
592
736
|
}
|
|
593
737
|
else {
|
|
594
|
-
const value = ModelUtils.getQueryParamValueMongo(
|
|
595
|
-
for (let i = 0; i <
|
|
738
|
+
const value = ModelUtils.getQueryParamValueMongo(query[key]);
|
|
739
|
+
for (let i = 0; i < numQueries; i++) {
|
|
596
740
|
queries[i][key] = value;
|
|
597
741
|
}
|
|
598
742
|
}
|
|
@@ -635,7 +779,7 @@ export class ModelUtils {
|
|
|
635
779
|
await classLoader.load();
|
|
636
780
|
// Go through each class and determine which ones implements the `@Model` decorator.
|
|
637
781
|
classLoader.getClasses().forEach((clazz, name) => {
|
|
638
|
-
const isModel = Reflect.getMetadata("rrst:
|
|
782
|
+
const isModel = Reflect.getMetadata("rrst:datasource", clazz) !== undefined;
|
|
639
783
|
if (isModel) {
|
|
640
784
|
result.set(name, clazz);
|
|
641
785
|
}
|
|
@@ -649,4 +793,7 @@ export class ModelUtils {
|
|
|
649
793
|
}
|
|
650
794
|
}
|
|
651
795
|
ModelUtils.idPropertyCache = new Map();
|
|
796
|
+
ModelUtils.readOnlyPropertyCache = new Map();
|
|
797
|
+
/** Maximum accepted length of a client-supplied `like()` search pattern. */
|
|
798
|
+
ModelUtils.MAX_LIKE_PATTERN_LENGTH = 100;
|
|
652
799
|
//# sourceMappingURL=ModelUtils.js.map
|