Skip to content

Commit 58a9571

Browse files
committed
feat: added html-to-text support for API friendly responses
1 parent 3b4da8f commit 58a9571

File tree

5 files changed

+1658
-1507
lines changed

5 files changed

+1658
-1507
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
language: node_js
22
node_js:
3-
- '10'
43
- '12'
4+
- 'node'
55
- 'lts/*'
66
after_success:
77
npm run coverage

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,13 @@ try {
217217
```
218218

219219

220+
## API Friendly Messages
221+
222+
By default if `ctx.api` is true, then [html-to-text](https://github.com/werk85/node-html-to-text) will be invoked upon the `err.message`, thus converting all the HTML markup into text format.
223+
224+
You can also specify a base URI in the environment variable for rendering as `process.env.ERROR_HANDLER_BASE_URL`, e.g. `BERROR_HANDLER_BASE_URL=https://example.com` (omit trailing slash), and any HTML links such as `<a href="/foo/bar/baz">Click here</a>` will be converted to `[Click here][1]` with a `[1]` link appended of `https://example.com/foo/bar/baz`.
225+
226+
220227
## License
221228

222229
[MIT](LICENSE) © Nick Baugh

package.json

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,45 +13,46 @@
1313
"Nick Baugh <[email protected]>"
1414
],
1515
"dependencies": {
16-
"@hapi/boom": "^9.0.0",
17-
"camelcase": "^5.3.1",
18-
"capitalize": "^2.0.1",
16+
"@hapi/boom": "^9.1.0",
17+
"camelcase": "^6.0.0",
18+
"capitalize": "^2.0.3",
1919
"co": "^4.6.0",
2020
"debug": "^4.1.1",
21+
"html-to-text": "^5.1.1",
2122
"humanize-string": "^2.1.0",
2223
"lodash": "^4.17.15",
23-
"statuses": "^1.5.0",
24+
"statuses": "^2.0.0",
2425
"toidentifier": "^1.0.0"
2526
},
2627
"devDependencies": {
27-
"@babel/cli": "^7.8.3",
28-
"@babel/core": "^7.8.3",
29-
"@babel/preset-env": "^7.8.3",
28+
"@babel/cli": "^7.8.4",
29+
"@babel/core": "^7.9.6",
30+
"@babel/preset-env": "^7.9.6",
3031
"@commitlint/cli": "^8.3.5",
3132
"@commitlint/config-conventional": "^8.3.4",
32-
"@koa/router": "^8.0.6",
33+
"@koa/router": "^8.0.8",
3334
"ava": "2.x",
34-
"codecov": "^3.6.2",
35-
"cross-env": "6.x",
36-
"eslint": "^6.8.0",
35+
"codecov": "^3.6.5",
36+
"cross-env": "^7.0.2",
37+
"eslint": "6.x",
3738
"eslint-config-xo-lass": "^1.0.3",
38-
"eslint-plugin-node": "^11.0.0",
39-
"fixpack": "^2.3.1",
40-
"husky": "3.x",
39+
"eslint-plugin-node": "^11.1.0",
40+
"fixpack": "^3.0.6",
41+
"husky": "^4.2.5",
4142
"koa": "^2.11.0",
4243
"koa-404-handler": "^0.0.2",
4344
"koa-basic-auth": "^4.0.0",
4445
"koa-connect-flash": "^0.1.2",
4546
"koa-convert": "^1.2.0",
4647
"koa-generic-session": "^2.0.4",
4748
"koa-redis": "^4.0.1",
48-
"lint-staged": "^10.0.4",
49-
"nyc": "^15.0.0",
50-
"redis": "^2.8.0",
51-
"remark-cli": "^7.0.1",
52-
"remark-preset-github": "^0.0.16",
49+
"lint-staged": "^10.2.2",
50+
"nyc": "^15.0.1",
51+
"redis": "^3.0.2",
52+
"remark-cli": "^8.0.0",
53+
"remark-preset-github": "^1.0.0",
5354
"supertest": "^4.0.2",
54-
"xo": "^0.25.3"
55+
"xo": "0.25"
5556
},
5657
"engines": {
5758
"node": ">=7.6.0"

src/index.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ const path = require('path');
44
const Boom = require('@hapi/boom');
55
const Debug = require('debug');
66
const _ = require('lodash');
7-
const capitalize = require('capitalize');
87
const camelCase = require('camelcase');
8+
const capitalize = require('capitalize');
99
const co = require('co');
10+
const htmlToText = require('html-to-text');
1011
const humanize = require('humanize-string');
1112
const statuses = require('statuses');
1213
const toIdentifier = require('toidentifier');
@@ -97,7 +98,10 @@ async function errorHandler(err) {
9798
err.statusCode = err.status;
9899
this.statusCode = err.statusCode;
99100
this.status = this.statusCode;
100-
this.body = new Boom.Boom(err.message, {
101+
102+
const friendlyAPIMessage = makeAPIFriendly(this, err.message);
103+
104+
this.body = new Boom.Boom(friendlyAPIMessage, {
101105
statusCode: err.status
102106
}).output.payload;
103107

@@ -212,6 +216,19 @@ async function errorHandler(err) {
212216
this.res.end(this.body);
213217
}
214218

219+
function makeAPIFriendly(ctx, message) {
220+
if (!ctx.api) return message;
221+
message = htmlToText.fromString(message, {
222+
wordwrap: false,
223+
linkHrefBaseUrl: process.env.ERROR_HANDLER_BASE_URL
224+
? process.env.ERROR_HANDLER_BASE_URL
225+
: '',
226+
hideLinkHrefIfSameAsText: true,
227+
ignoreImage: true
228+
});
229+
return message;
230+
}
231+
215232
function parseValidationError(ctx, err) {
216233
// translate messages
217234
const translate = message =>
@@ -255,9 +272,10 @@ function parseValidationError(ctx, err) {
255272
err.message = translate(_.values(err.errors)[0].message);
256273
} else {
257274
const errors = _.map(_.map(_.values(err.errors), 'message'), translate);
258-
err.message = ctx.api
259-
? errors.join(', ')
260-
: `<ul class="text-left mb-0"><li>${errors.join('</li><li>')}</li></ul>`;
275+
err.message = makeAPIFriendly(
276+
ctx,
277+
`<ul class="text-left mb-0"><li>${errors.join('</li><li>')}</li></ul>`
278+
);
261279
}
262280

263281
// this ensures the error shows up client-side

0 commit comments

Comments
 (0)