Skip to content

Commit 8496d32

Browse files
committed
feat(runtime): support better error formatting
This adds both browser detection for pretty error messages and adds support for string error messages fix #64, fix #65
1 parent 86da166 commit 8496d32

File tree

5 files changed

+92
-6
lines changed

5 files changed

+92
-6
lines changed

__tests__/runtime/route.test.ts

+61-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Request as ExpressRequest,
66
Response as ExpressResponse,
77
} from 'express';
8+
import { Request as MockRequest } from 'jest-express/lib/request';
89
import { Response as MockResponse } from 'jest-express/lib/response';
910
import { twiml } from 'twilio';
1011
import { StartCliConfig } from '../../src/config/start';
@@ -25,12 +26,70 @@ const mockResponse = (new MockResponse() as unknown) as ExpressResponse;
2526
mockResponse.type = jest.fn(() => mockResponse);
2627

2728
describe('handleError function', () => {
28-
test('calls correct response methods', () => {
29+
test('returns string error', () => {
30+
const mockRequest = (new MockRequest() as unknown) as ExpressRequest;
31+
mockRequest['useragent'] = {
32+
isDesktop: true,
33+
isMobile: false,
34+
} as ExpressUseragent.UserAgent;
35+
36+
handleError('string error', mockRequest, mockResponse);
37+
38+
expect(mockResponse.status).toHaveBeenCalledWith(500);
39+
expect(mockResponse.send).toHaveBeenCalledWith('string error');
40+
});
41+
42+
test('handles objects as error argument', () => {
43+
const mockRequest = (new MockRequest() as unknown) as ExpressRequest;
44+
mockRequest['useragent'] = {
45+
isDesktop: true,
46+
isMobile: false,
47+
} as ExpressUseragent.UserAgent;
48+
49+
handleError({ errorMessage: 'oh no' }, mockRequest, mockResponse);
50+
51+
expect(mockResponse.status).toHaveBeenCalledWith(500);
52+
expect(mockResponse.send).toHaveBeenCalledWith({ errorMessage: 'oh no' });
53+
});
54+
55+
test('wraps error object for desktop requests', () => {
56+
const mockRequest = (new MockRequest() as unknown) as ExpressRequest;
57+
mockRequest['useragent'] = {
58+
isDesktop: true,
59+
isMobile: false,
60+
} as ExpressUseragent.UserAgent;
61+
2962
const err = new Error('Failed to execute');
30-
handleError(err, (mockResponse as unknown) as ExpressResponse);
63+
handleError(err, mockRequest, mockResponse);
3164
expect(mockResponse.status).toHaveBeenCalledWith(500);
3265
expect(mockResponse.send).toHaveBeenCalledWith(wrapErrorInHtml(err));
3366
});
67+
68+
test('wraps error object for mobile requests', () => {
69+
const mockRequest = (new MockRequest() as unknown) as ExpressRequest;
70+
mockRequest['useragent'] = {
71+
isDesktop: false,
72+
isMobile: true,
73+
} as ExpressUseragent.UserAgent;
74+
75+
const err = new Error('Failed to execute');
76+
handleError(err, mockRequest, mockResponse);
77+
expect(mockResponse.status).toHaveBeenCalledWith(500);
78+
expect(mockResponse.send).toHaveBeenCalledWith(wrapErrorInHtml(err));
79+
});
80+
81+
test('returns string version of error for other requests', () => {
82+
const mockRequest = (new MockRequest() as unknown) as ExpressRequest;
83+
mockRequest['useragent'] = {
84+
isDesktop: false,
85+
isMobile: false,
86+
} as ExpressUseragent.UserAgent;
87+
88+
const err = new Error('Failed to execute');
89+
handleError(err, mockRequest, mockResponse);
90+
expect(mockResponse.status).toHaveBeenCalledWith(500);
91+
expect(mockResponse.send).toHaveBeenCalledWith(err.toString());
92+
});
3493
});
3594

3695
describe('constructEvent function', () => {

package-lock.json

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"debug": "^3.1.0",
5555
"dotenv": "^6.2.0",
5656
"express": "^4.16.3",
57+
"express-useragent": "^1.0.13",
5758
"fast-redact": "^1.5.0",
5859
"got": "^9.6.0",
5960
"inquirer": "^6.5.0",
@@ -82,6 +83,7 @@
8283
"@types/common-tags": "^1.8.0",
8384
"@types/debug": "^4.1.4",
8485
"@types/dotenv": "^6.1.1",
86+
"@types/express-useragent": "^0.2.21",
8587
"@types/got": "^9.6.0",
8688
"@types/jest": "^24.0.15",
8789
"@types/listr": "^0.14.0",

src/runtime/route.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,22 @@ export function constructGlobalScope(config: StartCliConfig): void {
6565
}
6666

6767
export function handleError(
68-
err: Error,
68+
err: Error | string | object,
69+
req: ExpressRequest,
6970
res: ExpressResponse,
7071
functionFilePath?: string
7172
) {
7273
res.status(500);
73-
res.type('text/html');
74-
res.send(wrapErrorInHtml(err, functionFilePath));
74+
if (!(err instanceof Error)) {
75+
res.send(err);
76+
} else {
77+
if (req.useragent && (req.useragent.isDesktop || req.useragent.isMobile)) {
78+
res.type('text/html');
79+
res.send(wrapErrorInHtml(err, functionFilePath));
80+
} else {
81+
res.send(err.toString());
82+
}
83+
}
7584
}
7685

7786
export function isTwiml(obj: object): boolean {
@@ -131,7 +140,7 @@ export function functionToRoute(
131140
) {
132141
debug('Function execution %s finished', req.path);
133142
if (err) {
134-
handleError(err, res, functionFilePath);
143+
handleError(err, req, res, functionFilePath);
135144
return;
136145
}
137146
handleSuccess(responseObject, res);

src/runtime/server.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import express, {
66
Request as ExpressRequest,
77
Response as ExpressResponse,
88
} from 'express';
9+
import userAgentMiddleware from 'express-useragent';
910
import nocache from 'nocache';
1011
import { StartCliConfig } from '../config/start';
1112
import { wrapErrorInHtml } from '../utils/error-html';
@@ -48,6 +49,7 @@ export async function createServer(
4849
debug('Starting server with config: %p', config);
4950

5051
const app = express();
52+
app.use(userAgentMiddleware.express());
5153
app.use(bodyParser.urlencoded({ extended: false }));
5254
app.use(bodyParser.json());
5355
app.get('/favicon.ico', (req, res) => {

0 commit comments

Comments
 (0)