Skip to content

Commit

Permalink
feat: add support for rendering statusPages from exception handler
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Nov 11, 2019
1 parent 83c1565 commit fa4410b
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 1 deletion.
23 changes: 22 additions & 1 deletion src/HttpExceptionHandler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ export abstract class HttpExceptionHandler {
'E_ROUTE_NOT_FOUND',
]

/**
* Map of status pages to render, instead of making the
* regular response
*/
protected statusPages: { [key: number]: string } = {}

/**
* A flag to disable status pages during development
*/
protected disableStatusPagesInDevelopment: boolean = false

constructor (protected logger: LoggerContract) {
}

Expand Down Expand Up @@ -96,13 +107,23 @@ export abstract class HttpExceptionHandler {
* which the app is runing
*/
protected async makeHtmlResponse (error: any, ctx: HttpContextContract) {
if (process.env.NODE_ENV === 'development') {
if (
process.env.NODE_ENV === 'development' &&
(!this.statusPages[error.status] || this.disableStatusPagesInDevelopment)
) {
const Youch = require('youch')
const html = await new Youch(error, ctx.request.request).toHTML()
ctx.response.status(error.status).send(html)
return
}

/**
* Render status pages
*/
if (ctx['view'] && this.statusPages[error.status]) {
ctx['view'].render(this.statusPages[error.status], { error })
}

ctx.response.status(error.status).send(`<h1> ${error.message} </h1>`)
}

Expand Down
143 changes: 143 additions & 0 deletions test/exception-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,147 @@ test.group('HttpExceptionHandler', () => {
const response = await handler.handle(new InvalidAuth('bad request'), ctx)
assert.equal(response, 'foo')
})

test('render status page when defined', async (assert) => {
assert.plan(2)

class AppHandler extends HttpExceptionHandler {
protected statusPages = {
404: '404.edge',
}

protected context () {
return { username: 'virk' }
}
}

class InvalidAuth extends Exception {
constructor (message) {
super(message, 404, 'E_INVALID_AUTH')
}
}

const logger = new FakeLogger(loggerConfig)
const handler = new AppHandler(logger)

const ctx = HttpContext.create('/', {}, logger, new Profiler({}).create(''), encryption)
ctx['view'] = {
render (view, data) {
assert.equal(view, '404.edge')
assert.equal(data.error.message, 'E_INVALID_AUTH: bad request')
},
}

ctx.request.request.headers = { accept: 'text/html' }
await handler.handle(new InvalidAuth('bad request'), ctx)
})

test('do not render status page when content negotiation passes for json', async (assert) => {
class AppHandler extends HttpExceptionHandler {
protected statusPages = {
404: '404.edge',
}

protected context () {
return { username: 'virk' }
}
}

class InvalidAuth extends Exception {
constructor (message) {
super(message, 404, 'E_INVALID_AUTH')
}
}

const logger = new FakeLogger(loggerConfig)
const handler = new AppHandler(logger)

const ctx = HttpContext.create('/', {}, logger, new Profiler({}).create(''), encryption)
ctx['view'] = {
render () {
throw new Error('Not expected')
},
}

ctx.request.request.headers = { accept: 'application/json' }
await handler.handle(new InvalidAuth('bad request'), ctx)
assert.deepEqual(ctx.response.lazyBody!.args, [{ message: 'E_INVALID_AUTH: bad request' }, false])
})

test('do not render status page when disabled for development mode', async (assert) => {
process.env.NODE_ENV = 'development'

class AppHandler extends HttpExceptionHandler {
protected statusPages = {
404: '404.edge',
}

protected disableStatusPagesInDevelopment = true

protected context () {
return { username: 'virk' }
}
}

class InvalidAuth extends Exception {
constructor (message) {
super(message, 404, 'E_INVALID_AUTH')
}
}

const logger = new FakeLogger(loggerConfig)
const handler = new AppHandler(logger)

const ctx = HttpContext.create('/', {}, logger, new Profiler({}).create(''), encryption)
ctx['view'] = {
render () {
throw new Error('Not expected')
},
}

ctx.request.request.headers = { accept: 'text/html' }
await handler.handle(new InvalidAuth('bad request'), ctx)
assert.isTrue(/youch/.test(ctx.response.lazyBody!.args[0]))

delete process.env.NODE_ENV
})

test('always render status page when in production mode', async (assert) => {
assert.plan(2)

process.env.NODE_ENV = 'production'

class AppHandler extends HttpExceptionHandler {
protected statusPages = {
404: '404.edge',
}

protected disableStatusPagesInDevelopment = true

protected context () {
return { username: 'virk' }
}
}

class InvalidAuth extends Exception {
constructor (message) {
super(message, 404, 'E_INVALID_AUTH')
}
}

const logger = new FakeLogger(loggerConfig)
const handler = new AppHandler(logger)

const ctx = HttpContext.create('/', {}, logger, new Profiler({}).create(''), encryption)
ctx['view'] = {
render (view, data) {
assert.equal(view, '404.edge')
assert.equal(data.error.message, 'E_INVALID_AUTH: bad request')
},
}

ctx.request.request.headers = { accept: 'text/html' }
await handler.handle(new InvalidAuth('bad request'), ctx)
delete process.env.NODE_ENV
})
})

0 comments on commit fa4410b

Please sign in to comment.