From f46f864f7e38c3a64bdab93ab07d1ebac90a2aab Mon Sep 17 00:00:00 2001 From: Eran Hammer Date: Thu, 16 Jul 2020 11:36:44 -0700 Subject: [PATCH] Replace joi with validate --- lib/config.js | 450 ++++++++++++++++++++++----------------------- lib/route.js | 30 +-- lib/server.js | 3 +- lib/validation.js | 22 ++- package.json | 7 +- test/request.js | 2 +- test/route.js | 2 +- test/validation.js | 2 +- 8 files changed, 257 insertions(+), 261 deletions(-) diff --git a/lib/config.js b/lib/config.js index 8a6590b57..37c2656cd 100755 --- a/lib/config.js +++ b/lib/config.js @@ -2,7 +2,7 @@ const Os = require('os'); -const Joi = require('@hapi/joi'); +const Validate = require('@hapi/validate'); const internals = {}; @@ -39,22 +39,22 @@ exports.enable = function (options) { }; -internals.access = Joi.object({ - entity: Joi.valid('user', 'app', 'any'), - scope: [false, Joi.array().items(Joi.string()).single().min(1)] +internals.access = Validate.object({ + entity: Validate.valid('user', 'app', 'any'), + scope: [false, Validate.array().items(Validate.string()).single().min(1)] }); -internals.auth = Joi.alternatives([ - Joi.string(), +internals.auth = Validate.alternatives([ + Validate.string(), internals.access.keys({ - mode: Joi.valid('required', 'optional', 'try'), - strategy: Joi.string(), - strategies: Joi.array().items(Joi.string()).min(1), - access: Joi.array().items(internals.access.min(1)).single().min(1), + mode: Validate.valid('required', 'optional', 'try'), + strategy: Validate.string(), + strategies: Validate.array().items(Validate.string()).min(1), + access: Validate.array().items(internals.access.min(1)).single().min(1), payload: [ - Joi.valid('required', 'optional'), - Joi.boolean() + Validate.valid('required', 'optional'), + Validate.boolean() ] }) .without('strategy', 'strategies') @@ -62,144 +62,144 @@ internals.auth = Joi.alternatives([ ]); -internals.event = Joi.object({ - method: Joi.array().items(Joi.function()).single(), - options: Joi.object({ - before: Joi.array().items(Joi.string()).single(), - after: Joi.array().items(Joi.string()).single(), - bind: Joi.any(), - sandbox: Joi.valid('server', 'plugin'), - timeout: Joi.number().integer().min(1) +internals.event = Validate.object({ + method: Validate.array().items(Validate.function()).single(), + options: Validate.object({ + before: Validate.array().items(Validate.string()).single(), + after: Validate.array().items(Validate.string()).single(), + bind: Validate.any(), + sandbox: Validate.valid('server', 'plugin'), + timeout: Validate.number().integer().min(1) }) .default({}) }); -internals.exts = Joi.array() - .items(internals.event.keys({ type: Joi.string().required() })).single(); +internals.exts = Validate.array() + .items(internals.event.keys({ type: Validate.string().required() })).single(); -internals.failAction = Joi.alternatives([ - Joi.valid('error', 'log', 'ignore'), - Joi.function() +internals.failAction = Validate.alternatives([ + Validate.valid('error', 'log', 'ignore'), + Validate.function() ]) .default('error'); -internals.routeBase = Joi.object({ - app: Joi.object().allow(null), +internals.routeBase = Validate.object({ + app: Validate.object().allow(null), auth: internals.auth.allow(false), - bind: Joi.object().allow(null), - cache: Joi.object({ - expiresIn: Joi.number(), - expiresAt: Joi.string(), - privacy: Joi.valid('default', 'public', 'private'), - statuses: Joi.array().items(Joi.number().integer().min(200)).min(1).single().default([200, 204]), - otherwise: Joi.string().default('no-cache') + bind: Validate.object().allow(null), + cache: Validate.object({ + expiresIn: Validate.number(), + expiresAt: Validate.string(), + privacy: Validate.valid('default', 'public', 'private'), + statuses: Validate.array().items(Validate.number().integer().min(200)).min(1).single().default([200, 204]), + otherwise: Validate.string().default('no-cache') }) .allow(false) .default(), - compression: Joi.object() - .pattern(/.+/, Joi.object()) + compression: Validate.object() + .pattern(/.+/, Validate.object()) .default(), - cors: Joi.object({ - origin: Joi.array().min(1).allow('ignore').default(['*']), - maxAge: Joi.number().default(86400), - headers: Joi.array().items(Joi.string()).default(['Accept', 'Authorization', 'Content-Type', 'If-None-Match']), - additionalHeaders: Joi.array().items(Joi.string()).default([]), - exposedHeaders: Joi.array().items(Joi.string()).default(['WWW-Authenticate', 'Server-Authorization']), - additionalExposedHeaders: Joi.array().items(Joi.string()).default([]), - credentials: Joi.boolean().when('origin', { is: 'ignore', then: false }).default(false) + cors: Validate.object({ + origin: Validate.array().min(1).allow('ignore').default(['*']), + maxAge: Validate.number().default(86400), + headers: Validate.array().items(Validate.string()).default(['Accept', 'Authorization', 'Content-Type', 'If-None-Match']), + additionalHeaders: Validate.array().items(Validate.string()).default([]), + exposedHeaders: Validate.array().items(Validate.string()).default(['WWW-Authenticate', 'Server-Authorization']), + additionalExposedHeaders: Validate.array().items(Validate.string()).default([]), + credentials: Validate.boolean().when('origin', { is: 'ignore', then: false }).default(false) }) .allow(false, true) .default(false), - ext: Joi.object({ - onPreAuth: Joi.array().items(internals.event).single(), - onCredentials: Joi.array().items(internals.event).single(), - onPostAuth: Joi.array().items(internals.event).single(), - onPreHandler: Joi.array().items(internals.event).single(), - onPostHandler: Joi.array().items(internals.event).single(), - onPreResponse: Joi.array().items(internals.event).single(), - onPostResponse: Joi.array().items(internals.event).single() + ext: Validate.object({ + onPreAuth: Validate.array().items(internals.event).single(), + onCredentials: Validate.array().items(internals.event).single(), + onPostAuth: Validate.array().items(internals.event).single(), + onPreHandler: Validate.array().items(internals.event).single(), + onPostHandler: Validate.array().items(internals.event).single(), + onPreResponse: Validate.array().items(internals.event).single(), + onPostResponse: Validate.array().items(internals.event).single() }) .default({}), - files: Joi.object({ - relativeTo: Joi.string().pattern(/^([\/\.])|([A-Za-z]:\\)|(\\\\)/).default('.') + files: Validate.object({ + relativeTo: Validate.string().pattern(/^([\/\.])|([A-Za-z]:\\)|(\\\\)/).default('.') }) .default(), - json: Joi.object({ - replacer: Joi.alternatives(Joi.function(), Joi.array()).allow(null).default(null), - space: Joi.number().allow(null).default(null), - suffix: Joi.string().allow(null).default(null), - escape: Joi.boolean().default(false) + json: Validate.object({ + replacer: Validate.alternatives(Validate.function(), Validate.array()).allow(null).default(null), + space: Validate.number().allow(null).default(null), + suffix: Validate.string().allow(null).default(null), + escape: Validate.boolean().default(false) }) .default(), - jsonp: Joi.string(), - log: Joi.object({ - collect: Joi.boolean().default(false) + jsonp: Validate.string(), + log: Validate.object({ + collect: Validate.boolean().default(false) }) .default(), - payload: Joi.object({ - output: Joi.valid('data', 'stream', 'file').default('data'), - parse: Joi.boolean().allow('gunzip').default(true), - multipart: Joi.object({ - output: Joi.valid('data', 'stream', 'file', 'annotated').required() + payload: Validate.object({ + output: Validate.valid('data', 'stream', 'file').default('data'), + parse: Validate.boolean().allow('gunzip').default(true), + multipart: Validate.object({ + output: Validate.valid('data', 'stream', 'file', 'annotated').required() }) .default(false) .allow(true, false), - allow: Joi.array().items(Joi.string()).single(), - override: Joi.string(), - protoAction: Joi.valid('error', 'remove', 'ignore').default('error'), - maxBytes: Joi.number().integer().positive().default(1024 * 1024), - uploads: Joi.string().default(Os.tmpdir()), + allow: Validate.array().items(Validate.string()).single(), + override: Validate.string(), + protoAction: Validate.valid('error', 'remove', 'ignore').default('error'), + maxBytes: Validate.number().integer().positive().default(1024 * 1024), + uploads: Validate.string().default(Os.tmpdir()), failAction: internals.failAction, - timeout: Joi.number().integer().positive().allow(false).default(10 * 1000), - defaultContentType: Joi.string().default('application/json'), - compression: Joi.object() - .pattern(/.+/, Joi.object()) + timeout: Validate.number().integer().positive().allow(false).default(10 * 1000), + defaultContentType: Validate.string().default('application/json'), + compression: Validate.object() + .pattern(/.+/, Validate.object()) .default() }) .default(), - plugins: Joi.object(), - response: Joi.object({ - disconnectStatusCode: Joi.number().integer().min(400).default(499), - emptyStatusCode: Joi.valid(200, 204).default(204), + plugins: Validate.object(), + response: Validate.object({ + disconnectStatusCode: Validate.number().integer().min(400).default(499), + emptyStatusCode: Validate.valid(200, 204).default(204), failAction: internals.failAction, - modify: Joi.boolean(), - options: Joi.object(), - ranges: Joi.boolean().default(true), - sample: Joi.number().min(0).max(100).when('modify', { then: Joi.forbidden() }), - schema: Joi.alternatives(Joi.object(), Joi.array(), Joi.function()).allow(true, false), - status: Joi.object().pattern(/\d\d\d/, Joi.alternatives(Joi.object(), Joi.array(), Joi.function()).allow(true, false)) + modify: Validate.boolean(), + options: Validate.object(), + ranges: Validate.boolean().default(true), + sample: Validate.number().min(0).max(100).when('modify', { then: Validate.forbidden() }), + schema: Validate.alternatives(Validate.object(), Validate.array(), Validate.function()).allow(true, false), + status: Validate.object().pattern(/\d\d\d/, Validate.alternatives(Validate.object(), Validate.array(), Validate.function()).allow(true, false)) }) .default(), - security: Joi.object({ - hsts: Joi.alternatives([ - Joi.object({ - maxAge: Joi.number(), - includeSubdomains: Joi.boolean(), - includeSubDomains: Joi.boolean(), - preload: Joi.boolean() + security: Validate.object({ + hsts: Validate.alternatives([ + Validate.object({ + maxAge: Validate.number(), + includeSubdomains: Validate.boolean(), + includeSubDomains: Validate.boolean(), + preload: Validate.boolean() }), - Joi.boolean(), - Joi.number() + Validate.boolean(), + Validate.number() ]) .default(15768000), - xframe: Joi.alternatives([ - Joi.boolean(), - Joi.valid('sameorigin', 'deny'), - Joi.object({ - rule: Joi.valid('sameorigin', 'deny', 'allow-from'), - source: Joi.string() + xframe: Validate.alternatives([ + Validate.boolean(), + Validate.valid('sameorigin', 'deny'), + Validate.object({ + rule: Validate.valid('sameorigin', 'deny', 'allow-from'), + source: Validate.string() }) ]) .default('deny'), - xss: Joi.boolean().default(true), - noOpen: Joi.boolean().default(true), - noSniff: Joi.boolean().default(true), - referrer: Joi.alternatives([ - Joi.boolean().valid(false), - Joi.valid('', 'no-referrer', 'no-referrer-when-downgrade', + xss: Validate.boolean().default(true), + noOpen: Validate.boolean().default(true), + noSniff: Validate.boolean().default(true), + referrer: Validate.alternatives([ + Validate.boolean().valid(false), + Validate.valid('', 'no-referrer', 'no-referrer-when-downgrade', 'unsafe-url', 'same-origin', 'origin', 'strict-origin', 'origin-when-cross-origin', 'strict-origin-when-cross-origin') ]) @@ -207,223 +207,223 @@ internals.routeBase = Joi.object({ }) .allow(null, false, true) .default(false), - state: Joi.object({ - parse: Joi.boolean().default(true), + state: Validate.object({ + parse: Validate.boolean().default(true), failAction: internals.failAction }) .default(), - timeout: Joi.object({ - socket: Joi.number().integer().positive().allow(false), - server: Joi.number().integer().positive().allow(false).default(false) + timeout: Validate.object({ + socket: Validate.number().integer().positive().allow(false), + server: Validate.number().integer().positive().allow(false).default(false) }) .default(), - validate: Joi.object({ - headers: Joi.alternatives(Joi.object(), Joi.array(), Joi.function()).allow(null, true), - params: Joi.alternatives(Joi.object(), Joi.array(), Joi.function()).allow(null, true), - query: Joi.alternatives(Joi.object(), Joi.array(), Joi.function()).allow(null, false, true), - payload: Joi.alternatives(Joi.object(), Joi.array(), Joi.function()).allow(null, false, true), - state: Joi.alternatives(Joi.object(), Joi.array(), Joi.function()).allow(null, false, true), + validate: Validate.object({ + headers: Validate.alternatives(Validate.object(), Validate.array(), Validate.function()).allow(null, true), + params: Validate.alternatives(Validate.object(), Validate.array(), Validate.function()).allow(null, true), + query: Validate.alternatives(Validate.object(), Validate.array(), Validate.function()).allow(null, false, true), + payload: Validate.alternatives(Validate.object(), Validate.array(), Validate.function()).allow(null, false, true), + state: Validate.alternatives(Validate.object(), Validate.array(), Validate.function()).allow(null, false, true), failAction: internals.failAction, - errorFields: Joi.object(), - options: Joi.object().default(), - validator: Joi.object() + errorFields: Validate.object(), + options: Validate.object().default(), + validator: Validate.object() }) .default() }); -internals.server = Joi.object({ - address: Joi.string().hostname(), - app: Joi.object().allow(null), - autoListen: Joi.boolean(), - cache: Joi.allow(null), // Validated elsewhere - compression: Joi.object({ - minBytes: Joi.number().min(1).integer().default(1024) +internals.server = Validate.object({ + address: Validate.string().hostname(), + app: Validate.object().allow(null), + autoListen: Validate.boolean(), + cache: Validate.allow(null), // Validated elsewhere + compression: Validate.object({ + minBytes: Validate.number().min(1).integer().default(1024) }) .allow(false) .default(), - debug: Joi.object({ - request: Joi.array().items(Joi.string()).single().allow(false).default(['implementation']), - log: Joi.array().items(Joi.string()).single().allow(false) + debug: Validate.object({ + request: Validate.array().items(Validate.string()).single().allow(false).default(['implementation']), + log: Validate.array().items(Validate.string()).single().allow(false) }) .allow(false) .default(), - host: Joi.string().hostname().allow(null), - info: Joi.object({ - remote: Joi.boolean().default(false) + host: Validate.string().hostname().allow(null), + info: Validate.object({ + remote: Validate.boolean().default(false) }) .default({}), - listener: Joi.any(), - load: Joi.object({ - sampleInterval: Joi.number().integer().min(0).default(0) + listener: Validate.any(), + load: Validate.object({ + sampleInterval: Validate.number().integer().min(0).default(0) }) .unknown() .default(), - mime: Joi.object().allow(null).default(null), - operations: Joi.object({ - cleanStop: Joi.boolean().default(true) + mime: Validate.object().allow(null).default(null), + operations: Validate.object({ + cleanStop: Validate.boolean().default(true) }) .default(), - plugins: Joi.object(), - port: Joi.alternatives([ - Joi.number().integer().min(0), // TCP port - Joi.string().pattern(/\//), // Unix domain socket - Joi.string().pattern(/^\\\\\.\\pipe\\/) // Windows named pipe + plugins: Validate.object(), + port: Validate.alternatives([ + Validate.number().integer().min(0), // TCP port + Validate.string().pattern(/\//), // Unix domain socket + Validate.string().pattern(/^\\\\\.\\pipe\\/) // Windows named pipe ]) .allow(null), - query: Joi.object({ - parser: Joi.function() + query: Validate.object({ + parser: Validate.function() }) .default(), - router: Joi.object({ - isCaseSensitive: Joi.boolean().default(true), - stripTrailingSlash: Joi.boolean().default(false) + router: Validate.object({ + isCaseSensitive: Validate.boolean().default(true), + stripTrailingSlash: Validate.boolean().default(false) }) .default(), routes: internals.routeBase.default(), - state: Joi.object(), // Cookie defaults - tls: Joi.alternatives([ - Joi.object().allow(null), - Joi.boolean() + state: Validate.object(), // Cookie defaults + tls: Validate.alternatives([ + Validate.object().allow(null), + Validate.boolean() ]), - uri: Joi.string().pattern(/[^/]$/) + uri: Validate.string().pattern(/[^/]$/) }); -internals.vhost = Joi.alternatives([ - Joi.string().hostname(), - Joi.array().items(Joi.string().hostname()).min(1) +internals.vhost = Validate.alternatives([ + Validate.string().hostname(), + Validate.array().items(Validate.string().hostname()).min(1) ]); -internals.handler = Joi.alternatives([ - Joi.function(), - Joi.object().length(1) +internals.handler = Validate.alternatives([ + Validate.function(), + Validate.object().length(1) ]); -internals.route = Joi.object({ - method: Joi.string().pattern(/^[a-zA-Z0-9!#\$%&'\*\+\-\.^_`\|~]+$/).required(), - path: Joi.string().required(), - rules: Joi.object(), +internals.route = Validate.object({ + method: Validate.string().pattern(/^[a-zA-Z0-9!#\$%&'\*\+\-\.^_`\|~]+$/).required(), + path: Validate.string().required(), + rules: Validate.object(), vhost: internals.vhost, // Validated in route construction - handler: Joi.any(), - options: Joi.any(), - config: Joi.any() // Backwards compatibility + handler: Validate.any(), + options: Validate.any(), + config: Validate.any() // Backwards compatibility }) .without('config', 'options'); internals.pre = [ - Joi.function(), - Joi.object({ - method: Joi.alternatives(Joi.string(), Joi.function()).required(), - assign: Joi.string(), - mode: Joi.valid('serial', 'parallel'), + Validate.function(), + Validate.object({ + method: Validate.alternatives(Validate.string(), Validate.function()).required(), + assign: Validate.string(), + mode: Validate.valid('serial', 'parallel'), failAction: internals.failAction }) ]; internals.routeConfig = internals.routeBase.keys({ - description: Joi.string(), - id: Joi.string(), - isInternal: Joi.boolean(), + description: Validate.string(), + id: Validate.string(), + isInternal: Validate.boolean(), notes: [ - Joi.string(), - Joi.array().items(Joi.string()) + Validate.string(), + Validate.array().items(Validate.string()) ], - pre: Joi.array().items(...internals.pre.concat(Joi.array().items(...internals.pre).min(1))), + pre: Validate.array().items(...internals.pre.concat(Validate.array().items(...internals.pre).min(1))), tags: [ - Joi.string(), - Joi.array().items(Joi.string()) + Validate.string(), + Validate.array().items(Validate.string()) ] }); -internals.cacheConfig = Joi.alternatives([ - Joi.function(), - Joi.object({ - name: Joi.string().invalid('_default'), - shared: Joi.boolean(), +internals.cacheConfig = Validate.alternatives([ + Validate.function(), + Validate.object({ + name: Validate.string().invalid('_default'), + shared: Validate.boolean(), provider: [ - Joi.function(), + Validate.function(), { - constructor: Joi.function().required(), - options: Joi.object({ - partition: Joi.string().default('hapi-cache') + constructor: Validate.function().required(), + options: Validate.object({ + partition: Validate.string().default('hapi-cache') }) .unknown() // Catbox client validates other keys .default({}) } ], - engine: Joi.object() + engine: Validate.object() }) .xor('provider', 'engine') ]); -internals.cache = Joi.array().items(internals.cacheConfig).min(1).single(); +internals.cache = Validate.array().items(internals.cacheConfig).min(1).single(); -internals.cachePolicy = Joi.object({ - cache: Joi.string().allow(null).allow(''), - segment: Joi.string(), - shared: Joi.boolean() +internals.cachePolicy = Validate.object({ + cache: Validate.string().allow(null).allow(''), + segment: Validate.string(), + shared: Validate.boolean() }) .unknown(); // Catbox policy validates other keys -internals.method = Joi.object({ - bind: Joi.object().allow(null), - generateKey: Joi.function(), +internals.method = Validate.object({ + bind: Validate.object().allow(null), + generateKey: Validate.function(), cache: internals.cachePolicy }); -internals.methodObject = Joi.object({ - name: Joi.string().required(), - method: Joi.function().required(), - options: Joi.object() +internals.methodObject = Validate.object({ + name: Validate.string().required(), + method: Validate.function().required(), + options: Validate.object() }); -internals.register = Joi.object({ +internals.register = Validate.object({ once: true, - routes: Joi.object({ - prefix: Joi.string().pattern(/^\/.+/), + routes: Validate.object({ + prefix: Validate.string().pattern(/^\/.+/), vhost: internals.vhost }) .default({}) }); -internals.semver = Joi.string(); +internals.semver = Validate.string(); internals.plugin = internals.register.keys({ - options: Joi.any(), - plugin: Joi.object({ - register: Joi.function().required(), - name: Joi.string().when('pkg.name', { is: Joi.exist(), otherwise: Joi.required() }), - version: Joi.string(), - multiple: Joi.boolean().default(false), + options: Validate.any(), + plugin: Validate.object({ + register: Validate.function().required(), + name: Validate.string().when('pkg.name', { is: Validate.exist(), otherwise: Validate.required() }), + version: Validate.string(), + multiple: Validate.boolean().default(false), dependencies: [ - Joi.array().items(Joi.string()).single(), - Joi.object().pattern(/.+/, internals.semver) + Validate.array().items(Validate.string()).single(), + Validate.object().pattern(/.+/, internals.semver) ], once: true, - requirements: Joi.object({ - hapi: Joi.string(), - node: Joi.string() + requirements: Validate.object({ + hapi: Validate.string(), + node: Validate.string() }) .default(), - pkg: Joi.object({ - name: Joi.string(), - version: Joi.string().default('0.0.0') + pkg: Validate.object({ + name: Validate.string(), + version: Validate.string().default('0.0.0') }) .unknown() .default({}) @@ -434,10 +434,10 @@ internals.plugin = internals.register.keys({ .unknown(); -internals.rules = Joi.object({ - validate: Joi.object({ - schema: Joi.alternatives(Joi.object(), Joi.array()).required(), - options: Joi.object() +internals.rules = Validate.object({ + validate: Validate.object({ + schema: Validate.alternatives(Validate.object(), Validate.array()).required(), + options: Validate.object() .default({ allowUnknown: true }) }) }); diff --git a/lib/route.js b/lib/route.js index 93b155135..a1e840de5 100755 --- a/lib/route.js +++ b/lib/route.js @@ -6,8 +6,8 @@ const Boom = require('@hapi/boom'); const Bounce = require('@hapi/bounce'); const Catbox = require('@hapi/catbox'); const Hoek = require('@hapi/hoek'); -const Joi = require('@hapi/joi'); const Subtext = require('@hapi/subtext'); +const Validate = require('@hapi/validate'); const Auth = require('./auth'); const Config = require('./config'); @@ -192,10 +192,8 @@ exports = module.exports = internals.Route = class { this._assert(!validation.params || this.params.length, 'Cannot set path parameters validations without path parameters'); - const validator = this._validator(); - for (const type of ['headers', 'params', 'query', 'payload', 'state']) { - validation[type] = Validation.compile(validation[type], validator); + validation[type] = Validation.compile(validation[type], this.settings.validate.validator, this.realm, this._core); } if (this.settings.response.schema !== undefined || @@ -213,9 +211,9 @@ exports = module.exports = internals.Route = class { this.settings.response._validate = false; } else { - this.settings.response.schema = Validation.compile(rule, validator); + this.settings.response.schema = Validation.compile(rule, this.settings.validate.validator, this.realm, this._core); for (const code of statuses) { - this.settings.response.status[code] = Validation.compile(this.settings.response.status[code], validator); + this.settings.response.status[code] = Validation.compile(this.settings.response.status[code], this.settings.validate.validator, this.realm, this._core); } } } @@ -375,24 +373,6 @@ exports = module.exports = internals.Route = class { stackStartFunction: this._assert }); } - - _validator() { - - if (this.settings.validate.validator) { - return this.settings.validate.validator; - } - - let realm = this.realm; - while (realm) { - if (realm.validator) { - return realm.validator; - } - - realm = realm.parent; - } - - return this._core.validator; - } }; @@ -528,7 +508,7 @@ internals.rules = function (rules, info, server) { let realm = server.realm; while (realm) { if (realm._rules) { - const source = !realm._rules.settings.validate ? rules : Joi.attempt(rules, realm._rules.settings.validate.schema, realm._rules.settings.validate.options); + const source = !realm._rules.settings.validate ? rules : Validate.attempt(rules, realm._rules.settings.validate.schema, realm._rules.settings.validate.options); const config = realm._rules.processor(source, info); if (config) { configs.unshift(config); diff --git a/lib/server.js b/lib/server.js index b66053abc..b9a910181 100755 --- a/lib/server.js +++ b/lib/server.js @@ -1,7 +1,6 @@ 'use strict'; const Hoek = require('@hapi/hoek'); -const Joi = require('@hapi/joi'); const Shot = require('@hapi/shot'); const Somever = require('@hapi/somever'); const Teamwork = require('@hapi/teamwork'); @@ -534,7 +533,7 @@ internals.Server = class { const settings = Config.apply('rules', options); if (settings.validate) { const schema = settings.validate.schema; - settings.validate.schema = Joi.compile(schema); + settings.validate.schema = Validation.compile(schema, null, this.realm, this._core); } this.realm._rules = { processor, settings }; diff --git a/lib/validation.js b/lib/validation.js index 05f605144..2424cb226 100755 --- a/lib/validation.js +++ b/lib/validation.js @@ -2,7 +2,7 @@ const Boom = require('@hapi/boom'); const Hoek = require('@hapi/hoek'); -const Joi = require('@hapi/joi'); +const Validate = require('@hapi/validate'); const internals = {}; @@ -17,12 +17,14 @@ exports.validator = function (validator) { }; -exports.compile = function (rule, validator) { +exports.compile = function (rule, validator, realm, core) { + + validator = validator || internals.validator(realm, core); // false - nothing allowed if (rule === false) { - return Joi.object({}).allow(null); + return Validate.object({}).allow(null); } // Custom function @@ -50,6 +52,20 @@ exports.compile = function (rule, validator) { }; +internals.validator = function (realm, core) { + + while (realm) { + if (realm.validator) { + return realm.validator; + } + + realm = realm.parent; + } + + return core.validator; +}; + + exports.headers = function (request) { return internals.input('headers', request); diff --git a/package.json b/package.json index d2537bc2a..f09370b16 100755 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ "@hapi/catbox-memory": "5.x.x", "@hapi/heavy": "7.x.x", "@hapi/hoek": "9.x.x", - "@hapi/joi": "17.x.x", "@hapi/mimos": "5.x.x", "@hapi/podium": "4.x.x", "@hapi/shot": "5.x.x", @@ -35,7 +34,8 @@ "@hapi/statehood": "^7.0.2", "@hapi/subtext": "^7.0.3", "@hapi/teamwork": "4.x.x", - "@hapi/topo": "5.x.x" + "@hapi/topo": "5.x.x", + "@hapi/validate": "^1.1.0" }, "devDependencies": { "@hapi/code": "8.x.x", @@ -44,7 +44,8 @@ "@hapi/lab": "22.x.x", "@hapi/wreck": "17.x.x", "@hapi/vision": "6.x.x", - "handlebars": "4.x.x" + "handlebars": "4.x.x", + "joi": "17.x.x" }, "scripts": { "test": "lab -a @hapi/code -t 100 -L -m 5000", diff --git a/test/request.js b/test/request.js index 019435925..349ee03ce 100755 --- a/test/request.js +++ b/test/request.js @@ -9,7 +9,7 @@ const Boom = require('@hapi/boom'); const Code = require('@hapi/code'); const Hapi = require('..'); const Hoek = require('@hapi/hoek'); -const Joi = require('@hapi/joi'); +const Joi = require('joi'); const Lab = require('@hapi/lab'); const Teamwork = require('@hapi/teamwork'); const Wreck = require('@hapi/wreck'); diff --git a/test/route.js b/test/route.js index d40620316..02ad10504 100755 --- a/test/route.js +++ b/test/route.js @@ -5,7 +5,7 @@ const Path = require('path'); const Code = require('@hapi/code'); const Hapi = require('..'); const Inert = require('@hapi/inert'); -const Joi = require('@hapi/joi'); +const Joi = require('joi'); const Lab = require('@hapi/lab'); const Subtext = require('@hapi/subtext'); diff --git a/test/validation.js b/test/validation.js index 97ff28bf1..4ac18239d 100755 --- a/test/validation.js +++ b/test/validation.js @@ -4,7 +4,7 @@ const Boom = require('@hapi/boom'); const Code = require('@hapi/code'); const Hapi = require('..'); const Inert = require('@hapi/inert'); -const Joi = require('@hapi/joi'); +const Joi = require('joi'); const JoiLegacy = require('@hapi/joi-legacy-test'); const Lab = require('@hapi/lab');