exframe-middleware 1.0.1 → 1.3.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/README.md +2 -2
- package/index.js +0 -2
- package/lib/authorizeUser.js +0 -1
- package/lib/buildExpressRequestContext.js +0 -1
- package/lib/handleErrors.js +16 -2
- package/package.json +19 -13
- package/sonar-project.properties +21 -0
- package/test/handleErrors.test.js +110 -14
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ errors and reports their status and message
|
|
|
40
40
|
client.use(handleErrors().mq);
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
standardRouteResponseBuilder
|
|
44
44
|
---
|
|
45
45
|
|
|
46
46
|
builds the response object based on input and if single or multi response
|
|
@@ -50,6 +50,6 @@ example from in production
|
|
|
50
50
|
subscribe(
|
|
51
51
|
'startBoBTransfers',
|
|
52
52
|
validateRequestRPC(schemas.startBoBTransfer),
|
|
53
|
-
(context, message) => startBoBTransfer.start(context, message).then(
|
|
53
|
+
(context, message) => startBoBTransfer.start(context, message).then(standardRouteResponseBuilder.singleItem)
|
|
54
54
|
)
|
|
55
55
|
```
|
package/index.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
const handleErrors = require('./lib/handleErrors');
|
|
5
4
|
const standardRouteResponseBuilder = require('./lib/standardRouteResponse');
|
|
6
5
|
const buildExpressRequestContext = require('./lib/buildExpressRequestContext');
|
|
7
6
|
const authorizeUser = require('./lib/authorizeUser');
|
|
8
7
|
const buildCspFromHeader = require('./lib/buildCspFromHeaders');
|
|
9
8
|
|
|
10
|
-
|
|
11
9
|
module.exports = {
|
|
12
10
|
/**
|
|
13
11
|
* @param {Object} config the configuration object
|
package/lib/authorizeUser.js
CHANGED
package/lib/handleErrors.js
CHANGED
|
@@ -3,7 +3,21 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* error
|
|
5
5
|
*/
|
|
6
|
-
module.exports = (context,
|
|
6
|
+
module.exports = (context, rawError, optionalIncomingMessage) => {
|
|
7
|
+
let error = rawError;
|
|
8
|
+
|
|
9
|
+
// exframe-workflow v4 creates a parent error object with an errors array that can contain multiple child errors
|
|
10
|
+
// originating from forked workflows. This check moves that errors array to the top level so that the workflow
|
|
11
|
+
// error can be processed in the same way as pre-v4 exframe-workflow errors below.
|
|
12
|
+
// NOTE: schema validation errors can also have an errors array, but in that case the objects in the array are
|
|
13
|
+
// not complete errors and represent each schema validation error from a single request. These objects will not
|
|
14
|
+
// have a "statusCode" property and is the reason for the check here to keep them out of the workflow error logic.
|
|
15
|
+
if (Array.isArray(rawError.errors) && rawError.errors.length > 0 && rawError.errors[0].statusCode !== undefined) {
|
|
16
|
+
if (rawError.errors.length > 1) error = rawError.errors;
|
|
17
|
+
else error = rawError.errors[0] || rawError;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { errorCode } = error;
|
|
7
21
|
let { message } = error;
|
|
8
22
|
let status;
|
|
9
23
|
|
|
@@ -45,5 +59,5 @@ module.exports = (context, error, optionalIncomingMessage) => {
|
|
|
45
59
|
}
|
|
46
60
|
else context.log.warn('There was an issue with the client request', { message: optionalIncomingMessage || error.message, errorDetails: error });
|
|
47
61
|
|
|
48
|
-
return { status, message };
|
|
62
|
+
return { status, message, errorCode };
|
|
49
63
|
};
|
package/package.json
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exframe-middleware",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"config": {
|
|
7
|
+
"reporter": "mocha-exzeo-reporter"
|
|
8
|
+
},
|
|
6
9
|
"scripts": {
|
|
7
|
-
"test": "nyc mocha",
|
|
10
|
+
"test": "nyc mocha --reporter $npm_package_config_reporter",
|
|
8
11
|
"pretest": "npm run lint",
|
|
9
12
|
"dev-test": "nyc mocha",
|
|
10
13
|
"lint": "eslint --ignore-pattern test* ."
|
|
11
14
|
},
|
|
12
15
|
"repository": {
|
|
13
16
|
"type": "git",
|
|
14
|
-
"url": "
|
|
17
|
+
"url": "https://bitbucket.org/exzeo-usa/exframe",
|
|
18
|
+
"directory": "packages/exframe-middleware"
|
|
15
19
|
},
|
|
16
|
-
"author": "",
|
|
20
|
+
"author": "Exzeo",
|
|
17
21
|
"license": "ISC",
|
|
18
22
|
"homepage": "https://bitbucket.org/exzeo-usa/exframe-middleware#readme",
|
|
19
23
|
"devDependencies": {
|
|
20
|
-
"chai": "
|
|
21
|
-
"eslint": "
|
|
22
|
-
"eslint-config-airbnb-base": "
|
|
23
|
-
"eslint-plugin-import": "^2.
|
|
24
|
-
"mocha": "
|
|
25
|
-
"
|
|
26
|
-
"
|
|
24
|
+
"chai": "*",
|
|
25
|
+
"eslint": "*",
|
|
26
|
+
"eslint-config-airbnb-base": "*",
|
|
27
|
+
"eslint-plugin-import": "^2.22.0",
|
|
28
|
+
"mocha": "*",
|
|
29
|
+
"mocha-exzeo-reporter": "*",
|
|
30
|
+
"nyc": "*",
|
|
31
|
+
"sinon": "*"
|
|
27
32
|
},
|
|
28
33
|
"dependencies": {
|
|
29
|
-
"exframe-errors": "^1.
|
|
30
|
-
}
|
|
34
|
+
"exframe-errors": "^1.2.0"
|
|
35
|
+
},
|
|
36
|
+
"gitHead": "1959247957f1a308c44d4c5210712125ab6e5573"
|
|
31
37
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# The language the application is written in.
|
|
2
|
+
; sonar.language=js
|
|
3
|
+
sonar.language=JavaScript
|
|
4
|
+
|
|
5
|
+
# Path is relative to the sonar-project.properties file. Defaults to .
|
|
6
|
+
sonar.sources=.
|
|
7
|
+
|
|
8
|
+
# # Encoding of the source code. Default is default system encoding
|
|
9
|
+
sonar.sourceEncoding=UTF-8
|
|
10
|
+
|
|
11
|
+
# # Patterns used to exclude some source files from analysis.
|
|
12
|
+
sonar.exclusions=test*/**/*.js
|
|
13
|
+
|
|
14
|
+
# # Patterns used to include some test files and only these ones in analysis.
|
|
15
|
+
sonar.test.inclusions=test*/**/*.js
|
|
16
|
+
|
|
17
|
+
# Patterns used to exclude some files from coverage report.
|
|
18
|
+
sonar.coverage.exclusions=test*/**/*.js,node_modules/*,documentation/combined/lcov-report/*,junit/*
|
|
19
|
+
|
|
20
|
+
# Paths (absolute or relative) to the files with LCOV data.
|
|
21
|
+
sonar.javascript.lcov.reportPaths=documentation/combined/lcov.info
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
const errors = require('exframe-errors');
|
|
5
4
|
const { expect } = require('chai');
|
|
6
5
|
const sinon = require('sinon');
|
|
7
6
|
|
|
8
7
|
const { fakeLogger } = require('./support/utils');
|
|
9
|
-
const
|
|
8
|
+
const handleErrors = require('../index').handleErrors();
|
|
10
9
|
|
|
11
10
|
context('handleErrors', () => {
|
|
12
11
|
describe('status buckets', () => {
|
|
13
12
|
it('403 beats everything else', async () => {
|
|
14
|
-
const { status, message } = await
|
|
13
|
+
const { status, message } = await handleErrors.mq(
|
|
15
14
|
[
|
|
16
15
|
new errors.Unauthorized('nah brah'),
|
|
17
16
|
new errors.InvalidInput(),
|
|
@@ -26,7 +25,7 @@ context('handleErrors', () => {
|
|
|
26
25
|
});
|
|
27
26
|
|
|
28
27
|
it('then 401', async () => {
|
|
29
|
-
const { status, message } = await
|
|
28
|
+
const { status, message } = await handleErrors.mq(
|
|
30
29
|
[
|
|
31
30
|
new errors.InvalidInput(),
|
|
32
31
|
new errors.ResourceNotFound(),
|
|
@@ -40,7 +39,7 @@ context('handleErrors', () => {
|
|
|
40
39
|
});
|
|
41
40
|
|
|
42
41
|
it('then 500', async () => {
|
|
43
|
-
const { status, message } = await
|
|
42
|
+
const { status, message } = await handleErrors.mq(
|
|
44
43
|
[
|
|
45
44
|
new errors.InvalidInput(),
|
|
46
45
|
new errors.ExframeBase('some message')
|
|
@@ -53,7 +52,7 @@ context('handleErrors', () => {
|
|
|
53
52
|
});
|
|
54
53
|
|
|
55
54
|
it('then groups 400 type errors', async () => {
|
|
56
|
-
const { status, message } = await
|
|
55
|
+
const { status, message } = await handleErrors.mq(
|
|
57
56
|
[
|
|
58
57
|
new errors.InvalidInput(), // 400
|
|
59
58
|
new errors.ResourceNotFound() // 404
|
|
@@ -66,7 +65,7 @@ context('handleErrors', () => {
|
|
|
66
65
|
});
|
|
67
66
|
|
|
68
67
|
it('but gives a specific error status when all errors are the same', async () => {
|
|
69
|
-
const { status, message } = await
|
|
68
|
+
const { status, message } = await handleErrors.mq(
|
|
70
69
|
[
|
|
71
70
|
new errors.ResourceNotFound(),
|
|
72
71
|
new errors.ResourceNotFound(),
|
|
@@ -82,21 +81,28 @@ context('handleErrors', () => {
|
|
|
82
81
|
|
|
83
82
|
describe('single errors', () => {
|
|
84
83
|
it('handles single errors', async () => {
|
|
85
|
-
const { status, message } = await
|
|
84
|
+
const { status, message } = await handleErrors.mq(new errors.InvalidInput('some message, ya'), { log: fakeLogger });
|
|
86
85
|
expect(status).to.equal(400);
|
|
87
86
|
expect(message).to.equal('some message, ya');
|
|
88
87
|
});
|
|
89
88
|
|
|
90
89
|
it('defaults errors to 500 errors', async () => {
|
|
91
|
-
const { status, message } = await
|
|
90
|
+
const { status, message } = await handleErrors.mq('not an error object', { log: fakeLogger });
|
|
92
91
|
expect(status).to.equal(500);
|
|
93
92
|
expect(message).to.equal('Internal Server Error');
|
|
94
93
|
});
|
|
95
94
|
|
|
96
95
|
it('handles baseExzeoError', async () => {
|
|
97
|
-
const { status, message } = await
|
|
96
|
+
const { status, message } = await handleErrors.mq(new errors.ExframeBase('this is my message'), { log: fakeLogger });
|
|
97
|
+
expect(status).to.equal(500);
|
|
98
|
+
expect(message).to.equal('this is my message');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('returns errorCode when provided', async () => {
|
|
102
|
+
const { status, message, errorCode } = await handleErrors.mq(new errors.ExframeBase('this is my message', 'ERROR_CODE'), { log: fakeLogger });
|
|
98
103
|
expect(status).to.equal(500);
|
|
99
104
|
expect(message).to.equal('this is my message');
|
|
105
|
+
expect(errorCode).to.equal('ERROR_CODE');
|
|
100
106
|
});
|
|
101
107
|
});
|
|
102
108
|
|
|
@@ -110,16 +116,17 @@ context('handleErrors', () => {
|
|
|
110
116
|
const sendSpy = sinon.spy(expressResponseObject, 'send');
|
|
111
117
|
const doneSpy = sinon.spy();
|
|
112
118
|
|
|
113
|
-
const response =
|
|
119
|
+
const response = handleErrors.express(new errors.ResourceNotFound('Resource not found'), { log: fakeLogger }, expressResponseObject, doneSpy);
|
|
114
120
|
|
|
115
121
|
expect(doneSpy.callCount).to.equal(0);
|
|
116
122
|
expect(statusSpy.callCount).to.equal(1);
|
|
117
123
|
expect(statusSpy.args).to.deep.equal([[404]]);
|
|
118
124
|
expect(sendSpy.callCount).to.equal(1);
|
|
119
|
-
expect(sendSpy.args).to.deep.equal([[{ status: 404, message: 'Resource not found' }]]);
|
|
125
|
+
expect(sendSpy.args).to.deep.equal([[{ status: 404, message: 'Resource not found', errorCode: 'NOT_FOUND' }]]);
|
|
120
126
|
expect(response).to.deep.equal({
|
|
121
127
|
status: 404,
|
|
122
|
-
message: 'Resource not found'
|
|
128
|
+
message: 'Resource not found',
|
|
129
|
+
errorCode: 'NOT_FOUND'
|
|
123
130
|
});
|
|
124
131
|
|
|
125
132
|
sinon.reset();
|
|
@@ -134,7 +141,7 @@ context('handleErrors', () => {
|
|
|
134
141
|
const sendSpy = sinon.spy(expressResponseObject, 'send');
|
|
135
142
|
const doneSpy = sinon.spy();
|
|
136
143
|
|
|
137
|
-
|
|
144
|
+
handleErrors.express(null, { log: fakeLogger }, expressResponseObject, doneSpy);
|
|
138
145
|
|
|
139
146
|
expect(doneSpy.callCount).to.equal(1);
|
|
140
147
|
expect(statusSpy.callCount).to.equal(0);
|
|
@@ -143,4 +150,93 @@ context('handleErrors', () => {
|
|
|
143
150
|
sinon.reset();
|
|
144
151
|
});
|
|
145
152
|
});
|
|
153
|
+
describe('exframe-workflow style errors', () => {
|
|
154
|
+
it('handles multiple errors', async () => {
|
|
155
|
+
const testError = new Error('A thing happened');
|
|
156
|
+
testError.errors = [
|
|
157
|
+
new errors.InvalidInput('You messed up'),
|
|
158
|
+
new errors.ResourceNotFound('Something is not there')
|
|
159
|
+
];
|
|
160
|
+
const { status, message } = await handleErrors.mq(testError, { log: fakeLogger });
|
|
161
|
+
expect(status).to.equal(400);
|
|
162
|
+
expect(message).to.equal('multiple errors occured: [You messed up, Something is not there]');
|
|
163
|
+
});
|
|
164
|
+
it('handles a single error', async () => {
|
|
165
|
+
const testError = new Error('A thing happened');
|
|
166
|
+
testError.errors = [
|
|
167
|
+
new errors.InvalidInput('You messed up')
|
|
168
|
+
];
|
|
169
|
+
const { status, message } = await handleErrors.mq(testError, { log: fakeLogger });
|
|
170
|
+
expect(status).to.equal(400);
|
|
171
|
+
expect(message).to.equal('You messed up');
|
|
172
|
+
});
|
|
173
|
+
it('handles an empty errors array', async () => {
|
|
174
|
+
const testError = new Error('A thing happened');
|
|
175
|
+
testError.errors = [];
|
|
176
|
+
const { status, message } = await handleErrors.mq(testError, { log: fakeLogger });
|
|
177
|
+
expect(status).to.equal(500);
|
|
178
|
+
expect(message).to.equal('A thing happened');
|
|
179
|
+
});
|
|
180
|
+
it('handles an errors field that is not an array', async () => {
|
|
181
|
+
const testError = new Error('A thing happened');
|
|
182
|
+
testError.errors = 123;
|
|
183
|
+
const { status, message } = await handleErrors.mq(testError, { log: fakeLogger });
|
|
184
|
+
expect(status).to.equal(500);
|
|
185
|
+
expect(message).to.equal('A thing happened');
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('schema validation errors', () => {
|
|
190
|
+
it('handles a single validation error', async () => {
|
|
191
|
+
const testError = new errors.InvalidInput('You messed up');
|
|
192
|
+
testError.errors = [
|
|
193
|
+
{
|
|
194
|
+
keyword: 'required',
|
|
195
|
+
dataPath: '',
|
|
196
|
+
schemaPath: '#/required',
|
|
197
|
+
params: {
|
|
198
|
+
missingProperty: 'banana'
|
|
199
|
+
},
|
|
200
|
+
message: 'should have required property \'banana\''
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
const { status, message } = await handleErrors.mq(testError, { log: fakeLogger });
|
|
204
|
+
expect(status).to.equal(400);
|
|
205
|
+
expect(message).to.equal('You messed up');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('handles multiple validation error', async () => {
|
|
209
|
+
const testError = new errors.InvalidInput('You messed up');
|
|
210
|
+
testError.errors = [
|
|
211
|
+
{
|
|
212
|
+
keyword: 'required',
|
|
213
|
+
dataPath: '',
|
|
214
|
+
schemaPath: '#/required',
|
|
215
|
+
params: {
|
|
216
|
+
missingProperty: 'banana'
|
|
217
|
+
},
|
|
218
|
+
message: 'should have required property \'banana\''
|
|
219
|
+
}, {
|
|
220
|
+
keyword: 'required',
|
|
221
|
+
dataPath: '',
|
|
222
|
+
schemaPath: '#/required',
|
|
223
|
+
params: {
|
|
224
|
+
missingProperty: 'pineapple'
|
|
225
|
+
},
|
|
226
|
+
message: 'should have required property \'pineapple\''
|
|
227
|
+
}, {
|
|
228
|
+
keyword: 'required',
|
|
229
|
+
dataPath: '',
|
|
230
|
+
schemaPath: '#/required',
|
|
231
|
+
params: {
|
|
232
|
+
missingProperty: 'kiwi'
|
|
233
|
+
},
|
|
234
|
+
message: 'should have required property \'kiwi\''
|
|
235
|
+
}
|
|
236
|
+
];
|
|
237
|
+
const { status, message } = await handleErrors.mq(testError, { log: fakeLogger });
|
|
238
|
+
expect(status).to.equal(400);
|
|
239
|
+
expect(message).to.equal('You messed up');
|
|
240
|
+
});
|
|
241
|
+
});
|
|
146
242
|
});
|