fastify 3.19.1 → 3.20.2
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/docs/ContentTypeParser.md +56 -2
- package/docs/Decorators.md +3 -0
- package/docs/Ecosystem.md +5 -1
- package/docs/Getting-Started.md +2 -2
- package/docs/Reply.md +6 -5
- package/docs/Request.md +20 -1
- package/docs/Routes.md +3 -3
- package/docs/Server.md +7 -5
- package/docs/TypeScript.md +3 -3
- package/fastify.d.ts +4 -2
- package/fastify.js +28 -6
- package/lib/contentTypeParser.js +46 -1
- package/lib/logger.js +1 -1
- package/lib/pluginUtils.js +3 -1
- package/lib/reply.js +6 -2
- package/lib/request.js +6 -0
- package/lib/route.js +6 -3
- package/package.json +4 -4
- package/test/async-await.test.js +73 -0
- package/test/content-parser.test.js +76 -0
- package/test/custom-parser.test.js +203 -0
- package/test/internals/version.test.js +43 -0
- package/test/request-error.test.js +72 -0
- package/test/route.test.js +56 -0
- package/test/schema-special-usage.test.js +32 -0
- package/test/types/content-type-parser.test-d.ts +8 -0
- package/test/types/fastify.test-d.ts +16 -0
- package/test/types/instance.test-d.ts +40 -2
- package/test/types/logger.test-d.ts +23 -1
- package/test/types/reply.test-d.ts +2 -0
- package/test/types/request.test-d.ts +4 -0
- package/types/content-type-parser.d.ts +4 -0
- package/types/instance.d.ts +37 -6
- package/types/logger.d.ts +1 -0
- package/types/reply.d.ts +2 -0
- package/types/request.d.ts +2 -0
package/test/async-await.test.js
CHANGED
|
@@ -646,6 +646,79 @@ test('customErrorHandler only called if reply not already sent', t => {
|
|
|
646
646
|
})
|
|
647
647
|
})
|
|
648
648
|
|
|
649
|
+
// See https://github.com/fastify/fastify/issues/3209
|
|
650
|
+
test('setNotFoundHandler should accept return value', t => {
|
|
651
|
+
t.plan(3)
|
|
652
|
+
|
|
653
|
+
const fastify = Fastify()
|
|
654
|
+
|
|
655
|
+
fastify.get('/', async () => ({ hello: 'world' }))
|
|
656
|
+
|
|
657
|
+
fastify.setNotFoundHandler((req, reply) => {
|
|
658
|
+
reply.code(404)
|
|
659
|
+
return {
|
|
660
|
+
error: statusCodes['404'],
|
|
661
|
+
message: 'lost',
|
|
662
|
+
statusCode: 404
|
|
663
|
+
}
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
fastify.inject({
|
|
667
|
+
method: 'GET',
|
|
668
|
+
url: '/elsewhere'
|
|
669
|
+
}, (err, res) => {
|
|
670
|
+
t.error(err)
|
|
671
|
+
t.equal(res.statusCode, 404)
|
|
672
|
+
t.same(
|
|
673
|
+
{
|
|
674
|
+
error: statusCodes['404'],
|
|
675
|
+
message: 'lost',
|
|
676
|
+
statusCode: 404
|
|
677
|
+
},
|
|
678
|
+
JSON.parse(res.payload)
|
|
679
|
+
)
|
|
680
|
+
})
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
// See https://github.com/fastify/fastify/issues/3209
|
|
684
|
+
test('customErrorHandler should accept return value', t => {
|
|
685
|
+
t.plan(4)
|
|
686
|
+
|
|
687
|
+
const fastify = Fastify()
|
|
688
|
+
|
|
689
|
+
fastify.get('/', async (req, reply) => {
|
|
690
|
+
const error = new Error('ouch')
|
|
691
|
+
error.statusCode = 400
|
|
692
|
+
throw error
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
fastify.setErrorHandler((err, req, reply) => {
|
|
696
|
+
t.equal(err.message, 'ouch')
|
|
697
|
+
reply.code(401)
|
|
698
|
+
return {
|
|
699
|
+
error: statusCodes['401'],
|
|
700
|
+
message: 'kaboom',
|
|
701
|
+
statusCode: 401
|
|
702
|
+
}
|
|
703
|
+
})
|
|
704
|
+
|
|
705
|
+
fastify.inject({
|
|
706
|
+
method: 'GET',
|
|
707
|
+
url: '/'
|
|
708
|
+
}, (err, res) => {
|
|
709
|
+
t.error(err)
|
|
710
|
+
t.equal(res.statusCode, 401)
|
|
711
|
+
t.same(
|
|
712
|
+
{
|
|
713
|
+
error: statusCodes['401'],
|
|
714
|
+
message: 'kaboom',
|
|
715
|
+
statusCode: 401
|
|
716
|
+
},
|
|
717
|
+
JSON.parse(res.payload)
|
|
718
|
+
)
|
|
719
|
+
})
|
|
720
|
+
})
|
|
721
|
+
|
|
649
722
|
test('await self', async t => {
|
|
650
723
|
const app = Fastify()
|
|
651
724
|
t.equal(await app, app)
|
|
@@ -186,3 +186,79 @@ test('add', t => {
|
|
|
186
186
|
|
|
187
187
|
t.end()
|
|
188
188
|
})
|
|
189
|
+
|
|
190
|
+
test('remove', t => {
|
|
191
|
+
test('should remove default parser', t => {
|
|
192
|
+
t.plan(2)
|
|
193
|
+
|
|
194
|
+
const fastify = Fastify()
|
|
195
|
+
const contentTypeParser = fastify[keys.kContentTypeParser]
|
|
196
|
+
|
|
197
|
+
contentTypeParser.remove('application/json')
|
|
198
|
+
|
|
199
|
+
t.notOk(contentTypeParser.customParsers['application/json'])
|
|
200
|
+
t.notOk(contentTypeParser.parserList.find(parser => parser === 'application/json'))
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test('should remove RegExp parser', t => {
|
|
204
|
+
t.plan(2)
|
|
205
|
+
|
|
206
|
+
const fastify = Fastify()
|
|
207
|
+
fastify.addContentTypeParser(/^text\/*/, first)
|
|
208
|
+
|
|
209
|
+
const contentTypeParser = fastify[keys.kContentTypeParser]
|
|
210
|
+
|
|
211
|
+
contentTypeParser.remove(/^text\/*/)
|
|
212
|
+
|
|
213
|
+
t.notOk(contentTypeParser.customParsers[/^text\/*/])
|
|
214
|
+
t.notOk(contentTypeParser.parserRegExpList.find(parser => parser.toString() === /^text\/*/.toString()))
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
test('should throw an error if content type is neither string nor RegExp', t => {
|
|
218
|
+
t.plan(1)
|
|
219
|
+
|
|
220
|
+
const fastify = Fastify()
|
|
221
|
+
|
|
222
|
+
t.throws(() => fastify[keys.kContentTypeParser].remove(12), FST_ERR_CTP_INVALID_TYPE)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
test('should not throw error if content type does not exist', t => {
|
|
226
|
+
t.plan(1)
|
|
227
|
+
|
|
228
|
+
const fastify = Fastify()
|
|
229
|
+
|
|
230
|
+
t.doesNotThrow(() => fastify[keys.kContentTypeParser].remove('image/png'))
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
test('should not remove any content type parser if content type does not exist', t => {
|
|
234
|
+
t.plan(1)
|
|
235
|
+
|
|
236
|
+
const fastify = Fastify()
|
|
237
|
+
|
|
238
|
+
const contentTypeParser = fastify[keys.kContentTypeParser]
|
|
239
|
+
|
|
240
|
+
contentTypeParser.remove('image/png')
|
|
241
|
+
|
|
242
|
+
t.same(Object.keys(contentTypeParser.customParsers).length, 2)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
t.end()
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('remove all should remove all existing parsers and reset cache', t => {
|
|
249
|
+
t.plan(4)
|
|
250
|
+
|
|
251
|
+
const fastify = Fastify()
|
|
252
|
+
fastify.addContentTypeParser('application/xml', first)
|
|
253
|
+
fastify.addContentTypeParser(/^image\/.*/, first)
|
|
254
|
+
|
|
255
|
+
const contentTypeParser = fastify[keys.kContentTypeParser]
|
|
256
|
+
|
|
257
|
+
contentTypeParser.getParser('application/xml') // fill cache with one entry
|
|
258
|
+
contentTypeParser.removeAll()
|
|
259
|
+
|
|
260
|
+
t.same(contentTypeParser.cache.size, 0)
|
|
261
|
+
t.same(contentTypeParser.parserList.length, 0)
|
|
262
|
+
t.same(contentTypeParser.parserRegExpList.length, 0)
|
|
263
|
+
t.same(Object.keys(contentTypeParser.customParsers).length, 0)
|
|
264
|
+
})
|
|
@@ -1504,3 +1504,206 @@ test('should prefer string content types over RegExp ones', t => {
|
|
|
1504
1504
|
})
|
|
1505
1505
|
})
|
|
1506
1506
|
})
|
|
1507
|
+
|
|
1508
|
+
test('removeContentTypeParser should support arrays of content types to remove', t => {
|
|
1509
|
+
t.plan(8)
|
|
1510
|
+
|
|
1511
|
+
const fastify = Fastify()
|
|
1512
|
+
|
|
1513
|
+
fastify.addContentTypeParser('application/xml', function (req, payload, done) {
|
|
1514
|
+
payload.on('data', () => {})
|
|
1515
|
+
payload.on('end', () => {
|
|
1516
|
+
done(null, 'xml')
|
|
1517
|
+
})
|
|
1518
|
+
})
|
|
1519
|
+
|
|
1520
|
+
fastify.addContentTypeParser(/^image\/.*/, function (req, payload, done) {
|
|
1521
|
+
payload.on('data', () => {})
|
|
1522
|
+
payload.on('end', () => {
|
|
1523
|
+
done(null, 'image')
|
|
1524
|
+
})
|
|
1525
|
+
})
|
|
1526
|
+
|
|
1527
|
+
fastify.removeContentTypeParser([/^image\/.*/, 'application/json'])
|
|
1528
|
+
|
|
1529
|
+
fastify.post('/', (req, reply) => {
|
|
1530
|
+
reply.send(req.body)
|
|
1531
|
+
})
|
|
1532
|
+
|
|
1533
|
+
fastify.listen(0, err => {
|
|
1534
|
+
t.error(err)
|
|
1535
|
+
|
|
1536
|
+
sget({
|
|
1537
|
+
method: 'POST',
|
|
1538
|
+
url: 'http://localhost:' + fastify.server.address().port,
|
|
1539
|
+
body: '<?xml version="1.0">',
|
|
1540
|
+
headers: {
|
|
1541
|
+
'Content-Type': 'application/xml'
|
|
1542
|
+
}
|
|
1543
|
+
}, (err, response, body) => {
|
|
1544
|
+
t.error(err)
|
|
1545
|
+
t.equal(response.statusCode, 200)
|
|
1546
|
+
t.same(body.toString(), 'xml')
|
|
1547
|
+
})
|
|
1548
|
+
|
|
1549
|
+
sget({
|
|
1550
|
+
method: 'POST',
|
|
1551
|
+
url: 'http://localhost:' + fastify.server.address().port,
|
|
1552
|
+
body: '',
|
|
1553
|
+
headers: {
|
|
1554
|
+
'Content-Type': 'image/png'
|
|
1555
|
+
}
|
|
1556
|
+
}, (err, response, body) => {
|
|
1557
|
+
t.error(err)
|
|
1558
|
+
t.equal(response.statusCode, 415)
|
|
1559
|
+
})
|
|
1560
|
+
|
|
1561
|
+
sget({
|
|
1562
|
+
method: 'POST',
|
|
1563
|
+
url: 'http://localhost:' + fastify.server.address().port,
|
|
1564
|
+
body: '{test: "test"}',
|
|
1565
|
+
headers: {
|
|
1566
|
+
'Content-Type': 'application/json'
|
|
1567
|
+
}
|
|
1568
|
+
}, (err, response, body) => {
|
|
1569
|
+
t.error(err)
|
|
1570
|
+
t.equal(response.statusCode, 415)
|
|
1571
|
+
fastify.close()
|
|
1572
|
+
})
|
|
1573
|
+
})
|
|
1574
|
+
})
|
|
1575
|
+
|
|
1576
|
+
test('removeContentTypeParser should support encapsulation', t => {
|
|
1577
|
+
t.plan(6)
|
|
1578
|
+
|
|
1579
|
+
const fastify = Fastify()
|
|
1580
|
+
|
|
1581
|
+
fastify.addContentTypeParser('application/xml', function (req, payload, done) {
|
|
1582
|
+
payload.on('data', () => {})
|
|
1583
|
+
payload.on('end', () => {
|
|
1584
|
+
done(null, 'xml')
|
|
1585
|
+
})
|
|
1586
|
+
})
|
|
1587
|
+
|
|
1588
|
+
fastify.post('/', (req, reply) => {
|
|
1589
|
+
reply.send(req.body)
|
|
1590
|
+
})
|
|
1591
|
+
|
|
1592
|
+
fastify.register(function (instance, options, done) {
|
|
1593
|
+
instance.removeContentTypeParser('application/xml')
|
|
1594
|
+
|
|
1595
|
+
instance.post('/encapsulated', (req, reply) => {
|
|
1596
|
+
reply.send(req.body)
|
|
1597
|
+
})
|
|
1598
|
+
|
|
1599
|
+
done()
|
|
1600
|
+
})
|
|
1601
|
+
|
|
1602
|
+
fastify.listen(0, err => {
|
|
1603
|
+
t.error(err)
|
|
1604
|
+
|
|
1605
|
+
sget({
|
|
1606
|
+
method: 'POST',
|
|
1607
|
+
url: 'http://localhost:' + fastify.server.address().port + '/encapsulated',
|
|
1608
|
+
body: '<?xml version="1.0">',
|
|
1609
|
+
headers: {
|
|
1610
|
+
'Content-Type': 'application/xml'
|
|
1611
|
+
}
|
|
1612
|
+
}, (err, response, body) => {
|
|
1613
|
+
t.error(err)
|
|
1614
|
+
t.equal(response.statusCode, 415)
|
|
1615
|
+
})
|
|
1616
|
+
|
|
1617
|
+
sget({
|
|
1618
|
+
method: 'POST',
|
|
1619
|
+
url: 'http://localhost:' + fastify.server.address().port,
|
|
1620
|
+
body: '<?xml version="1.0">',
|
|
1621
|
+
headers: {
|
|
1622
|
+
'Content-Type': 'application/xml'
|
|
1623
|
+
}
|
|
1624
|
+
}, (err, response, body) => {
|
|
1625
|
+
t.error(err)
|
|
1626
|
+
t.equal(response.statusCode, 200)
|
|
1627
|
+
t.same(body.toString(), 'xml')
|
|
1628
|
+
fastify.close()
|
|
1629
|
+
})
|
|
1630
|
+
})
|
|
1631
|
+
})
|
|
1632
|
+
|
|
1633
|
+
test('removeAllContentTypeParsers should support encapsulation', t => {
|
|
1634
|
+
t.plan(6)
|
|
1635
|
+
|
|
1636
|
+
const fastify = Fastify()
|
|
1637
|
+
|
|
1638
|
+
fastify.post('/', (req, reply) => {
|
|
1639
|
+
reply.send(req.body)
|
|
1640
|
+
})
|
|
1641
|
+
|
|
1642
|
+
fastify.register(function (instance, options, done) {
|
|
1643
|
+
instance.removeAllContentTypeParsers()
|
|
1644
|
+
|
|
1645
|
+
instance.post('/encapsulated', (req, reply) => {
|
|
1646
|
+
reply.send(req.body)
|
|
1647
|
+
})
|
|
1648
|
+
|
|
1649
|
+
done()
|
|
1650
|
+
})
|
|
1651
|
+
|
|
1652
|
+
fastify.listen(0, err => {
|
|
1653
|
+
t.error(err)
|
|
1654
|
+
|
|
1655
|
+
sget({
|
|
1656
|
+
method: 'POST',
|
|
1657
|
+
url: 'http://localhost:' + fastify.server.address().port + '/encapsulated',
|
|
1658
|
+
body: '{}',
|
|
1659
|
+
headers: {
|
|
1660
|
+
'Content-Type': 'application/json'
|
|
1661
|
+
}
|
|
1662
|
+
}, (err, response, body) => {
|
|
1663
|
+
t.error(err)
|
|
1664
|
+
t.equal(response.statusCode, 415)
|
|
1665
|
+
})
|
|
1666
|
+
|
|
1667
|
+
sget({
|
|
1668
|
+
method: 'POST',
|
|
1669
|
+
url: 'http://localhost:' + fastify.server.address().port,
|
|
1670
|
+
body: '{"test":1}',
|
|
1671
|
+
headers: {
|
|
1672
|
+
'Content-Type': 'application/json'
|
|
1673
|
+
}
|
|
1674
|
+
}, (err, response, body) => {
|
|
1675
|
+
t.error(err)
|
|
1676
|
+
t.equal(response.statusCode, 200)
|
|
1677
|
+
t.same(JSON.parse(body.toString()).test, 1)
|
|
1678
|
+
fastify.close()
|
|
1679
|
+
})
|
|
1680
|
+
})
|
|
1681
|
+
})
|
|
1682
|
+
|
|
1683
|
+
test('cannot remove all content type parsers after binding', t => {
|
|
1684
|
+
t.plan(2)
|
|
1685
|
+
|
|
1686
|
+
const fastify = Fastify()
|
|
1687
|
+
|
|
1688
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1689
|
+
|
|
1690
|
+
fastify.listen(0, function (err) {
|
|
1691
|
+
t.error(err)
|
|
1692
|
+
|
|
1693
|
+
t.throws(() => fastify.removeAllContentTypeParsers())
|
|
1694
|
+
})
|
|
1695
|
+
})
|
|
1696
|
+
|
|
1697
|
+
test('cannot remove content type parsers after binding', t => {
|
|
1698
|
+
t.plan(2)
|
|
1699
|
+
|
|
1700
|
+
const fastify = Fastify()
|
|
1701
|
+
|
|
1702
|
+
t.teardown(fastify.close.bind(fastify))
|
|
1703
|
+
|
|
1704
|
+
fastify.listen(0, function (err) {
|
|
1705
|
+
t.error(err)
|
|
1706
|
+
|
|
1707
|
+
t.throws(() => fastify.removeContentTypeParser('application/json'))
|
|
1708
|
+
})
|
|
1709
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const t = require('tap')
|
|
4
|
+
const test = t.test
|
|
5
|
+
const proxyquire = require('proxyquire')
|
|
6
|
+
|
|
7
|
+
test('should output an undefined version in case of package.json not available', t => {
|
|
8
|
+
const Fastify = proxyquire('../..', { fs: { accessSync: () => { throw Error('error') } } })
|
|
9
|
+
t.plan(1)
|
|
10
|
+
const srv = Fastify()
|
|
11
|
+
t.equal(srv.version, undefined)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('should output an undefined version in case of package.json is not the fastify one', t => {
|
|
15
|
+
const Fastify = proxyquire('../..', { fs: { accessSync: () => { }, readFileSync: () => JSON.stringify({ name: 'foo', version: '6.6.6' }) } })
|
|
16
|
+
t.plan(1)
|
|
17
|
+
const srv = Fastify()
|
|
18
|
+
t.equal(srv.version, undefined)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('should skip the version check if the version is undefined', t => {
|
|
22
|
+
const Fastify = proxyquire('../..', { fs: { accessSync: () => { }, readFileSync: () => JSON.stringify({ name: 'foo', version: '6.6.6' }) } })
|
|
23
|
+
t.plan(3)
|
|
24
|
+
const srv = Fastify()
|
|
25
|
+
t.equal(srv.version, undefined)
|
|
26
|
+
|
|
27
|
+
plugin[Symbol.for('skip-override')] = false
|
|
28
|
+
plugin[Symbol.for('plugin-meta')] = {
|
|
29
|
+
name: 'plugin',
|
|
30
|
+
fastify: '>=99.0.0'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
srv.register(plugin)
|
|
34
|
+
|
|
35
|
+
srv.ready((err) => {
|
|
36
|
+
t.error(err)
|
|
37
|
+
t.pass('everything right')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
function plugin (instance, opts, done) {
|
|
41
|
+
done()
|
|
42
|
+
}
|
|
43
|
+
})
|
|
@@ -127,6 +127,78 @@ test('default clientError handler ignores ECONNRESET', t => {
|
|
|
127
127
|
})
|
|
128
128
|
})
|
|
129
129
|
|
|
130
|
+
test('default clientError handler ignores sockets in destroyed state', t => {
|
|
131
|
+
t.plan(1)
|
|
132
|
+
|
|
133
|
+
const fastify = Fastify({
|
|
134
|
+
bodyLimit: 1,
|
|
135
|
+
keepAliveTimeout: 100,
|
|
136
|
+
logger: {
|
|
137
|
+
level: 'trace'
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
fastify.server.on('clientError', () => {
|
|
141
|
+
// this handler is called after default handler, so we can make sure end was not called
|
|
142
|
+
t.pass()
|
|
143
|
+
})
|
|
144
|
+
fastify.server.emit('clientError', new Error(), {
|
|
145
|
+
destroyed: true,
|
|
146
|
+
end () {
|
|
147
|
+
t.fail('end should not be called')
|
|
148
|
+
},
|
|
149
|
+
destroy () {
|
|
150
|
+
t.fail('destroy should not be called')
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('default clientError handler destroys sockets in writable state', t => {
|
|
156
|
+
t.plan(1)
|
|
157
|
+
|
|
158
|
+
const fastify = Fastify({
|
|
159
|
+
bodyLimit: 1,
|
|
160
|
+
keepAliveTimeout: 100,
|
|
161
|
+
logger: {
|
|
162
|
+
level: 'trace'
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
fastify.server.emit('clientError', new Error(), {
|
|
167
|
+
destroyed: false,
|
|
168
|
+
writable: true,
|
|
169
|
+
encrypted: true,
|
|
170
|
+
end () {
|
|
171
|
+
t.fail('end should not be called')
|
|
172
|
+
},
|
|
173
|
+
destroy () {
|
|
174
|
+
t.pass('destroy should be called')
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test('default clientError handler destroys http sockets in non-writable state', t => {
|
|
180
|
+
t.plan(1)
|
|
181
|
+
|
|
182
|
+
const fastify = Fastify({
|
|
183
|
+
bodyLimit: 1,
|
|
184
|
+
keepAliveTimeout: 100,
|
|
185
|
+
logger: {
|
|
186
|
+
level: 'trace'
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
fastify.server.emit('clientError', new Error(), {
|
|
191
|
+
destroyed: false,
|
|
192
|
+
writable: false,
|
|
193
|
+
end () {
|
|
194
|
+
t.fail('end should not be called')
|
|
195
|
+
},
|
|
196
|
+
destroy () {
|
|
197
|
+
t.pass('destroy should be called')
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
130
202
|
test('error handler binding', t => {
|
|
131
203
|
t.plan(5)
|
|
132
204
|
|
package/test/route.test.js
CHANGED
|
@@ -1190,3 +1190,59 @@ test('HEAD routes properly auto created for GET routes when prefixTrailingSlash:
|
|
|
1190
1190
|
t.equal(trailingSlashReply.statusCode, 200)
|
|
1191
1191
|
t.equal(noneTrailingReply.statusCode, 200)
|
|
1192
1192
|
})
|
|
1193
|
+
|
|
1194
|
+
test('Request and Reply share the route config', async t => {
|
|
1195
|
+
t.plan(3)
|
|
1196
|
+
|
|
1197
|
+
const fastify = Fastify()
|
|
1198
|
+
|
|
1199
|
+
const config = {
|
|
1200
|
+
this: 'is a string',
|
|
1201
|
+
thisIs: function aFunction () {}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
fastify.route({
|
|
1205
|
+
method: 'GET',
|
|
1206
|
+
url: '/',
|
|
1207
|
+
config,
|
|
1208
|
+
handler: (req, reply) => {
|
|
1209
|
+
t.same(req.context, reply.context)
|
|
1210
|
+
t.same(req.context.config, reply.context.config)
|
|
1211
|
+
t.match(req.context.config, config, 'there are url and method additional properties')
|
|
1212
|
+
|
|
1213
|
+
reply.send({ hello: 'world' })
|
|
1214
|
+
}
|
|
1215
|
+
})
|
|
1216
|
+
|
|
1217
|
+
await fastify.inject('/')
|
|
1218
|
+
})
|
|
1219
|
+
|
|
1220
|
+
test('Will not try to re-createprefixed HEAD route if it already exists and exposeHeadRoutes is true', async (t) => {
|
|
1221
|
+
t.plan(1)
|
|
1222
|
+
|
|
1223
|
+
const fastify = Fastify({ exposeHeadRoutes: true })
|
|
1224
|
+
|
|
1225
|
+
fastify.register((scope, opts, next) => {
|
|
1226
|
+
scope.route({
|
|
1227
|
+
method: 'HEAD',
|
|
1228
|
+
path: '/route',
|
|
1229
|
+
handler: (req, reply) => {
|
|
1230
|
+
reply.header('content-type', 'text/plain')
|
|
1231
|
+
reply.send('custom HEAD response')
|
|
1232
|
+
}
|
|
1233
|
+
})
|
|
1234
|
+
scope.route({
|
|
1235
|
+
method: 'GET',
|
|
1236
|
+
path: '/route',
|
|
1237
|
+
handler: (req, reply) => {
|
|
1238
|
+
reply.send({ ok: true })
|
|
1239
|
+
}
|
|
1240
|
+
})
|
|
1241
|
+
|
|
1242
|
+
next()
|
|
1243
|
+
}, { prefix: '/prefix' })
|
|
1244
|
+
|
|
1245
|
+
await fastify.ready()
|
|
1246
|
+
|
|
1247
|
+
t.ok(true)
|
|
1248
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
|
+
const Joi = require('@hapi/joi')
|
|
4
5
|
const AJV = require('ajv')
|
|
5
6
|
const S = require('fluent-json-schema')
|
|
6
7
|
const Fastify = require('..')
|
|
@@ -748,3 +749,34 @@ test('multiple refs with the same ids', t => {
|
|
|
748
749
|
t.same(res.json(), { hello: 'world' })
|
|
749
750
|
})
|
|
750
751
|
})
|
|
752
|
+
|
|
753
|
+
test('JOI validation overwrite request headers', t => {
|
|
754
|
+
t.plan(3)
|
|
755
|
+
const schemaValidator = ({ schema }) => data => {
|
|
756
|
+
const validationResult = schema.validate(data)
|
|
757
|
+
return validationResult
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
const fastify = Fastify()
|
|
761
|
+
fastify.setValidatorCompiler(schemaValidator)
|
|
762
|
+
|
|
763
|
+
fastify.get('/', {
|
|
764
|
+
schema: {
|
|
765
|
+
headers: Joi.object({
|
|
766
|
+
'user-agent': Joi.string().required(),
|
|
767
|
+
host: Joi.string().required()
|
|
768
|
+
})
|
|
769
|
+
}
|
|
770
|
+
}, (request, reply) => {
|
|
771
|
+
reply.send(request.headers)
|
|
772
|
+
})
|
|
773
|
+
|
|
774
|
+
fastify.inject('/', (err, res) => {
|
|
775
|
+
t.error(err)
|
|
776
|
+
t.equal(res.statusCode, 200)
|
|
777
|
+
t.same(res.json(), {
|
|
778
|
+
'user-agent': 'lightMyRequest',
|
|
779
|
+
host: 'localhost:80'
|
|
780
|
+
})
|
|
781
|
+
})
|
|
782
|
+
})
|
|
@@ -62,3 +62,11 @@ expectType<FastifyBodyParser<string>>(fastify().getDefaultJsonParser('error', 'i
|
|
|
62
62
|
expectError(fastify().getDefaultJsonParser('error', 'skip'))
|
|
63
63
|
|
|
64
64
|
expectError(fastify().getDefaultJsonParser('nothing', 'ignore'))
|
|
65
|
+
|
|
66
|
+
expectType<void>(fastify().removeAllContentTypeParsers())
|
|
67
|
+
expectError(fastify().removeAllContentTypeParsers('contentType'))
|
|
68
|
+
|
|
69
|
+
expectType<void>(fastify().removeContentTypeParser('contentType'))
|
|
70
|
+
expectType<void>(fastify().removeContentTypeParser(/contentType+.*/))
|
|
71
|
+
expectType<void>(fastify().removeContentTypeParser(['contentType', /contentType+.*/]))
|
|
72
|
+
expectError(fastify().removeContentTypeParser({}))
|
|
@@ -50,6 +50,10 @@ expectAssignable<FastifyInstance>(fastify({ disableRequestLogging: true }))
|
|
|
50
50
|
expectAssignable<FastifyInstance>(fastify({ requestIdLogLabel: 'request-id' }))
|
|
51
51
|
expectAssignable<FastifyInstance>(fastify({ onProtoPoisoning: 'error' }))
|
|
52
52
|
expectAssignable<FastifyInstance>(fastify({ onConstructorPoisoning: 'error' }))
|
|
53
|
+
expectAssignable<FastifyInstance>(fastify({ serializerOpts: { rounding: 'ceil' } }))
|
|
54
|
+
expectAssignable<FastifyInstance>(fastify({ serializerOpts: { ajv: { missingRefs: 'ignore' } } }))
|
|
55
|
+
expectAssignable<FastifyInstance>(fastify({ serializerOpts: { schema: { } } }))
|
|
56
|
+
expectAssignable<FastifyInstance>(fastify({ serializerOpts: { otherProp: { } } }))
|
|
53
57
|
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>(fastify({ logger: true }))
|
|
54
58
|
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyLoggerInstance>>(fastify({ logger: true }))
|
|
55
59
|
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyLoggerInstance>>(fastify({
|
|
@@ -135,6 +139,18 @@ expectAssignable<FastifyInstance>(fastify({
|
|
|
135
139
|
}),
|
|
136
140
|
validate () {},
|
|
137
141
|
deriveConstraint: () => 'foo'
|
|
142
|
+
},
|
|
143
|
+
withObjectValue: {
|
|
144
|
+
name: 'withObjectValue',
|
|
145
|
+
storage: () => ({
|
|
146
|
+
get: () => () => {},
|
|
147
|
+
set: () => { },
|
|
148
|
+
del: () => { },
|
|
149
|
+
empty: () => { }
|
|
150
|
+
}),
|
|
151
|
+
validate () {},
|
|
152
|
+
deriveConstraint: () => {}
|
|
153
|
+
|
|
138
154
|
}
|
|
139
155
|
}
|
|
140
156
|
}))
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import fastify, {
|
|
2
|
-
|
|
1
|
+
import fastify, {
|
|
2
|
+
FastifyBodyParser,
|
|
3
|
+
FastifyError,
|
|
4
|
+
FastifyInstance,
|
|
5
|
+
FastifyLoggerInstance,
|
|
6
|
+
ValidationResult
|
|
7
|
+
} from '../../fastify'
|
|
8
|
+
import { expectAssignable, expectError, expectNotAssignable, expectType } from 'tsd'
|
|
3
9
|
import { FastifyRequest } from '../../types/request'
|
|
4
10
|
import { FastifyReply } from '../../types/reply'
|
|
5
11
|
import { HookHandlerDoneFunction } from '../../types/hooks'
|
|
@@ -124,3 +130,35 @@ expectType<InitialConfig>(fastify().initialConfig)
|
|
|
124
130
|
expectType<FastifyBodyParser<string>>(server.defaultTextParser)
|
|
125
131
|
|
|
126
132
|
expectType<FastifyBodyParser<string>>(server.getDefaultJsonParser('ignore', 'error'))
|
|
133
|
+
|
|
134
|
+
expectType<string>(server.printRoutes({ includeHooks: true, commonPrefix: false, includeMeta: true }))
|
|
135
|
+
|
|
136
|
+
expectType<string>(server.printRoutes({ includeMeta: ['key1', Symbol('key2')] }))
|
|
137
|
+
|
|
138
|
+
expectType<string>(server.printRoutes())
|
|
139
|
+
|
|
140
|
+
server.decorate<(x: string) => void>('test', function (x: string): void {
|
|
141
|
+
expectType<FastifyInstance>(this)
|
|
142
|
+
})
|
|
143
|
+
server.decorate('test', function (x: string): void {
|
|
144
|
+
expectType<FastifyInstance>(this)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
server.decorateRequest<(x: string, y: number) => void>('test', function (x: string, y: number): void {
|
|
148
|
+
expectType<FastifyRequest>(this)
|
|
149
|
+
})
|
|
150
|
+
server.decorateRequest('test', function (x: string, y: number): void {
|
|
151
|
+
expectType<FastifyRequest>(this)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
server.decorateReply<(x: string) => void>('test', function (x: string): void {
|
|
155
|
+
expectType<FastifyReply>(this)
|
|
156
|
+
})
|
|
157
|
+
server.decorateReply('test', function (x: string): void {
|
|
158
|
+
expectType<FastifyReply>(this)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
expectError(server.decorate<string>('test', true))
|
|
162
|
+
expectError(server.decorate<(myNumber: number) => number>('test', function (myNumber: number): string {
|
|
163
|
+
return ''
|
|
164
|
+
}))
|