91 lines
2.7 KiB
TypeScript
91 lines
2.7 KiB
TypeScript
|
import {ResponseFactory} from "./ResponseFactory"
|
||
|
import {ErrorWithContext, HTTPStatus} from "@extollo/util"
|
||
|
import {Request} from "../lifecycle/Request";
|
||
|
import * as api from "./api"
|
||
|
|
||
|
export function error(
|
||
|
error: Error | string,
|
||
|
status: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR,
|
||
|
output: 'json' | 'html' | 'auto' = 'auto'
|
||
|
): ErrorResponseFactory {
|
||
|
if ( typeof error === 'string' ) error = new Error(error)
|
||
|
return new ErrorResponseFactory(error, status, output)
|
||
|
}
|
||
|
|
||
|
export class ErrorResponseFactory extends ResponseFactory {
|
||
|
protected targetMode: 'json' | 'html' | 'auto' = 'auto'
|
||
|
|
||
|
constructor(
|
||
|
public readonly error: Error,
|
||
|
status: HTTPStatus,
|
||
|
output: 'json' | 'html' | 'auto' = 'auto'
|
||
|
) {
|
||
|
super()
|
||
|
this.status(status)
|
||
|
this.mode(output)
|
||
|
}
|
||
|
|
||
|
public mode(output: 'json' | 'html' | 'auto'): ErrorResponseFactory {
|
||
|
this.targetMode = output
|
||
|
return this
|
||
|
}
|
||
|
|
||
|
public async write(request: Request) {
|
||
|
request = await super.write(request)
|
||
|
const wants = request.wants()
|
||
|
|
||
|
if ( this.targetMode === 'json' || (this.targetMode === 'auto' && wants === 'json') ) {
|
||
|
request.response.setHeader('Content-Type', 'application/json')
|
||
|
request.response.body = this.buildJSON(this.error)
|
||
|
} else if ( this.targetMode === 'html' || (this.targetMode === 'auto' && (wants === 'html' || wants === 'unknown')) ) {
|
||
|
request.response.setHeader('Content-Type', 'text/html')
|
||
|
request.response.body = this.buildHTML(this.error)
|
||
|
}
|
||
|
|
||
|
// FIXME XML support
|
||
|
|
||
|
return request
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Build the HTML display for the given error.
|
||
|
* @param {Error} error
|
||
|
* @return string
|
||
|
*/
|
||
|
protected buildHTML(error: Error) {
|
||
|
let context: any
|
||
|
if ( error instanceof ErrorWithContext ) {
|
||
|
context = error.context
|
||
|
if ( error.originalError ) {
|
||
|
error = error.originalError
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let str = `
|
||
|
<b>Sorry, an unexpected error occurred while processing your request.</b>
|
||
|
<br>
|
||
|
<pre><code>
|
||
|
Name: ${error.name}
|
||
|
Message: ${error.message}
|
||
|
Stack trace:
|
||
|
- ${error.stack ? error.stack.split(/\s+at\s+/).slice(1).join('<br> - ') : 'none'}
|
||
|
</code></pre>
|
||
|
`
|
||
|
|
||
|
if ( context && typeof context === 'object' ) {
|
||
|
str += `
|
||
|
<pre><code>
|
||
|
Context:
|
||
|
${Object.keys(context).map(key => ` - ${key} : ${context[key]}`)}
|
||
|
</code></pre>
|
||
|
`
|
||
|
}
|
||
|
|
||
|
return str
|
||
|
}
|
||
|
|
||
|
protected buildJSON(error: Error) {
|
||
|
return JSON.stringify(api.error(error))
|
||
|
}
|
||
|
}
|