Skip to content

Commit

Permalink
feat(error-response): support passing error objects to HealthCheckErr…
Browse files Browse the repository at this point in the history
…or (#175)
  • Loading branch information
chriswiggins committed Mar 15, 2021
1 parent af62cbe commit b8d8fdb
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 39 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ const options = {
// health check options
healthChecks: {
'/healthcheck': healthCheck, // a function returning a promise indicating service health,
verbatim: true // [optional = false] use object returned from /healthcheck verbatim in response
verbatim: true, // [optional = false] use object returned from /healthcheck verbatim in response,
__unsafeExposeStackTraces: true // [optional = false] return stack traces in error response if healthchecks throw errors
},
caseInsensitive, // [optional] whether given health checks routes are case insensitive (defaults to false)

Expand Down
21 changes: 18 additions & 3 deletions lib/terminus.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,22 @@ async function sendSuccess (res, { info, verbatim }) {
}

async function sendFailure (res, options) {
const { error, onSendFailureDuringShutdown } = options
const { error, onSendFailureDuringShutdown, exposeStackTraces } = options

function replaceErrors (_, value) {
if (value instanceof Error) {
const error = {}

Object.getOwnPropertyNames(value).forEach(function (key) {
if (exposeStackTraces !== true && key === 'stack') return
error[key] = value[key]
})

return error
}

return value
}

if (onSendFailureDuringShutdown) {
await onSendFailureDuringShutdown()
Expand All @@ -46,7 +61,7 @@ async function sendFailure (res, options) {
status: 'error',
error: error,
details: error
}))
}, replaceErrors))
}
res.end(FAILURE_RESPONSE)
}
Expand All @@ -73,7 +88,7 @@ function decorateWithHealthCheck (server, state, options) {
info = await healthCheck()
} catch (error) {
logger('healthcheck failed', error)
return sendFailure(res, { error: error.causes })
return sendFailure(res, { error: error.causes, exposeStackTraces: healthChecks.__unsafeExposeStackTraces })
}
return sendSuccess(res, { info, verbatim: healthChecks.verbatim })
}
Expand Down
140 changes: 108 additions & 32 deletions lib/terminus.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const fetch = require('node-fetch')
const { createTerminus } = require('./terminus')
const { HealthCheckError } = require('./terminus-error')

process.setMaxListeners(100) // Removes node's built in max listeners warning while we're testing

describe('Terminus', () => {
let server

Expand Down Expand Up @@ -214,43 +216,117 @@ describe('Terminus', () => {
expect(loggerRan).to.eql(true)
})

it('includes error on reject', async () => {
let onHealthCheckRan = false

createTerminus(server, {
healthChecks: {
'/health': () => {
onHealthCheckRan = true
const myError = new HealthCheckError('failed', {
fornite: 'client down',
redis: {
disk: 100
}
})
return Promise.reject(myError)
describe('includes error on reject', async () => {
const errors = [
new Error('test error 1'),
new Error('test error 2')
]

it('Returns custom objects as errors', async () => {
let onHealthCheckRan = false

createTerminus(server, {
healthChecks: {
'/health': () => {
onHealthCheckRan = true
const myError = new HealthCheckError('failed', {
fornite: 'client down',
redis: {
disk: 100
}
})
return Promise.reject(myError)
}
}
}
})
server.listen(8000)

const res = await fetch('http://localhost:8000/health')
expect(res.status).to.eql(503)
expect(onHealthCheckRan).to.eql(true)
const json = await res.json()
expect(json).to.deep.eql({
status: 'error',
error: {
fornite: 'client down',
redis: {
disk: 100
}
},
details: {
fornite: 'client down',
redis: {
disk: 100
}
}
})
})
server.listen(8000)

const res = await fetch('http://localhost:8000/health')
expect(res.status).to.eql(503)
expect(onHealthCheckRan).to.eql(true)
const json = await res.json()
expect(json).to.deep.eql({
status: 'error',
error: {
fornite: 'client down',
redis: {
disk: 100
it('Returns error objects as errors and does not include stack traces by default', async () => {
let onHealthCheckRan = false
createTerminus(server, {
healthChecks: {
'/health': () => {
onHealthCheckRan = true
const myError = new HealthCheckError('failed', errors)
return Promise.reject(myError)
}
}
},
details: {
fornite: 'client down',
redis: {
disk: 100
})

server.listen(8000)

const res = await fetch('http://localhost:8000/health')
expect(res.status).to.eql(503)
expect(onHealthCheckRan).to.eql(true)
const json = await res.json()

const errorMessages = [{
message: errors[0].message
}, {
message: errors[1].message
}]

expect(json).to.deep.eql({
status: 'error',
error: errorMessages,
details: errorMessages
})
})

it('Returns error objects as errors and includes stack traces if __unsafeExposeStackTraces is set', async () => {
let onHealthCheckRan = false
createTerminus(server, {
healthChecks: {
'/health': () => {
onHealthCheckRan = true
const myError = new HealthCheckError('failed', errors)
return Promise.reject(myError)
},
__unsafeExposeStackTraces: true
}
}
})

server.listen(8000)

const res = await fetch('http://localhost:8000/health')
expect(res.status).to.eql(503)
expect(onHealthCheckRan).to.eql(true)
const json = await res.json()

const errorMessages = [{
message: errors[0].message,
stack: errors[0].stack.toString()
}, {
message: errors[1].message,
stack: errors[1].stack.toString()
}]

expect(json).to.deep.eql({
status: 'error',
error: errorMessages,
details: errorMessages
})
})
})

Expand Down
5 changes: 2 additions & 3 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ declare module "@godaddy/terminus" {
}

export type HealthCheckMap = {
[key: string]: HealthCheck | boolean;
} | {
verbatim: boolean
verbatim?: boolean
__unsafeExposeStackTraces?: boolean;
[key: string]: HealthCheck | boolean;
}

Expand Down

0 comments on commit b8d8fdb

Please sign in to comment.