Merge pull request #5682 from tchak/replace-jquery-with-fetch

Use fetch instead of jQuery
This commit is contained in:
Paul Chavard 2020-10-13 11:35:28 +02:00 committed by GitHub
commit 2e2086f020
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 51 deletions

View file

@ -1,4 +1,4 @@
import { to, getJSON } from '@utils'; import { getJSON } from '@utils';
export default class OperationsQueue { export default class OperationsQueue {
constructor(baseUrl) { constructor(baseUrl) {
@ -30,23 +30,27 @@ export default class OperationsQueue {
async exec(operation) { async exec(operation) {
const { path, method, payload, resolve, reject } = operation; const { path, method, payload, resolve, reject } = operation;
const url = `${this.baseUrl}${path}`; const url = `${this.baseUrl}${path}`;
const [data, xhr] = await to(getJSON(url, payload, method));
if (xhr) { try {
handleError(xhr, reject); const data = await getJSON(url, payload, method);
} else {
resolve(data); resolve(data);
} catch (e) {
handleError(e, reject);
} }
} }
} }
function handleError(xhr, reject) { async function handleError({ response, message }, reject) {
if (response) {
try { try {
const { const {
errors: [message] errors: [message]
} = JSON.parse(xhr.responseText); } = await response.json();
reject(message);
} catch {
reject(await response.text());
}
} else {
reject(message); reject(message);
} catch (e) {
reject(xhr.responseText);
} }
} }

View file

@ -5,7 +5,6 @@ import jQuery from 'jquery';
import '../shared/page-update-event'; import '../shared/page-update-event';
import '../shared/activestorage/ujs'; import '../shared/activestorage/ujs';
import '../shared/rails-ujs-fix';
import '../shared/safari-11-file-xhr-workaround'; import '../shared/safari-11-file-xhr-workaround';
import '../shared/remote-input'; import '../shared/remote-input';
import '../shared/franceconnect'; import '../shared/franceconnect';

View file

@ -7,7 +7,6 @@ import ReactRailsUJS from 'react_ujs';
import '../shared/page-update-event'; import '../shared/page-update-event';
import '../shared/activestorage/ujs'; import '../shared/activestorage/ujs';
import '../shared/remote-poller'; import '../shared/remote-poller';
import '../shared/rails-ujs-fix';
import '../shared/safari-11-file-xhr-workaround'; import '../shared/safari-11-file-xhr-workaround';
import '../shared/remote-input'; import '../shared/remote-input';
import '../shared/franceconnect'; import '../shared/franceconnect';

View file

@ -1,20 +0,0 @@
import jQuery from 'jquery';
// rails-ujs installs CSRFProtection for its own ajax implementation. We might need
// CSRFProtection for jQuery initiated requests. This code is from jquery-ujs.
jQuery.ajaxPrefilter((options, originalOptions, xhr) => {
if (!options.crossDomain) {
CSRFProtection(xhr);
}
});
function csrfToken() {
return jQuery('meta[name=csrf-token]').attr('content');
}
function CSRFProtection(xhr) {
let token = csrfToken();
if (token) {
xhr.setRequestHeader('X-CSRF-Token', token);
}
}

View file

@ -1,9 +1,8 @@
import Rails from '@rails/ujs'; import Rails from '@rails/ujs';
import $ from 'jquery';
import debounce from 'debounce'; import debounce from 'debounce';
export { debounce }; export { debounce };
export const { fire } = Rails; export const { fire, csrfToken } = Rails;
export function show(el) { export function show(el) {
el && el.classList.remove('hidden'); el && el.classList.remove('hidden');
@ -67,17 +66,20 @@ export function ajax(options) {
}); });
} }
export function getJSON(url, data, method = 'get') { export function getJSON(url, data, method = 'GET') {
data = method !== 'get' && data ? JSON.stringify(data) : data; const { query, ...options } = fetchOptions(data, method);
return Promise.resolve(
$.ajax({ return fetch(`${url}${query}`, options).then((response) => {
method, if (response.ok) {
url, if (response.status === 204) {
data, return null;
contentType: 'application/json', }
dataType: 'json' return response.json();
}) }
); const error = new Error(response.statusText || response.status);
error.response = response;
throw error;
});
} }
export function scrollTo(container, scrollTo) { export function scrollTo(container, scrollTo) {
@ -95,10 +97,6 @@ export function on(selector, eventName, fn) {
); );
} }
export function to(promise) {
return promise.then((result) => [result]).catch((error) => [null, error]);
}
export function isNumeric(n) { export function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n); return !isNaN(parseFloat(n)) && isFinite(n);
} }
@ -120,3 +118,50 @@ export function timeoutable(promise, timeoutDelay) {
}); });
return Promise.race([promise, timeoutPromise]); return Promise.race([promise, timeoutPromise]);
} }
const FETCH_TIMEOUT = 30 * 1000; // 30 sec
function fetchOptions(data, method = 'GET') {
const options = {
query: '',
method: method.toUpperCase(),
headers: {
accept: 'application/json',
'x-csrf-token': csrfToken(),
'x-requested-with': 'XMLHttpRequest'
},
credentials: 'same-origin'
};
if (data) {
if (options.method === 'GET') {
options.query = objectToQuerystring(data);
} else {
options.headers['content-type'] = 'application/json';
options.body = JSON.stringify(data);
}
}
if (window.AbortController) {
const controller = new AbortController();
options.signal = controller.signal;
setTimeout(() => {
controller.abort();
}, FETCH_TIMEOUT);
}
return options;
}
function objectToQuerystring(obj) {
return Object.keys(obj).reduce(function (query, key, i) {
return [
query,
i === 0 ? '?' : '&',
encodeURIComponent(key),
'=',
encodeURIComponent(obj[key])
].join('');
}, '');
}