import { Request , Response , NextFunction , Application } from 'express' ; import responseTime from 'response-time' ; type FunctionType = ( app : Application ) => ( req : Request , res : Response , next : NextFunction ) => unknown ; interface CollectedData { [ key ] : { reqId : string ; reqRoute : string ; middlewares : { fnName : string ; elapsedTime : number ; } [ ] ; } ; } interface RouteData { [ key ] : CollectedData ; } enum Jagtestercommands { updateLayer , running , endTest } const getMiddleware = ( app ) => { let collectedData = { } ; let routeData = { } ; let isPrototypeChanged = false ; const resetLayerPrototype = ( ) => { app . _router . stack [ 0 ] . __proto__ . handle_request = originalLayerHandleRequest ; isPrototypeChanged = false ; collectedData = { } ; routeData = { } ; } ; const updateLayerPrototype = ( ) => { app . _router . stack [ 0 ] . __proto__ . handle_request = newLayerHandleRequest ; isPrototypeChanged = true ; collectedData = { } ; routeData = { } ; } ; const originalLayerHandleRequest = function handle ( req , res , next ) { const fn = this . handle ; if ( fn . length > 3 ) { return next ( ) ; } try { fn ( req , res , next ) ; } catch ( err ) { next ( err ) ; } } ; const newLayerHandleRequest = function handle ( req , res , next ) { const fn = this . handle ; if ( fn . length > 3 ) { return next ( ) ; } try { const fnName = this . name , reqId = req . headers . jagtesterreqid ? req . headers . jagtesterreqid . toString ( ) : undefined , reqRoute = req . originalUrl ; if ( ! routeData [ reqRoute ] ) { const newCollectedData = { } ; newCollectedData [ reqId ] = { reqId , reqRoute , middlewares : [ ] , } ; routeData [ reqRoute ] = newCollectedData ; } else { if ( reqId && ! routeData [ reqRoute ] [ reqId ] ) { routeData [ reqRoute ] [ reqId ] = { reqId , reqRoute , middlewares : [ ] , } ; } } if ( reqId && ! collectedData [ reqId ] ) { collectedData [ reqId ] = { reqId , reqRoute , middlewares : [ ] , } ; } if ( reqId ) { routeData [ reqRoute ] [ reqId ] . middlewares . push ( { fnName , elapsedTime : 0 , } ) ; collectedData [ reqId ] . middlewares . push ( { fnName , elapsedTime : 0 , } ) ; } const beforeFunctionCall = Date . now ( ) ; fn ( req , res , function ( ) { if ( reqId && routeData [ reqRoute ] && routeData [ reqRoute ] [ reqId ] ) { const lastElIndex = routeData [ reqRoute ] [ reqId ] . middlewares . length - 1 ; routeData [ reqRoute ] [ reqId ] . middlewares [ lastElIndex ] . elapsedTime = Date . now ( ) - beforeFunctionCall ; } next ( ) ; } ) ; } catch ( err ) { next ( err ) ; } } ; return ( req , res , next ) => { const jagtestercommand = + req . headers . jagtestercommand ; switch ( jagtestercommand ) { case Jagtestercommands . running : if ( ! isPrototypeChanged ) { updateLayerPrototype ( ) ; } break ; case Jagtestercommands . updateLayer : updateLayerPrototype ( ) ; return res . json ( { jagtester : true } ) ; case Jagtestercommands . endTest : res . json ( routeData ) ; resetLayerPrototype ( ) ; return ; default : if ( isPrototypeChanged ) { resetLayerPrototype ( ) ; } break ; } return responseTime ( { suffix : false } ) ( req , res , next ) ; } ; } ; export default getMiddleware ; module . exports = getMiddleware ;