chore: make Request and ServerRequest pure JS class

And use UI for dealing with dataloading/dataload events (used
for progress bar).
This commit is contained in:
Yohan Boniface 2024-02-01 12:45:30 +01:00
parent 4a99f357f2
commit 0ebb7615aa
2 changed files with 39 additions and 46 deletions

View file

@ -1,7 +1,6 @@
// Uses `L._`` from Leaflet.i18n which we cannot import as a module yet // Uses `L._`` from Leaflet.i18n which we cannot import as a module yet
import { Evented, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js' import { Evented, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
export class RequestError extends Error {} export class RequestError extends Error {}
export class HTTPError extends RequestError { export class HTTPError extends RequestError {
@ -20,10 +19,8 @@ export class NOKError extends RequestError {
} }
} }
const BaseRequest = Evented.extend({ class BaseRequest {
_fetch: async function (method, uri, headers, data) { async _fetch(method, uri, headers, data) {
const id = Math.random()
this.fire('dataloading', { id: id })
let response let response
try { try {
@ -35,32 +32,29 @@ const BaseRequest = Evented.extend({
}) })
} catch (error) { } catch (error) {
console.error(error) console.error(error)
this.fire('dataload', { id: id })
throw new HTTPError(error.message) throw new HTTPError(error.message)
} }
if (!response.ok) { if (!response.ok) {
this.fire('dataload', { id: id })
throw new NOKError(response) throw new NOKError(response)
} }
// TODO
// - error handling
// - UI connection / events
this.fire('dataload', { id: id })
return response return response
}, }
}) }
// Basic class to issue request // Basic class to issue request
// It returns a response, or null in case of error // It returns a response, or null in case of error
// In case of error, an alert is sent, but non 20X status are not handled // In case of error, an alert is sent, but non 20X status are not handled
// The consumer must check the response status by hand // The consumer must check the response status by hand
export const Request = BaseRequest.extend({ export class Request extends BaseRequest {
initialize: function (ui) { constructor(ui) {
super()
this.ui = ui this.ui = ui
}, }
_fetch: async function (method, uri, headers, data) { async _fetch(method, uri, headers, data) {
const id = Math.random()
this.ui.fire('dataloading', { id: id })
try { try {
const response = await BaseRequest.prototype._fetch.call( const response = await BaseRequest.prototype._fetch.call(
this, this,
@ -73,43 +67,44 @@ export const Request = BaseRequest.extend({
} catch (error) { } catch (error) {
if (error instanceof NOKError) return this._onNOK(error) if (error instanceof NOKError) return this._onNOK(error)
return this._onError(error) return this._onError(error)
} finally {
this.ui.fire('dataload', { id: id })
} }
}, }
get: async function (uri, headers) { async get(uri, headers) {
return await this._fetch('GET', uri, headers) return await this._fetch('GET', uri, headers)
}, }
post: async function (uri, headers, data) { async post(uri, headers, data) {
return await this._fetch('POST', uri, headers, data) return await this._fetch('POST', uri, headers, data)
}, }
_onError: function (error) { _onError(error) {
this.ui.alert({ content: L._('Problem in the response'), level: 'error' }) this.ui.alert({ content: L._('Problem in the response'), level: 'error' })
}, }
_onNOK: function (error) { _onNOK(error) {
this._onError(error) this._onError(error)
return error.response return error.response
}, }
}
})
// Adds uMap specifics to requests handling // Adds uMap specifics to requests handling
// like logging, CSRF, etc. // like logging, CSRF, etc.
// It expects only json responses. // It expects only json responses.
// Returns an array of three elements: [data, response, error] // Returns an array of three elements: [data, response, error]
// The consumer must check the error to proceed or not with using the data or response // The consumer must check the error to proceed or not with using the data or response
export const ServerRequest = Request.extend({ export class ServerRequest extends Request {
_fetch: async function (method, uri, headers, data) { async _fetch(method, uri, headers, data) {
// Add a flag so backend can know we are in ajax and adapt the response // Add a flag so backend can know we are in ajax and adapt the response
// See is_ajax in utils.py // See is_ajax in utils.py
headers = headers || {} headers = headers || {}
headers['X-Requested-With'] = 'XMLHttpRequest' headers['X-Requested-With'] = 'XMLHttpRequest'
return await Request.prototype._fetch.call(this, method, uri, headers, data) return await Request.prototype._fetch.call(this, method, uri, headers, data)
}, }
post: async function (uri, headers, data) { async post(uri, headers, data) {
const token = document.cookie.replace( const token = document.cookie.replace(
/(?:(?:^|.*;\s*)csrftoken\s*\=\s*([^;]*).*$)|^.*$/, /(?:(?:^|.*;\s*)csrftoken\s*\=\s*([^;]*).*$)|^.*$/,
'$1' '$1'
@ -120,14 +115,14 @@ export const ServerRequest = Request.extend({
} }
const response = await Request.prototype.post.call(this, uri, headers, data) const response = await Request.prototype.post.call(this, uri, headers, data)
return await this._as_json(response) return await this._as_json(response)
}, }
get: async function (uri, headers) { async get(uri, headers) {
const response = await Request.prototype.get.call(this, uri, headers) const response = await Request.prototype.get.call(this, uri, headers)
return await this._as_json(response) return await this._as_json(response)
}, }
_as_json: async function (response) { async _as_json(response) {
if (Array.isArray(response)) return response if (Array.isArray(response)) return response
try { try {
const data = await response.json() const data = await response.json()
@ -142,13 +137,13 @@ export const ServerRequest = Request.extend({
} catch (error) { } catch (error) {
return this._onError(error) return this._onError(error)
} }
}, }
_onError: function (error) { _onError(error) {
return [{}, null, error] return [{}, null, error]
}, }
_onNOK: function (error) { _onNOK(error) {
if (error.status === 403) { if (error.status === 403) {
this.ui.alert({ this.ui.alert({
content: message || L._('Action not allowed :('), content: message || L._('Action not allowed :('),
@ -156,5 +151,5 @@ export const ServerRequest = Request.extend({
}) })
} }
return [{}, error.response, error] return [{}, error.response, error]
}, }
}) }

View file

@ -98,12 +98,10 @@ L.U.Map.include({
this.urls = new window.umap.URLs(this.options.urls) this.urls = new window.umap.URLs(this.options.urls)
this.ui = new L.U.UI(this._container) this.ui = new L.U.UI(this._container)
this.ui.on('dataloading', (e) => this.fire('dataloading', e))
this.ui.on('dataload', (e) => this.fire('dataload', e))
this.server = new window.umap.ServerRequest(this.ui) this.server = new window.umap.ServerRequest(this.ui)
this.server.on('dataloading', (e) => this.fire('dataloading', e))
this.server.on('dataload', (e) => this.fire('dataload', e))
this.request = new window.umap.Request(this.ui) this.request = new window.umap.Request(this.ui)
this.request.on('dataloading', (e) => this.fire('dataloading', e))
this.request.on('dataload', (e) => this.fire('dataload', e))
this.initLoader() this.initLoader()
this.name = this.options.name this.name = this.options.name