strong-error-handler 1.0.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,36 @@
1
+ <!--
2
+ - Please ask questions at https://groups.google.com/forum/#!forum/loopbackjs or
3
+ https://gitter.im/strongloop/loopback
4
+
5
+ - Immediate support is available through our subscription plans, see
6
+ https://strongloop.com/api-connect-faqs/
7
+ -->
8
+
9
+ ### Bug or feature request
10
+
11
+ <!--
12
+ Mark your choice with an "x" (eg. [x], NOT [*]).
13
+ -->
14
+
15
+ - [ ] Bug
16
+ - [ ] Feature request
17
+
18
+ ### Description of feature (or steps to reproduce if bug)
19
+
20
+
21
+
22
+ ### Link to sample repo to reproduce issue (if bug)
23
+
24
+
25
+
26
+ ### Expected result
27
+
28
+
29
+
30
+ ### Actual result (if bug)
31
+
32
+
33
+
34
+ ### Additional information (Node.js version, LoopBack version, etc)
35
+
36
+
@@ -0,0 +1,24 @@
1
+ ### Description
2
+
3
+
4
+ #### Related issues
5
+
6
+ <!--
7
+ Please use the following link syntaxes:
8
+
9
+ - #49 (to reference issues in the current repository)
10
+ - strongloop/loopback#49 (to reference issues in another repository)
11
+ -->
12
+
13
+ - None
14
+
15
+ ### Checklist
16
+
17
+ <!--
18
+ Please mark your choice with an "x" (i.e. [x], see
19
+ https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments)
20
+ -->
21
+
22
+ - [ ] New tests added or existing tests modified to cover all changes
23
+ - [ ] Code conforms with the [style
24
+ guide](http://loopback.io/doc/en/contrib/style-guide.html)
package/CHANGES.md CHANGED
@@ -1,3 +1,57 @@
1
+ 2017-01-30, Version 1.2.1
2
+ =========================
3
+
4
+ * Stop adding safeFields to original options arg (Miroslav Bajtoš)
5
+
6
+
7
+ 2017-01-30, Version 1.2.0
8
+ =========================
9
+
10
+ * Support options.safeFields (Zak Barbuto)
11
+
12
+ * Readme cleanup (#36) (Rand McKinney)
13
+
14
+ * xml support added (Ahmet Ozisik)
15
+
16
+ * Update paid support URL (Siddhi Pai)
17
+
18
+ * Downstream ignore dashboard-controller (Simon Ho)
19
+
20
+ * Update pt translation file (Candy)
21
+
22
+ * Make the examples more clear (Amir Jafarian)
23
+
24
+ * Fix readme (Amir Jafarian)
25
+
26
+
27
+ 2016-10-07, Version 1.1.1
28
+ =========================
29
+
30
+ * Update pt translation file (Candy)
31
+
32
+ * Update translation files - round#2 (Candy)
33
+
34
+ * globalization: add translated strings (gunjpan)
35
+
36
+
37
+ 2016-09-05, Version 1.1.0
38
+ =========================
39
+
40
+ * Fix minor Syntax error (Loay)
41
+
42
+ * Globalize strong-error-handler (David Cheung)
43
+
44
+ * Update eslint infrastructure (Loay)
45
+
46
+ * Add documentation (Loay)
47
+
48
+ * Improve grammar in readme. (Richard Pringle)
49
+
50
+ * Test with express instead of http server (David Cheung)
51
+
52
+ * HTML response for accepted headers (David Cheung)
53
+
54
+
1
55
  2016-05-26, Version 1.0.1
2
56
  =========================
3
57
 
package/README.md CHANGED
@@ -1,26 +1,25 @@
1
1
  # strong-error-handler
2
2
 
3
- Error handler for use in development (debug) and production environments.
3
+ This package is an error handler for use in both development (debug) and production environments.
4
4
 
5
- - When run in production mode, error responses are purposely undetailed
6
- in order to prevent leaking sensitive information.
5
+ In production mode, `strong-error-handler` omits details from error responses to prevent leaking sensitive information:
7
6
 
8
- - When in debug mode, detailed information such as stack traces
9
- are returned in the HTTP responses.
7
+ - For 5xx errors, the output contains only the status code and the status name from the HTTP specification.
8
+ - For 4xx errors, the output contains the full error message (`error.message`) and the contents of the `details`
9
+ property (`error.details`) that `ValidationError` typically uses to provide machine-readable details
10
+ about validation problems.
10
11
 
11
- JSON is the only supported response format at this time.
12
+ In debug mode, `strong-error-handler` returns full error stack traces and internal details of any error objects to the client in the HTTP responses.
12
13
 
13
- *There are plans to support other formats such as Text, HTML, and XML.*
14
-
15
- ## Install
14
+ ## Installation
16
15
 
17
16
  ```bash
18
- $ npm install strong-error-handler
17
+ $ npm install --save strong-error-handler
19
18
  ```
20
19
 
21
- ## Usage
20
+ ## Use
22
21
 
23
- In an express-based application:
22
+ In an Express-based application:
24
23
 
25
24
  ```js
26
25
  var express = require('express');
@@ -28,45 +27,177 @@ var errorHandler = require('strong-error-handler');
28
27
 
29
28
  var app = express();
30
29
  // setup your routes
31
- app.use(errorHandler({ /* options, see below */ }));
30
+ // `options` are set to default values. For more info, see `options` below.
31
+ // app.use(errorHandler({ /* options, see below */ }));
32
+ app.use(errorHandler({
33
+ debug: app.get('env') === 'development',
34
+ log: true,
35
+ }));
32
36
 
33
37
  app.listen(3000);
34
38
  ```
35
39
 
36
- In LoopBack applications, add the following entry to your
37
- `server/middleware.json` file.
40
+ In LoopBack applications, add the following entry to `server/middleware.json`:
38
41
 
39
42
  ```json
40
43
  {
41
44
  "final:after": {
42
45
  "strong-error-handler": {
43
46
  "params": {
47
+ "debug": false,
48
+ "log": true
44
49
  }
45
50
  }
46
51
  }
47
52
  }
48
53
  ```
49
54
 
50
- ## Options
55
+ In general, `strong-error-handler` must be the last middleware function registered.
56
+
57
+ The above configuration will log errors to the server console, but not return stack traces in HTTP responses.
58
+ For details on configuration options, see below.
59
+
60
+ ### Response format and content type
61
+
62
+ The `strong-error-handler` package supports HTML and JSON responses:
51
63
 
52
- #### debug
64
+ - When the object is a standard Error object, it returns the string provided by the stack property in HTML/text
65
+ responses.
66
+ - When the object is a non-Error object, it returns the result of `util.inspect` in HTML/text responses.
67
+ - For JSON responses, the result is an object with all enumerable properties from the object in the response.
53
68
 
54
- `boolean`, defaults to `false`.
69
+ The content type of the response depends on the request's `Accepts` header.
55
70
 
56
- When enabled, HTTP responses include all error properties, including
57
- sensitive data such as file paths, URLs and stack traces.
71
+ - For Accepts header `json` or `application/json`, the response content type is JSON.
72
+ - For Accepts header `html` or `text/html`, the response content type is HTML.
58
73
 
59
- #### log
74
+ *There are plans to support other formats such as Text and XML.*
60
75
 
61
- `boolean`, defaults to `true`.
76
+ ## Options
77
+
78
+ | Option | Type | Default | Description |
79
+ | ---- | ---- | ---- | ---- |
80
+ | debug | Boolean&nbsp;&nbsp;&nbsp; | `false` | If `true`, HTTP responses include all error properties, including sensitive data such as file paths, URLs and stack traces. See [Example output](#example) below. |
81
+ | log | Boolean | `true` | If `true`, all errors are printed via `console.error`, including an array of fields (custom error properties) that are safe to include in response messages (both 4xx and 5xx). <br/> If `false`, sends only the error back in the response. |
82
+
83
+ ### Customizing log format
62
84
 
63
- When enabled, all errors are printed via `console.error`.
85
+ **Express**
64
86
 
65
- Customization of the log format is intentionally not allowed. If you would like
66
- to use a different format/logger, disable this option and add your own custom
67
- error-handling middleware.
87
+ To use a different log format, add your own custom error-handling middleware then disable `errorHandler.log`.
88
+ For example, in an Express application:
68
89
 
69
90
  ```js
70
91
  app.use(myErrorLogger());
71
92
  app.use(errorHandler({ log: false }));
72
93
  ```
94
+
95
+ In general, add `strong-error-handler` as the last middleware function, just before calling `app.listen()`.
96
+
97
+ **LoopBack**
98
+
99
+ For LoopBack applications, put custom error-logging middleware in a separate file; for example, `server/middleware/error-logger.js`:
100
+
101
+ ```
102
+ module.exports = function(options) {
103
+ return function logError(err, req, res, next) {
104
+ console.log('unhandled error' ,err);
105
+ next(err);
106
+ };
107
+ };
108
+ ```
109
+
110
+ Then in `server/middleware.json`, specify your custom error logging function as follows:
111
+
112
+ ```
113
+ {
114
+ // ...
115
+ "final:after": {
116
+ "./middleware/error-logger": {},
117
+ "strong-error-handler": {
118
+ "params": {
119
+ log: false
120
+ }
121
+ }
122
+ }
123
+ ```
124
+
125
+ The default `middleware.development.json` file explicitly enables logging in strong-error-handler params, so you will need to change that file too.
126
+
127
+ ## Migration from old LoopBack error handler
128
+
129
+ NOTE: This is only required for applications scaffolded with old versions of the `slc loopback` tool.
130
+
131
+ To migrate a LoopBack 2.x application to use `strong-error-handler`:
132
+
133
+ 1. In `package.json` dependencies, remove `"errorhandler": "^x.x.x”,`
134
+ 1. Install the new error handler by entering the command:
135
+ <pre>npm install --save strong-error-handler</pre>
136
+ 1. In `server/config.json`, remove:
137
+ <pre>
138
+ "remoting": {
139
+ ...
140
+ "errorHandler": {
141
+ "disableStackTrace": false
142
+ }</pre>
143
+ and replace it with:
144
+ <pre>
145
+ "remoting": {
146
+ ...,
147
+ "rest": {
148
+ "handleErrors": false
149
+ }</pre>
150
+ 1. In `server/middleware.json`, remove:
151
+ <pre>
152
+ "final:after": {
153
+ "loopback#errorHandler": {}
154
+ }</pre>
155
+ and replace it with:
156
+ <pre>
157
+ "final:after": {
158
+ "strong-error-handler": {}
159
+ }</pre>
160
+ 1. Delete `server/middleware.production.json`.
161
+ 1. Create `server/middleware.development.json` containing:
162
+ <pre>
163
+ "final:after": {
164
+ "strong-error-handler": {
165
+ "params": {
166
+ "debug": true,
167
+ "log": true
168
+ }
169
+ }
170
+ }
171
+ </pre>
172
+
173
+ For more information, see
174
+ [Migrating apps to LoopBack 3.0](http://loopback.io/doc/en/lb3/Migrating-to-3.0.html#update-use-of-rest-error-handler).
175
+
176
+ ## Example
177
+
178
+ Error generated when `debug: false` :
179
+
180
+ ```
181
+ { error: { statusCode: 500, message: 'Internal Server Error' } }
182
+ ```
183
+
184
+ Error generated when `debug: true` :
185
+
186
+ ```
187
+ { error:
188
+ { statusCode: 500,
189
+ name: 'Error',
190
+ message: 'a test error message',
191
+ stack: 'Error: a test error message
192
+ at Context.<anonymous> (User/strong-error-handler/test/handler.test.js:220:21)
193
+ at callFnAsync (User/strong-error-handler/node_modules/mocha/lib/runnable.js:349:8)
194
+ at Test.Runnable.run (User/strong-error-handler/node_modules/mocha/lib/runnable.js:301:7)
195
+ at Runner.runTest (User/strong-error-handler/node_modules/mocha/lib/runner.js:422:10)
196
+ at User/strong-error-handler/node_modules/mocha/lib/runner.js:528:12
197
+ at next (User/strong-error-handler/node_modules/mocha/lib/runner.js:342:14)
198
+ at User/strong-error-handler/node_modules/mocha/lib/runner.js:352:7
199
+ at next (User/strong-error-handler/node_modules/mocha/lib/runner.js:284:14)
200
+ at Immediate._onImmediate (User/strong-error-handler/node_modules/mocha/lib/runner.js:320:5)
201
+ at tryOnImmediate (timers.js:543:15)
202
+ at processImmediate [as _immediateCallback] (timers.js:523:5)' }}
203
+ ```
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "Nicht verarbeitetes Array mit Fehlern für Anforderung {0} {1}\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "Nicht verarbeiteter Fehler für Anforderung {0} {1}: {2}"
4
+ }
5
+
@@ -0,0 +1,4 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "Unhandled array of errors for request {0} {1}\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "Unhandled error for request {0} {1}: {2}"
4
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "Matriz de errores no gestionada para la solicitud {0} {1}\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "Error no gestionado para la solicitud {0} {1}: {2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "Tableau d'erreurs non traitées pour la demande {0} {1}\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "Erreur non traitée pour la demande {0} {1} : {2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "Array di errori non gestito per la richieste {0} {1}\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "Errore non gestito per la richiesta {0} {1}: {2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "要求 {0} {1} に対する一連のエラーが処理されていません\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "要求 {0} {1} のエラーが処理されていません: {2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "요청 {0} {1}에 대해 처리되지 않는 오류 배열\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "요청 {0} {1}에 대해 처리되지 않는 오류: {2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "Onverwerkte array van fouten voor opdracht {0} {1}\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "Onverwerkte fout opgetreden voor opdracht {0} {1}: {2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "Matriz não manipulada de erros para solicitação {0} {1}\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "Erro não manipulado para solicitação {0} {1}: {2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "İstek için işlenemeyen hata dizisi: {0} {1}\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "{0} {1} isteği için işlenemeyen hata: {2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "未处理请求 {0} {1} 的错误数组\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "未处理请求 {0} {1} 的错误:{2}"
4
+ }
5
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "85312d2b987e2e44001db3d6963a1364": "要求 {0} {1} 發生許多未處理的錯誤\n",
3
+ "adcbd4c562b19f45fb5d3427619646d5": "要求 {0} {1} 發生未處理的錯誤:{2}"
4
+ }
5
+
package/lib/clone.js CHANGED
@@ -1,3 +1,4 @@
1
+ 'use strict';
1
2
  module.exports = cloneAllProperties;
2
3
 
3
4
  /**
@@ -0,0 +1,91 @@
1
+ // Copyright IBM Corp. 2016. All Rights Reserved.
2
+ // Node module: strong-error-handler
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ 'use strict';
7
+ var accepts = require('accepts');
8
+ var debug = require('debug')('strong-error-handler:http-response');
9
+ var sendJson = require('./send-json');
10
+ var sendHtml = require('./send-html');
11
+ var sendXml = require('./send-xml');
12
+ var util = require('util');
13
+
14
+ module.exports = negotiateContentProducer;
15
+
16
+ /**
17
+ * Handles req.accepts and req.query._format and options.defaultType
18
+ * to resolve the correct operation
19
+ *
20
+ * @param req request object
21
+ * @param {Object} options options of strong-error-handler
22
+ * @returns {Function} Opeartion function with signature `fn(res, data)`
23
+ */
24
+ function negotiateContentProducer(req, options) {
25
+ var SUPPORTED_TYPES = [
26
+ 'application/json', 'json',
27
+ 'text/html', 'html',
28
+ 'text/xml', 'xml',
29
+ ];
30
+
31
+ options = options || {};
32
+ var defaultType = 'json';
33
+
34
+ // checking if user provided defaultType is supported
35
+ if (options.defaultType) {
36
+ if (SUPPORTED_TYPES.indexOf(options.defaultType) > -1) {
37
+ debug('Accepting options.defaultType `%s`', options.defaultType);
38
+ defaultType = options.defaultType;
39
+ } else {
40
+ debug('defaultType: `%s` is not supported, ' +
41
+ 'falling back to defaultType: `%s`', options.defaultType, defaultType);
42
+ }
43
+ }
44
+
45
+ // decide to use resolvedType or defaultType
46
+ // Please note that accepts assumes the order of content-type is provided
47
+ // in the priority returned
48
+ // example
49
+ // Accepts: text/html, */*, application/json ---> will resolve as text/html
50
+ // Accepts: application/json, */*, text/html ---> will resolve as application/json
51
+ // Accepts: */*, application/json, text/html ---> will resolve as application/json
52
+ // eg. Chrome accepts defaults to `text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*`
53
+ // In this case `resolvedContentType` will result as: `text/html` due to the order given
54
+ var resolvedContentType = accepts(req).types(SUPPORTED_TYPES);
55
+ debug('Resolved content-type', resolvedContentType);
56
+ var contentType = resolvedContentType || defaultType;
57
+
58
+ // to receive _format from user's url param to overide the content type
59
+ // req.query (eg /api/Users/1?_format=json will overide content negotiation
60
+ // https://github.com/strongloop/strong-remoting/blob/ac3093dcfbb787977ca0229b0f672703859e52e1/lib/http-context.js#L643-L645
61
+ var query = req.query || {};
62
+ if (query._format) {
63
+ if (SUPPORTED_TYPES.indexOf(query._format) > -1) {
64
+ contentType = query._format;
65
+ } else {
66
+ // format passed through query but not supported
67
+ var msg = util.format('Response _format "%s" is not supported' +
68
+ 'used "%s" instead"', query._format, defaultType);
69
+ res.header('X-Warning', msg);
70
+ debug(msg);
71
+ }
72
+ }
73
+
74
+ debug('Content-negotiation: req.headers.accept: `%s` Resolved as: `%s`',
75
+ req.headers.accept, contentType);
76
+ return resolveOperation(contentType);
77
+ }
78
+
79
+ function resolveOperation(contentType) {
80
+ switch (contentType) {
81
+ case 'application/json':
82
+ case 'json':
83
+ return sendJson;
84
+ case 'text/html':
85
+ case 'html':
86
+ return sendHtml;
87
+ case 'text/xml':
88
+ case 'xml':
89
+ return sendXml;
90
+ }
91
+ }
@@ -8,7 +8,11 @@
8
8
  var cloneAllProperties = require('../lib/clone.js');
9
9
  var httpStatus = require('http-status');
10
10
 
11
- module.exports = function buildResponseData(err, isDebugMode) {
11
+ module.exports = function buildResponseData(err, options) {
12
+ // Debugging mode is disabled by default. When turned on (in dev),
13
+ // all error properties (including) stack traces are sent in the response
14
+ var isDebugMode = options.debug;
15
+
12
16
  if (Array.isArray(err) && isDebugMode) {
13
17
  err = serializeArrayOfErrors(err);
14
18
  }
@@ -31,6 +35,9 @@ module.exports = function buildResponseData(err, isDebugMode) {
31
35
  fillInternalError(data, err);
32
36
  }
33
37
 
38
+ var safeFields = options.safeFields || [];
39
+ fillSafeFields(data, err, safeFields);
40
+
34
41
  return data;
35
42
  };
36
43
 
@@ -76,3 +83,13 @@ function fillBadRequestError(data, err) {
76
83
  function fillInternalError(data, err) {
77
84
  data.message = httpStatus[data.statusCode] || 'Unknown Error';
78
85
  }
86
+
87
+ function fillSafeFields(data, err, safeFields) {
88
+ if (!Array.isArray(safeFields)) {
89
+ safeFields = [safeFields];
90
+ }
91
+
92
+ safeFields.forEach(function(field) {
93
+ data[field] = err[field];
94
+ });
95
+ }
package/lib/handler.js CHANGED
@@ -5,11 +5,14 @@
5
5
 
6
6
  'use strict';
7
7
 
8
+ var path = require('path');
9
+ var SG = require('strong-globalize');
10
+ SG.SetRootDir(path.resolve(__dirname, '..'));
8
11
  var buildResponseData = require('./data-builder');
9
12
  var debug = require('debug')('strong-error-handler');
10
13
  var format = require('util').format;
11
14
  var logToConsole = require('./logger');
12
- var sendJson = require('./send-json');
15
+ var negotiateContentProducer = require('./content-negotiation');
13
16
 
14
17
  function noop() {
15
18
  }
@@ -25,10 +28,6 @@ exports = module.exports = function createStrongErrorHandler(options) {
25
28
 
26
29
  debug('Initializing with options %j', options);
27
30
 
28
- // Debugging mode is disabled by default. When turned on (in dev),
29
- // all error properties (including) stack traces are sent in the response
30
- var isDebugMode = options.debug;
31
-
32
31
  // Log all errors via console.error (enabled by default)
33
32
  var logError = options.log !== false ? logToConsole : noop;
34
33
 
@@ -46,17 +45,13 @@ exports = module.exports = function createStrongErrorHandler(options) {
46
45
  if (!err.status && !err.statusCode && res.statusCode >= 400)
47
46
  err.statusCode = res.statusCode;
48
47
 
49
- var data = buildResponseData(err, isDebugMode);
48
+ var data = buildResponseData(err, options);
50
49
  debug('Response status %s data %j', data.statusCode, data);
51
50
 
52
51
  res.setHeader('X-Content-Type-Options', 'nosniff');
53
52
  res.statusCode = data.statusCode;
54
53
 
55
- // TODO: negotiate the content-type, take into account options.defaultType
56
- // For now, we always return JSON. See
57
- // - https://github.com/strongloop/strong-error-handler/issues/4
58
- // - https://github.com/strongloop/strong-error-handler/issues/5
59
- // - https://github.com/strongloop/strong-error-handler/issues/6
60
- sendJson(res, data);
54
+ var sendResponse = negotiateContentProducer(req, options);
55
+ sendResponse(res, data);
61
56
  };
62
57
  };
package/lib/logger.js CHANGED
@@ -6,17 +6,19 @@
6
6
  'use strict';
7
7
 
8
8
  var format = require('util').format;
9
+ var g = require('strong-globalize')();
9
10
 
10
11
  module.exports = function logToConsole(req, err) {
11
12
  if (!Array.isArray(err)) {
12
- console.error('Unhandled error for request %s %s: %s',
13
+ g.error('Unhandled error for request %s %s: %s',
13
14
  req.method, req.url, err.stack || err);
14
15
  return;
15
16
  }
16
17
 
18
+ var errMsg = g.f('Unhandled array of errors for request %s %s\n',
19
+ req.method, req.url);
17
20
  var errors = err.map(formatError).join('\n');
18
- console.error('Unhandled array of errors for request %s %s\n',
19
- req.method, req.url, errors);
21
+ console.error(errMsg, errors);
20
22
  };
21
23
 
22
24
  function formatError(err) {
@@ -0,0 +1,47 @@
1
+ // Copyright IBM Corp. 2016. All Rights Reserved.
2
+ // Node module: strong-error-handler
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ 'use strict';
7
+ var ejs = require('ejs');
8
+ var fs = require('fs');
9
+ var path = require('path');
10
+
11
+ var assetDir = path.resolve(__dirname, '../views');
12
+ var compiledTemplates = {
13
+ // loading default template and stylesheet
14
+ default: loadDefaultTemplates(),
15
+ };
16
+
17
+ module.exports = sendHtml;
18
+
19
+ function sendHtml(res, data, options) {
20
+ var toRender = {options: {}, data: data};
21
+ // TODO: ability to call non-default template functions from options
22
+ var body = compiledTemplates.default(toRender);
23
+ sendReponse(res, body);
24
+ }
25
+
26
+ /**
27
+ * Compile and cache the file with the `filename` key in options
28
+ *
29
+ * @param filepath (description)
30
+ * @returns {Function} render function with signature fn(data);
31
+ */
32
+ function compileTemplate(filepath) {
33
+ var options = {cache: true, filename: filepath};
34
+ var fileContent = fs.readFileSync(filepath, 'utf8');
35
+ return ejs.compile(fileContent, options);
36
+ }
37
+
38
+ // loads and cache default error templates
39
+ function loadDefaultTemplates() {
40
+ var defaultTemplate = path.resolve(assetDir, 'default-error.ejs');
41
+ return compileTemplate(defaultTemplate);
42
+ }
43
+
44
+ function sendReponse(res, body) {
45
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
46
+ res.end(body);
47
+ }
@@ -0,0 +1,14 @@
1
+ // Copyright IBM Corp. 2016. All Rights Reserved.
2
+ // Node module: strong-error-handler
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ 'use strict';
7
+
8
+ var js2xmlparser = require('js2xmlparser');
9
+
10
+ module.exports = function sendXml(res, data) {
11
+ var content = js2xmlparser.parse('error', data);
12
+ res.setHeader('Content-Type', 'text/xml; charset=utf-8');
13
+ res.end(content, 'utf-8');
14
+ };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "strong-error-handler",
3
3
  "description": "Error handler for use in development and production environments.",
4
4
  "license": "MIT",
5
- "version": "1.0.1",
5
+ "version": "1.2.1",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/strongloop/strong-error-handler.git"
@@ -14,14 +14,27 @@
14
14
  "posttest": "npm run lint"
15
15
  },
16
16
  "dependencies": {
17
+ "accepts": "^1.3.3",
17
18
  "debug": "^2.2.0",
18
- "http-status": "^0.2.2"
19
+ "ejs": "^2.4.2",
20
+ "http-status": "^0.2.2",
21
+ "js2xmlparser": "^2.0.2",
22
+ "strong-globalize": "^2.6.7"
19
23
  },
20
24
  "devDependencies": {
21
25
  "chai": "^2.1.1",
22
- "eslint": "^2.5.3",
23
- "eslint-config-loopback": "^3.0.0",
26
+ "eslint": "^2.13.1",
27
+ "eslint-config-loopback": "^4.0.0",
28
+ "express": "^4.13.4",
24
29
  "mocha": "^2.1.0",
25
30
  "supertest": "^1.1.0"
31
+ },
32
+ "browser": {
33
+ "strong-error-handler": false
34
+ },
35
+ "ci": {
36
+ "downstreamIgnoreList": [
37
+ "dashboard-controller"
38
+ ]
26
39
  }
27
40
  }
@@ -0,0 +1,25 @@
1
+ <html>
2
+ <head>
3
+ <meta charset='utf-8'>
4
+ <title><%- data.name || data.message %></title>
5
+ <style><%- include style.css %></style>
6
+ </head>
7
+ <body>
8
+ <div id="wrapper">
9
+ <h1><%- data.name %></h1>
10
+ <h2><em><%- data.statusCode %></em> <%- data.message %></h2>
11
+ <%
12
+ // display all the non-standard properties
13
+ var standardProps = ['name', 'statusCode', 'message', 'stack'];
14
+ for (var prop in data) {
15
+ if (standardProps.indexOf(prop) == -1 && data[prop]) { %>
16
+ <div><b><%- prop %></b>: <%- data[prop] %></div>
17
+ <% }
18
+ }
19
+ if (data.stack) { %>
20
+ <pre id="stacktrace"><%- data.stack %></pre>
21
+ <% }
22
+ %>
23
+ </div>
24
+ </body>
25
+ </html>
@@ -0,0 +1,31 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ outline: 0;
5
+ }
6
+ body {
7
+ padding: 80px 100px;
8
+ font: 13px "Helvetica Neue", "Lucida Grande", "Arial";
9
+ background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9));
10
+ background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9);
11
+ background-repeat: no-repeat;
12
+ color: #555;
13
+ -webkit-font-smoothing: antialiased;
14
+ }
15
+ h1, h2 {
16
+ font-size: 22px;
17
+ color: #343434;
18
+ }
19
+ h1 em, h2 em {
20
+ padding: 0 5px;
21
+ font-weight: normal;
22
+ }
23
+ h1 {
24
+ font-size: 60px;
25
+ }
26
+ h2 {
27
+ margin: 10px 0;
28
+ }
29
+ ul li {
30
+ list-style: none;
31
+ }