fastify 3.11.0 → 3.14.1
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/README.md +15 -10
- package/SECURITY.md +2 -2
- package/build/build-validation.js +25 -1
- package/docs/Benchmarking.md +3 -3
- package/docs/ContentTypeParser.md +21 -0
- package/docs/Decorators.md +1 -1
- package/docs/Ecosystem.md +38 -26
- package/docs/Encapsulation.md +4 -1
- package/docs/Errors.md +41 -41
- package/docs/Getting-Started.md +2 -2
- package/docs/HTTP2.md +1 -1
- package/docs/Hooks.md +35 -4
- package/docs/LTS.md +1 -1
- package/docs/Lifecycle.md +1 -1
- package/docs/Logging.md +4 -4
- package/docs/Middleware.md +3 -3
- package/docs/Migration-Guide-V3.md +3 -3
- package/docs/Plugins-Guide.md +15 -15
- package/docs/Plugins.md +7 -7
- package/docs/Recommendations.md +3 -4
- package/docs/Reply.md +4 -4
- package/docs/Request.md +1 -1
- package/docs/Routes.md +65 -10
- package/docs/Server.md +80 -14
- package/docs/Serverless.md +43 -64
- package/docs/Style-Guide.md +24 -19
- package/docs/Testing.md +5 -5
- package/docs/TypeScript.md +159 -17
- package/docs/Validation-and-Serialization.md +62 -6
- package/docs/Write-Plugin.md +3 -3
- package/fastify.d.ts +14 -3
- package/fastify.js +52 -18
- package/lib/configValidator.js +288 -53
- package/lib/contentTypeParser.js +28 -7
- package/lib/errors.js +1 -1
- package/lib/pluginOverride.js +5 -5
- package/lib/reply.js +35 -28
- package/lib/reqIdGenFactory.js +2 -1
- package/lib/request.js +1 -1
- package/lib/route.js +20 -27
- package/lib/schema-compilers.js +5 -3
- package/lib/schema-controller.js +106 -0
- package/lib/schemas.js +13 -23
- package/lib/symbols.js +1 -3
- package/lib/warnings.js +4 -0
- package/package.json +22 -17
- package/test/constrained-routes.test.js +184 -0
- package/test/content-parser.test.js +179 -7
- package/test/context-config.test.js +52 -0
- package/test/custom-parser.test.js +262 -2
- package/test/hooks-async.test.js +46 -0
- package/test/hooks.test.js +47 -0
- package/test/internals/initialConfig.test.js +30 -5
- package/test/internals/reply.test.js +2 -2
- package/test/internals/request.test.js +3 -9
- package/test/pretty-print.test.js +28 -0
- package/test/route.test.js +0 -2
- package/test/schema-feature.test.js +134 -4
- package/test/schema-serialization.test.js +42 -0
- package/test/schema-special-usage.test.js +234 -0
- package/test/schema-validation.test.js +1 -1
- package/test/stream.test.js +90 -0
- package/test/throw.test.js +1 -1
- package/test/types/content-type-parser.test-d.ts +8 -2
- package/test/types/fastify.test-d.ts +27 -0
- package/test/types/instance.test-d.ts +43 -1
- package/test/types/logger.test-d.ts +8 -3
- package/test/types/reply.test-d.ts +2 -1
- package/test/types/schema.test-d.ts +52 -1
- package/test/versioned-routes.test.js +99 -18
- package/types/content-type-parser.d.ts +10 -4
- package/types/instance.d.ts +57 -7
- package/types/logger.d.ts +1 -1
- package/types/reply.d.ts +2 -1
- package/types/route.d.ts +15 -11
- package/types/schema.d.ts +5 -4
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
4
|
const Fastify = require('..')
|
|
5
5
|
const fp = require('fastify-plugin')
|
|
6
|
-
const {
|
|
6
|
+
const { kSchemaController } = require('../lib/symbols.js')
|
|
7
7
|
|
|
8
8
|
const echoParams = (req, reply) => { reply.send(req.params) }
|
|
9
9
|
const echoBody = (req, reply) => { reply.send(req.body) }
|
|
@@ -38,7 +38,7 @@ test('The schemas should be added to an internal storage', t => {
|
|
|
38
38
|
const fastify = Fastify()
|
|
39
39
|
const schema = { $id: 'id', my: 'schema' }
|
|
40
40
|
fastify.addSchema(schema)
|
|
41
|
-
t.deepEqual(fastify[
|
|
41
|
+
t.deepEqual(fastify[kSchemaController].schemaBucket.store, { id: schema })
|
|
42
42
|
})
|
|
43
43
|
|
|
44
44
|
test('The schemas should be accessible via getSchemas', t => {
|
|
@@ -121,8 +121,8 @@ test('Get compilers is empty when settle on routes', t => {
|
|
|
121
121
|
url: '/'
|
|
122
122
|
}, (err, res) => {
|
|
123
123
|
t.error(err)
|
|
124
|
-
t.equal(fastify.validatorCompiler,
|
|
125
|
-
t.equal(fastify.serializerCompiler,
|
|
124
|
+
t.equal(fastify.validatorCompiler, undefined)
|
|
125
|
+
t.equal(fastify.serializerCompiler, undefined)
|
|
126
126
|
})
|
|
127
127
|
})
|
|
128
128
|
|
|
@@ -1159,3 +1159,133 @@ test('The schema compiler recreate itself if needed', t => {
|
|
|
1159
1159
|
|
|
1160
1160
|
fastify.ready(err => { t.error(err) })
|
|
1161
1161
|
})
|
|
1162
|
+
|
|
1163
|
+
test('Schema controller setter', t => {
|
|
1164
|
+
t.plan(2)
|
|
1165
|
+
Fastify({ schemaController: {} })
|
|
1166
|
+
t.pass('allow empty object')
|
|
1167
|
+
|
|
1168
|
+
try {
|
|
1169
|
+
Fastify({ schemaController: { bucket: {} } })
|
|
1170
|
+
t.fail('the bucket option must be a function')
|
|
1171
|
+
} catch (err) {
|
|
1172
|
+
t.is(err.message, "schemaController.bucket option should be a function, instead got 'object'")
|
|
1173
|
+
}
|
|
1174
|
+
})
|
|
1175
|
+
|
|
1176
|
+
test('Schema controller bucket', t => {
|
|
1177
|
+
t.plan(10)
|
|
1178
|
+
|
|
1179
|
+
let added = 0
|
|
1180
|
+
let builtBucket = 0
|
|
1181
|
+
|
|
1182
|
+
const initStoreQueue = []
|
|
1183
|
+
|
|
1184
|
+
function factoryBucket (storeInit) {
|
|
1185
|
+
builtBucket++
|
|
1186
|
+
t.deepEqual(initStoreQueue.pop(), storeInit)
|
|
1187
|
+
const store = new Map(storeInit)
|
|
1188
|
+
return {
|
|
1189
|
+
add (schema) {
|
|
1190
|
+
added++
|
|
1191
|
+
store.set(schema.$id, schema)
|
|
1192
|
+
},
|
|
1193
|
+
getSchema (id) {
|
|
1194
|
+
return store.get(id)
|
|
1195
|
+
},
|
|
1196
|
+
getSchemas () {
|
|
1197
|
+
// what is returned by this function, will be the `storeInit` parameter
|
|
1198
|
+
initStoreQueue.push(store)
|
|
1199
|
+
return store
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
const fastify = Fastify({
|
|
1205
|
+
schemaController: {
|
|
1206
|
+
bucket: factoryBucket
|
|
1207
|
+
}
|
|
1208
|
+
})
|
|
1209
|
+
|
|
1210
|
+
fastify.register(async (instance) => {
|
|
1211
|
+
instance.addSchema({ $id: 'b', type: 'string' })
|
|
1212
|
+
instance.addHook('onReady', function (done) {
|
|
1213
|
+
t.equals(instance.getSchemas().size, 2)
|
|
1214
|
+
done()
|
|
1215
|
+
})
|
|
1216
|
+
instance.register(async (subinstance) => {
|
|
1217
|
+
subinstance.addSchema({ $id: 'c', type: 'string' })
|
|
1218
|
+
subinstance.addHook('onReady', function (done) {
|
|
1219
|
+
t.equals(subinstance.getSchemas().size, 3)
|
|
1220
|
+
done()
|
|
1221
|
+
})
|
|
1222
|
+
})
|
|
1223
|
+
})
|
|
1224
|
+
|
|
1225
|
+
fastify.register(async (instance) => {
|
|
1226
|
+
instance.addHook('onReady', function (done) {
|
|
1227
|
+
t.equals(instance.getSchemas().size, 1)
|
|
1228
|
+
done()
|
|
1229
|
+
})
|
|
1230
|
+
})
|
|
1231
|
+
|
|
1232
|
+
fastify.addSchema({ $id: 'a', type: 'string' })
|
|
1233
|
+
|
|
1234
|
+
fastify.ready(err => {
|
|
1235
|
+
t.error(err)
|
|
1236
|
+
t.equals(added, 3, 'three schema added')
|
|
1237
|
+
t.equals(builtBucket, 4, 'one bucket built for every register call + 1 for the root instance')
|
|
1238
|
+
})
|
|
1239
|
+
})
|
|
1240
|
+
|
|
1241
|
+
test('setSchemaController per instance', t => {
|
|
1242
|
+
t.plan(7)
|
|
1243
|
+
const fastify = Fastify({})
|
|
1244
|
+
|
|
1245
|
+
fastify.register(async (instance1) => {
|
|
1246
|
+
instance1.setSchemaController({
|
|
1247
|
+
bucket: function factoryBucket (storeInit) {
|
|
1248
|
+
t.pass('instance1 has created the bucket')
|
|
1249
|
+
return {
|
|
1250
|
+
add (schema) { t.fail('add is not called') },
|
|
1251
|
+
getSchema (id) { t.fail('getSchema is not called') },
|
|
1252
|
+
getSchemas () { t.fail('getSchemas is not called') }
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
})
|
|
1256
|
+
})
|
|
1257
|
+
|
|
1258
|
+
fastify.register(async (instance2) => {
|
|
1259
|
+
const bSchema = { $id: 'b', type: 'string' }
|
|
1260
|
+
|
|
1261
|
+
instance2.setSchemaController({
|
|
1262
|
+
bucket: function factoryBucket (storeInit) {
|
|
1263
|
+
t.pass('instance2 has created the bucket')
|
|
1264
|
+
const map = {}
|
|
1265
|
+
return {
|
|
1266
|
+
add (schema) {
|
|
1267
|
+
t.equals(schema.$id, bSchema.$id, 'add is called')
|
|
1268
|
+
map[schema.$id] = schema
|
|
1269
|
+
},
|
|
1270
|
+
getSchema (id) {
|
|
1271
|
+
t.pass('getSchema is called')
|
|
1272
|
+
return map[id]
|
|
1273
|
+
},
|
|
1274
|
+
getSchemas () {
|
|
1275
|
+
t.pass('getSchemas is called')
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
})
|
|
1280
|
+
|
|
1281
|
+
instance2.addSchema(bSchema)
|
|
1282
|
+
|
|
1283
|
+
instance2.addHook('onReady', function (done) {
|
|
1284
|
+
instance2.getSchemas()
|
|
1285
|
+
t.deepEquals(instance2.getSchema('b'), bSchema, 'the schema are loaded')
|
|
1286
|
+
done()
|
|
1287
|
+
})
|
|
1288
|
+
})
|
|
1289
|
+
|
|
1290
|
+
fastify.ready(err => { t.error(err) })
|
|
1291
|
+
})
|
|
@@ -498,3 +498,45 @@ test('The schema changes the default error handler output', async t => {
|
|
|
498
498
|
t.equals(res.statusCode, 500)
|
|
499
499
|
t.deepEquals(res.json(), { error: 'Internal Server Error', message: '500 message', customId: 42 })
|
|
500
500
|
})
|
|
501
|
+
|
|
502
|
+
test('do not crash if status code serializer errors', async t => {
|
|
503
|
+
const fastify = Fastify()
|
|
504
|
+
|
|
505
|
+
const requiresFoo = {
|
|
506
|
+
properties: { foo: { type: 'string' } },
|
|
507
|
+
required: ['foo']
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const someUserErrorType2 = {
|
|
511
|
+
properties: {
|
|
512
|
+
code: { type: 'number' }
|
|
513
|
+
},
|
|
514
|
+
required: ['code']
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
fastify.get(
|
|
518
|
+
'/',
|
|
519
|
+
{
|
|
520
|
+
schema: {
|
|
521
|
+
query: requiresFoo,
|
|
522
|
+
response: { 400: someUserErrorType2 }
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
(request, reply) => {
|
|
526
|
+
t.fail('handler, should not be called')
|
|
527
|
+
}
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
const res = await fastify.inject({
|
|
531
|
+
path: '/',
|
|
532
|
+
query: {
|
|
533
|
+
notfoo: true
|
|
534
|
+
}
|
|
535
|
+
})
|
|
536
|
+
t.equals(res.statusCode, 500)
|
|
537
|
+
t.deepEquals(res.json(), {
|
|
538
|
+
statusCode: 500,
|
|
539
|
+
error: 'Internal Server Error',
|
|
540
|
+
message: '"code" is required!'
|
|
541
|
+
})
|
|
542
|
+
})
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
|
+
const AJV = require('ajv')
|
|
5
|
+
const S = require('fluent-json-schema')
|
|
4
6
|
const Fastify = require('..')
|
|
5
7
|
const ajvMergePatch = require('ajv-merge-patch')
|
|
6
8
|
|
|
@@ -221,3 +223,235 @@ test('Should handle $patch keywords in body', t => {
|
|
|
221
223
|
})
|
|
222
224
|
})
|
|
223
225
|
})
|
|
226
|
+
|
|
227
|
+
test("serializer read validator's schemas", t => {
|
|
228
|
+
t.plan(4)
|
|
229
|
+
const ajvInstance = new AJV()
|
|
230
|
+
|
|
231
|
+
const baseSchema = {
|
|
232
|
+
$id: 'http://example.com/schemas/base',
|
|
233
|
+
definitions: {
|
|
234
|
+
hello: { type: 'string' }
|
|
235
|
+
},
|
|
236
|
+
type: 'object',
|
|
237
|
+
properties: {
|
|
238
|
+
hello: { $ref: '#/definitions/hello' }
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const refSchema = {
|
|
243
|
+
$id: 'http://example.com/schemas/ref',
|
|
244
|
+
type: 'object',
|
|
245
|
+
properties: {
|
|
246
|
+
hello: { $ref: 'http://example.com/schemas/base#/definitions/hello' }
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
ajvInstance.addSchema(baseSchema)
|
|
251
|
+
ajvInstance.addSchema(refSchema)
|
|
252
|
+
|
|
253
|
+
const fastify = Fastify({
|
|
254
|
+
schemaController: {
|
|
255
|
+
bucket: function factory (storeInit) {
|
|
256
|
+
t.notOk(storeInit, 'is is always empty because fastify.addSchema is not called')
|
|
257
|
+
return {
|
|
258
|
+
getSchemas () {
|
|
259
|
+
return {
|
|
260
|
+
[baseSchema.$id]: ajvInstance.getSchema(baseSchema.$id).schema,
|
|
261
|
+
[refSchema.$id]: ajvInstance.getSchema(refSchema.$id).schema
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
fastify.setValidatorCompiler(function ({ schema }) {
|
|
270
|
+
return ajvInstance.compile(schema)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
fastify.get('/', {
|
|
274
|
+
schema: {
|
|
275
|
+
response: {
|
|
276
|
+
'2xx': ajvInstance.getSchema('http://example.com/schemas/ref').schema
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
handler (req, res) { res.send({ hello: 'world', evict: 'this' }) }
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
fastify.inject('/', (err, res) => {
|
|
283
|
+
t.error(err)
|
|
284
|
+
t.equals(res.statusCode, 200)
|
|
285
|
+
t.deepEquals(res.json(), { hello: 'world' })
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
test('setSchemaController in a plugin', t => {
|
|
290
|
+
t.plan(5)
|
|
291
|
+
const baseSchema = {
|
|
292
|
+
$id: 'urn:schema:base',
|
|
293
|
+
definitions: {
|
|
294
|
+
hello: { type: 'string' }
|
|
295
|
+
},
|
|
296
|
+
type: 'object',
|
|
297
|
+
properties: {
|
|
298
|
+
hello: { $ref: '#/definitions/hello' }
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const refSchema = {
|
|
303
|
+
$id: 'urn:schema:ref',
|
|
304
|
+
type: 'object',
|
|
305
|
+
properties: {
|
|
306
|
+
hello: { $ref: 'urn:schema:base#/definitions/hello' }
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const ajvInstance = new AJV()
|
|
311
|
+
ajvInstance.addSchema(baseSchema)
|
|
312
|
+
ajvInstance.addSchema(refSchema)
|
|
313
|
+
|
|
314
|
+
const fastify = Fastify()
|
|
315
|
+
fastify.register(schemaPlugin)
|
|
316
|
+
fastify.get('/', {
|
|
317
|
+
schema: {
|
|
318
|
+
query: ajvInstance.getSchema('urn:schema:ref').schema,
|
|
319
|
+
response: {
|
|
320
|
+
'2xx': ajvInstance.getSchema('urn:schema:ref').schema
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
handler (req, res) {
|
|
324
|
+
res.send({ hello: 'world', evict: 'this' })
|
|
325
|
+
}
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
fastify.inject('/', (err, res) => {
|
|
329
|
+
t.error(err)
|
|
330
|
+
t.equals(res.statusCode, 200)
|
|
331
|
+
t.deepEquals(res.json(), { hello: 'world' })
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
async function schemaPlugin (server) {
|
|
335
|
+
server.setSchemaController({
|
|
336
|
+
bucket () {
|
|
337
|
+
t.pass('the bucket is created')
|
|
338
|
+
return {
|
|
339
|
+
addSchema (source) {
|
|
340
|
+
ajvInstance.addSchema(source)
|
|
341
|
+
},
|
|
342
|
+
getSchema (id) {
|
|
343
|
+
return ajvInstance.getSchema(id).schema
|
|
344
|
+
},
|
|
345
|
+
getSchemas () {
|
|
346
|
+
return {
|
|
347
|
+
'urn:schema:base': baseSchema,
|
|
348
|
+
'urn:schema:ref': refSchema
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
server.setValidatorCompiler(function ({ schema }) {
|
|
355
|
+
t.pass('the querystring schema is compiled')
|
|
356
|
+
return ajvInstance.compile(schema)
|
|
357
|
+
})
|
|
358
|
+
}
|
|
359
|
+
schemaPlugin[Symbol.for('skip-override')] = true
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
test('side effect on schema let the server crash', async t => {
|
|
363
|
+
const firstSchema = {
|
|
364
|
+
$id: 'example1',
|
|
365
|
+
type: 'object',
|
|
366
|
+
properties: {
|
|
367
|
+
name: {
|
|
368
|
+
type: 'string'
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const reusedSchema = {
|
|
374
|
+
$id: 'example2',
|
|
375
|
+
type: 'object',
|
|
376
|
+
properties: {
|
|
377
|
+
name: {
|
|
378
|
+
oneOf: [
|
|
379
|
+
{
|
|
380
|
+
$ref: 'example1'
|
|
381
|
+
}
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const fastify = Fastify()
|
|
388
|
+
fastify.addSchema(firstSchema)
|
|
389
|
+
|
|
390
|
+
fastify.post('/a', {
|
|
391
|
+
handler: async () => 'OK',
|
|
392
|
+
schema: {
|
|
393
|
+
body: reusedSchema,
|
|
394
|
+
response: { 200: reusedSchema }
|
|
395
|
+
}
|
|
396
|
+
})
|
|
397
|
+
fastify.post('/b', {
|
|
398
|
+
handler: async () => 'OK',
|
|
399
|
+
schema: {
|
|
400
|
+
body: reusedSchema,
|
|
401
|
+
response: { 200: reusedSchema }
|
|
402
|
+
}
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
await fastify.ready()
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
test('only response schema trigger AJV pollution', async t => {
|
|
409
|
+
const ShowSchema = S.object().id('ShowSchema').prop('name', S.string())
|
|
410
|
+
const ListSchema = S.array().id('ListSchema').items(S.ref('ShowSchema#'))
|
|
411
|
+
|
|
412
|
+
const fastify = Fastify()
|
|
413
|
+
fastify.addSchema(ListSchema)
|
|
414
|
+
fastify.addSchema(ShowSchema)
|
|
415
|
+
|
|
416
|
+
const routeResponseSchemas = {
|
|
417
|
+
schema: { response: { 200: S.ref('ListSchema#') } }
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
fastify.register(
|
|
421
|
+
async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
|
|
422
|
+
{ prefix: '/prefix1' }
|
|
423
|
+
)
|
|
424
|
+
fastify.register(
|
|
425
|
+
async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
|
|
426
|
+
{ prefix: '/prefix2' }
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
await fastify.ready()
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
test('only response schema trigger AJV pollution #2', async t => {
|
|
433
|
+
const ShowSchema = S.object().id('ShowSchema').prop('name', S.string())
|
|
434
|
+
const ListSchema = S.array().id('ListSchema').items(S.ref('ShowSchema#'))
|
|
435
|
+
|
|
436
|
+
const fastify = Fastify()
|
|
437
|
+
fastify.addSchema(ListSchema)
|
|
438
|
+
fastify.addSchema(ShowSchema)
|
|
439
|
+
|
|
440
|
+
const routeResponseSchemas = {
|
|
441
|
+
schema: {
|
|
442
|
+
params: S.ref('ListSchema#'),
|
|
443
|
+
response: { 200: S.ref('ListSchema#') }
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
fastify.register(
|
|
448
|
+
async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
|
|
449
|
+
{ prefix: '/prefix1' }
|
|
450
|
+
)
|
|
451
|
+
fastify.register(
|
|
452
|
+
async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
|
|
453
|
+
{ prefix: '/prefix2' }
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
await fastify.ready()
|
|
457
|
+
})
|
|
@@ -178,7 +178,7 @@ test('Encapsulation', t => {
|
|
|
178
178
|
|
|
179
179
|
fastify.register((instance, opts, done) => {
|
|
180
180
|
instance.post('/clean', function (req, reply) {
|
|
181
|
-
t.equals(instance.validatorCompiler,
|
|
181
|
+
t.equals(instance.validatorCompiler, undefined)
|
|
182
182
|
reply.send({ foo: 'bar' })
|
|
183
183
|
})
|
|
184
184
|
done()
|
package/test/stream.test.js
CHANGED
|
@@ -263,6 +263,68 @@ test('Destroying streams prematurely should call close method', t => {
|
|
|
263
263
|
})
|
|
264
264
|
})
|
|
265
265
|
|
|
266
|
+
test('Destroying streams prematurely should call close method when destroy is not a function', t => {
|
|
267
|
+
t.plan(7)
|
|
268
|
+
|
|
269
|
+
let fastify = null
|
|
270
|
+
const logStream = split(JSON.parse)
|
|
271
|
+
try {
|
|
272
|
+
fastify = Fastify({
|
|
273
|
+
logger: {
|
|
274
|
+
stream: logStream,
|
|
275
|
+
level: 'info'
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
} catch (e) {
|
|
279
|
+
t.fail()
|
|
280
|
+
}
|
|
281
|
+
const stream = require('stream')
|
|
282
|
+
const http = require('http')
|
|
283
|
+
|
|
284
|
+
// Test that "premature close" errors are logged with level warn
|
|
285
|
+
logStream.on('data', line => {
|
|
286
|
+
if (line.res) {
|
|
287
|
+
t.equal(line.msg, 'stream closed prematurely')
|
|
288
|
+
t.equal(line.level, 30)
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
fastify.get('/', function (request, reply) {
|
|
293
|
+
t.pass('Received request')
|
|
294
|
+
|
|
295
|
+
let sent = false
|
|
296
|
+
const reallyLongStream = new stream.Readable({
|
|
297
|
+
read: function () {
|
|
298
|
+
if (!sent) {
|
|
299
|
+
this.push(Buffer.from('hello\n'))
|
|
300
|
+
}
|
|
301
|
+
sent = true
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
reallyLongStream.destroy = true
|
|
305
|
+
reallyLongStream.close = () => t.ok('called')
|
|
306
|
+
reply.send(reallyLongStream)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
fastify.listen(0, err => {
|
|
310
|
+
t.error(err)
|
|
311
|
+
fastify.server.unref()
|
|
312
|
+
|
|
313
|
+
const port = fastify.server.address().port
|
|
314
|
+
|
|
315
|
+
http.get(`http://localhost:${port}`, function (response) {
|
|
316
|
+
t.strictEqual(response.statusCode, 200)
|
|
317
|
+
response.on('readable', function () {
|
|
318
|
+
response.destroy()
|
|
319
|
+
})
|
|
320
|
+
// Node bug? Node never emits 'close' here.
|
|
321
|
+
response.on('aborted', function () {
|
|
322
|
+
t.pass('Response closed')
|
|
323
|
+
})
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
})
|
|
327
|
+
|
|
266
328
|
test('Destroying streams prematurely should call abort method', t => {
|
|
267
329
|
t.plan(7)
|
|
268
330
|
|
|
@@ -418,3 +480,31 @@ test('should support send module 200 and 404', t => {
|
|
|
418
480
|
})
|
|
419
481
|
})
|
|
420
482
|
})
|
|
483
|
+
|
|
484
|
+
test('should destroy stream when response is ended', t => {
|
|
485
|
+
t.plan(4)
|
|
486
|
+
const stream = require('stream')
|
|
487
|
+
const fastify = Fastify()
|
|
488
|
+
|
|
489
|
+
fastify.get('/error', function (req, reply) {
|
|
490
|
+
const reallyLongStream = new stream.Readable({
|
|
491
|
+
read: function () {},
|
|
492
|
+
destroy: function (err, callback) {
|
|
493
|
+
t.ok('called')
|
|
494
|
+
callback(err)
|
|
495
|
+
}
|
|
496
|
+
})
|
|
497
|
+
reply.code(200).send(reallyLongStream)
|
|
498
|
+
reply.raw.end(Buffer.from('hello\n'))
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
fastify.listen(0, err => {
|
|
502
|
+
t.error(err)
|
|
503
|
+
fastify.server.unref()
|
|
504
|
+
|
|
505
|
+
sget(`http://localhost:${fastify.server.address().port}/error`, function (err, response) {
|
|
506
|
+
t.error(err)
|
|
507
|
+
t.strictEqual(response.statusCode, 200)
|
|
508
|
+
})
|
|
509
|
+
})
|
|
510
|
+
})
|
package/test/throw.test.js
CHANGED
|
@@ -21,7 +21,7 @@ test('Fastify should throw on multiple assignment to the same route', t => {
|
|
|
21
21
|
fastify.get('/', () => {})
|
|
22
22
|
|
|
23
23
|
fastify.ready(err => {
|
|
24
|
-
t.is(err.message, "Method 'GET' already declared for route '/'")
|
|
24
|
+
t.is(err.message, "Method 'GET' already declared for route '/' with constraints '{}'")
|
|
25
25
|
})
|
|
26
26
|
})
|
|
27
27
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import fastify from '../../fastify'
|
|
2
|
-
import { expectType } from 'tsd'
|
|
1
|
+
import fastify, { FastifyContentTypeParser } from '../../fastify'
|
|
2
|
+
import { expectError, expectType } from 'tsd'
|
|
3
3
|
import { IncomingMessage } from 'http'
|
|
4
4
|
import { FastifyRequest } from '../../types/request'
|
|
5
5
|
|
|
@@ -56,3 +56,9 @@ expectType<void>(fastify().addContentTypeParser<Buffer>('bodyContentType', { par
|
|
|
56
56
|
expectType<Buffer>(body)
|
|
57
57
|
return null
|
|
58
58
|
}))
|
|
59
|
+
|
|
60
|
+
expectType<FastifyContentTypeParser>(fastify().getDefaultJsonParser('error', 'ignore'))
|
|
61
|
+
|
|
62
|
+
expectError(fastify().getDefaultJsonParser('error', 'skip'))
|
|
63
|
+
|
|
64
|
+
expectError(fastify().getDefaultJsonParser('nothing', 'ignore'))
|
|
@@ -98,6 +98,33 @@ expectAssignable<FastifyInstance>(fastify({
|
|
|
98
98
|
deriveVersion: () => 'foo'
|
|
99
99
|
}
|
|
100
100
|
}))
|
|
101
|
+
expectAssignable<FastifyInstance>(fastify({ constraints: {} }))
|
|
102
|
+
expectAssignable<FastifyInstance>(fastify({
|
|
103
|
+
constraints: {
|
|
104
|
+
version: {
|
|
105
|
+
name: 'version',
|
|
106
|
+
storage: () => ({
|
|
107
|
+
get: () => () => {},
|
|
108
|
+
set: () => { },
|
|
109
|
+
del: () => { },
|
|
110
|
+
empty: () => { }
|
|
111
|
+
}),
|
|
112
|
+
validate () {},
|
|
113
|
+
deriveConstraint: () => 'foo'
|
|
114
|
+
},
|
|
115
|
+
host: {
|
|
116
|
+
name: 'host',
|
|
117
|
+
storage: () => ({
|
|
118
|
+
get: () => () => {},
|
|
119
|
+
set: () => { },
|
|
120
|
+
del: () => { },
|
|
121
|
+
empty: () => { }
|
|
122
|
+
}),
|
|
123
|
+
validate () {},
|
|
124
|
+
deriveConstraint: () => 'foo'
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}))
|
|
101
128
|
expectAssignable<FastifyInstance>(fastify({ return503OnClosing: true }))
|
|
102
129
|
expectAssignable<FastifyInstance>(fastify({
|
|
103
130
|
ajv: {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import fastify, { FastifyError, FastifyInstance, ValidationResult } from '../../fastify'
|
|
1
|
+
import fastify, { FastifyContentTypeParser, FastifyError, FastifyInstance, ValidationResult } from '../../fastify'
|
|
2
2
|
import { expectAssignable, expectError, expectType } from 'tsd'
|
|
3
|
+
import { FastifyRequest } from '../../types/request'
|
|
4
|
+
import { FastifyReply } from '../../types/reply'
|
|
5
|
+
import { HookHandlerDoneFunction } from '../../types/hooks'
|
|
3
6
|
|
|
4
7
|
const server = fastify()
|
|
5
8
|
|
|
@@ -15,6 +18,8 @@ expectAssignable<FastifyInstance>(server.addSchema({
|
|
|
15
18
|
|
|
16
19
|
expectType<Record<string, unknown>>(server.getSchemas())
|
|
17
20
|
expectType<unknown>(server.getSchema('SchemaId'))
|
|
21
|
+
expectType<string>(server.printRoutes())
|
|
22
|
+
expectType<string>(server.printPlugins())
|
|
18
23
|
|
|
19
24
|
expectAssignable<FastifyInstance>(
|
|
20
25
|
server.setErrorHandler(function (error, request, reply) {
|
|
@@ -36,6 +41,19 @@ server.setErrorHandler(fastifyErrorHandler)
|
|
|
36
41
|
function nodeJSErrorHandler (error: NodeJS.ErrnoException) {}
|
|
37
42
|
server.setErrorHandler(nodeJSErrorHandler)
|
|
38
43
|
|
|
44
|
+
function notFoundHandler (request: FastifyRequest, reply: FastifyReply) {}
|
|
45
|
+
function notFoundpreHandlerHandler (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) { done() }
|
|
46
|
+
async function notFoundpreHandlerAsyncHandler (request: FastifyRequest, reply: FastifyReply) {}
|
|
47
|
+
function notFoundpreValidationHandler (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) { done() }
|
|
48
|
+
async function notFoundpreValidationAsyncHandler (request: FastifyRequest, reply: FastifyReply) {}
|
|
49
|
+
|
|
50
|
+
server.setNotFoundHandler(notFoundHandler)
|
|
51
|
+
server.setNotFoundHandler({ preHandler: notFoundpreHandlerHandler }, notFoundHandler)
|
|
52
|
+
server.setNotFoundHandler({ preHandler: notFoundpreHandlerAsyncHandler }, notFoundHandler)
|
|
53
|
+
server.setNotFoundHandler({ preValidation: notFoundpreValidationHandler }, notFoundHandler)
|
|
54
|
+
server.setNotFoundHandler({ preValidation: notFoundpreValidationAsyncHandler }, notFoundHandler)
|
|
55
|
+
server.setNotFoundHandler({ preHandler: notFoundpreHandlerHandler, preValidation: notFoundpreValidationHandler }, notFoundHandler)
|
|
56
|
+
|
|
39
57
|
function invalidErrorHandler (error: number) {}
|
|
40
58
|
expectError(server.setErrorHandler(invalidErrorHandler))
|
|
41
59
|
|
|
@@ -84,3 +102,27 @@ expectType<FastifyInstance>(fastify().get('/', {
|
|
|
84
102
|
expectAssignable<void>(server.errorHandler(error, request, reply))
|
|
85
103
|
}
|
|
86
104
|
}))
|
|
105
|
+
|
|
106
|
+
type InitialConfig = Readonly<{
|
|
107
|
+
connectionTimeout?: number,
|
|
108
|
+
keepAliveTimeout?: number,
|
|
109
|
+
bodyLimit?: number,
|
|
110
|
+
caseSensitive?: boolean,
|
|
111
|
+
http2?: boolean,
|
|
112
|
+
https?: boolean | Readonly<{ allowHTTP1: boolean }>,
|
|
113
|
+
ignoreTrailingSlash?: boolean,
|
|
114
|
+
disableRequestLogging?: boolean,
|
|
115
|
+
maxParamLength?: number,
|
|
116
|
+
onProtoPoisoning?: 'error' | 'remove' | 'ignore',
|
|
117
|
+
onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
|
|
118
|
+
pluginTimeout?: number,
|
|
119
|
+
requestIdHeader?: string,
|
|
120
|
+
requestIdLogLabel?: string,
|
|
121
|
+
http2SessionTimeout?: number
|
|
122
|
+
}>
|
|
123
|
+
|
|
124
|
+
expectType<InitialConfig>(fastify().initialConfig)
|
|
125
|
+
|
|
126
|
+
expectType<FastifyContentTypeParser>(server.defaultTextParser)
|
|
127
|
+
|
|
128
|
+
expectType<FastifyContentTypeParser>(server.getDefaultJsonParser('ignore', 'error'))
|