-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
validation.js
127 lines (112 loc) · 4.26 KB
/
validation.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
121
122
123
124
125
126
127
'use strict'
const {
kSchemaHeaders: headersSchema,
kSchemaParams: paramsSchema,
kSchemaQuerystring: querystringSchema,
kSchemaBody: bodySchema,
kSchemaResponse: responseSchema
} = require('./symbols')
const scChecker = /^[1-5]{1}[0-9]{2}$|^[1-5]xx$|^default$/
function compileSchemasForSerialization (context, compile) {
if (!context.schema || !context.schema.response) {
return
}
const { method, url } = context.config || {}
context[responseSchema] = Object.keys(context.schema.response)
.reduce(function (acc, statusCode) {
const schema = context.schema.response[statusCode]
statusCode = statusCode.toLowerCase()
if (!scChecker.exec(statusCode)) {
throw new Error('response schemas should be nested under a valid status code, e.g { 2xx: { type: "object" } }')
}
acc[statusCode] = compile({
schema,
url,
method,
httpStatus: statusCode
})
return acc
}, {})
}
function compileSchemasForValidation (context, compile, isCustom) {
const { schema } = context
if (!schema) {
return
}
const { method, url } = context.config || {}
const headers = schema.headers
// the or part is used for backward compatibility
if (headers && (isCustom || Object.getPrototypeOf(headers) !== Object.prototype)) {
// do not mess with schema when custom validator applied, e.g. Joi, Typebox
context[headersSchema] = compile({ schema: headers, method, url, httpPart: 'headers' })
} else if (headers) {
// The header keys are case insensitive
// https://tools.ietf.org/html/rfc2616#section-4.2
const headersSchemaLowerCase = {}
Object.keys(headers).forEach(k => { headersSchemaLowerCase[k] = headers[k] })
if (headersSchemaLowerCase.required instanceof Array) {
headersSchemaLowerCase.required = headersSchemaLowerCase.required.map(h => h.toLowerCase())
}
if (headers.properties) {
headersSchemaLowerCase.properties = {}
Object.keys(headers.properties).forEach(k => {
headersSchemaLowerCase.properties[k.toLowerCase()] = headers.properties[k]
})
}
context[headersSchema] = compile({ schema: headersSchemaLowerCase, method, url, httpPart: 'headers' })
}
if (schema.body) {
context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
}
if (schema.querystring) {
context[querystringSchema] = compile({ schema: schema.querystring, method, url, httpPart: 'querystring' })
}
if (schema.params) {
context[paramsSchema] = compile({ schema: schema.params, method, url, httpPart: 'params' })
}
}
function validateParam (validatorFunction, request, paramName) {
const isUndefined = request[paramName] === undefined
const ret = validatorFunction && validatorFunction(isUndefined ? null : request[paramName])
if (ret === false) return validatorFunction.errors
if (ret && ret.error) return ret.error
if (ret && ret.value) request[paramName] = ret.value
return false
}
function validate (context, request) {
const params = validateParam(context[paramsSchema], request, 'params')
if (params) {
return wrapValidationError(params, 'params', context.schemaErrorFormatter)
}
const body = validateParam(context[bodySchema], request, 'body')
if (body) {
return wrapValidationError(body, 'body', context.schemaErrorFormatter)
}
const query = validateParam(context[querystringSchema], request, 'query')
if (query) {
return wrapValidationError(query, 'querystring', context.schemaErrorFormatter)
}
const headers = validateParam(context[headersSchema], request, 'headers')
if (headers) {
return wrapValidationError(headers, 'headers', context.schemaErrorFormatter)
}
return null
}
function wrapValidationError (result, dataVar, schemaErrorFormatter) {
if (result instanceof Error) {
result.statusCode = result.statusCode || 400
result.validationContext = result.validationContext || dataVar
return result
}
const error = schemaErrorFormatter(result, dataVar)
error.statusCode = error.statusCode || 400
error.validation = result
error.validationContext = dataVar
return error
}
module.exports = {
symbols: { bodySchema, querystringSchema, responseSchema, paramsSchema, headersSchema },
compileSchemasForValidation,
compileSchemasForSerialization,
validate
}