sleepy-serv 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/middleware.js CHANGED
@@ -1,37 +1,33 @@
1
1
  import Ajv from 'ajv'
2
2
  import addFormats from 'ajv-formats'
3
3
 
4
+ import { formatError } from './utils'
5
+
4
6
  import {
5
7
  BadRequestError,
6
8
  UnsupportedMediaTypeError,
7
9
  UnprocessableContentError,
8
10
  } from './errors'
9
11
 
10
- const SCHEMA_EMPTY = {
11
- type: 'object',
12
- properties: {},
13
- }
14
-
15
- let _customFormats = {}
12
+ let _schemasCompiled = false
13
+ let _customFormats = null
16
14
 
17
- function formatError (prefix, input) {
18
- const fixedPath = input.instancePath || '/'
19
- const suffixPath = fixedPath.replace(/\//g, '.').replace('.', '')
15
+ async function parseBody (req) {
16
+ try {
17
+ const result = await req.json()
20
18
 
21
- return {
22
- path: [prefix, suffixPath].filter(item => item).join('.'),
23
- message: input.message,
19
+ return result
20
+ } catch {
21
+ throw new BadRequestError('Invalid JSON')
24
22
  }
25
23
  }
26
24
 
27
25
  function buildFormatterSchema (schema) {
28
- if (!schema) {
29
- return SCHEMA_EMPTY
30
- }
31
-
32
26
  const properties = Object
33
27
  .entries(schema)
34
- .map(([key, config]) => [key, {
28
+ .map(([key, config]) => [
29
+ key,
30
+ {
35
31
  type: 'string',
36
32
  [config.type]: config.value,
37
33
  },
@@ -47,70 +43,96 @@ function buildFormatterSchema (schema) {
47
43
  }
48
44
  }
49
45
 
50
- export async function parseJson (req, res, next) {
51
- const contentType = req.headers.get('content-type')
46
+ function compileSchemas (schemas) {
47
+ return Object
48
+ .entries(schemas)
49
+ .reduce((accum, [key, schema]) => {
50
+ const formattedSchema = key !== 'body'
51
+ ? buildFormatterSchema(schema)
52
+ : schema
53
+
54
+ return [
55
+ ...accum,
56
+ [key, formattedSchema],
57
+ ]
58
+ }, [])
59
+ .map(([key, schema]) => {
60
+ const ajv = new Ajv({
61
+ allErrors: true,
62
+ removeAdditional: 'all',
63
+ })
64
+
65
+ addFormats(ajv)
66
+
67
+ Object
68
+ .entries(_customFormats ?? [])
69
+ .forEach(([k, v]) => ajv.addFormat(k, v))
70
+
71
+ const validator = ajv.compile(schema)
72
+
73
+ return [key, validator]
74
+ })
75
+ }
52
76
 
53
- if (!contentType) {
54
- return next()
55
- }
77
+ export function parseJsonBody () {
78
+ return async (req, res, next) => {
79
+ const contentType = req.headers.get('content-type')
56
80
 
57
- if (contentType !== 'application/json') {
58
- throw new UnsupportedMediaTypeError('content-type')
59
- }
81
+ if (!contentType) {
82
+ return next(res)
83
+ }
60
84
 
61
- try {
62
- res.body = await req.json()
63
- } catch (err) {
64
- console.error(err)
85
+ if (!contentType.startsWith('application/json')) {
86
+ throw new UnsupportedMediaTypeError('content-type')
87
+ }
65
88
 
66
- throw new BadRequestError('Body is invalid JSON')
67
- }
89
+ const body = await parseBody(req)
68
90
 
69
- return next()
91
+ return next(body)
92
+ }
70
93
  }
71
94
 
72
95
  export function setValidationFormats (formats) {
96
+ if (_customFormats) {
97
+ console.warn('setValidationFormats() - already initialized')
98
+ }
99
+
100
+ if (_schemasCompiled) {
101
+ console.warn('setValidationFormats() - called after compilation')
102
+ }
103
+
73
104
  _customFormats = formats
74
105
  }
75
106
 
76
- export function validateSchema (schemas) {
77
- return function (req, res, next) {
78
- const formattedSchemas = {
79
- headers: buildFormatterSchema(schemas.headers),
80
- params: buildFormatterSchema(schemas.params),
81
- query: buildFormatterSchema(schemas.query),
82
- body: schemas.body || SCHEMA_EMPTY,
83
- }
107
+ /* only for testing purposes */
84
108
 
85
- const ajv = new Ajv({
86
- allErrors: true,
87
- removeAdditional: 'all',
88
- })
109
+ export function resetValidationFormatsState () {
110
+ _customFormats = null
111
+ _schemasCompiled = false
112
+ }
89
113
 
90
- addFormats(ajv)
114
+ export function validateSchemas (schemas) {
115
+ const entries = compileSchemas(schemas)
91
116
 
92
- Object
93
- .entries(_customFormats)
94
- .forEach(([k, v]) => ajv.addFormat(k, v))
117
+ _schemasCompiled = true
95
118
 
96
- const errors = Object
97
- .entries(formattedSchemas)
98
- .reduce((accum, [key, schema]) => {
99
- const data = key === 'body' ? res[key] : req[key]
100
- const valid = schema ? ajv.validate(schema, data) : true
119
+ return (req, res, next) => {
120
+ const errors = entries.reduce((accum, [key, validator]) => {
121
+ const data = key === 'body' ? res : req[key]
122
+ const valid = validator(data)
101
123
 
102
- return !valid
103
- ? [
104
- ...accum,
105
- ...ajv.errors.map(item => formatError(key, item)),
106
- ]
107
- : accum
108
- }, [])
124
+ return !valid
125
+ ? [
126
+ ...accum,
127
+ ...validator.errors.map(item => formatError(key, item)),
128
+ ]
129
+ : accum
130
+ }, [])
109
131
 
110
132
  if (errors.length > 0) {
111
133
  throw new UnprocessableContentError(errors)
112
134
  }
113
135
 
114
- return next()
136
+ return next(res)
115
137
  }
116
138
  }