Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add support for hapi 18. #286

Merged
merged 4 commits into from
Jul 31, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/instrumentation/@hapi/hapi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict'

module.exports = require('../hapi')
1 change: 1 addition & 0 deletions lib/instrumentations.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = function instrumentations() {
'director': {type: MODULE_TYPE.WEB_FRAMEWORK},
'express': {type: MODULE_TYPE.WEB_FRAMEWORK},
'generic-pool': {type: MODULE_TYPE.GENERIC},
'@hapi/hapi': {type: MODULE_TYPE.WEB_FRAMEWORK},
'hapi': {type: MODULE_TYPE.WEB_FRAMEWORK},
'ioredis': {type: MODULE_TYPE.DATASTORE},
'koa': {module: '@newrelic/koa'},
Expand Down
150 changes: 150 additions & 0 deletions test/versioned/hapi/hapi-post-18/capture-params.tap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
'use strict'

var DESTINATIONS = require('../../../../lib/config/attribute-filter').DESTINATIONS
var tap = require('tap')
var request = require('request')
var helper = require('../../../lib/agent_helper')
var utils = require('./hapi-17-utils')
var HTTP_ATTS = require('../../../lib/fixtures').httpAttributes

tap.test('Hapi capture params support', function(t) {
t.autoend()

var agent = null
var server = null
var port = null

t.beforeEach(function(done) {
agent = helper.instrumentMockedAgent({
attributes: {
enabled: true,
include: ['request.parameters.*']
}
})

server = utils.getServer()

agent.config.attributes.enabled = true
done()
})

t.afterEach(function() {
helper.unloadAgent(agent)
return server.stop()
})

t.test('simple case with no params', function(t) {
agent.on('transactionFinished', function(transaction) {
t.ok(transaction.trace, 'transaction has a trace.')
var attributes = transaction.trace.attributes.get(DESTINATIONS.TRANS_TRACE)
HTTP_ATTS.forEach(function(key) {
t.ok(attributes[key], 'Trace contains expected HTTP attribute: ' + key)
})
})

server.route({
method: 'GET',
path: '/test/',
handler: function() {
t.ok(agent.getTransaction(), 'transaction is available inside route handler')
return { status: 'ok' }
}
})

server.start().then(function() {
port = server.info.port
makeRequest(t, 'http://localhost:' + port + '/test/')
})
})

t.test('case with route params', function(t) {
agent.on('transactionFinished', function(tx) {
t.ok(tx.trace, 'transaction has a trace.')
var attributes = tx.trace.attributes.get(DESTINATIONS.TRANS_TRACE)
t.equal(
attributes['request.parameters.id'], '1337',
'Trace attributes include `id` route param'
)
})

server.route({
method: 'GET',
path: '/test/{id}/',
handler: function() {
t.ok(agent.getTransaction(), 'transaction is available')
return { status: 'ok' }
}
})

server.start().then(function() {
port = server.info.port
makeRequest(t, 'http://localhost:' + port + '/test/1337/')
})
})

t.test('case with query params', function(t) {
agent.on('transactionFinished', function(tx) {
t.ok(tx.trace, 'transaction has a trace.')
var attributes = tx.trace.attributes.get(DESTINATIONS.TRANS_TRACE)
t.equal(
attributes['request.parameters.name'], 'hapi',
'Trace attributes include `name` query param'
)
})

server.route({
method: 'GET',
path: '/test/',
handler: function() {
t.ok(agent.getTransaction(), 'transaction is available')
return { status: 'ok' }
}
})

server.start().then(function() {
port = server.info.port
makeRequest(t, 'http://localhost:' + port + '/test/?name=hapi')
})
})

t.test('case with both route and query params', function(t) {
agent.on('transactionFinished', function(tx) {
t.ok(tx.trace, 'transaction has a trace.')
var attributes = tx.trace.attributes.get(DESTINATIONS.TRANS_TRACE)
t.equal(
attributes['request.parameters.id'], '1337',
'Trace attributes include `id` route param'
)
t.equal(
attributes['request.parameters.name'], 'hapi',
'Trace attributes include `name` query param'
)
})

server.route({
method: 'GET',
path: '/test/{id}/',
handler: function() {
t.ok(agent.getTransaction(), 'transaction is available')
return { status: 'ok' }
}
})

server.start().then(function() {
port = server.info.port
makeRequest(t, 'http://localhost:' + port + '/test/1337/?name=hapi')
})
})
})

function makeRequest(t, uri) {
var params = {
uri: uri,
json: true
}
request.get(params, function(err, res, body) {
t.equal(res.statusCode, 200, "nothing exploded")
t.deepEqual(body, {status: 'ok'}, "got expected response")
t.end()
})
}
223 changes: 223 additions & 0 deletions test/versioned/hapi/hapi-post-18/errors.tap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
'use strict'

var helper = require('../../../lib/agent_helper')
var http = require('http')
var tap = require('tap')
var utils = require('./hapi-17-utils')

var agent
var server
var port

tap.test('Hapi v17 error handling', function(t) {
t.autoend()

t.beforeEach(function(done) {
agent = helper.instrumentMockedAgent()

server = utils.getServer()
done()
})

t.afterEach(function() {
helper.unloadAgent(agent)
return server.stop()
})

t.test('does not report error when handler returns a string', function(t) {
server.route({
method: 'GET',
path: '/test',
handler: function() {
return 'ok'
}
})

runTest(t, function(errors, statusCode) {
t.equals(errors.length, 0, 'should have no errors')
t.equals(statusCode, 200, 'should have a 200 status code')
t.end()
})
})

t.test('reports error when an instance of Error is returned', function(t) {
server.route({
method: 'GET',
path: '/test',
handler: function() {
return Promise.reject(new Error('rejected promise error'))
}
})

runTest(t, function(errors, statusCode) {
t.equals(errors.length, 1, 'should have one error')

t.equals(
errors[0][2],
'rejected promise error',
'should have expected error message'
)

t.equals(statusCode, 500, 'should have expected error code')
t.end()
})
})

t.test('reports error when thrown from a route', function(t) {
server.route({
method: 'GET',
path: '/test',
handler: function() {
throw new Error('thrown error')
}
})

runTest(t, function(errors, statusCode) {
t.equals(errors.length, 1, 'should have one error')
t.equals(errors[0][2], 'thrown error', 'should have expected error message')
t.equals(statusCode, 500, 'should have expected error code')
t.end()
})
})

t.test('reports error when thrown from a middleware', function(t) {
server.ext('onRequest', function() {
throw new Error('middleware error')
})

server.route({
method: 'GET',
path: '/test',
handler: function() {
return 'ok'
}
})

runTest(t, function(errors, statusCode) {
t.equals(errors.length, 1, 'should have one error')
t.equals(errors[0][2], 'middleware error', 'should have expected error message')
t.equals(statusCode, 500, 'should have expected error code')
t.end()
})
})

t.test('reports error when error handler replies with transformed error', (t) => {
server.ext('onPreResponse', (req) => {
t.ok(req.response instanceof Error, 'preResponse has error')
req.response.output.statusCode = 400
return req.response
})

server.route({
method: 'GET',
path: '/test',
handler: () => {
throw new Error('route handler error')
}
})

runTest(t, (errors, statusCode) => {
t.equals(errors.length, 1, 'has 1 reported error')
t.equals(errors[0][2], 'route handler error', 'has correct error message')
t.equals(statusCode, 400, 'has expected 400 status code')
t.end()
})
})

t.test('reports error when error handler continues with transformed response', (t) => {
server.ext('onPreResponse', (req, h) => {
t.ok(req.response instanceof Error, 'preResponse has error')
req.response.output.statusCode = 400
return h.continue
})

server.route({
method: 'GET',
path: '/test',
handler: () => {
throw new Error('route handler error')
}
})

runTest(t, (errors, statusCode) => {
t.equals(errors.length, 1, 'has 1 reported error')
t.equals(errors[0][2], 'route handler error', 'has correct error message')
t.equals(statusCode, 400, 'has expected 400 status code')
t.end()
})
})

t.test('reports error when error handler continues with original response', (t) => {
server.ext('onPreResponse', (req, h) => {
t.ok(req.response instanceof Error, 'preResponse has error')
return h.continue
})

server.route({
method: 'GET',
path: '/test',
handler: () => {
throw new Error('route handler error')
}
})

runTest(t, (errors, statusCode) => {
t.equals(errors.length, 1, 'has 1 reported error')
t.equals(errors[0][2], 'route handler error', 'has correct error message')
t.equals(statusCode, 500, 'has expected 500 status code')
t.end()
})
})

t.test('should not report error when error handler responds', (t) => {
server.ext('onPreResponse', (req) => {
t.ok(req.response.isBoom, 'preResponse has error')
return null
})

server.route({
method: 'GET',
path: '/test',
handler: () => {
throw new Error('route handler error')
}
})

runTest(t, (errors, statusCode) => {
t.equals(errors.length, 0, 'has no reported errors')
t.equals(statusCode, 200, 'has expected 200 status')
t.end()
})
})
})

function runTest(t, callback) {
var statusCode
var errors

agent.on('transactionFinished', function() {
errors = agent.errors.errors
if (statusCode) {
callback(errors, statusCode)
}
})

var endpoint = '/test'
server.start().then(function() {
port = server.info.port
makeRequest(endpoint, function(response) {
statusCode = response.statusCode
if (errors) {
callback(errors, statusCode)
}
response.resume()
})
})
t.tearDown(function() {
server.stop()
})
}

function makeRequest(path, callback) {
http.request({port: port, path: path}, callback).end()
}
Loading