Skip to content

Commit 97a3556

Browse files
author
John Conway
authored
Merge pull request #6 from PilotConway/feature/requestError
Refactor Errors
2 parents 413d791 + 5215e95 commit 97a3556

12 files changed

+202
-118
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
},
1717
"devDependencies": {
1818
"codecov": "^3.5.0",
19+
"nock": "^10.0.6",
1920
"typescript": "3.3.3"
2021
},
2122
"scripts": {

src/client/__mocks__/getRequest.js

Lines changed: 0 additions & 77 deletions
This file was deleted.

src/client/client.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ const client = {
3636
/**
3737
* Runs a get operation on the provided path and returns the data.
3838
*
39+
* @param {boolean} ok False if the response contains an error.
40+
* @param {number} status The http status code of the response.
41+
* @param {string} statusText A message if provided to describe the status response. Null if none
42+
* was supplied.
3943
* @param {object|array} data The data from the server.
4044
* @param {AjaxResponse} rawResponse The RxJS AJAX Response object from the server.
4145
* @param {object} links The links object containing any link header URLS. All properties are
@@ -51,7 +55,10 @@ const client = {
5155
const links = processLinks(response);
5256

5357
return {
58+
ok: response.ok,
5459
data: response.response,
60+
status: response.status,
61+
statusText: (response.response || {}).message || response.message || null,
5562
rawResponse: response,
5663
links,
5764
};

src/client/client.spec.js

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import client from './client';
2-
// Require get request import for mocks to work
3-
// eslint-disable-next-line no-unused-vars
4-
import getRequest from './getRequest';
2+
import nock from 'nock';
53

6-
// SEE __mocks__/getRequest for which endpoint paths result in what types of
7-
// responses from the getRequest().toPromise() method.
8-
jest.mock('./getRequest');
4+
// make sure no requests are actually sent
5+
nock.disableNetConnect();
96

107
describe('raw client', () => {
118
it('calls getRequest and returns data', async () => {
12-
const response = await client.get('foo');
9+
nock('http://localhost')
10+
.get('/foo')
11+
.reply(200, { bar: 'baz' });
12+
const response = await client.get('http://localhost/foo');
1313

1414
expect(response.data).toEqual({ bar: 'baz' });
1515
expect(response.links).toEqual({
@@ -20,30 +20,64 @@ describe('raw client', () => {
2020
});
2121
});
2222

23-
it('rejects when when an error occurs', async () => {
24-
expect(client.get('/404')).rejects.toThrow();
23+
it('responds with an error response when when an error from the server occurs', async () => {
24+
nock('http://localhost')
25+
.get('/404')
26+
.reply(404, { message: 'Not Found' });
27+
const response = await client.get('http://localhost/404');
28+
expect(response.ok).toBe(false);
29+
expect(response.data).toEqual({ message: 'Not Found' });
30+
expect(response.status).toEqual(404);
31+
expect(response.statusText).toEqual('Not Found');
32+
});
33+
34+
it('Has statusText set to ajax error messsage when no message is in the response', async () => {
35+
nock('http://localhost')
36+
.get('/500')
37+
.reply(500);
38+
const response = await client.get('http://localhost/500');
39+
expect(response.ok).toBe(false);
40+
expect(response.data).toEqual(null);
41+
expect(response.status).toEqual(500);
42+
expect(response.statusText).toEqual('ajax error 500');
2543
});
2644

2745
describe('links', () => {
2846
it('returns next link', async () => {
47+
nock('http://localhost')
48+
.get('/link/next')
49+
.reply(200, {}, { Link: '<https://example.com/foo?page=7>; rel="next"' });
50+
2951
const response = await client.get('/link/next');
3052

3153
expect(response.links.next).toEqual('https://example.com/foo?page=7');
3254
});
3355

3456
it('returns previous link', async () => {
57+
nock('http://localhost')
58+
.get('/link/prev')
59+
.reply(200, {}, { Link: '<https://example.com/foo?page=5>; rel="prev"' });
60+
3561
const response = await client.get('/link/prev');
3662

3763
expect(response.links.prev).toEqual('https://example.com/foo?page=5');
3864
});
3965

4066
it('returns first link', async () => {
67+
nock('http://localhost')
68+
.get('/link/first')
69+
.reply(200, {}, { Link: '<https://example.com/foo?page=1>; rel="first"' });
70+
4171
const response = await client.get('/link/first');
4272

4373
expect(response.links.first).toEqual('https://example.com/foo?page=1');
4474
});
4575

4676
it('returns last link', async () => {
77+
nock('http://localhost')
78+
.get('/link/last')
79+
.reply(200, {}, { Link: '<https://example.com/foo?page=10>; rel="last"' });
80+
4781
const response = await client.get('/link/last');
4882

4983
expect(response.links.last).toEqual('https://example.com/foo?page=10');

src/client/createClient.spec.js

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import createClient from './createClient';
2-
// Require get request import for mocks to work
3-
// eslint-disable-next-line no-unused-vars
4-
import getRequest from './getRequest';
2+
import nock from 'nock';
53

6-
// SEE __mocks__/getRequest for which endpoint paths result in what types of
7-
// responses from the getRequest().toPromise() method.
8-
jest.mock('./getRequest');
4+
nock.disableNetConnect();
95

106
describe('createClient', () => {
117
it('intializes', () => {
@@ -30,29 +26,63 @@ describe('createClient', () => {
3026

3127
describe('get()', () => {
3228
it('createdClient getRequest creates full url from relative endpoint', async () => {
29+
nock('https://example.com:443')
30+
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
31+
.get('/api/v2/foo')
32+
.reply(200);
33+
3334
const client = createClient('https://example.com/api/v2');
3435
const response = await client.get('foo');
3536

36-
expect(response.data).toEqual({ url: 'https://example.com/api/v2/foo' });
37+
expect(response.rawResponse.xhr.responseURL).toEqual('https://example.com/api/v2/foo');
3738
});
3839

3940
it('does not double leading /', async () => {
41+
nock('https://example.com:443')
42+
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
43+
.get('/api/v2/foo')
44+
.reply(200, { url: 'https://example.com/api/v2/foo' });
45+
4046
const client = createClient('https://example.com/api/v2');
4147
const response = await client.get('/foo');
4248

43-
expect(response.data).toEqual({ url: 'https://example.com/api/v2/foo' });
49+
expect(response.rawResponse.xhr.responseURL).toEqual('https://example.com/api/v2/foo');
4450
});
4551

4652
it('does not modify full urls', async () => {
53+
const gitHubScope = nock('https://github.com')
54+
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
55+
.get('/users')
56+
.reply(200, { url: 'https://github.com/users' });
57+
58+
const exampleScope = nock('https://example.com')
59+
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
60+
.get('/api/v2/users')
61+
.reply(404, { url: 'https://exmaple.com/api/v2/users' });
62+
4763
const client = createClient('https://example.com/api/v2');
4864
const response = await client.get('https://github.com/users');
4965

66+
expect(gitHubScope.isDone()).toBe(true);
67+
expect(exampleScope.isDone()).toBe(false);
68+
expect(response.ok).toBe(true);
69+
expect(response.status).toEqual(200);
5070
expect(response.data).toEqual({ url: 'https://github.com/users' });
5171
});
5272

5373
it('rejects when when an error occurs', async () => {
74+
nock('https://example.com')
75+
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
76+
.get('/api/v2/404')
77+
.reply(404, { message: 'Not Found' });
78+
5479
const client = createClient('https://example.com/api/v2');
55-
expect(client.get('/404')).rejects.toThrow();
80+
const response = await client.get('/404');
81+
82+
expect(response.ok).toBe(false);
83+
expect(response.data).toEqual({ message: 'Not Found' });
84+
expect(response.status).toEqual(404);
85+
expect(response.statusText).toEqual('Not Found');
5686
});
5787
});
5888
});

src/client/getRequest.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
import { ajax } from 'rxjs/ajax';
22
import { catchError, map } from 'rxjs/operators';
3-
import { throwError, of } from 'rxjs';
3+
import { of } from 'rxjs';
44

55
const getRequest = path =>
66
ajax(path).pipe(
77
map(response => {
8-
if (response === null) {
9-
return throwError('API Timed out', response);
10-
}
8+
response.ok = true;
119
return response;
1210
}),
1311
catchError(error => {
12+
error.ok = false;
1413
return of(error);
1514
}),
1615
);

src/client/getRequest.spec.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import nock from 'nock';
2+
3+
import getRequest from './getRequest';
4+
5+
describe('getRequest', () => {
6+
it('returns response', async () => {
7+
const data = { foo: 'bar' };
8+
nock('http://localhost')
9+
.get('/foo')
10+
.reply(200, data);
11+
12+
const response = await getRequest('http://localhost/foo').toPromise();
13+
expect(response.ok).toBe(true);
14+
expect(response.response).toEqual(data);
15+
expect(response.status).toBe(200);
16+
});
17+
18+
it('returns error', async () => {
19+
const data = { message: 'Error' };
20+
nock('http://localhost')
21+
.get('/foo')
22+
.reply(500, data);
23+
24+
const response = await getRequest('http://localhost/foo').toPromise();
25+
expect(response.ok).toBe(false);
26+
expect(response.response).toEqual(data);
27+
expect(response.status).toBe(500);
28+
});
29+
});

src/components/Dashboard.jsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@ export default function Dashboard() {
1818
const [users, loading, error] = useEndpointData(
1919
`https://api.github.com/usersdsafasdfa?per_page=5`,
2020
);
21+
2122
return (
22-
<Box
23-
display="flex"
24-
flexDirection="row"
25-
justifyContent="space-between"
26-
flexWrap="wrap"
27-
>
23+
<Box display="flex" flexDirection="row" justifyContent="space-between" flexWrap="wrap">
2824
<Box width={{ xs: '100%', md: 350 }} m={2}>
2925
<UsersCard />
3026
</Box>

src/components/ReposCard/ReposList.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export default function ReposList({ repos, isLoading, error }) {
1313
}
1414

1515
if (error || !Array.isArray(repos)) {
16-
return <Banner variant="error" message="An error occured loading repos." />;
16+
const message = (error || {}).statusText || 'An unknown error occured loading users. ';
17+
return <Banner variant="error" message={message} />;
1718
}
1819

1920
return (

src/components/UsersCard/UsersList.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ export default function UsersList({ users, isLoading, error }) {
1515
}
1616

1717
if (error || !Array.isArray(users)) {
18-
return <Banner variant="error" message="An error occured loading users." />;
18+
const message = (error || {}).statusText || 'An unknown error occured loading users. ';
19+
return <Banner variant="error" message={message} />;
1920
}
2021

2122
return (

src/useEndpointData.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@ export default function useEndpointData(path, options) {
7575
setLoading(true);
7676
try {
7777
const response = await client.get(currentEndpoint);
78-
setValue(response.data);
78+
79+
if (response.ok) {
80+
setValue(response.data);
81+
} else {
82+
setError(response);
83+
}
7984

8085
// If we aren't a paged response, then these will be null
8186
setNextLink(response.links.next);
@@ -90,11 +95,5 @@ export default function useEndpointData(path, options) {
9095
};
9196
load();
9297
}, [path, currentEndpoint, client]);
93-
return [
94-
value,
95-
isLoading,
96-
error,
97-
{ getNext, getPrevious, getFirst, getLast },
98-
client,
99-
];
98+
return [value, isLoading, error, { getNext, getPrevious, getFirst, getLast }, client];
10099
}

0 commit comments

Comments
 (0)