Skip to content

Commit 7d46cff

Browse files
committed
Allow arbitrary fetch() options to be specified
Previously we could only set the JWT token on the underlying fetch() call. Now any model can enhance the fetch options by overriding `getFetchOptions()`.
1 parent 8e11066 commit 7d46cff

File tree

5 files changed

+50
-13
lines changed

5 files changed

+50
-13
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ lib
66
lib-esm
77
_bundles
88
yarn.lock
9+
yarn-error.log

src/model.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import IncludeDirective from './util/include-directive';
1212
import DirtyChecker from './util/dirty-check';
1313
import ValidationErrors from './util/validation-errors';
1414
import relationshipIdentifiersFor from './util/relationship-identifiers';
15-
import Request from './request';
15+
import Request, { FetchOptions } from './request';
1616
import * as _cloneDeep from './util/clonedeep';
1717
let cloneDeep: any = (<any>_cloneDeep).default || _cloneDeep;
1818
if (cloneDeep.default) {
@@ -70,6 +70,12 @@ export default class Model {
7070
}
7171
}
7272

73+
static getFetchOptions() : FetchOptions {
74+
return {
75+
jwt: this.getJWT()
76+
}
77+
}
78+
7379
static getJWTOwner() : typeof Model {
7480
if (this.isJWTOwner) {
7581
return this;

src/request.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
11
import Config from './configuration';
22
import colorize from './util/colorize';
3+
import cloneDeep from './util/clonedeep';
4+
5+
// RequestInit is the type expected by `fetch()` API.
6+
export interface FetchOptions extends RequestInit {
7+
jwt? : string | undefined
8+
}
39

410
export default class Request {
5-
get(url : string, options: Object) : Promise<any> {
11+
get(url : string, options: FetchOptions) : Promise<any> {
612
options['method'] = 'GET';
713
return this._fetchWithLogging(url, options);
814
}
915

10-
post(url: string, payload: Object, options: Object) : Promise<any> {
16+
post(url: string, payload: Object, options: FetchOptions) : Promise<any> {
1117
options['method'] = 'POST';
1218
options['body'] = JSON.stringify(payload);
1319

1420
return this._fetchWithLogging(url, options);
1521
}
1622

17-
put(url: string, payload: Object, options: Object) : Promise<any> {
23+
put(url: string, payload: Object, options: FetchOptions) : Promise<any> {
1824
options['method'] = 'PUT';
1925
options['body'] = JSON.stringify(payload);
2026

2127
return this._fetchWithLogging(url, options);
2228
}
2329

24-
delete(url: string, options: Object) : Promise<any> {
30+
delete(url: string, options: FetchOptions) : Promise<any> {
2531
options['method'] = 'DELETE';
2632
return this._fetchWithLogging(url, options);
2733
}
@@ -36,7 +42,7 @@ export default class Request {
3642
Config.logger.debug(colorize('bold', JSON.stringify(responseJSON, null, 4)));
3743
}
3844

39-
private _fetchWithLogging(url: string, options: Object) : Promise<any> {
45+
private _fetchWithLogging(url: string, options: FetchOptions) : Promise<any> {
4046
this._logRequest(options['method'], url);
4147
let promise = this._fetch(url, options);
4248
promise.then((response : any) => {
@@ -45,10 +51,13 @@ export default class Request {
4551
return promise;
4652
}
4753

48-
private _fetch(url: string, options: Object) : Promise<any> {
54+
private _fetch(url: string, opts: FetchOptions) : Promise<any> {
4955
return new Promise((resolve, reject) => {
56+
// Clone options since we are changing the object
57+
let options : RequestInit = cloneDeep(opts as RequestInit)
58+
5059
let headers = this.buildHeaders(options);
51-
options['headers'] = headers;
60+
options.headers = headers;
5261

5362
let fetchPromise = fetch(url, options);
5463
fetchPromise.then((response) => {
@@ -57,17 +66,24 @@ export default class Request {
5766
resolve(response);
5867
}).catch((e) => { throw(e); });
5968
});
69+
6070
fetchPromise.catch(reject);
6171
});
6272
}
6373

64-
private buildHeaders(options: Object) : any {
74+
private buildHeaders(options: FetchOptions) : any {
6575
let headers = {};
76+
77+
if (typeof options.headers == 'object') {
78+
headers = options.headers
79+
}
80+
6681
headers['Accept'] = 'application/json';
6782
headers['Content-Type'] = 'application/json';
6883

69-
if (options['jwt']) {
70-
headers['Authorization'] = `Token token="${options['jwt']}"`;
84+
if (options.jwt) {
85+
headers['Authorization'] = `Token token="${options.jwt}"`;
86+
delete options.jwt
7187
}
7288

7389
return headers;

src/scope.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,9 @@ export default class Scope {
219219
url = `${url}?${qp}`;
220220
}
221221
let request = new Request();
222-
let jwt = this.model.getJWT();
222+
let fetchOpts = this.model.getFetchOptions()
223223

224-
return request.get(url, { jwt }).then((response) => {
224+
return request.get(url, fetchOpts).then((response) => {
225225
let jwtHeader = response.headers.get('X-JWT');
226226
if (jwtHeader) {
227227
this.model.setJWT(jwtHeader);

test/unit/model-test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,20 @@ describe('Model', function() {
134134
});
135135
});
136136

137+
describe('#getFetchOptions', function() {
138+
beforeEach(function() {
139+
ApplicationRecord.jwt = 'g3tm3';
140+
});
141+
142+
afterEach(function() {
143+
ApplicationRecord.jwt = null;
144+
});
145+
146+
it('includes the jwt', function() {
147+
expect(Author.getFetchOptions().jwt).to.eq('g3tm3');
148+
});
149+
})
150+
137151
describe('#isType', function() {
138152
it('checks the jsonapiType of class', function() {
139153
instance = new Author()

0 commit comments

Comments
 (0)