forked from newrelic/node-newrelic
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfastify.js
173 lines (149 loc) · 4.96 KB
/
fastify.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
'use strict'
const semver = require('semver')
const {
buildMiddlewareSpecForRouteHandler,
buildMiddlewareSpecForMiddlewareFunction
} = require('./fastify/spec-builders')
const { MiddlewareMounterSpec } = require('../shim/specs')
/**
* These are the events that occur during a fastify
* request
* see: https://www.fastify.io/docs/latest/Lifecycle/
*
* Note: preSerialization and onSend happen after the route handler
* executes. `onResponse` does not execute until after the client
* sends the response so it'll never be in scope of the transaction
*/
const REQUEST_HOOKS = [
'onRequest',
'preParsing',
'preValidation',
'preHandler',
'preSerialization',
'onSend',
'onResponse',
'onError'
]
/**
* Sets up fastify route handler
*
* Fastify's onRoute hook will fire whenever
* a route is registered. This is the most straight
* forward way to get at a fastify route definition.
* Not only are we _not_ relying on private implementations
* that could change, fastify is pretty good about protecting
* those private implementations from access, and getting
* at them would require a lot of gymnastics and hard to
* maintain code
*
* @param shim
* @param fastify
*/
const setupRouteHandler = (shim, fastify) => {
fastify.addHook('onRoute', (routeOptions) => {
if (!routeOptions.handler) {
return
}
/**
* recordMiddleware handler call
*
* The WebFramework shim treats the main route handler like any other
* i.e. dont be confused by the call to recordMiddleware -- we don't
* have a recordRouteHandler, everything goes through recordMiddleware
*/
const newRouteHandler = shim.recordMiddleware(
routeOptions.handler,
buildMiddlewareSpecForRouteHandler(shim, routeOptions.path)
)
routeOptions.handler = newRouteHandler
})
shim.wrap(fastify, 'addHook', function addWrapHook(shim, fn) {
return function wrappedAddHook() {
const args = shim.argsToArray.apply(shim, arguments)
const hookName = args[0]
if (REQUEST_HOOKS.includes(hookName)) {
const middlewareFunction = args[1]
const name = `${hookName}/${shim.getName(middlewareFunction)}`
const middlewareSpec = buildMiddlewareSpecForMiddlewareFunction(shim, name)
const newMiddlewareFunction = shim.recordMiddleware(middlewareFunction, middlewareSpec)
args[1] = newMiddlewareFunction
}
return fn.apply(this, args)
}
})
}
module.exports = function initialize(agent, fastify, moduleName, shim) {
shim.setFramework(shim.FASTIFY)
const fastifyVersion = shim.pkgVersion
const isv3Plus = semver.satisfies(fastifyVersion, '>=3.0.0')
/**
* Fastify exports a function, so we need to use wrapExport
*/
const wrappedExport = shim.wrapExport(fastify, function wrapFastifyModule(shim, fn) {
return function wrappedFastifyModule() {
// call original function to get the fastify object (which is singleton-ish)
const fastifyForWrapping = fn.apply(this, arguments)
setupRouteHandler(shim, fastifyForWrapping)
setupMiddlewareHandlers(shim, fastifyForWrapping, isv3Plus)
return fastifyForWrapping
}
})
if (isv3Plus) {
setupExports(fastify, wrappedExport)
}
}
function setupMiddlewareHandlers(shim, fastify, isv3Plus) {
const mounterSpec = new MiddlewareMounterSpec({
route: shim.FIRST,
wrapper: wrapMiddleware
})
if (isv3Plus) {
// Fastify v3+ does not ship with traditional Node.js middleware mounting.
// This style is accomplished leveraging decorators. Both middie (which was built-in in v2)
// and fastify-express mount a 'use' function for mounting middleware.
shim.wrap(fastify, 'decorate', function wrapDecorate(shim, fn) {
return function wrappedDecorate() {
const name = arguments[0]
if (name !== 'use') {
return fn.apply(this, arguments)
}
const args = shim.argsToArray.apply(shim, arguments)
args[1] = shim.wrapMiddlewareMounter(args[1], mounterSpec)
return fn.apply(this, args)
}
})
} else {
shim.wrapMiddlewareMounter(fastify, 'use', mounterSpec)
}
}
function wrapMiddleware(shim, middleware, name, route) {
if (shim.isWrapped(middleware)) {
return middleware
}
// prefixing the segment name for middleware execution
// with the Fastify lifecycle hook
const segmentName = `onRequest/${name}`
const spec = buildMiddlewareSpecForMiddlewareFunction(shim, segmentName, route)
return shim.recordMiddleware(middleware, spec)
}
/**
* module.exports = fastify
* module.exports.fastify = fastify
* module.exports.default = fastify
*
* @param original
* @param wrappedExport
*/
function setupExports(original, wrappedExport) {
wrappedExport.fastify = original.fastify
if (original.fastify) {
wrappedExport.fastify = wrappedExport
}
if (original.default) {
wrappedExport.default = wrappedExport
}
}