exframe-middleware 1.1.0 → 1.3.2
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/.eslintrc.json +1 -1
- package/index.js +2 -1
- package/lib/authorizeUser.js +6 -2
- package/lib/handleErrors.js +14 -1
- package/package.json +17 -11
- package/sonar-project.properties +21 -0
- package/test/authorizeUser.test.js +52 -17
- package/test/handleErrors.test.js +101 -13
package/.eslintrc.json
CHANGED
package/index.js
CHANGED
package/lib/authorizeUser.js
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const errors = require('exframe-errors');
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* @param {Object} context
|
|
7
|
+
* @param {Object} context.user
|
|
8
|
+
*/
|
|
9
|
+
module.exports = ({ user }) => {
|
|
10
|
+
if (!user)
|
|
7
11
|
throw new errors.Unauthorized('Could not authorize user');
|
|
8
12
|
};
|
package/lib/handleErrors.js
CHANGED
|
@@ -3,7 +3,20 @@
|
|
|
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
|
+
|
|
7
20
|
const { errorCode } = error;
|
|
8
21
|
let { message } = error;
|
|
9
22
|
let status;
|
package/package.json
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exframe-middleware",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.2",
|
|
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": "
|
|
24
|
+
"chai": "*",
|
|
25
|
+
"eslint": "*",
|
|
26
|
+
"eslint-config-airbnb-base": "*",
|
|
23
27
|
"eslint-plugin-import": "^2.22.0",
|
|
24
|
-
"mocha": "
|
|
25
|
-
"
|
|
26
|
-
"
|
|
28
|
+
"mocha": "*",
|
|
29
|
+
"mocha-exzeo-reporter": "*",
|
|
30
|
+
"nyc": "*",
|
|
31
|
+
"sinon": "*"
|
|
27
32
|
},
|
|
28
33
|
"dependencies": {
|
|
29
34
|
"exframe-errors": "^1.2.0"
|
|
30
|
-
}
|
|
35
|
+
},
|
|
36
|
+
"gitHead": "6c4adb8a4ad4a5a60f1b98fb94dc4a13026eb796"
|
|
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
|
|
@@ -6,26 +6,61 @@ const errors = require('exframe-errors');
|
|
|
6
6
|
const { authorizeUser } = require('../index');
|
|
7
7
|
|
|
8
8
|
context('authorizeUser', () => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
describe('mq', () => {
|
|
10
|
+
it('throws unauthorized error when no user', () => {
|
|
11
|
+
try { authorizeUser.mq({}); }
|
|
12
|
+
catch (e) {
|
|
13
|
+
expect(e).to.be.an.instanceOf(errors.Unauthorized);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
expect.fail('authorizeUser should have thrown an Unauthorized error');
|
|
18
|
+
});
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
it('does not throw an error when there is a user', () => {
|
|
21
|
+
try { authorizeUser.mq({ user: 'something' }); }
|
|
22
|
+
catch (e) { expect.fail('authorizeUser should not have thrown an error'); }
|
|
23
|
+
});
|
|
22
24
|
});
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
describe('express - user exists directly on the request object', () => {
|
|
27
|
+
it('calls the next function when there is a user', () => {
|
|
28
|
+
let called = 0;
|
|
29
|
+
const next = () => ++called;
|
|
30
|
+
authorizeUser.express({ user: {} }, {}, next);
|
|
31
|
+
|
|
32
|
+
expect(called).to.equal(1);
|
|
33
|
+
});
|
|
34
|
+
it('throws unauthorized error when there is no user', () => {
|
|
35
|
+
let called = 0;
|
|
36
|
+
const next = () => ++called;
|
|
37
|
+
try { authorizeUser.express({ }, {}, next); }
|
|
38
|
+
catch (e) {
|
|
39
|
+
expect(e).to.be.an.instanceOf(errors.Unauthorized);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
expect.fail('authorizeUser should have thrown an Unauthorized error');
|
|
43
|
+
expect(called).to.equal(0);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
28
46
|
|
|
29
|
-
|
|
47
|
+
describe('express - user exists within the context on the request object', () => {
|
|
48
|
+
it('calls the next function when there is a user', () => {
|
|
49
|
+
let called = 0;
|
|
50
|
+
const next = () => ++called;
|
|
51
|
+
authorizeUser.express({ context: { user: {} } }, {}, next);
|
|
52
|
+
expect(called).to.equal(1);
|
|
53
|
+
});
|
|
54
|
+
it('throws unauthorized error when there is no user', () => {
|
|
55
|
+
let called = 0;
|
|
56
|
+
const next = () => ++called;
|
|
57
|
+
try { authorizeUser.express({ context: {} }, {}, next); }
|
|
58
|
+
catch (e) {
|
|
59
|
+
expect(e).to.be.an.instanceOf(errors.Unauthorized);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
expect.fail('authorizeUser should have thrown an Unauthorized error');
|
|
63
|
+
expect(called).to.equal(0);
|
|
64
|
+
});
|
|
30
65
|
});
|
|
31
66
|
});
|
|
@@ -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
|
|
68
|
+
const { status, message } = await handleErrors.mq(
|
|
70
69
|
[
|
|
71
70
|
new errors.ResourceNotFound(),
|
|
72
71
|
new errors.ResourceNotFound(),
|
|
@@ -82,25 +81,25 @@ 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 });
|
|
98
97
|
expect(status).to.equal(500);
|
|
99
98
|
expect(message).to.equal('this is my message');
|
|
100
99
|
});
|
|
101
100
|
|
|
102
101
|
it('returns errorCode when provided', async () => {
|
|
103
|
-
const { status, message, errorCode } = await
|
|
102
|
+
const { status, message, errorCode } = await handleErrors.mq(new errors.ExframeBase('this is my message', 'ERROR_CODE'), { log: fakeLogger });
|
|
104
103
|
expect(status).to.equal(500);
|
|
105
104
|
expect(message).to.equal('this is my message');
|
|
106
105
|
expect(errorCode).to.equal('ERROR_CODE');
|
|
@@ -117,7 +116,7 @@ context('handleErrors', () => {
|
|
|
117
116
|
const sendSpy = sinon.spy(expressResponseObject, 'send');
|
|
118
117
|
const doneSpy = sinon.spy();
|
|
119
118
|
|
|
120
|
-
const response =
|
|
119
|
+
const response = handleErrors.express(new errors.ResourceNotFound('Resource not found'), { log: fakeLogger }, expressResponseObject, doneSpy);
|
|
121
120
|
|
|
122
121
|
expect(doneSpy.callCount).to.equal(0);
|
|
123
122
|
expect(statusSpy.callCount).to.equal(1);
|
|
@@ -142,7 +141,7 @@ context('handleErrors', () => {
|
|
|
142
141
|
const sendSpy = sinon.spy(expressResponseObject, 'send');
|
|
143
142
|
const doneSpy = sinon.spy();
|
|
144
143
|
|
|
145
|
-
|
|
144
|
+
handleErrors.express(null, { log: fakeLogger }, expressResponseObject, doneSpy);
|
|
146
145
|
|
|
147
146
|
expect(doneSpy.callCount).to.equal(1);
|
|
148
147
|
expect(statusSpy.callCount).to.equal(0);
|
|
@@ -151,4 +150,93 @@ context('handleErrors', () => {
|
|
|
151
150
|
sinon.reset();
|
|
152
151
|
});
|
|
153
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
|
+
});
|
|
154
242
|
});
|