kuzzle 2.56.0 → 2.57.0-beta.2
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/dist/bin/copy-binaries.js +71 -4
- package/dist/bin/start-kuzzle-server +191 -97
- package/dist/index.d.ts +31 -1
- package/dist/index.js +45 -2
- package/dist/lib/api/controllers/adminController.d.ts +4 -3
- package/dist/lib/api/controllers/adminController.js +1 -2
- package/dist/lib/api/controllers/authController.d.ts +37 -16
- package/dist/lib/api/controllers/authController.js +88 -39
- package/dist/lib/api/controllers/baseController.d.ts +10 -5
- package/dist/lib/api/controllers/baseController.js +36 -15
- package/dist/lib/api/controllers/bulkController.d.ts +11 -7
- package/dist/lib/api/controllers/bulkController.js +28 -5
- package/dist/lib/api/controllers/clusterController.d.ts +2 -2
- package/dist/lib/api/controllers/clusterController.js +3 -3
- package/dist/lib/api/controllers/collectionController.d.ts +57 -21
- package/dist/lib/api/controllers/collectionController.js +40 -11
- package/dist/lib/api/controllers/debugController.d.ts +1 -1
- package/dist/lib/api/controllers/documentController.d.ts +137 -28
- package/dist/lib/api/controllers/documentController.js +156 -125
- package/dist/lib/api/controllers/index.d.ts +27 -12
- package/dist/lib/api/controllers/index.js +28 -13
- package/dist/lib/api/controllers/indexController.d.ts +17 -10
- package/dist/lib/api/controllers/indexController.js +13 -11
- package/dist/lib/api/controllers/memoryStorageController.d.ts +2 -5
- package/dist/lib/api/controllers/memoryStorageController.js +199 -127
- package/dist/lib/api/controllers/realtimeController.d.ts +19 -9
- package/dist/lib/api/controllers/realtimeController.js +8 -4
- package/dist/lib/api/controllers/securityController.d.ts +42 -29
- package/dist/lib/api/controllers/securityController.js +35 -19
- package/dist/lib/api/controllers/serverController.d.ts +114 -19
- package/dist/lib/api/controllers/serverController.js +69 -29
- package/dist/lib/api/documentExtractor.d.ts +9 -7
- package/dist/lib/api/documentExtractor.js +105 -41
- package/dist/lib/api/funnel.d.ts +131 -29
- package/dist/lib/api/funnel.js +436 -225
- package/dist/lib/api/httpRoutes.d.ts +16 -11
- package/dist/lib/api/httpRoutes.js +272 -2
- package/dist/lib/api/openapi/OpenApiManager.d.ts +1 -1
- package/dist/lib/api/openapi/openApiGenerator.d.ts +1 -1
- package/dist/lib/api/rateLimiter.d.ts +15 -5
- package/dist/lib/api/rateLimiter.js +42 -14
- package/dist/lib/api/request/kuzzleRequest.d.ts +83 -12
- package/dist/lib/api/request/kuzzleRequest.js +88 -23
- package/dist/lib/api/request/requestContext.d.ts +20 -5
- package/dist/lib/api/request/requestContext.js +14 -4
- package/dist/lib/api/request/requestInput.d.ts +37 -6
- package/dist/lib/api/request/requestInput.js +22 -9
- package/dist/lib/api/request/requestResponse.d.ts +45 -9
- package/dist/lib/api/request/requestResponse.js +34 -3
- package/dist/lib/cluster/command.d.ts +131 -37
- package/dist/lib/cluster/command.js +243 -71
- package/dist/lib/cluster/idCardHandler.d.ts +18 -5
- package/dist/lib/cluster/idCardHandler.js +52 -19
- package/dist/lib/cluster/index.d.ts +8 -2
- package/dist/lib/cluster/index.js +6 -2
- package/dist/lib/cluster/node.d.ts +127 -49
- package/dist/lib/cluster/node.js +412 -179
- package/dist/lib/cluster/protobuf/command.proto +18 -0
- package/dist/lib/cluster/protobuf/commandMessages.d.ts +104 -0
- package/dist/lib/cluster/protobuf/commandMessages.js +30 -0
- package/dist/lib/cluster/protobuf/syncMessages.d.ts +184 -0
- package/dist/lib/cluster/protobuf/syncMessages.js +23 -0
- package/dist/lib/cluster/publisher.d.ts +118 -78
- package/dist/lib/cluster/publisher.js +174 -72
- package/dist/lib/cluster/state.d.ts +15 -9
- package/dist/lib/cluster/state.js +12 -8
- package/dist/lib/cluster/subscriber.d.ts +185 -131
- package/dist/lib/cluster/subscriber.js +357 -95
- package/dist/lib/cluster/workers/IDCardRenewer.d.ts +39 -8
- package/dist/lib/cluster/workers/IDCardRenewer.js +88 -23
- package/dist/lib/config/default.config.d.ts +2 -2
- package/dist/lib/config/default.config.js +10 -3
- package/dist/lib/config/documentEventAliases.d.ts +1 -1
- package/dist/lib/config/index.d.ts +2 -5
- package/dist/lib/config/index.js +30 -17
- package/dist/lib/core/auth/formatProcessing.d.ts +3 -3
- package/dist/lib/core/auth/passportResponse.d.ts +1 -1
- package/dist/lib/core/auth/passportWrapper.d.ts +1 -1
- package/dist/lib/core/auth/passportWrapper.js +14 -13
- package/dist/lib/core/auth/tokenManager.d.ts +24 -3
- package/dist/lib/core/auth/tokenManager.js +47 -19
- package/dist/lib/core/backend/applicationManager.d.ts +2 -2
- package/dist/lib/core/backend/backend.d.ts +16 -5
- package/dist/lib/core/backend/backend.js +14 -6
- package/dist/lib/core/backend/backendCluster.d.ts +1 -1
- package/dist/lib/core/backend/backendConfig.d.ts +17 -5
- package/dist/lib/core/backend/backendConfig.js +1 -0
- package/dist/lib/core/backend/backendController.d.ts +1 -1
- package/dist/lib/core/backend/backendController.js +8 -1
- package/dist/lib/core/backend/backendErrors.d.ts +15 -8
- package/dist/lib/core/backend/backendErrors.js +5 -0
- package/dist/lib/core/backend/backendHook.d.ts +1 -1
- package/dist/lib/core/backend/backendImport.d.ts +1 -1
- package/dist/lib/core/backend/backendOpenApi.d.ts +3 -2
- package/dist/lib/core/backend/backendPipe.d.ts +1 -1
- package/dist/lib/core/backend/backendPipe.js +9 -1
- package/dist/lib/core/backend/backendPlugin.d.ts +2 -2
- package/dist/lib/core/backend/backendSubscription.d.ts +2 -2
- package/dist/lib/core/backend/backendVault.d.ts +1 -1
- package/dist/lib/core/backend/backendVault.js +8 -1
- package/dist/lib/core/cache/cacheEngine.d.ts +7 -4
- package/dist/lib/core/cache/cacheEngine.js +25 -8
- package/dist/lib/core/debug/kuzzleDebugger.d.ts +2 -1
- package/dist/lib/core/debug/kuzzleDebugger.js +31 -17
- package/dist/lib/core/network/accessLogger.d.ts +33 -18
- package/dist/lib/core/network/accessLogger.js +135 -73
- package/dist/lib/core/network/clientConnection.d.ts +13 -11
- package/dist/lib/core/network/clientConnection.js +33 -9
- package/dist/lib/core/network/context.d.ts +17 -22
- package/dist/lib/core/network/context.js +43 -26
- package/dist/lib/core/network/entryPoint.d.ts +38 -16
- package/dist/lib/core/network/entryPoint.js +119 -48
- package/dist/lib/core/network/httpRouter/index.d.ts +20 -50
- package/dist/lib/core/network/httpRouter/index.js +61 -75
- package/dist/lib/core/network/httpRouter/routeHandler.d.ts +17 -32
- package/dist/lib/core/network/httpRouter/routeHandler.js +26 -36
- package/dist/lib/core/network/httpRouter/routePart.d.ts +9 -18
- package/dist/lib/core/network/httpRouter/routePart.js +61 -43
- package/dist/lib/core/network/httpRouter/routeTypes.d.ts +3 -0
- package/dist/lib/core/network/httpRouter/routeTypes.js +23 -0
- package/dist/lib/core/network/networkEntryPoint.d.ts +20 -0
- package/dist/lib/core/network/networkEntryPoint.js +23 -0
- package/dist/lib/core/network/protocolManifest.d.ts +5 -4
- package/dist/lib/core/network/protocolManifest.js +6 -3
- package/dist/lib/core/network/protocols/httpMessage.d.ts +25 -18
- package/dist/lib/core/network/protocols/httpMessage.js +14 -11
- package/dist/lib/core/network/protocols/httpwsProtocol.d.ts +84 -93
- package/dist/lib/core/network/protocols/httpwsProtocol.js +284 -129
- package/dist/lib/core/network/protocols/internalProtocol.d.ts +26 -15
- package/dist/lib/core/network/protocols/internalProtocol.js +17 -16
- package/dist/lib/core/network/protocols/mqttProtocol.d.ts +56 -32
- package/dist/lib/core/network/protocols/mqttProtocol.js +91 -44
- package/dist/lib/core/network/protocols/protocol.d.ts +85 -21
- package/dist/lib/core/network/protocols/protocol.js +83 -27
- package/dist/lib/core/network/router.d.ts +13 -22
- package/dist/lib/core/network/router.js +59 -46
- package/dist/lib/core/plugin/plugin.d.ts +57 -51
- package/dist/lib/core/plugin/plugin.js +218 -103
- package/dist/lib/core/plugin/pluginContext.d.ts +54 -11
- package/dist/lib/core/plugin/pluginContext.js +99 -58
- package/dist/lib/core/plugin/pluginManifest.d.ts +6 -2
- package/dist/lib/core/plugin/pluginManifest.js +18 -11
- package/dist/lib/core/plugin/pluginRepository.d.ts +35 -44
- package/dist/lib/core/plugin/pluginRepository.js +35 -50
- package/dist/lib/core/plugin/pluginsManager.d.ts +128 -46
- package/dist/lib/core/plugin/pluginsManager.js +468 -231
- package/dist/lib/core/plugin/privilegedContext.d.ts +4 -9
- package/dist/lib/core/plugin/privilegedContext.js +5 -10
- package/dist/lib/core/realtime/actionEnum.d.ts +12 -21
- package/dist/lib/core/realtime/actionEnum.js +6 -7
- package/dist/lib/core/realtime/channel.d.ts +11 -5
- package/dist/lib/core/realtime/channel.js +17 -2
- package/dist/lib/core/realtime/connectionRooms.d.ts +2 -2
- package/dist/lib/core/realtime/hotelClerk.d.ts +12 -6
- package/dist/lib/core/realtime/hotelClerk.js +29 -11
- package/dist/lib/core/realtime/index.d.ts +8 -3
- package/dist/lib/core/realtime/index.js +12 -5
- package/dist/lib/core/realtime/notification/document.d.ts +54 -25
- package/dist/lib/core/realtime/notification/document.js +10 -13
- package/dist/lib/core/realtime/notification/index.d.ts +9 -3
- package/dist/lib/core/realtime/notification/index.js +10 -4
- package/dist/lib/core/realtime/notification/server.d.ts +7 -10
- package/dist/lib/core/realtime/notification/server.js +4 -7
- package/dist/lib/core/realtime/notification/user.d.ts +45 -21
- package/dist/lib/core/realtime/notification/user.js +8 -9
- package/dist/lib/core/realtime/notifier.d.ts +52 -40
- package/dist/lib/core/realtime/notifier.js +60 -25
- package/dist/lib/core/realtime/room.d.ts +1 -1
- package/dist/lib/core/realtime/subscription.d.ts +11 -6
- package/dist/lib/core/realtime/subscription.js +5 -1
- package/dist/lib/core/security/index.d.ts +14 -6
- package/dist/lib/core/security/index.js +27 -13
- package/dist/lib/core/security/profileRepository.d.ts +24 -19
- package/dist/lib/core/security/profileRepository.js +23 -10
- package/dist/lib/core/security/roleRepository.d.ts +60 -23
- package/dist/lib/core/security/roleRepository.js +122 -53
- package/dist/lib/core/security/securityLoader.d.ts +33 -18
- package/dist/lib/core/security/securityLoader.js +43 -16
- package/dist/lib/core/security/tokenRepository.d.ts +21 -7
- package/dist/lib/core/security/tokenRepository.js +50 -23
- package/dist/lib/core/security/userRepository.d.ts +44 -21
- package/dist/lib/core/security/userRepository.js +59 -19
- package/dist/lib/core/shared/KoncordeWrapper.d.ts +1 -1
- package/dist/lib/core/shared/ObjectRepository.d.ts +80 -18
- package/dist/lib/core/shared/ObjectRepository.js +57 -29
- package/dist/lib/core/shared/abstractManifest.d.ts +21 -8
- package/dist/lib/core/shared/abstractManifest.js +57 -17
- package/dist/lib/core/shared/sdk/embeddedSdk.d.ts +3 -1
- package/dist/lib/core/shared/sdk/embeddedSdk.js +8 -7
- package/dist/lib/core/shared/sdk/funnelProtocol.d.ts +2 -1
- package/dist/lib/core/shared/sdk/funnelProtocol.js +12 -3
- package/dist/lib/core/shared/sdk/impersonatedSdk.d.ts +11 -5
- package/dist/lib/core/shared/sdk/impersonatedSdk.js +22 -9
- package/dist/lib/core/shared/store.d.ts +45 -29
- package/dist/lib/core/shared/store.js +48 -27
- package/dist/lib/core/statistics/statistics.d.ts +1 -1
- package/dist/lib/core/storage/clientAdapter.d.ts +39 -21
- package/dist/lib/core/storage/clientAdapter.js +88 -48
- package/dist/lib/core/storage/indexCache.d.ts +8 -0
- package/dist/lib/core/storage/indexCache.js +16 -7
- package/dist/lib/core/storage/storageEngine.d.ts +7 -6
- package/dist/lib/core/storage/storageEngine.js +11 -8
- package/dist/lib/core/validation/baseType.d.ts +22 -22
- package/dist/lib/core/validation/baseType.js +11 -26
- package/dist/lib/core/validation/specification.d.ts +148 -0
- package/dist/lib/core/validation/specification.js +34 -0
- package/dist/lib/core/validation/typeOptions.d.ts +62 -0
- package/dist/lib/core/validation/typeOptions.js +23 -0
- package/dist/lib/core/validation/types/anything.d.ts +3 -6
- package/dist/lib/core/validation/types/anything.js +7 -7
- package/dist/lib/core/validation/types/boolean.d.ts +5 -12
- package/dist/lib/core/validation/types/boolean.js +7 -12
- package/dist/lib/core/validation/types/date.d.ts +19 -11
- package/dist/lib/core/validation/types/date.js +212 -161
- package/dist/lib/core/validation/types/email.d.ts +7 -11
- package/dist/lib/core/validation/types/email.js +13 -17
- package/dist/lib/core/validation/types/enum.d.ts +7 -11
- package/dist/lib/core/validation/types/enum.js +39 -20
- package/dist/lib/core/validation/types/geoPoint.d.ts +5 -13
- package/dist/lib/core/validation/types/geoPoint.js +10 -15
- package/dist/lib/core/validation/types/geoShape.d.ts +22 -21
- package/dist/lib/core/validation/types/geoShape.js +113 -129
- package/dist/lib/core/validation/types/geoShapeUtils.d.ts +15 -0
- package/dist/lib/core/validation/types/geoShapeUtils.js +75 -0
- package/dist/lib/core/validation/types/integer.d.ts +7 -5
- package/dist/lib/core/validation/types/integer.js +7 -13
- package/dist/lib/core/validation/types/ipAddress.d.ts +7 -11
- package/dist/lib/core/validation/types/ipAddress.js +13 -17
- package/dist/lib/core/validation/types/numeric.d.ts +7 -11
- package/dist/lib/core/validation/types/numeric.js +22 -29
- package/dist/lib/core/validation/types/object.d.ts +8 -15
- package/dist/lib/core/validation/types/object.js +12 -21
- package/dist/lib/core/validation/types/string.d.ts +7 -11
- package/dist/lib/core/validation/types/string.js +22 -29
- package/dist/lib/core/validation/types/url.d.ts +7 -11
- package/dist/lib/core/validation/types/url.js +14 -18
- package/dist/lib/core/validation/validation.d.ts +119 -80
- package/dist/lib/core/validation/validation.js +470 -412
- package/dist/lib/core/validation/validationUtils.d.ts +31 -0
- package/dist/lib/core/validation/validationUtils.js +190 -0
- package/dist/lib/kerror/codes/4-plugin.json +0 -12
- package/dist/lib/kerror/codes/index.d.ts +12 -2
- package/dist/lib/kerror/codes/index.js +5 -4
- package/dist/lib/kerror/errors/badRequestError.d.ts +1 -1
- package/dist/lib/kerror/errors/badRequestError.js +1 -1
- package/dist/lib/kerror/errors/externalServiceError.d.ts +1 -1
- package/dist/lib/kerror/errors/externalServiceError.js +1 -1
- package/dist/lib/kerror/errors/forbiddenError.d.ts +1 -1
- package/dist/lib/kerror/errors/forbiddenError.js +1 -1
- package/dist/lib/kerror/errors/gatewayTimeoutError.d.ts +1 -1
- package/dist/lib/kerror/errors/gatewayTimeoutError.js +1 -1
- package/dist/lib/kerror/errors/internalError.d.ts +1 -1
- package/dist/lib/kerror/errors/internalError.js +1 -1
- package/dist/lib/kerror/errors/kuzzleError.d.ts +32 -3
- package/dist/lib/kerror/errors/kuzzleError.js +21 -7
- package/dist/lib/kerror/errors/multipleErrorsError.d.ts +2 -2
- package/dist/lib/kerror/errors/multipleErrorsError.js +2 -2
- package/dist/lib/kerror/errors/notFoundError.d.ts +1 -1
- package/dist/lib/kerror/errors/notFoundError.js +1 -1
- package/dist/lib/kerror/errors/partialError.d.ts +8 -2
- package/dist/lib/kerror/errors/partialError.js +22 -9
- package/dist/lib/kerror/errors/pluginImplementationError.d.ts +9 -1
- package/dist/lib/kerror/errors/pluginImplementationError.js +9 -3
- package/dist/lib/kerror/errors/preconditionError.d.ts +1 -1
- package/dist/lib/kerror/errors/preconditionError.js +1 -1
- package/dist/lib/kerror/errors/serviceUnavailableError.d.ts +1 -1
- package/dist/lib/kerror/errors/serviceUnavailableError.js +1 -1
- package/dist/lib/kerror/errors/sizeLimitError.d.ts +1 -1
- package/dist/lib/kerror/errors/sizeLimitError.js +1 -1
- package/dist/lib/kerror/errors/tooManyRequestsError.d.ts +1 -1
- package/dist/lib/kerror/errors/tooManyRequestsError.js +1 -1
- package/dist/lib/kerror/errors/unauthorizedError.d.ts +1 -1
- package/dist/lib/kerror/errors/unauthorizedError.js +1 -1
- package/dist/lib/kerror/index.d.ts +19 -13
- package/dist/lib/kerror/index.js +60 -19
- package/dist/lib/kuzzle/Logger.d.ts +2 -2
- package/dist/lib/kuzzle/dumpGenerator.d.ts +12 -8
- package/dist/lib/kuzzle/dumpGenerator.js +130 -62
- package/dist/lib/kuzzle/event/KuzzleEventEmitter.d.ts +18 -9
- package/dist/lib/kuzzle/event/KuzzleEventEmitter.js +45 -18
- package/dist/lib/kuzzle/event/pipeRunner.d.ts +37 -16
- package/dist/lib/kuzzle/event/pipeRunner.js +73 -26
- package/dist/lib/kuzzle/event/waterfall.d.ts +11 -1
- package/dist/lib/kuzzle/event/waterfall.js +38 -13
- package/dist/lib/kuzzle/internalIndexHandler.d.ts +11 -174
- package/dist/lib/kuzzle/internalIndexHandler.js +58 -29
- package/dist/lib/kuzzle/kuzzle.d.ts +25 -10
- package/dist/lib/kuzzle/kuzzle.js +74 -17
- package/dist/lib/kuzzle/kuzzleStateEnum.d.ts +16 -12
- package/dist/lib/kuzzle/kuzzleStateEnum.js +5 -7
- package/dist/lib/kuzzle/vault.d.ts +5 -1
- package/dist/lib/kuzzle/vault.js +29 -17
- package/dist/lib/model/security/profile.d.ts +34 -5
- package/dist/lib/model/security/profile.js +6 -5
- package/dist/lib/model/security/rights.d.ts +12 -4
- package/dist/lib/model/security/rights.js +7 -6
- package/dist/lib/model/security/role.d.ts +8 -4
- package/dist/lib/model/security/role.js +12 -6
- package/dist/lib/model/security/token.d.ts +8 -0
- package/dist/lib/model/security/user.d.ts +8 -2
- package/dist/lib/model/security/user.js +7 -4
- package/dist/lib/model/storage/apiKey.d.ts +55 -26
- package/dist/lib/model/storage/apiKey.js +97 -24
- package/dist/lib/model/storage/baseModel.d.ts +77 -66
- package/dist/lib/model/storage/baseModel.js +21 -34
- package/dist/lib/service/cache/redis.d.ts +53 -36
- package/dist/lib/service/cache/redis.js +148 -71
- package/dist/lib/service/service.d.ts +14 -19
- package/dist/lib/service/service.js +41 -22
- package/dist/lib/service/storage/7/elasticsearch.d.ts +136 -93
- package/dist/lib/service/storage/7/elasticsearch.js +218 -165
- package/dist/lib/service/storage/7/esWrapper.d.ts +30 -13
- package/dist/lib/service/storage/7/esWrapper.js +58 -37
- package/dist/lib/service/storage/8/elasticsearch.d.ts +153 -119
- package/dist/lib/service/storage/8/elasticsearch.js +286 -213
- package/dist/lib/service/storage/8/esWrapper.d.ts +30 -13
- package/dist/lib/service/storage/8/esWrapper.js +59 -38
- package/dist/lib/service/storage/commons/queryTranslator.d.ts +3 -3
- package/dist/lib/service/storage/commons/queryTranslator.js +71 -35
- package/dist/lib/types/ApiRoute.d.ts +12 -0
- package/dist/lib/types/ApiRoute.js +23 -0
- package/dist/lib/types/ClientConnection.d.ts +1 -1
- package/dist/lib/types/EventHandler.d.ts +10 -0
- package/dist/lib/types/Global.d.ts +8 -3
- package/dist/lib/types/HttpMessage.d.ts +2 -2
- package/dist/lib/types/HttpStream.d.ts +1 -1
- package/dist/lib/types/HttpStream.js +3 -1
- package/dist/lib/types/JSONObject.d.ts +9 -0
- package/dist/lib/types/JSONObject.js +23 -0
- package/dist/lib/types/Kuzzle.d.ts +5 -3
- package/dist/lib/types/KuzzleDocument.d.ts +1 -1
- package/dist/lib/types/KuzzleWebSocket.d.ts +24 -0
- package/dist/lib/types/KuzzleWebSocket.js +23 -0
- package/dist/lib/types/OpenApiDefinition.d.ts +1 -1
- package/dist/lib/types/Plugin.d.ts +33 -8
- package/dist/lib/types/PluginInstance.d.ts +67 -0
- package/dist/lib/types/PluginInstance.js +23 -0
- package/dist/lib/types/Policy.d.ts +1 -1
- package/dist/lib/types/Target.d.ts +8 -0
- package/dist/lib/types/Token.d.ts +14 -2
- package/dist/lib/types/config/HttpConfiguration.d.ts +5 -0
- package/dist/lib/types/config/KuzzleConfiguration.d.ts +102 -1
- package/dist/lib/types/config/PluginsConfiguration.d.ts +3 -2
- package/dist/lib/types/config/SecurityConfiguration.d.ts +6 -2
- package/dist/lib/types/config/ServerConfiguration.d.ts +2 -2
- package/dist/lib/types/config/ServicesConfiguration.d.ts +1 -1
- package/dist/lib/types/config/cache/BaseCacheRedisConfiguration.d.ts +131 -0
- package/dist/lib/types/config/cache/BaseCacheRedisConfiguration.js +3 -0
- package/dist/lib/types/config/internalCache/InternalCacheRedisConfiguration.d.ts +2 -106
- package/dist/lib/types/config/publicCache/PublicCacheRedisConfiguration.d.ts +2 -26
- package/dist/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.d.ts +3 -0
- package/dist/lib/types/controllers/Controller.d.ts +12 -3
- package/dist/lib/types/controllers/ControllerDefinition.d.ts +2 -2
- package/dist/lib/types/controllers/{adminControlller.type.js → adminController.type.js} +1 -1
- package/dist/lib/types/controllers/authController.type.d.ts +2 -2
- package/dist/lib/types/core/auth/formatProcessing.type.d.ts +7 -1
- package/dist/lib/types/errors/ErrorDomains.d.ts +1 -1
- package/dist/lib/types/events/EventGenericDocument.d.ts +10 -9
- package/dist/lib/types/events/EventProtocol.d.ts +4 -4
- package/dist/lib/types/index.d.ts +7 -0
- package/dist/lib/types/index.js +7 -0
- package/dist/lib/types/shared/StoreCollectionsDefinition.d.ts +1 -1
- package/dist/lib/types/storage/7/Elasticsearch.d.ts +62 -13
- package/dist/lib/types/storage/8/Elasticsearch.d.ts +62 -13
- package/dist/lib/util/Inflector.d.ts +4 -1
- package/dist/lib/util/Inflector.js +0 -3
- package/dist/lib/util/assertType.d.ts +24 -27
- package/dist/lib/util/assertType.js +28 -39
- package/dist/lib/util/async.js +19 -6
- package/dist/lib/util/asyncStore.js +1 -1
- package/dist/lib/util/bufferedPassThrough.d.ts +9 -0
- package/dist/lib/util/bufferedPassThrough.js +27 -13
- package/dist/lib/util/bytes.d.ts +1 -1
- package/dist/lib/util/bytes.js +6 -2
- package/dist/lib/util/debug.d.ts +3 -1
- package/dist/lib/util/debug.js +42 -10
- package/dist/lib/util/deprecate.d.ts +9 -1
- package/dist/lib/util/deprecate.js +10 -6
- package/dist/lib/util/didYouMean.d.ts +2 -1
- package/dist/lib/util/didYouMean.js +8 -4
- package/dist/lib/util/distributedLock.d.ts +56 -0
- package/dist/lib/util/distributedLock.js +166 -0
- package/dist/lib/util/dump-collection.d.ts +1 -1
- package/dist/lib/util/dump-collection.js +4 -5
- package/dist/lib/util/extractFields.d.ts +15 -11
- package/dist/lib/util/extractFields.js +8 -9
- package/dist/lib/util/koncordeCompat.d.ts +2 -2
- package/dist/lib/util/koncordeCompat.js +33 -0
- package/dist/lib/util/memoize.d.ts +7 -6
- package/dist/lib/util/memoize.js +3 -3
- package/dist/lib/util/mutex.d.ts +22 -2
- package/dist/lib/util/mutex.js +39 -1
- package/dist/lib/util/name-generator.js +14 -2
- package/dist/lib/util/promback.d.ts +20 -11
- package/dist/lib/util/promback.js +14 -7
- package/dist/lib/util/requestAssertions.d.ts +30 -10
- package/dist/lib/util/requestAssertions.js +125 -121
- package/dist/lib/util/safeObject.d.ts +3 -3
- package/dist/lib/util/safeObject.js +7 -3
- package/dist/lib/util/stackTrace.d.ts +13 -5
- package/dist/lib/util/stackTrace.js +17 -12
- package/dist/lib/util/thrown.d.ts +13 -0
- package/dist/lib/util/thrown.js +46 -0
- package/dist/package.json +26 -19
- package/package.json +26 -19
- package/dist/lib/util/wildcard.d.ts +0 -1
- package/dist/lib/util/wildcard.js +0 -46
- /package/dist/lib/types/controllers/{adminControlller.type.d.ts → adminController.type.d.ts} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
* Kuzzle, a backend software, self-hostable and ready to use
|
|
3
4
|
* to power modern apps
|
|
@@ -18,120 +19,263 @@
|
|
|
18
19
|
* See the License for the specific language governing permissions and
|
|
19
20
|
* limitations under the License.
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
46
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
47
|
+
};
|
|
48
|
+
const node_util_1 = require("node:util");
|
|
49
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
50
|
+
const koncorde_1 = require("koncorde");
|
|
51
|
+
const lodash_1 = require("lodash");
|
|
52
|
+
const kerror = __importStar(require("../../kerror"));
|
|
53
|
+
const errors_1 = require("../../kerror/errors");
|
|
54
|
+
const debug_1 = __importDefault(require("../../util/debug"));
|
|
55
|
+
const koncordeCompat_1 = require("../../util/koncordeCompat");
|
|
56
|
+
const safeObject_1 = require("../../util/safeObject");
|
|
57
|
+
const validationUtils_1 = require("./validationUtils");
|
|
58
|
+
const anything_1 = __importDefault(require("./types/anything"));
|
|
59
|
+
const boolean_1 = __importDefault(require("./types/boolean"));
|
|
60
|
+
const date_1 = __importDefault(require("./types/date"));
|
|
61
|
+
const email_1 = __importDefault(require("./types/email"));
|
|
62
|
+
const enum_1 = __importDefault(require("./types/enum"));
|
|
63
|
+
const geoPoint_1 = __importDefault(require("./types/geoPoint"));
|
|
64
|
+
const geoShape_1 = __importDefault(require("./types/geoShape"));
|
|
65
|
+
const integer_1 = __importDefault(require("./types/integer"));
|
|
66
|
+
const ipAddress_1 = __importDefault(require("./types/ipAddress"));
|
|
67
|
+
const numeric_1 = __importDefault(require("./types/numeric"));
|
|
68
|
+
const object_1 = __importDefault(require("./types/object"));
|
|
69
|
+
const string_1 = __importDefault(require("./types/string"));
|
|
70
|
+
const url_1 = __importDefault(require("./types/url"));
|
|
71
|
+
const debug = (0, debug_1.default)("core:validation");
|
|
30
72
|
const assertionError = kerror.wrap("validation", "assert");
|
|
31
73
|
/**
|
|
32
|
-
*
|
|
74
|
+
* The 13 built-in types, in the order `init` registers them.
|
|
75
|
+
*
|
|
76
|
+
* They were loaded by `require(`./types/${typeFile}`)` over a list of names.
|
|
77
|
+
* That is a runtime `require` in a function body, which is the third spelling
|
|
78
|
+
* of the defect TD-49 catalogued: a bundler cannot see it, so the module is
|
|
79
|
+
* unloadable from a runner that resolves the import graph itself. Naming the
|
|
80
|
+
* constructors statically also lets `addType`'s contract be checked at compile
|
|
81
|
+
* time for the built-ins, where before only the runtime assertions in `addType`
|
|
82
|
+
* stood between a typo and a broken boot.
|
|
83
|
+
*/
|
|
84
|
+
const BUILT_IN_TYPES = [
|
|
85
|
+
anything_1.default,
|
|
86
|
+
boolean_1.default,
|
|
87
|
+
date_1.default,
|
|
88
|
+
email_1.default,
|
|
89
|
+
enum_1.default,
|
|
90
|
+
geoPoint_1.default,
|
|
91
|
+
geoShape_1.default,
|
|
92
|
+
integer_1.default,
|
|
93
|
+
ipAddress_1.default,
|
|
94
|
+
numeric_1.default,
|
|
95
|
+
object_1.default,
|
|
96
|
+
string_1.default,
|
|
97
|
+
url_1.default,
|
|
98
|
+
];
|
|
99
|
+
/**
|
|
100
|
+
* The message of whatever was thrown.
|
|
101
|
+
*
|
|
102
|
+
* `catch` answers `unknown`, and the values that reach these handlers are not
|
|
103
|
+
* all Errors: a plugin's validation type may throw anything, and the code has
|
|
104
|
+
* always simply read `.message` off it. Narrowing to `Error` here would change
|
|
105
|
+
* what those messages say, which the specs pin.
|
|
106
|
+
*/
|
|
107
|
+
function messageOf(error) {
|
|
108
|
+
if (error instanceof Error) {
|
|
109
|
+
return error.message;
|
|
110
|
+
}
|
|
111
|
+
if (typeof error === "object" && error !== null) {
|
|
112
|
+
const message = "message" in error ? error.message : undefined;
|
|
113
|
+
// `inspect`, not `String`: an object stringifies to "[object Object]".
|
|
114
|
+
return typeof message === "string" ? message : (0, node_util_1.inspect)(error);
|
|
115
|
+
}
|
|
116
|
+
// A primitive. The strictness marker used to be a thrown string, and
|
|
117
|
+
// reading it back verbatim is what recognises it.
|
|
118
|
+
return typeof error === "string" ? error : (0, node_util_1.inspect)(error);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The marker `recurseFieldValidation` throws when a document holds a field its
|
|
122
|
+
* specification does not declare and the specification is strict.
|
|
123
|
+
*
|
|
124
|
+
* It is caught by message — `error.message !== "strictness"` — at two levels,
|
|
125
|
+
* and carries the offending field name for the message the catcher builds. It
|
|
126
|
+
* was a plain `Error` with an untyped `details` property bolted on.
|
|
127
|
+
*/
|
|
128
|
+
/**
|
|
129
|
+
* The field name a strictness marker carries, read off whatever was thrown for
|
|
130
|
+
* the same reason `messageOf` exists.
|
|
33
131
|
*/
|
|
132
|
+
function detailedField(error) {
|
|
133
|
+
if (error instanceof StrictnessError) {
|
|
134
|
+
return error.details.field;
|
|
135
|
+
}
|
|
136
|
+
if (typeof error === "object" && error !== null && "details" in error) {
|
|
137
|
+
const details = error.details;
|
|
138
|
+
if (typeof details === "object" && details !== null && "field" in details) {
|
|
139
|
+
// A field name is a string; anything else is not one to report.
|
|
140
|
+
return typeof details.field === "string" ? details.field : undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
class StrictnessError extends Error {
|
|
146
|
+
constructor(field) {
|
|
147
|
+
super("strictness");
|
|
148
|
+
this.details = { field };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Narrows the "a curated value, or a report of why it could not be curated"
|
|
153
|
+
* pair that four of the curation methods answer with.
|
|
154
|
+
*
|
|
155
|
+
* The pair is not a discriminated union — the success branch is the curated
|
|
156
|
+
* value itself and carries no tag — so the discriminator has to be the failure
|
|
157
|
+
* shape. `"isValid" in result` is what makes this a guard rather than a cast,
|
|
158
|
+
* and TD-43's `casts` ratchet is the reason it matters.
|
|
159
|
+
*/
|
|
160
|
+
function isCurationFailure(result) {
|
|
161
|
+
return "isValid" in result && result.isValid === false;
|
|
162
|
+
}
|
|
34
163
|
class Validation {
|
|
35
164
|
constructor() {
|
|
36
|
-
/** @type {...ValidationType} */
|
|
37
165
|
this.types = {};
|
|
38
|
-
/** @type {string[]} */
|
|
39
166
|
this.typeAllowsChildren = [];
|
|
40
|
-
/** @type {DocumentSpecification} */
|
|
41
167
|
this.specification = {};
|
|
42
|
-
|
|
43
|
-
this.koncorde = new Koncorde();
|
|
168
|
+
this.koncorde = new koncorde_1.Koncorde();
|
|
44
169
|
this.rawConfiguration = {};
|
|
45
170
|
this.logger = global.kuzzle.log.child("core:validation");
|
|
46
171
|
}
|
|
47
172
|
/**
|
|
48
|
-
*
|
|
173
|
+
* Registers every built-in validation type.
|
|
49
174
|
*/
|
|
50
175
|
init() {
|
|
51
|
-
for (const
|
|
52
|
-
"anything",
|
|
53
|
-
"boolean",
|
|
54
|
-
"date",
|
|
55
|
-
"email",
|
|
56
|
-
"enum",
|
|
57
|
-
"geoPoint",
|
|
58
|
-
"geoShape",
|
|
59
|
-
"integer",
|
|
60
|
-
"ipAddress",
|
|
61
|
-
"numeric",
|
|
62
|
-
"object",
|
|
63
|
-
"string",
|
|
64
|
-
"url",
|
|
65
|
-
]) {
|
|
66
|
-
const TypeConstructor = require(`./types/${typeFile}`);
|
|
176
|
+
for (const TypeConstructor of BUILT_IN_TYPES) {
|
|
67
177
|
this.addType(new TypeConstructor());
|
|
68
178
|
}
|
|
69
179
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Validates a document against its collection specifications
|
|
72
|
-
*
|
|
73
|
-
* @param {Request} request
|
|
74
|
-
* @param {boolean} [verbose]
|
|
75
|
-
* @returns {Promise.<{documentBody: *, errorMessages:string[], valid: boolean}|KuzzleRequest>}
|
|
76
|
-
*/
|
|
77
180
|
async validate(request, verbose = false) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
181
|
+
// Read from `input.args`, which is what the deprecated `input.resource`
|
|
182
|
+
// getters return: same values, typed as those getters declare them.
|
|
183
|
+
const { args } = request.input;
|
|
184
|
+
const _id = args._id;
|
|
185
|
+
const index = args.index;
|
|
186
|
+
const collection = args.collection;
|
|
187
|
+
// `has` rather than a plain lookup: `index` and `collection` come from the
|
|
188
|
+
// request, so an inherited property must not answer for a specification.
|
|
189
|
+
const indexSpec = index !== null && (0, safeObject_1.has)(this.specification, index)
|
|
190
|
+
? this.specification[index]
|
|
191
|
+
: undefined;
|
|
192
|
+
const collectionSpec = ((0, safeObject_1.isPlainObject)(this.specification) &&
|
|
193
|
+
indexSpec !== undefined &&
|
|
194
|
+
collection !== null &&
|
|
195
|
+
(0, safeObject_1.has)(indexSpec, collection) &&
|
|
196
|
+
indexSpec[collection]) ||
|
|
81
197
|
{};
|
|
82
|
-
|
|
83
|
-
if (request.input.controller === "document" &&
|
|
84
|
-
request.input.action === "update") {
|
|
85
|
-
isUpdate = true;
|
|
86
|
-
const document = await global.kuzzle.ask("core:storage:public:document:get", index, collection, _id);
|
|
87
|
-
// Avoid side effects on the request during the update validation
|
|
88
|
-
body = _.cloneDeep(request.input.body);
|
|
89
|
-
_.defaultsDeep(body, document._source);
|
|
90
|
-
}
|
|
198
|
+
const { body, isUpdate } = await this.resolveValidationBody(request, index, collection, _id);
|
|
91
199
|
const errorMessages = verbose ? {} : [];
|
|
92
200
|
let isValid = true;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
manageErrorMessage("document", errorMessages, `The document validation is strict. Cannot add unspecified sub-field "${error.details.field}"`, verbose);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (collectionSpec.validators) {
|
|
109
|
-
const filters = koncordeTest(this.koncorde, index, collection, body, _id);
|
|
110
|
-
if (filters.length === 0 || filters[0] !== collectionSpec.validators) {
|
|
111
|
-
isValid = false;
|
|
112
|
-
manageErrorMessage("document", errorMessages, "The document does not match validation filters.", verbose);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
201
|
+
// `collectionSpec` is always truthy — it falls back to `{}` above — so the
|
|
202
|
+
// enclosing `if (collectionSpec)` the JavaScript had was dead.
|
|
203
|
+
const children = collectionSpec.fields?.children;
|
|
204
|
+
if (children) {
|
|
205
|
+
isValid = this.checkDocumentFields(body, children,
|
|
206
|
+
// Truthiness, as in v2.56.0: curation stores `spec.strict || false`
|
|
207
|
+
// unchecked, so a stored `"true"` or `1` has always meant strict.
|
|
208
|
+
Boolean(collectionSpec.strict), errorMessages, verbose);
|
|
209
|
+
}
|
|
210
|
+
if (collectionSpec.validators && index !== null && collection !== null) {
|
|
211
|
+
isValid =
|
|
212
|
+
this.checkValidators(index, collection, _id, body, collectionSpec.validators, errorMessages, verbose) && isValid;
|
|
115
213
|
}
|
|
116
214
|
if (!verbose) {
|
|
117
215
|
// We only modify the request if the validation succeeds
|
|
118
|
-
if (
|
|
119
|
-
request.input.body = this.recurseApplyDefault(isUpdate, request.input.body,
|
|
216
|
+
if (children) {
|
|
217
|
+
request.input.body = this.recurseApplyDefault(isUpdate, request.input.body ?? {}, children);
|
|
120
218
|
}
|
|
121
219
|
return request;
|
|
122
220
|
}
|
|
123
221
|
return { errorMessages, valid: isValid };
|
|
124
222
|
}
|
|
125
223
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
224
|
+
* The body validation runs against: the request's own, or — on an update —
|
|
225
|
+
* a copy merged over the stored document, so that a partial update is
|
|
226
|
+
* validated as the document it will produce.
|
|
227
|
+
*/
|
|
228
|
+
async resolveValidationBody(request, index, collection, _id) {
|
|
229
|
+
if (request.input.controller !== "document" ||
|
|
230
|
+
request.input.action !== "update") {
|
|
231
|
+
return { body: request.input.body, isUpdate: false };
|
|
232
|
+
}
|
|
233
|
+
const document = await global.kuzzle.ask("core:storage:public:document:get", index, collection, _id);
|
|
234
|
+
// Avoid side effects on the request during the update validation
|
|
235
|
+
const body = (0, lodash_1.cloneDeep)(request.input.body ?? {}); // NOSONAR structuredClone is not equivalent here: specifications and documents are user data, and a conversion does not get to change clone semantics
|
|
236
|
+
(0, lodash_1.defaultsDeep)(body, document._source);
|
|
237
|
+
return { body, isUpdate: true };
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Walks the document against the collection's field tree, turning a
|
|
241
|
+
* root-level strictness breach into a message rather than an exception.
|
|
242
|
+
*/
|
|
243
|
+
checkDocumentFields(body, children, strict, errorMessages, verbose) {
|
|
244
|
+
try {
|
|
245
|
+
return this.recurseFieldValidation(body, children, strict, errorMessages, verbose);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
// The strictness message can be received here only if it happens at
|
|
249
|
+
// the validation of the document's root. Matched on the message, not on
|
|
250
|
+
// `instanceof StrictnessError`: a validation type is plugin code and may
|
|
251
|
+
// throw its own object, which is what the specs feed this.
|
|
252
|
+
if (messageOf(error) !== "strictness") {
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
(0, validationUtils_1.manageErrorMessage)("document", errorMessages, `The document validation is strict. Cannot add unspecified sub-field "${detailedField(error)}"`, verbose);
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Checks the document against the collection's Koncorde validator filter.
|
|
261
|
+
*/
|
|
262
|
+
checkValidators(index, collection, _id, body, validators, errorMessages, verbose) {
|
|
263
|
+
const filters = (0, koncordeCompat_1.koncordeTest)(this.koncorde, index, collection, body ?? {}, _id ?? undefined);
|
|
264
|
+
if (filters.length === 0 || filters[0] !== validators) {
|
|
265
|
+
(0, validationUtils_1.manageErrorMessage)("document", errorMessages, "The document does not match validation filters.", verbose);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Fills a document subset in with the default values its specification
|
|
272
|
+
* declares, recursing through the fields that allow children.
|
|
129
273
|
*/
|
|
130
274
|
recurseApplyDefault(isUpdate, documentSubset, collectionSpecSubset) {
|
|
131
|
-
Object.
|
|
132
|
-
const
|
|
133
|
-
if (has(documentSubset, fieldName) &&
|
|
134
|
-
this.
|
|
275
|
+
Object.entries(collectionSpecSubset).forEach(([fieldName, specSubset]) => {
|
|
276
|
+
const field = documentSubset[fieldName];
|
|
277
|
+
if ((0, safeObject_1.has)(documentSubset, fieldName) &&
|
|
278
|
+
this.typeOf(specSubset.type).allowChildren &&
|
|
135
279
|
specSubset.children) {
|
|
136
280
|
if (Array.isArray(field)) {
|
|
137
281
|
for (let i = 0; i < field.length; i++) {
|
|
@@ -143,134 +287,156 @@ class Validation {
|
|
|
143
287
|
}
|
|
144
288
|
}
|
|
145
289
|
else if (specSubset.defaultValue &&
|
|
146
|
-
(field === null || (!isUpdate && !has(documentSubset, fieldName)))) {
|
|
290
|
+
(field === null || (!isUpdate && !(0, safeObject_1.has)(documentSubset, fieldName)))) {
|
|
147
291
|
documentSubset[fieldName] = specSubset.defaultValue;
|
|
148
292
|
}
|
|
149
293
|
});
|
|
150
294
|
return documentSubset;
|
|
151
295
|
}
|
|
152
296
|
/**
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* @
|
|
156
|
-
*
|
|
157
|
-
*
|
|
297
|
+
* Validates every field of a document subset against its specification.
|
|
298
|
+
*
|
|
299
|
+
* @throws {StrictnessError} when `strictness` holds and the subset declares a
|
|
300
|
+
* field the specification does not — caught by the two callers, which turn it
|
|
301
|
+
* into a message naming the field.
|
|
158
302
|
*/
|
|
159
303
|
recurseFieldValidation(documentSubset, collectionSpecSubset, strictness, errorMessages, verbose) {
|
|
160
304
|
if (strictness) {
|
|
161
|
-
for (const field of Object.keys(documentSubset)) {
|
|
305
|
+
for (const field of Object.keys(documentSubset ?? {})) {
|
|
162
306
|
if (!collectionSpecSubset[field]) {
|
|
163
|
-
|
|
164
|
-
error.details = { field };
|
|
165
|
-
throw error;
|
|
307
|
+
throw new StrictnessError(field);
|
|
166
308
|
}
|
|
167
309
|
}
|
|
168
310
|
}
|
|
169
311
|
if (!verbose) {
|
|
170
312
|
// We stop as soon as one field is not valid
|
|
171
|
-
return Object.keys(collectionSpecSubset).every((fieldName) => this.isValidField(fieldName, documentSubset, collectionSpecSubset, strictness, errorMessages, verbose));
|
|
313
|
+
return Object.keys(collectionSpecSubset).every((fieldName) => this.isValidField(fieldName, documentSubset ?? {}, collectionSpecSubset, strictness, errorMessages, verbose));
|
|
172
314
|
}
|
|
173
315
|
// We try to validate every field in order to get all error messages if any
|
|
174
|
-
return Object.keys(collectionSpecSubset).reduce((reductionResult, fieldName) => this.isValidField(fieldName, documentSubset, collectionSpecSubset, strictness, errorMessages, verbose) && reductionResult, true);
|
|
316
|
+
return Object.keys(collectionSpecSubset).reduce((reductionResult, fieldName) => this.isValidField(fieldName, documentSubset ?? {}, collectionSpecSubset, strictness, errorMessages, verbose) && reductionResult, true);
|
|
175
317
|
}
|
|
176
318
|
/**
|
|
177
|
-
*
|
|
178
|
-
* @param {*} documentSubset
|
|
179
|
-
* @param {StructuredFieldSpecification} collectionSpecSubset
|
|
180
|
-
* @param {boolean} strictness
|
|
181
|
-
* @param {string[]} errorMessages
|
|
182
|
-
* @param {boolean} verbose
|
|
183
|
-
* @returns {boolean}
|
|
319
|
+
* Validates one field of a document subset against its specification.
|
|
184
320
|
*/
|
|
185
321
|
isValidField(fieldName, documentSubset, collectionSpecSubset, strictness, errorMessages, verbose) {
|
|
186
|
-
/** @type StructuredFieldSpecification */
|
|
187
322
|
const field = collectionSpecSubset[fieldName];
|
|
188
|
-
|
|
323
|
+
// Both callers drive this from the map's own keys; a name that is not in
|
|
324
|
+
// it has no specification to check the document against.
|
|
325
|
+
if (field === undefined) {
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
189
328
|
if (field.mandatory &&
|
|
190
|
-
!has(field, "defaultValue") &&
|
|
191
|
-
|
|
192
|
-
manageErrorMessage(field.path, errorMessages, "The field is mandatory.", verbose);
|
|
329
|
+
!(0, safeObject_1.has)(field, "defaultValue") &&
|
|
330
|
+
(0, lodash_1.isNil)(documentSubset[fieldName])) {
|
|
331
|
+
(0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, "The field is mandatory.", verbose);
|
|
193
332
|
return false;
|
|
194
333
|
}
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
fieldValues = documentSubset[fieldName];
|
|
334
|
+
if ((0, lodash_1.isNil)(documentSubset[fieldName])) {
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
const fieldValues = this.resolveFieldValues(field, documentSubset[fieldName], errorMessages, verbose);
|
|
338
|
+
if (fieldValues === null) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
const type = this.typeOf(field.type);
|
|
342
|
+
const nestedStrictness = type.allowChildren
|
|
343
|
+
? type.getStrictness(field.typeOptions, strictness)
|
|
344
|
+
: false;
|
|
345
|
+
let result = true;
|
|
346
|
+
for (const val of fieldValues) {
|
|
347
|
+
if (!this.validateFieldValue(field, val, errorMessages, verbose)) {
|
|
348
|
+
return false;
|
|
213
349
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
fieldValues = [documentSubset[fieldName]];
|
|
350
|
+
if (type.allowChildren && field.children) {
|
|
351
|
+
result =
|
|
352
|
+
this.validateFieldChildren(field, val, nestedStrictness, errorMessages, verbose) && result;
|
|
220
353
|
}
|
|
221
|
-
|
|
222
|
-
|
|
354
|
+
}
|
|
355
|
+
return result;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Answers the values to validate — the array itself for a multivalued field,
|
|
359
|
+
* a single-element list otherwise — or `null` once it has filed the reason
|
|
360
|
+
* the field's arity is wrong.
|
|
361
|
+
*/
|
|
362
|
+
resolveFieldValues(field, value, errorMessages, verbose) {
|
|
363
|
+
if (!field.multivalued.value) {
|
|
364
|
+
if (Array.isArray(value)) {
|
|
365
|
+
(0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, "The field is not a multivalued field; Multiple values provided.", verbose);
|
|
366
|
+
return null;
|
|
223
367
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
368
|
+
return [value];
|
|
369
|
+
}
|
|
370
|
+
if (!Array.isArray(value)) {
|
|
371
|
+
(0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, "The field must be multivalued, unary value provided.", verbose);
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const { minCount, maxCount } = field.multivalued;
|
|
375
|
+
if (minCount !== undefined && value.length < minCount) {
|
|
376
|
+
(0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, `Not enough elements. Minimum count is set to ${minCount}.`, verbose);
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
if (maxCount !== undefined && value.length > maxCount) {
|
|
380
|
+
(0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, `Too many elements. Maximum count is set to ${maxCount}.`, verbose);
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
return value;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Hands one value to the field's registered type.
|
|
387
|
+
*/
|
|
388
|
+
validateFieldValue(field, val, errorMessages, verbose) {
|
|
389
|
+
const fieldErrors = [];
|
|
390
|
+
if (this.typeOf(field.type).validate(field.typeOptions, val, fieldErrors)) {
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
if (fieldErrors.length === 0) {
|
|
394
|
+
// We still want to trigger an error, even if no message is provided
|
|
395
|
+
(0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, "An error has occurred during validation.", verbose);
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
fieldErrors.forEach((message) => (0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, message, verbose));
|
|
399
|
+
}
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Recurses into a value's children, turning this field's strictness breach
|
|
404
|
+
* into a message rather than an exception.
|
|
405
|
+
*/
|
|
406
|
+
validateFieldChildren(field, val, nestedStrictness, errorMessages, verbose) {
|
|
407
|
+
try {
|
|
408
|
+
return this.recurseFieldValidation(val, field.children ?? {}, nestedStrictness, errorMessages, verbose);
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
if (messageOf(error) === "strictness") {
|
|
412
|
+
(0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, `The field is set to "strict"; cannot add unspecified sub-field "${detailedField(error)}".`, verbose);
|
|
413
|
+
}
|
|
414
|
+
else if (verbose) {
|
|
415
|
+
(0, validationUtils_1.manageErrorMessage)(field.path, errorMessages, messageOf(error), verbose);
|
|
255
416
|
}
|
|
417
|
+
else {
|
|
418
|
+
throw error;
|
|
419
|
+
}
|
|
420
|
+
return false;
|
|
256
421
|
}
|
|
257
|
-
return result;
|
|
258
422
|
}
|
|
259
423
|
/**
|
|
260
|
-
*
|
|
424
|
+
* Loads every stored specification, curates it, and swaps the result in.
|
|
425
|
+
*
|
|
426
|
+
* A collection whose specification does not curate is logged and skipped, so
|
|
427
|
+
* one bad specification cannot keep the others from being applied.
|
|
261
428
|
*/
|
|
262
429
|
curateSpecification() {
|
|
263
|
-
const promises = []
|
|
264
|
-
|
|
430
|
+
const promises = [];
|
|
431
|
+
const specification = {};
|
|
432
|
+
return (0, validationUtils_1.getValidationConfiguration)().then((validation) => {
|
|
265
433
|
this.rawConfiguration = validation;
|
|
266
|
-
for (const indexName of Object.
|
|
267
|
-
for (const collectionName of Object.
|
|
268
|
-
const promise = this.curateCollectionSpecification(indexName, collectionName,
|
|
434
|
+
for (const [indexName, collections] of Object.entries(this.rawConfiguration)) {
|
|
435
|
+
for (const [collectionName, collectionSpec] of Object.entries(collections)) {
|
|
436
|
+
const promise = this.curateCollectionSpecification(indexName, collectionName, collectionSpec)
|
|
269
437
|
.then((curatedSpec) => {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
specification[indexName][collectionName] = curatedSpec;
|
|
438
|
+
const indexSpec = (specification[indexName] ?? (specification[indexName] = {}));
|
|
439
|
+
indexSpec[collectionName] = curatedSpec;
|
|
274
440
|
return null;
|
|
275
441
|
})
|
|
276
442
|
.catch((error) => {
|
|
@@ -281,24 +447,20 @@ class Validation {
|
|
|
281
447
|
promises.push(promise);
|
|
282
448
|
}
|
|
283
449
|
}
|
|
284
|
-
return
|
|
450
|
+
return bluebird_1.default.all(promises).then(() => {
|
|
285
451
|
this.specification = specification;
|
|
286
452
|
});
|
|
287
453
|
});
|
|
288
454
|
}
|
|
289
455
|
/**
|
|
290
|
-
*
|
|
291
|
-
* @param {string} collectionName
|
|
292
|
-
* @param {CollectionSpecification} collectionSpec
|
|
293
|
-
* @param {boolean} [verboseErrors]
|
|
294
|
-
* @returns {Promise<object>}
|
|
456
|
+
* Reports whether a specification would curate, without registering it.
|
|
295
457
|
*/
|
|
296
458
|
validateFormat(indexName, collectionName, collectionSpec, verboseErrors = false) {
|
|
297
459
|
// We make a deep clone to avoid side effects
|
|
298
|
-
const specification =
|
|
460
|
+
const specification = (0, lodash_1.cloneDeep)(collectionSpec); // NOSONAR structuredClone is not equivalent here: specifications and documents are user data, and a conversion does not get to change clone semantics
|
|
299
461
|
return (this.curateCollectionSpecification(indexName, collectionName, specification, true, verboseErrors)
|
|
300
462
|
.then((result) => {
|
|
301
|
-
if (verboseErrors && result
|
|
463
|
+
if (verboseErrors && isCurationFailure(result)) {
|
|
302
464
|
return result;
|
|
303
465
|
}
|
|
304
466
|
return { isValid: true };
|
|
@@ -307,24 +469,15 @@ class Validation {
|
|
|
307
469
|
// to know what is going wrong with the given spec
|
|
308
470
|
.catch((error) => ({ errors: [error], isValid: false })));
|
|
309
471
|
}
|
|
310
|
-
/**
|
|
311
|
-
* @param {string} index
|
|
312
|
-
* @param {string} collection
|
|
313
|
-
* @param {CollectionSpecification} spec
|
|
314
|
-
* @param {boolean} [dryRun]
|
|
315
|
-
* @param {boolean} [verbose]
|
|
316
|
-
* @returns {Promise<CollectionSpecification>}
|
|
317
|
-
* @rejects PreconditionError
|
|
318
|
-
*/
|
|
319
472
|
async curateCollectionSpecification(index, collection, spec, dryRun = false, verbose = false) {
|
|
320
|
-
let error
|
|
473
|
+
let error;
|
|
321
474
|
const processed = {
|
|
322
475
|
fields: {},
|
|
323
476
|
strict: spec.strict || false,
|
|
324
477
|
validators: null,
|
|
325
478
|
};
|
|
326
479
|
const allowed = ["strict", "fields", "validators"];
|
|
327
|
-
if (!checkAllowedProperties(spec, allowed)) {
|
|
480
|
+
if (!(0, validationUtils_1.checkAllowedProperties)(spec, allowed)) {
|
|
328
481
|
error = assertionError.get("unexpected_properties", `${index}.${collection}`, allowed.join(", "));
|
|
329
482
|
if (verbose) {
|
|
330
483
|
return { errors: [error.message], isValid: false };
|
|
@@ -333,50 +486,68 @@ class Validation {
|
|
|
333
486
|
}
|
|
334
487
|
if (spec.fields) {
|
|
335
488
|
const result = this.structureCollectionValidation(spec, index, collection, verbose);
|
|
336
|
-
if (result
|
|
489
|
+
if (isCurationFailure(result)) {
|
|
337
490
|
if (verbose) {
|
|
338
491
|
// do not fail fast if we need verbose errors
|
|
339
492
|
return result;
|
|
340
493
|
}
|
|
341
|
-
throw assertionError.get("invalid_specifications", result.errors.join("\n\t- "));
|
|
494
|
+
throw assertionError.get("invalid_specifications", (result.errors ?? []).join("\n\t- "));
|
|
342
495
|
}
|
|
343
496
|
processed.fields = result;
|
|
344
497
|
}
|
|
345
498
|
if (spec.validators && Array.isArray(spec.validators)) {
|
|
346
|
-
|
|
347
|
-
const filterId = this.curateValidatorFilter(index, collection, spec.validators, dryRun);
|
|
348
|
-
processed.validators = filterId;
|
|
349
|
-
}
|
|
350
|
-
catch (e) {
|
|
351
|
-
this.logger.error(e);
|
|
352
|
-
throw assertionError.getFrom(e, "invalid_filters", e.message);
|
|
353
|
-
}
|
|
499
|
+
processed.validators = this.curateValidators(index, collection, spec.validators, dryRun);
|
|
354
500
|
}
|
|
355
501
|
return processed;
|
|
356
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* Registers a specification's validator filter and answers its id.
|
|
505
|
+
*
|
|
506
|
+
* The wrapping is what `curateCollectionSpecification` did inline: a filter
|
|
507
|
+
* Koncorde refuses is an `invalid_filters` assertion, logged on the way
|
|
508
|
+
* past.
|
|
509
|
+
*/
|
|
510
|
+
curateValidators(index, collection, validators, dryRun) {
|
|
511
|
+
try {
|
|
512
|
+
return this.curateValidatorFilter(index, collection, validators, dryRun);
|
|
513
|
+
}
|
|
514
|
+
catch (e) {
|
|
515
|
+
this.logger.error(e);
|
|
516
|
+
throw assertionError.getFrom(e instanceof Error ? e : new Error(messageOf(e)), "invalid_filters", messageOf(e));
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Curates every field of a specification, then assembles them into a tree.
|
|
521
|
+
*/
|
|
357
522
|
structureCollectionValidation(collectionSpec, indexName, collectionName, verboseErrors = false) {
|
|
523
|
+
var _a;
|
|
358
524
|
const fields = {};
|
|
359
|
-
let errors = []
|
|
360
|
-
|
|
525
|
+
let errors = [];
|
|
526
|
+
let maxDepth = 0;
|
|
527
|
+
for (const [fieldName, fieldSpec] of Object.entries(collectionSpec.fields ?? {})) {
|
|
361
528
|
const // We deep clone the field because we will modify it
|
|
362
|
-
fieldSpecClone =
|
|
529
|
+
fieldSpecClone = (0, lodash_1.cloneDeep)(fieldSpec); // NOSONAR structuredClone is not equivalent here: specifications and documents are user data, and a conversion does not get to change clone semantics
|
|
363
530
|
try {
|
|
364
531
|
const result = this.curateFieldSpecification(fieldSpecClone, indexName, collectionName, fieldName, verboseErrors);
|
|
365
|
-
if (result.isValid === false) {
|
|
366
|
-
|
|
367
|
-
|
|
532
|
+
if (result.isValid === false || result.fieldSpec === undefined) {
|
|
533
|
+
const resultErrors = result.errors ?? [];
|
|
534
|
+
errors = errors.concat(resultErrors);
|
|
535
|
+
this.logger.error(resultErrors.join("\n"));
|
|
368
536
|
}
|
|
369
537
|
else {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
538
|
+
// `curateFieldSpecification` answers everything but the field's
|
|
539
|
+
// place in the tree; the key it was filed under is what gives it one.
|
|
540
|
+
const path = fieldName.split("/");
|
|
541
|
+
const field = {
|
|
542
|
+
...result.fieldSpec,
|
|
543
|
+
depth: path.length,
|
|
544
|
+
path,
|
|
545
|
+
};
|
|
373
546
|
if (field.depth > maxDepth) {
|
|
374
547
|
maxDepth = field.depth;
|
|
375
548
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
fields[field.depth].push(field);
|
|
549
|
+
const sameDepth = (fields[_a = field.depth] ?? (fields[_a] = []));
|
|
550
|
+
sameDepth.push(field);
|
|
380
551
|
}
|
|
381
552
|
}
|
|
382
553
|
catch (error) {
|
|
@@ -388,18 +559,15 @@ class Validation {
|
|
|
388
559
|
return { errors, isValid: false };
|
|
389
560
|
}
|
|
390
561
|
if (Object.keys(fields).length > 0) {
|
|
391
|
-
return curateStructuredFields(this.typeAllowsChildren, fields, maxDepth);
|
|
562
|
+
return (0, validationUtils_1.curateStructuredFields)(this.typeAllowsChildren, fields, maxDepth);
|
|
392
563
|
}
|
|
393
564
|
return {};
|
|
394
565
|
}
|
|
395
566
|
/**
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
* @
|
|
400
|
-
* @param {boolean} [verboseErrors]
|
|
401
|
-
* @returns {object}
|
|
402
|
-
* @throws PreconditionError
|
|
567
|
+
* Fills a field specification's defaults in and hands its `typeOptions` to
|
|
568
|
+
* the type that owns them.
|
|
569
|
+
*
|
|
570
|
+
* @throws {PreconditionError}
|
|
403
571
|
*/
|
|
404
572
|
curateFieldSpecification(fieldSpec, indexName, collectionName, fieldName, verboseErrors = false) {
|
|
405
573
|
const errors = [];
|
|
@@ -407,45 +575,42 @@ class Validation {
|
|
|
407
575
|
if (result.isValid === false) {
|
|
408
576
|
return result;
|
|
409
577
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
fieldSpec.typeOptions
|
|
418
|
-
}
|
|
419
|
-
const allowed = this.
|
|
420
|
-
if (!checkAllowedProperties(
|
|
578
|
+
// Built rather than `defaultsDeep`-ed in place: the defaults are what turn
|
|
579
|
+
// a submitted field into a curated one, and saying so is what lets the
|
|
580
|
+
// return type be `UnplacedCuratedField` instead of the submitted shape.
|
|
581
|
+
const curated = {
|
|
582
|
+
...fieldSpec,
|
|
583
|
+
mandatory: fieldSpec.mandatory ?? false,
|
|
584
|
+
multivalued: { value: false, ...fieldSpec.multivalued },
|
|
585
|
+
typeOptions: fieldSpec.typeOptions ?? {},
|
|
586
|
+
};
|
|
587
|
+
const allowed = this.typeOf(curated.type).allowedTypeOptions || [];
|
|
588
|
+
if (!(0, validationUtils_1.checkAllowedProperties)(curated.typeOptions, allowed)) {
|
|
421
589
|
throwOrStoreError(assertionError.get("unexpected_properties", `${indexName}.${collectionName}.${fieldName}`, allowed.join(", ")), verboseErrors, errors);
|
|
422
590
|
}
|
|
423
591
|
try {
|
|
424
|
-
|
|
592
|
+
curated.typeOptions = this.typeOf(curated.type).validateFieldSpecification(curated.typeOptions);
|
|
425
593
|
}
|
|
426
594
|
catch (e) {
|
|
595
|
+
const cause = e instanceof Error ? e : new Error(messageOf(e));
|
|
427
596
|
if (!verboseErrors) {
|
|
428
|
-
if (e instanceof KuzzleError) {
|
|
597
|
+
if (e instanceof errors_1.KuzzleError) {
|
|
429
598
|
throw e;
|
|
430
599
|
}
|
|
431
|
-
throw kerror.getFrom(
|
|
600
|
+
throw kerror.getFrom(cause, "plugin", "runtime", "unexpected_error", cause.message);
|
|
432
601
|
}
|
|
433
|
-
errors.push(
|
|
602
|
+
errors.push(cause.message);
|
|
434
603
|
}
|
|
435
604
|
if (errors.length > 0) {
|
|
436
605
|
return { errors, isValid: false };
|
|
437
606
|
}
|
|
438
|
-
debug("Loaded field validator: %s/%s/%s: %o", indexName, collectionName, fieldName,
|
|
439
|
-
return { fieldSpec, isValid: true };
|
|
607
|
+
debug("Loaded field validator: %s/%s/%s: %o", indexName, collectionName, fieldName, curated);
|
|
608
|
+
return { fieldSpec: curated, isValid: true };
|
|
440
609
|
}
|
|
441
610
|
/**
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
* @
|
|
445
|
-
* @param {string} fieldName
|
|
446
|
-
* @param {boolean} [verboseErrors]
|
|
447
|
-
* @returns {object}
|
|
448
|
-
* @throws PreconditionError
|
|
611
|
+
* Checks a field specification's shape, before any type is consulted.
|
|
612
|
+
*
|
|
613
|
+
* @throws {PreconditionError}
|
|
449
614
|
*/
|
|
450
615
|
curateFieldSpecificationFormat(fieldSpec, indexName, collectionName, fieldName, verboseErrors = false) {
|
|
451
616
|
const errors = [];
|
|
@@ -457,49 +622,24 @@ class Validation {
|
|
|
457
622
|
"multivalued",
|
|
458
623
|
"typeOptions",
|
|
459
624
|
];
|
|
460
|
-
if (!checkAllowedProperties(fieldSpec, props)) {
|
|
625
|
+
if (!(0, validationUtils_1.checkAllowedProperties)(fieldSpec, props)) {
|
|
461
626
|
throwOrStoreError(assertionError.get("unexpected_properties", `${indexName}.${collectionName}.${fieldName}`, props.join(", ")), verboseErrors, errors);
|
|
462
627
|
}
|
|
463
|
-
if (!has(fieldSpec, "type")) {
|
|
628
|
+
if (!(0, safeObject_1.has)(fieldSpec, "type")) {
|
|
464
629
|
throwOrStoreError(assertionError.get("missing_type", `${indexName}.${collectionName}.${fieldName}`), verboseErrors, errors);
|
|
465
630
|
}
|
|
466
|
-
if (!has(this.types, fieldSpec.type)) {
|
|
631
|
+
if (!(0, safeObject_1.has)(this.types, fieldSpec.type)) {
|
|
467
632
|
throwOrStoreError(assertionError.get("unknown_type", `${indexName}.${collectionName}.${fieldName}`, fieldSpec.type), verboseErrors, errors);
|
|
468
633
|
}
|
|
469
|
-
|
|
470
|
-
const multivaluedProps = ["value", "minCount", "maxCount"];
|
|
471
|
-
if (!checkAllowedProperties(fieldSpec.multivalued, multivaluedProps)) {
|
|
472
|
-
throwOrStoreError(assertionError.get("unexpected_properties", `${indexName}.${collectionName}.${fieldName}.multivalued`, multivaluedProps.join(", ")), verboseErrors, errors);
|
|
473
|
-
}
|
|
474
|
-
if (!has(fieldSpec.multivalued, "value")) {
|
|
475
|
-
throwOrStoreError(assertionError.get("missing_value", `${indexName}.${collectionName}.${fieldName}.multivalued`), verboseErrors, errors);
|
|
476
|
-
}
|
|
477
|
-
if (typeof fieldSpec.multivalued.value !== "boolean") {
|
|
478
|
-
throwOrStoreError(assertionError.get("invalid_type", `${indexName}.${collectionName}.${fieldName}.multivalued.value`, "boolean"), verboseErrors, errors);
|
|
479
|
-
}
|
|
480
|
-
for (const unexpected of ["minCount", "maxCount"]) {
|
|
481
|
-
if (!fieldSpec.multivalued.value &&
|
|
482
|
-
has(fieldSpec.multivalued, unexpected)) {
|
|
483
|
-
throwOrStoreError(assertionError.get("not_multivalued", `${indexName}.${collectionName}.${fieldName}`, unexpected), verboseErrors, errors);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (has(fieldSpec.multivalued, "minCount") &&
|
|
487
|
-
has(fieldSpec.multivalued, "maxCount") &&
|
|
488
|
-
fieldSpec.multivalued.minCount > fieldSpec.multivalued.maxCount) {
|
|
489
|
-
throwOrStoreError(assertionError.get("invalid_range", `${indexName}.${collectionName}.${fieldName}`, "minCount", "maxCount"), verboseErrors, errors);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
634
|
+
checkMultivaluedSpecification(fieldSpec, indexName, collectionName, fieldName, verboseErrors, errors);
|
|
492
635
|
if (errors.length > 0) {
|
|
493
636
|
return { errors, isValid: false };
|
|
494
637
|
}
|
|
495
638
|
return { isValid: true };
|
|
496
639
|
}
|
|
497
640
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
* @param {*} validatorFilter
|
|
501
|
-
* @param {boolean} dryRun
|
|
502
|
-
* @returns {string}
|
|
641
|
+
* Registers a collection's validators as a Koncorde filter and answers its
|
|
642
|
+
* id — or validates it and answers `null`, on a dry run.
|
|
503
643
|
*/
|
|
504
644
|
curateValidatorFilter(indexName, collectionName, validatorFilter, dryRun) {
|
|
505
645
|
const query = {
|
|
@@ -510,12 +650,17 @@ class Validation {
|
|
|
510
650
|
this.koncorde.validate(query);
|
|
511
651
|
if (!dryRun) {
|
|
512
652
|
debug("Registering filter validator %s/%s: %O", indexName, collectionName, query);
|
|
513
|
-
return this.koncorde.register(query, toKoncordeIndex(indexName, collectionName));
|
|
653
|
+
return this.koncorde.register(query, (0, koncordeCompat_1.toKoncordeIndex)(indexName, collectionName));
|
|
514
654
|
}
|
|
515
655
|
return null;
|
|
516
656
|
}
|
|
517
657
|
/**
|
|
518
|
-
*
|
|
658
|
+
* Registers a validation type, built-in or plugin-provided.
|
|
659
|
+
*
|
|
660
|
+
* This is public API: `pluginContext` hands it to every plugin, so the
|
|
661
|
+
* runtime assertions below are the contract and stay exactly as they were —
|
|
662
|
+
* a plugin's type is not checked by the compiler.
|
|
663
|
+
*
|
|
519
664
|
* @throws {PluginImplementationError}
|
|
520
665
|
*/
|
|
521
666
|
addType(validationType) {
|
|
@@ -530,7 +675,7 @@ class Validation {
|
|
|
530
675
|
typeof validationType.validateFieldSpecification !== "function") {
|
|
531
676
|
throw kerror.get("validation", "types", "missing_function", validationType.typeName, "validateFieldSpecification");
|
|
532
677
|
}
|
|
533
|
-
if (has(validationType, "allowChildren") && validationType.allowChildren) {
|
|
678
|
+
if ((0, safeObject_1.has)(validationType, "allowChildren") && validationType.allowChildren) {
|
|
534
679
|
if (!validationType.getStrictness ||
|
|
535
680
|
typeof validationType.getStrictness !== "function") {
|
|
536
681
|
throw kerror.get("validation", "types", "missing_function", validationType.typeName, "getStrictness");
|
|
@@ -542,151 +687,64 @@ class Validation {
|
|
|
542
687
|
}
|
|
543
688
|
this.types[validationType.typeName] = validationType;
|
|
544
689
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
690
|
+
/**
|
|
691
|
+
* The validation type a curated field declares.
|
|
692
|
+
*
|
|
693
|
+
* Every caller reaches this through a field that `curateFieldSpecification`
|
|
694
|
+
* has been through, and that method refuses a `type` the map does not hold —
|
|
695
|
+
* so the throw here is the invariant restated where the compiler can see it,
|
|
696
|
+
* not a new failure mode.
|
|
697
|
+
*
|
|
698
|
+
* @throws {PreconditionError} when the type is not registered
|
|
699
|
+
*/
|
|
700
|
+
typeOf(typeName) {
|
|
701
|
+
const type = this.types[typeName];
|
|
702
|
+
if (type === undefined) {
|
|
703
|
+
throw kerror.get("validation", "types", "missing_type_name", typeName);
|
|
704
|
+
}
|
|
705
|
+
return type;
|
|
554
706
|
}
|
|
555
|
-
return !Object.keys(object).some((propertyName) => !allowedProperties.includes(propertyName));
|
|
556
707
|
}
|
|
557
708
|
/**
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
* @throws PreconditionError
|
|
709
|
+
* Checks a field's `multivalued` block. Lifted verbatim out of
|
|
710
|
+
* `curateFieldSpecificationFormat`, whose cognitive complexity the `.js` → `.ts`
|
|
711
|
+
* rename re-scored as new code.
|
|
562
712
|
*/
|
|
563
|
-
function
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
};
|
|
568
|
-
for (let i = 1; i <= maxDepth; i++) {
|
|
569
|
-
if (!has(fields, i)) {
|
|
570
|
-
throw assertionError.get("missing_nested_spec");
|
|
571
|
-
}
|
|
572
|
-
fields[i].forEach((field) => {
|
|
573
|
-
const parent = getParent(structuredFields, field.path), childKey = field.path[field.path.length - 1];
|
|
574
|
-
if (!parent.root && typeAllowsChildren.indexOf(parent.type) === -1) {
|
|
575
|
-
throw assertionError.get("unexpected_children", parent.type);
|
|
576
|
-
}
|
|
577
|
-
if (!has(parent, "children")) {
|
|
578
|
-
parent.children = {};
|
|
579
|
-
}
|
|
580
|
-
parent.children[childKey] = field;
|
|
581
|
-
});
|
|
713
|
+
function checkMultivaluedSpecification(fieldSpec, indexName, collectionName, fieldName, verboseErrors, errors) {
|
|
714
|
+
const multivalued = fieldSpec.multivalued;
|
|
715
|
+
if (multivalued === undefined) {
|
|
716
|
+
return;
|
|
582
717
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
* @param {StructuredFieldSpecification} structuredFields
|
|
587
|
-
* @param {string[]}fieldPath
|
|
588
|
-
* @returns {StructuredFieldSpecification}
|
|
589
|
-
*/
|
|
590
|
-
function getParent(structuredFields, fieldPath) {
|
|
591
|
-
if (fieldPath.length === 1) {
|
|
592
|
-
return structuredFields;
|
|
718
|
+
const multivaluedProps = ["value", "minCount", "maxCount"];
|
|
719
|
+
if (!(0, validationUtils_1.checkAllowedProperties)(multivalued, multivaluedProps)) {
|
|
720
|
+
throwOrStoreError(assertionError.get("unexpected_properties", `${indexName}.${collectionName}.${fieldName}.multivalued`, multivaluedProps.join(", ")), verboseErrors, errors);
|
|
593
721
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
722
|
+
if (!(0, safeObject_1.has)(multivalued, "value")) {
|
|
723
|
+
throwOrStoreError(assertionError.get("missing_value", `${indexName}.${collectionName}.${fieldName}.multivalued`), verboseErrors, errors);
|
|
724
|
+
}
|
|
725
|
+
if (typeof multivalued.value !== "boolean") {
|
|
726
|
+
throwOrStoreError(assertionError.get("invalid_type", `${indexName}.${collectionName}.${fieldName}.multivalued.value`, "boolean"), verboseErrors, errors);
|
|
727
|
+
}
|
|
728
|
+
for (const unexpected of ["minCount", "maxCount"]) {
|
|
729
|
+
if (!multivalued.value && (0, safeObject_1.has)(multivalued, unexpected)) {
|
|
730
|
+
throwOrStoreError(assertionError.get("not_multivalued", `${indexName}.${collectionName}.${fieldName}`, unexpected), verboseErrors, errors);
|
|
598
731
|
}
|
|
599
|
-
pointer = pointer.children[fieldPath[i]];
|
|
600
732
|
}
|
|
601
|
-
|
|
733
|
+
if (multivalued.minCount !== undefined &&
|
|
734
|
+
multivalued.maxCount !== undefined &&
|
|
735
|
+
multivalued.minCount > multivalued.maxCount) {
|
|
736
|
+
throwOrStoreError(assertionError.get("invalid_range", `${indexName}.${collectionName}.${fieldName}`, "minCount", "maxCount"), verboseErrors, errors);
|
|
737
|
+
}
|
|
602
738
|
}
|
|
603
739
|
/**
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
* @param {string[]} errors
|
|
740
|
+
* Rethrows, or collects the message — the single place the verbose/fail-fast
|
|
741
|
+
* choice is made during curation.
|
|
607
742
|
*/
|
|
608
743
|
function throwOrStoreError(error, doNotThrow, errorMessages) {
|
|
609
744
|
if (!doNotThrow) {
|
|
610
745
|
throw error;
|
|
611
746
|
}
|
|
612
|
-
errorMessages.push(error
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* @param {string|string[]} errorContext
|
|
616
|
-
* @param {string[]|{documentScope:string[], fieldScope: {children: ...*}}} errorHolder
|
|
617
|
-
* @param {string} message
|
|
618
|
-
* @param {boolean} structured
|
|
619
|
-
*/
|
|
620
|
-
function manageErrorMessage(errorContext, errorHolder, message, structured) {
|
|
621
|
-
if (structured) {
|
|
622
|
-
if (errorContext === "document") {
|
|
623
|
-
if (!errorHolder.documentScope) {
|
|
624
|
-
errorHolder.documentScope = [];
|
|
625
|
-
}
|
|
626
|
-
errorHolder.documentScope.push(message);
|
|
627
|
-
}
|
|
628
|
-
else {
|
|
629
|
-
if (!errorHolder.fieldScope) {
|
|
630
|
-
errorHolder.fieldScope = {};
|
|
631
|
-
}
|
|
632
|
-
let pointer = errorHolder.fieldScope;
|
|
633
|
-
for (let i = 0; i < errorContext.length; i++) {
|
|
634
|
-
if (!pointer.children) {
|
|
635
|
-
pointer.children = {};
|
|
636
|
-
}
|
|
637
|
-
if (!has(pointer.children, errorContext[i])) {
|
|
638
|
-
pointer.children[errorContext[i]] = {};
|
|
639
|
-
}
|
|
640
|
-
pointer = pointer.children[errorContext[i]];
|
|
641
|
-
}
|
|
642
|
-
if (!has(pointer, "messages")) {
|
|
643
|
-
pointer.messages = [];
|
|
644
|
-
}
|
|
645
|
-
pointer.messages.push(message);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
else if (errorContext === "document") {
|
|
649
|
-
throw kerror.get("validation", "check", "failed_document", message);
|
|
650
|
-
}
|
|
651
|
-
else {
|
|
652
|
-
throw kerror.get("validation", "check", "failed_field", errorContext.join("."), message);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Retrieve the plugins list from the database and returns it,
|
|
657
|
-
* along with their configuration
|
|
658
|
-
*
|
|
659
|
-
* @returns {Promise}
|
|
660
|
-
*/
|
|
661
|
-
function getValidationConfiguration() {
|
|
662
|
-
return global.kuzzle.internalIndex
|
|
663
|
-
.search("validations", {}, { from: 0, size: 1000 })
|
|
664
|
-
.then((result) => {
|
|
665
|
-
let validation = {};
|
|
666
|
-
if (result && Array.isArray(result.hits) && result.hits.length > 0) {
|
|
667
|
-
for (const { _source, _id } of result.hits) {
|
|
668
|
-
const collectionName = `${_id.split("#")[0]}/${_id.split("#")[1]}`;
|
|
669
|
-
if (!get(_source, "index")) {
|
|
670
|
-
throw assertionError.get("incorrect_validation_format", collectionName, "index");
|
|
671
|
-
}
|
|
672
|
-
if (!get(_source, "collection")) {
|
|
673
|
-
throw assertionError.get("incorrect_validation_format", collectionName, "collection");
|
|
674
|
-
}
|
|
675
|
-
if (!get(_source, "validation")) {
|
|
676
|
-
throw assertionError.get("incorrect_validation_format", collectionName, "validation");
|
|
677
|
-
}
|
|
678
|
-
if (!has(validation, _source.index)) {
|
|
679
|
-
validation[_source.index] = {};
|
|
680
|
-
}
|
|
681
|
-
validation[_source.index][_source.collection] = _source.validation;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
else if (global.kuzzle.config.validation) {
|
|
685
|
-
// We can't wait prepareDb as it runs outside of the rest of the start
|
|
686
|
-
validation = global.kuzzle.config.validation;
|
|
687
|
-
}
|
|
688
|
-
return validation;
|
|
689
|
-
});
|
|
747
|
+
errorMessages.push(messageOf(error));
|
|
690
748
|
}
|
|
691
749
|
module.exports = Validation;
|
|
692
750
|
//# sourceMappingURL=validation.js.map
|