diff --git a/app/javascript/dashboard/api/ApiClient.js b/app/javascript/dashboard/api/ApiClient.js index 6b57d32b6..f730ac0e9 100644 --- a/app/javascript/dashboard/api/ApiClient.js +++ b/app/javascript/dashboard/api/ApiClient.js @@ -1,12 +1,20 @@ /* global axios */ const DEFAULT_API_VERSION = 'v1'; +const DEFAULT_OPTIONS = { + apiVersion: DEFAULT_API_VERSION, + enterprise: false, + accountScoped: false, + cancelPreviousRequests: false, +}; class ApiClient { constructor(resource, options = {}) { - this.apiVersion = `/api/${options.apiVersion || DEFAULT_API_VERSION}`; - this.options = options; + this.options = { ...DEFAULT_OPTIONS, ...options }; + this.apiVersion = `/api/${this.options.apiVersion}`; this.resource = resource; + this.abortControllers = new Map(); + this.activeRequests = new Map(); } get url() { @@ -39,24 +47,105 @@ class ApiClient { return url; } - get() { - return axios.get(this.url); + generateRequestKey(method, id = '', customRequestKey = '') { + if (customRequestKey) return customRequestKey; + return `${method}-${this.url}${id ? `/${id}` : ''}`; } - show(id) { - return axios.get(`${this.url}/${id}`); + cancelRequest(requestKey) { + const controller = this.abortControllers.get(requestKey); + if (controller) { + controller.abort(); + this.abortControllers.delete(requestKey); + return true; + } + return false; } - create(data) { - return axios.post(this.url, data); + setupRequest(method, options = {}) { + if (!this.options.cancelPreviousRequests) { + return {}; + } + const { id, customRequestKey } = options; + const requestKey = this.generateRequestKey(method, id, customRequestKey); + + const previousRequests = this.activeRequests.get(method) || []; + const remainingRequests = previousRequests.filter(key => { + if (key === requestKey) { + this.cancelRequest(key); + return false; + } + return true; + }); + + this.activeRequests.set(method, [...remainingRequests, requestKey]); + + const controller = new AbortController(); + this.abortControllers.set(requestKey, controller); + + return { requestKey, signal: controller.signal }; } - update(id, data) { - return axios.patch(`${this.url}/${id}`, data); + async request(method, config = {}) { + const { id, data, customRequestKey, ...axiosConfig } = config; + const { requestKey, signal } = this.setupRequest(method, { + id, + customRequestKey, + }); + try { + const url = id ? `${this.url}/${id}` : this.url; + return await axios({ + method, + url, + data, + signal, + ...axiosConfig, + }); + } finally { + this.abortControllers.delete(requestKey); + if (this.options.cancelPreviousRequests) { + const methodRequests = this.activeRequests.get(method) || []; + this.activeRequests.set( + method, + methodRequests.filter(key => key !== requestKey) + ); + } + } } - delete(id) { - return axios.delete(`${this.url}/${id}`); + get(config = {}) { + return this.request('GET', config); + } + + show(id, config = {}) { + return this.request('GET', { ...config, id }); + } + + create(data, config = {}) { + return this.request('POST', { ...config, data }); + } + + update(id, data, config = {}) { + return this.request('PATCH', { ...config, id, data }); + } + + delete(id, config = {}) { + return this.request('DELETE', { ...config, id }); + } + + cancelAllRequests() { + this.abortControllers.forEach(controller => controller.abort()); + this.abortControllers.clear(); + this.activeRequests.clear(); + } + + // Get all active request keys + getActiveRequests() { + return Array.from(this.abortControllers.keys()); + } + + destroy() { + this.cancelAllRequests(); } } diff --git a/app/javascript/dashboard/api/reports.js b/app/javascript/dashboard/api/reports.js index 52fa7f444..72b6cc679 100644 --- a/app/javascript/dashboard/api/reports.js +++ b/app/javascript/dashboard/api/reports.js @@ -5,7 +5,11 @@ const getTimeOffset = () => -new Date().getTimezoneOffset() / 60; class ReportsAPI extends ApiClient { constructor() { - super('reports', { accountScoped: true, apiVersion: 'v2' }); + super('reports', { + accountScoped: true, + apiVersion: 'v2', + cancelPreviousRequests: true, + }); } getReports({ @@ -17,7 +21,8 @@ class ReportsAPI extends ApiClient { groupBy, businessHours, }) { - return axios.get(`${this.url}`, { + return this.request('GET', { + customRequestKey: `${metric}-report-GET`, params: { metric, since: from, @@ -47,7 +52,9 @@ class ReportsAPI extends ApiClient { } getConversationMetric(type = 'account', page = 1) { - return axios.get(`${this.url}/conversations`, { + return this.request('GET', { + requestPath: `${this.url}/conversations`, + params: { type, page,