UNPKG

newrelic

Version:
172 lines (147 loc) 4.96 kB
/* * 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(...args) { 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(...args) { const name = args[0] if (name !== 'use') { return fn.apply(this, args) } 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 } }