|
1 | 1 | import Config from './configuration';
|
| 2 | +import Model from './model'; |
2 | 3 | import colorize from './util/colorize';
|
| 4 | +import patchExtends from './custom-extend'; |
| 5 | +patchExtends() |
| 6 | + |
| 7 | +class RequestError extends Error { |
| 8 | + url: string |
| 9 | + options: RequestInit |
| 10 | + originalError: Error |
| 11 | + |
| 12 | + constructor(message: string, url: string, options: RequestInit, originalError: Error) { |
| 13 | + super(message) |
| 14 | + this.url = url |
| 15 | + this.options = options |
| 16 | + this.originalError = originalError |
| 17 | + } |
| 18 | +} |
| 19 | + |
| 20 | +class ResponseError extends Error { |
| 21 | + response: Response |
| 22 | + originalError: Error |
| 23 | + |
| 24 | + constructor(response: Response | null, message?: string, originalError?: Error) { |
| 25 | + super(message || 'Invalid Response') |
| 26 | + this.response = response |
| 27 | + this.originalError = originalError |
| 28 | + } |
| 29 | +} |
3 | 30 |
|
4 | 31 | export default class Request {
|
| 32 | + modelClass: typeof Model |
| 33 | + |
| 34 | + constructor(modelClass: typeof Model) { |
| 35 | + this.modelClass = modelClass |
| 36 | + } |
| 37 | + |
5 | 38 | get(url : string, options: RequestInit) : Promise<any> {
|
6 | 39 | options.method = 'GET';
|
7 | 40 | return this._fetchWithLogging(url, options);
|
@@ -39,23 +72,54 @@ export default class Request {
|
39 | 72 | private _fetchWithLogging(url: string, options: RequestInit) : Promise<any> {
|
40 | 73 | this._logRequest(options.method, url);
|
41 | 74 | let promise = this._fetch(url, options);
|
42 |
| - promise.then((response : any) => { |
| 75 | + return promise.then((response : any) => { |
43 | 76 | this._logResponse(response['jsonPayload']);
|
| 77 | + return response |
44 | 78 | });
|
45 |
| - return promise; |
46 | 79 | }
|
47 | 80 |
|
48 | 81 | private _fetch(url: string, options: RequestInit) : Promise<any> {
|
49 | 82 | return new Promise((resolve, reject) => {
|
| 83 | + try { |
| 84 | + this.modelClass.beforeFetch(url, options) |
| 85 | + } catch(e) { |
| 86 | + reject(new RequestError('beforeFetch failed; review Config.beforeFetch', url, options, e)) |
| 87 | + } |
| 88 | + |
50 | 89 | let fetchPromise = fetch(url, options);
|
51 | 90 | fetchPromise.then((response) => {
|
52 |
| - response.json().then((json) => { |
53 |
| - response['jsonPayload'] = json; |
54 |
| - resolve(response); |
55 |
| - }).catch((e) => { throw(e); }); |
| 91 | + this._handleResponse(response, resolve, reject) |
56 | 92 | });
|
57 | 93 |
|
58 |
| - fetchPromise.catch(reject); |
| 94 | + fetchPromise.catch((e) => { |
| 95 | + // Fetch itself failed (usually network error) |
| 96 | + reject(new ResponseError(null, e.message, e)) |
| 97 | + }) |
| 98 | + }); |
| 99 | + } |
| 100 | + |
| 101 | + private _handleResponse(response: Response, resolve: Function, reject: Function) : void { |
| 102 | + response.json().then((json) => { |
| 103 | + try { |
| 104 | + this.modelClass.afterFetch(response, json) |
| 105 | + } catch(e) { |
| 106 | + // afterFetch middleware failed |
| 107 | + reject(new ResponseError(response, 'afterFetch failed; review Config.afterFetch', e)) |
| 108 | + } |
| 109 | + |
| 110 | + if (response.status >= 500) { |
| 111 | + reject(new ResponseError(response, 'Server Error')) |
| 112 | + } else if (response.status !== 422 && json['data'] === undefined) { |
| 113 | + // Bad JSON, for instance an errors payload |
| 114 | + // Allow 422 since we specially handle validation errors |
| 115 | + reject(new ResponseError(response, 'invalid json')) |
| 116 | + } |
| 117 | + |
| 118 | + response['jsonPayload'] = json; |
| 119 | + resolve(response); |
| 120 | + }).catch((e) => { |
| 121 | + // The response was probably not in JSON format |
| 122 | + reject(new ResponseError(response, 'invalid json', e)) |
59 | 123 | });
|
60 | 124 | }
|
61 | 125 | }
|
0 commit comments