Skip to content

Commit

Permalink
Add pre-fetch function to ajax module (#1335)
Browse files Browse the repository at this point in the history
The pre-fetch function is executed before every network request
made by the ajax module. It can be used to run actions before
fetch() is invoked by Fauxton, even allowing one to  modify
the original fetch() parameters.
  • Loading branch information
Antonio-Maranhao committed Feb 15, 2022
1 parent 0c75407 commit f24fe21
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 22 deletions.
53 changes: 52 additions & 1 deletion app/core/__tests__/ajax.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import {
get,
put,
post,
deleteRequest
deleteRequest,
setPreFetchFn,
defaultPreFetch,

} from '../ajax';

describe('Fauxton Ajax', () => {
Expand Down Expand Up @@ -209,4 +212,52 @@ describe('Fauxton Ajax', () => {
});
});
});

describe('pre-fetch function', () => {
afterEach(() => {
setPreFetchFn(defaultPreFetch);
});

const successResponse = {
status: 200,
body: {
ok: true
}
};
it('by default calls fetch with the same params', () => {
fetchMock.postOnce('/testing', successResponse);
return post('/testing', JSON.stringify({a: 123}), {extraOption: 'foo'})
.then(resp =>{
expect(resp.ok).toBeTruthy();
expect(fetchMock.lastOptions().extraOption).toEqual('foo');
});
});

it('is called and then fetch is called', () => {
fetchMock.postOnce('/testing_transformed', successResponse);
let prefetchCalled = false;
const mockPreFetch = (url, options) => {
prefetchCalled = true;
return Promise.resolve({url: url + '_transformed', options});
};
setPreFetchFn(mockPreFetch);
return post('/testing', JSON.stringify({a: 123}))
.then(resp =>{
expect(prefetchCalled).toEqual(true);
expect(resp.ok).toBeTruthy();
expect(fetchMock.lastOptions().body).toBe('"{\\"a\\":123}"');
});
});

it('fails when trying to set an invalid function or null/undefined', () => {
const caseNotFunction = () => { setPreFetchFn('not a function'); };
expect(caseNotFunction).toThrow();
const caseNull = () => { setPreFetchFn(null); };
expect(caseNull).toThrow();
const caseUndefined = () => { setPreFetchFn(undefined); };
expect(caseUndefined).toThrow();
const caseInvalidFunction = () => { setPreFetchFn((onlyOneParam) => { return onlyOneParam; }); };
expect(caseInvalidFunction).toThrow();
});
});
});
71 changes: 50 additions & 21 deletions app/core/ajax.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,32 @@ import 'rxjs/add/operator/filter';
*/
export const fetchObserver = new Subject();

// The default pre-fetch function which simply resolves to the original parameters.
export function defaultPreFetch(url, options) {
return Promise.resolve({url, options});
}

let _preFetchFn = defaultPreFetch;

/**
* setPreFetchFn - sets a 'pre-fetch' function that is executed before each network request
* originated from this module, i.e. before fetch() is executed. Any fetch() calls from
* outside this module are not affected.
*
* The provided function will receive the 'url' and 'options' parameters that would be sent to fetch(),
* and it is expected to return a Promise that resolves to a {url, options} object.
* Once this Promise resolves, fetch() is then executed with the 'url' and 'options' returned by the Promise.
* This means, the 'pre-fetch' function can transform the original values before fetch() is called.
*
* @param {function} fn The pre-fetch function
*/
export const setPreFetchFn = fn => {
if (fn && typeof fn === "function" && fn.length === 2) {
_preFetchFn = fn;
} else {
throw new Error('preFetch must be a function that accepts two parameters (url and options) like the native fetch()');
}
};

/**
* json - The lowlevel fetch request with some basic headers
Expand All @@ -29,28 +55,31 @@ export const fetchObserver = new Subject();
* @return {Promise}
*/
export const json = (url, method = "GET", opts = {}) => {
return fetch(
url,
defaultsDeep(
{},
opts,
{
method,
credentials: "include",
headers: {
accept: "application/json",
"Content-Type": "application/json",
"Pragma":"no-cache" //Disables cache for IE11
},
cache: "no-cache"
}
)
).then(resp => {
fetchObserver.next(resp);
if (opts.raw) {
return resp;
const fetchOptions = defaultsDeep(
{},
opts,
{
method,
credentials: "include",
headers: {
accept: "application/json",
"Content-Type": "application/json",
"Pragma":"no-cache" //Disables cache for IE11
},
cache: "no-cache"
}
return resp.json();
);
return _preFetchFn(url, fetchOptions).then((result) => {
return fetch(
result.url,
result.options,
).then(resp => {
fetchObserver.next(resp);
if (opts.raw) {
return resp;
}
return resp.json();
});
});
};

Expand Down

0 comments on commit f24fe21

Please sign in to comment.