@bitfinex/lib-util-err-js 0.0.0-stage → 0.2.0
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.
- package/CHANGELOG.md +7 -0
- package/README.md +35 -2
- package/examples/index.js +14 -0
- package/index.js +11 -0
- package/package.json +47 -4
- package/src/api.error.handler.js +49 -0
- package/src/errors/grc.generic.error.js +20 -0
- package/src/errors/grc.user.error.js +33 -0
- package/test/api.error.handler.test.js +98 -0
- package/test/generic.error.test.js +39 -0
- package/test/user.error.test.js +40 -0
package/CHANGELOG.md
ADDED
package/README.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
-
#
|
|
1
|
+
# lib-util-err-js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The lib includes the utilities that help on resolving errors that should be shown to the endusers.
|
|
4
|
+
|
|
5
|
+
## Installing
|
|
6
|
+
|
|
7
|
+
```console
|
|
8
|
+
npm install --save https://github.com/bitfinexcom/lib-util-err-js.git
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Testing
|
|
12
|
+
|
|
13
|
+
```console
|
|
14
|
+
npm test
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
const { apiErrorHandler, GrcUserError, constants: { ERR_CODES } } = require('..')
|
|
21
|
+
|
|
22
|
+
const err = new Error('ERR_CONNECTION_FAILED')
|
|
23
|
+
const userErr = new GrcUserError('ERR_INVALID_CREDENTIALS', 'AUTH_ACTION', ERR_CODES.ERR_AUTH_FAIL)
|
|
24
|
+
|
|
25
|
+
const cb = (err) => {
|
|
26
|
+
console.error('- Response from action')
|
|
27
|
+
console.error(err)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
apiErrorHandler(err, 'AUTH_FUNCTION', cb)
|
|
31
|
+
apiErrorHandler(userErr, 'AUTH_FUNC', cb)
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Authors
|
|
36
|
+
- vigan.abd
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { apiErrorHandler, GrcUserError, constants: { ERR_CODES } } = require('..')
|
|
4
|
+
|
|
5
|
+
const err = new Error('ERR_CONNECTION_FAILED')
|
|
6
|
+
const userErr = new GrcUserError('ERR_INVALID_CREDENTIALS', 'AUTH_ACTION', ERR_CODES.ERR_AUTH_FAIL)
|
|
7
|
+
|
|
8
|
+
const cb = (err) => {
|
|
9
|
+
console.error('- Response from action')
|
|
10
|
+
console.error(err)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
apiErrorHandler(err, 'AUTH_FUNCTION', cb)
|
|
14
|
+
apiErrorHandler(userErr, 'AUTH_FUNC', cb)
|
package/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const GrcUserError = require('./src/errors/grc.user.error')
|
|
4
|
+
const GrcGenericError = require('./src/errors/grc.generic.error')
|
|
5
|
+
const apiErrorHandler = require('./src/api.error.handler')
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
GrcUserError,
|
|
9
|
+
GrcGenericError,
|
|
10
|
+
apiErrorHandler
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitfinex/lib-util-err-js",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Bitfinex lib utility for handling error messages",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"coverage": "nyc --reporter=html mocha",
|
|
8
|
+
"format": "standard --fix",
|
|
9
|
+
"lint": "standard",
|
|
10
|
+
"test": "npm run lint && npm run unit",
|
|
11
|
+
"unit": "NODE_ENV=test mocha ./test"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"bitfinex",
|
|
15
|
+
"utils",
|
|
16
|
+
"error"
|
|
17
|
+
],
|
|
18
|
+
"author": {
|
|
19
|
+
"name": "vigan.abd",
|
|
20
|
+
"email": "vigan.abdurrahmani@bitfinex.com"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/bitfinexcom/lib-util-err-js.git"
|
|
26
|
+
},
|
|
27
|
+
"maintainers": [
|
|
28
|
+
{
|
|
29
|
+
"name": "prdn",
|
|
30
|
+
"email": "paolo@bitfinex.com"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "vigan.abd",
|
|
34
|
+
"email": "vigan.abdurrahmani@bitfinex.com"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/bitfinexcom/lib-util-err-js/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/bitfinexcom/lib-util-err-js#readme",
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"chai": "^4.3.4",
|
|
43
|
+
"dirty-chai": "^2.0.1",
|
|
44
|
+
"mocha": "^9.1.3",
|
|
45
|
+
"nyc": "^15.1.0",
|
|
46
|
+
"sinon": "^12.0.1",
|
|
47
|
+
"standard": "^16.0.4"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const GrcUserError = require('./errors/grc.user.error')
|
|
4
|
+
const GrcGenericError = require('./errors/grc.generic.error')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @callback ErrorCallbackFunction
|
|
8
|
+
* @param {Error} err
|
|
9
|
+
* @returns {void}
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Error handler utility that is useful for hiding internal errors from endusers
|
|
14
|
+
*
|
|
15
|
+
* @param {Error} err - Original error
|
|
16
|
+
* @param {string} [basename] - Optional, error base message
|
|
17
|
+
* @param {Object} [opts] - Optional, error handling options
|
|
18
|
+
* @param {boolean} [opts.force] - Optional, force original error message
|
|
19
|
+
* @param {ErrorCallbackFunction} cb - Callback function in format: `(err) => void`
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const apiErrorHandler = (err, basename, opts, cb) => {
|
|
23
|
+
if (typeof basename === 'function') {
|
|
24
|
+
cb = basename
|
|
25
|
+
basename = null
|
|
26
|
+
opts = { force: false }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof opts === 'function') {
|
|
30
|
+
cb = opts
|
|
31
|
+
opts = { force: false }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.error(new Date().toISOString(), basename, err)
|
|
35
|
+
|
|
36
|
+
if (err instanceof GrcUserError || opts.force) {
|
|
37
|
+
// in case if force flag is used, debugging purposes
|
|
38
|
+
let message = err.message || err.toString()
|
|
39
|
+
const prefix = basename && !message.startsWith(basename) ? `${basename}: ` : ''
|
|
40
|
+
message = prefix + message // wrap error with basename
|
|
41
|
+
const code = err.code || null
|
|
42
|
+
|
|
43
|
+
cb(new GrcUserError(message, code))
|
|
44
|
+
} else {
|
|
45
|
+
cb(new GrcGenericError(basename))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = apiErrorHandler
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const GrcUserError = require('./grc.user.error')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generic api error, useful for hiding errors like connection failures
|
|
7
|
+
*/
|
|
8
|
+
class GrcGenericError extends GrcUserError {
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} [basename] - Optional, error message prefix
|
|
11
|
+
*/
|
|
12
|
+
constructor (basename = null) {
|
|
13
|
+
super((basename ? `${basename}: ` : '') + 'generic error')
|
|
14
|
+
|
|
15
|
+
this.name = this.constructor.name
|
|
16
|
+
Error.captureStackTrace(this, this.constructor)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = GrcGenericError
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* User friendly error that is shown directly to endusers
|
|
5
|
+
*/
|
|
6
|
+
class GrcUserError extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} message - Error message
|
|
9
|
+
* @param {number} [code] - Optional, error code
|
|
10
|
+
*/
|
|
11
|
+
constructor (message, code = null) {
|
|
12
|
+
super(message)
|
|
13
|
+
|
|
14
|
+
this.name = this.constructor.name
|
|
15
|
+
this.code = code
|
|
16
|
+
|
|
17
|
+
Error.captureStackTrace(this, this.constructor)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This method is called from JSON.stringify function when serializing object
|
|
22
|
+
* @returns {{ message: string, code: number, name: string }}
|
|
23
|
+
*/
|
|
24
|
+
toJSON () {
|
|
25
|
+
return {
|
|
26
|
+
name: this.name,
|
|
27
|
+
message: this.message,
|
|
28
|
+
code: this.code
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = GrcUserError
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* eslint-env mocha */
|
|
4
|
+
|
|
5
|
+
const sinon = require('sinon')
|
|
6
|
+
const { expect } = require('chai').use(require('dirty-chai'))
|
|
7
|
+
const { format } = require('util')
|
|
8
|
+
const { GrcGenericError, GrcUserError, apiErrorHandler } = require('../')
|
|
9
|
+
|
|
10
|
+
describe('apiErrorHandler tests', () => {
|
|
11
|
+
let storedLog = ''
|
|
12
|
+
const logStub = sinon.stub(console, 'error')
|
|
13
|
+
|
|
14
|
+
before(() => {
|
|
15
|
+
logStub.callsFake((...params) => {
|
|
16
|
+
storedLog = format(...params)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
after(() => {
|
|
21
|
+
logStub.restore()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
storedLog = ''
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('it should hide original error message in case if it\'s not instance of GrcUserError', (done) => {
|
|
29
|
+
apiErrorHandler(new Error('Critical error in db'), 'MY_FUNCTION', {}, (err) => {
|
|
30
|
+
expect(err).to.be.instanceOf(GrcGenericError)
|
|
31
|
+
expect(err.message).to.be.equal('MY_FUNCTION: generic error')
|
|
32
|
+
expect(err.code).to.be.equal(null)
|
|
33
|
+
done()
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('it should return original error message in case if it\'s instance of GrcUserError', (done) => {
|
|
38
|
+
apiErrorHandler(new GrcUserError('invalid field', 10001), 'MY_FUNCTION', {}, (err) => {
|
|
39
|
+
expect(err).to.be.instanceOf(GrcUserError)
|
|
40
|
+
expect(err.message).to.be.equal('MY_FUNCTION: invalid field')
|
|
41
|
+
expect(err.code).to.be.equal(10001)
|
|
42
|
+
done()
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('it should return original error message in case if it has force flag', (done) => {
|
|
47
|
+
apiErrorHandler(new Error('Critical error in db'), 'MY_FUNCTION', { force: true }, (err) => {
|
|
48
|
+
expect(err).to.be.instanceOf(GrcUserError)
|
|
49
|
+
expect(err.message).to.be.equal('MY_FUNCTION: Critical error in db')
|
|
50
|
+
expect(err.code).to.be.equal(null) // since it's missing add default one!
|
|
51
|
+
done()
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('it should work without optional params', (done) => {
|
|
56
|
+
apiErrorHandler(new Error('Critical error in db'), (err) => {
|
|
57
|
+
expect(err).to.be.instanceOf(GrcGenericError)
|
|
58
|
+
expect(err.message).to.be.equal('generic error')
|
|
59
|
+
expect(err.code).to.be.equal(null)
|
|
60
|
+
done()
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('it should work without opts param', (done) => {
|
|
65
|
+
apiErrorHandler(new Error('Critical error in db'), 'MY_FUNCTION', (err) => {
|
|
66
|
+
expect(err).to.be.instanceOf(GrcGenericError)
|
|
67
|
+
expect(err.message).to.be.equal('MY_FUNCTION: generic error')
|
|
68
|
+
expect(err.code).to.be.equal(null)
|
|
69
|
+
done()
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('it should always log original error', (done) => {
|
|
74
|
+
apiErrorHandler(new GrcUserError('invalid field', 10001), (err) => {
|
|
75
|
+
expect(storedLog).not.to.be.null()
|
|
76
|
+
expect(storedLog.includes(err.trace))
|
|
77
|
+
done()
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('it should use toString method when error does not have message field', (done) => {
|
|
82
|
+
apiErrorHandler('Critical error in db', 'MY_FUNCTION', { force: true }, (err) => {
|
|
83
|
+
expect(err).to.be.instanceOf(GrcUserError)
|
|
84
|
+
expect(err.message).to.be.equal('MY_FUNCTION: Critical error in db')
|
|
85
|
+
expect(err.code).to.be.equal(null)
|
|
86
|
+
done()
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('it should avoid adding prefix more than once', (done) => {
|
|
91
|
+
apiErrorHandler(new GrcUserError('MY_FUNCTION: invalid field'), 'MY_FUNCTION', (err) => {
|
|
92
|
+
expect(err).to.be.instanceOf(GrcUserError)
|
|
93
|
+
expect(err.message).to.be.equal('MY_FUNCTION: invalid field')
|
|
94
|
+
expect(err.code).to.be.equal(null)
|
|
95
|
+
done()
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* eslint-env mocha */
|
|
4
|
+
|
|
5
|
+
const { expect } = require('chai').use(require('dirty-chai'))
|
|
6
|
+
const { GrcGenericError } = require('../')
|
|
7
|
+
|
|
8
|
+
describe('GenericError tests', () => {
|
|
9
|
+
it('it should parse fields properly', () => {
|
|
10
|
+
const err = new GrcGenericError('MY_FUNCTION')
|
|
11
|
+
expect(err).to.be.instanceOf(Error)
|
|
12
|
+
expect(err.name).to.be.equal('GrcGenericError')
|
|
13
|
+
expect(err.message).to.be.equal('MY_FUNCTION: generic error')
|
|
14
|
+
expect(err.code).to.be.equal(null)
|
|
15
|
+
expect(err.stack).to.be.a('string')
|
|
16
|
+
expect(err.stack.startsWith('GrcGenericError: MY_FUNCTION: generic error')).to.be.true()
|
|
17
|
+
expect(err.stack.includes(' at ')).to.be.true()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('it should work also without optional params', () => {
|
|
21
|
+
const err = new GrcGenericError()
|
|
22
|
+
|
|
23
|
+
expect(err.message).to.be.equal('generic error')
|
|
24
|
+
expect(err.code).to.be.equal(null)
|
|
25
|
+
expect(err.stack.startsWith('GrcGenericError: generic error')).to.be.true()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('it should serialize only specific fields', () => {
|
|
29
|
+
const serialized = JSON.stringify(new GrcGenericError('MY_FUNC'))
|
|
30
|
+
const deserialized = JSON.parse(serialized)
|
|
31
|
+
const expected = {
|
|
32
|
+
name: 'GrcGenericError',
|
|
33
|
+
message: 'MY_FUNC: generic error',
|
|
34
|
+
code: null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
expect(deserialized).to.be.eqls(expected)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* eslint-env mocha */
|
|
4
|
+
|
|
5
|
+
const { expect } = require('chai').use(require('dirty-chai'))
|
|
6
|
+
const { GrcUserError } = require('../')
|
|
7
|
+
|
|
8
|
+
describe('GrcUserError tests', () => {
|
|
9
|
+
it('it should parse fields properly', () => {
|
|
10
|
+
const err = new GrcUserError('invalid user input', 10001)
|
|
11
|
+
|
|
12
|
+
expect(err).to.be.instanceOf(Error)
|
|
13
|
+
expect(err.name).to.be.equal('GrcUserError')
|
|
14
|
+
expect(err.message).to.be.equal('invalid user input')
|
|
15
|
+
expect(err.code).to.be.equal(10001)
|
|
16
|
+
expect(err.stack).to.be.a('string')
|
|
17
|
+
expect(err.stack.startsWith('GrcUserError: invalid user input')).to.be.true()
|
|
18
|
+
expect(err.stack.includes(' at ')).to.be.true()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('it should work also without optional params', () => {
|
|
22
|
+
const err = new GrcUserError('invalid user input')
|
|
23
|
+
|
|
24
|
+
expect(err.message).to.be.equal('invalid user input')
|
|
25
|
+
expect(err.code).to.be.null()
|
|
26
|
+
expect(err.stack.startsWith('GrcUserError: invalid user input')).to.be.true()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('it should serialize only specific fields', () => {
|
|
30
|
+
const serialized = JSON.stringify(new GrcUserError('invalid user input', 10001))
|
|
31
|
+
const deserialized = JSON.parse(serialized)
|
|
32
|
+
const expected = {
|
|
33
|
+
name: 'GrcUserError',
|
|
34
|
+
message: 'invalid user input',
|
|
35
|
+
code: 10001
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
expect(deserialized).to.be.eqls(expected)
|
|
39
|
+
})
|
|
40
|
+
})
|