-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
120 lines (99 loc) · 3.29 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
'use strict'
const errors = require('restify-errors')
const joi = require('joi')
const middlewareDefaults = {
joiOptions: {
convert: true,
allowUnknown: false,
abortEarly: false
},
errorTransformer: (validationInput, joiError) => {
const retError = new errors.BadRequestError()
retError.body.message = joiError.message
retError.body.data = joiError.details
return retError
},
errorResponder: (transformedErr, req, res, next) => next(transformedErr),
keysToValidate: ['params', 'body', 'query', 'user', 'headers', 'trailers', 'files']
}
const optionsSchema = {
joiOptions: joi.object(),
errorTransformer: joi.func(),
errorResponder: joi.func(),
keysToValidate: joi.array().items(joi.string())
}
const checkOptions = (defaults, input) => {
const { joiOptions, errorTransformer, errorResponder, keysToValidate } = defaults
const schema = {
joiOptions: optionsSchema.joiOptions.default(joiOptions),
errorTransformer: optionsSchema.errorTransformer.default(errorTransformer),
errorResponder: optionsSchema.errorResponder.default(errorResponder),
keysToValidate: optionsSchema.keysToValidate.default(keysToValidate)
}
const { value, error } = joi.validate(input, schema, middlewareDefaults.joiOptions)
if (error) {
error.message = `Error: restify-joi-middleware, bad configuration found: ${error.message}`
throw error
}
return value
}
// should this use a lru cache?
const routeOptionsCache = {}
const getRouteOptions = (middlewareOptions, route) => {
const { name } = route
if (routeOptionsCache[name]) {
return routeOptionsCache[name]
}
// restify v7 uses req.route.spec
const routeDefinition = route.spec || route
const { options = middlewareOptions } = routeDefinition.validation || {}
if (!routeOptionsCache[name]) {
// validate route overrides, default to middleware options
routeOptionsCache[name] = checkOptions(middlewareOptions, options)
}
return routeOptionsCache[name]
}
const middleware = (mOptions = {}) => {
const middlewareOptions = checkOptions(middlewareDefaults, mOptions)
return (req, res, next) => {
// restify v7 uses req.route.spec
const routeDefinition = req.route.spec || req.route
const { schema } = routeDefinition.validation || {}
// no validation found on route
if (!schema) {
return setImmediate(next)
}
const {
joiOptions, errorTransformer, errorResponder, keysToValidate
} = getRouteOptions(middlewareOptions, req.route)
const toValidate = keysToValidate.reduce((accum, key) => {
// only include keys present in our validation
// validation can be a Joi schema, so the exclusion logic is a bit different
if (schema.isJoi) {
if (!joi.reach(schema, key)) {
return accum
}
} else if (!schema[key]) {
return accum
}
accum[key] = req[key]
return accum
}, {})
const { error, value } = joi.validate(toValidate, schema, joiOptions)
if (error) {
return errorResponder(
errorTransformer(toValidate, error),
req, res, next
)
}
// write defaults back to request
keysToValidate.forEach(key => {
if (!value[key] || !req[key]) {
return
}
req[key] = value[key]
})
next()
}
}
module.exports = middleware