diff options
| author | Jan-Hendrik Willms <tleilax+studip@gmail.com> | 2024-05-28 08:47:35 +0000 |
|---|---|---|
| committer | Jan-Hendrik Willms <tleilax+studip@gmail.com> | 2024-05-28 08:47:35 +0000 |
| commit | 0afb9c16eab3f9c1fd069b350d4fa9fee4c0ec73 (patch) | |
| tree | e68d8b304a4bcae48510b4454f740a839059ee8e | |
| parent | fe2b584cb79ba8a2c642be085cacdd82d526df25 (diff) | |
allow abstract api to work with real promises instead of deferreds and use it, fixes #3643
Closes #3643
Merge request studip/studip!2885
| -rw-r--r-- | resources/assets/javascripts/bootstrap/consultations.js | 4 | ||||
| -rw-r--r-- | resources/assets/javascripts/init.js | 2 | ||||
| -rw-r--r-- | resources/assets/javascripts/lib/abstract-api.js | 40 | ||||
| -rw-r--r-- | resources/assets/javascripts/lib/jsonapi.ts (renamed from resources/assets/javascripts/lib/jsonapi.js) | 8 | ||||
| -rw-r--r-- | resources/assets/javascripts/studip-ui.js | 4 | ||||
| -rw-r--r-- | resources/vue/components/MyCoursesTiles.vue | 6 | ||||
| -rw-r--r-- | resources/vue/store/ContentModulesStore.js | 2 | ||||
| -rw-r--r-- | resources/vue/store/MyCoursesStore.js | 2 |
8 files changed, 54 insertions, 14 deletions
diff --git a/resources/assets/javascripts/bootstrap/consultations.js b/resources/assets/javascripts/bootstrap/consultations.js index ef79d9c..51ffa85 100644 --- a/resources/assets/javascripts/bootstrap/consultations.js +++ b/resources/assets/javascripts/bootstrap/consultations.js @@ -10,9 +10,9 @@ $(document).on('click', '.consultation-delete-check:not(.ignore)', event => { } let requests = ids.map(id => { - return STUDIP.jsonapi.GET(`consultation-slots/${id}/bookings`).then(result => result.data.length); + return STUDIP.jsonapi.withPromises().get(`consultation-slots/${id}/bookings`).then(response => response.data.length); }); - $.when(...requests).done((...results) => { + Promise.all(requests).then((...results) => { if (results.some(result => result > 0)) { $(event.target).addClass('ignore').click().removeClass('ignore'); } else { diff --git a/resources/assets/javascripts/init.js b/resources/assets/javascripts/init.js index 2d592be..fe20c9a 100644 --- a/resources/assets/javascripts/init.js +++ b/resources/assets/javascripts/init.js @@ -38,7 +38,7 @@ import HeaderMagic from './lib/header_magic.js'; import i18n from './lib/i18n.js'; import Instschedule from './lib/instschedule.js'; import InlineEditing from './lib/inline-editing.js'; -import JSONAPI, { jsonapi } from './lib/jsonapi.js'; +import JSONAPI, { jsonapi } from './lib/jsonapi.ts'; import JSUpdater from './lib/jsupdater.js'; import Lightbox from './lib/lightbox.js'; import Markup from './lib/markup.js'; diff --git a/resources/assets/javascripts/lib/abstract-api.js b/resources/assets/javascripts/lib/abstract-api.js index 70a88db..95ae015 100644 --- a/resources/assets/javascripts/lib/abstract-api.js +++ b/resources/assets/javascripts/lib/abstract-api.js @@ -1,5 +1,20 @@ import Overlay from './overlay.js'; +class APIError extends Error +{ + static createWithJqXhr(message, jqXhr) { + const error = new APIError(message); + error.setJqXhr(jqXhr); + return error; + } + + jqXhr = null; + + setJqXhr(jqXhr) { + this.jqXhr = jqXhr; + } +} + class AbstractAPI { static get supportedMethods() { @@ -118,6 +133,31 @@ class AbstractAPI } }).join('&'); } + + withPromises() { + return new Proxy(this, { + get(target, prop, receiver) { + // This will allow http methods to be written as lowercase when called as methods + // (e.g. api.patch() instead of api.PATCH()) + if (target[prop] === undefined && AbstractAPI.supportedMethods.includes(prop.toUpperCase())) { + prop = prop.toUpperCase(); + } + + // Only handle calls to request methods + if (prop !== 'request') { + return Reflect.get(target, prop, receiver); + } + + // Return a wrapped promise that handles the deferred + return (url, options = {}) => new Promise((resolve, reject) => { + target[prop].apply(target, [url, options]).then( + (response) => resolve(response), + (jqXhr, textStatus, errorThrown) => reject(APIError.createWithJqXhr(errorThrown || textStatus, jqXhr)) + ); + }); + } + }) + } } // Create shortcut methods for easier access by method diff --git a/resources/assets/javascripts/lib/jsonapi.js b/resources/assets/javascripts/lib/jsonapi.ts index f3217bc..80176cc 100644 --- a/resources/assets/javascripts/lib/jsonapi.js +++ b/resources/assets/javascripts/lib/jsonapi.ts @@ -3,11 +3,11 @@ import AbstractAPI from './abstract-api.js'; // Actual JSONAPI object class JSONAPI extends AbstractAPI { - constructor(version = 1) { + constructor(version: number = 1) { super(`jsonapi.php/v${version}`); } - encodeData (data, method) { + encodeData (data: any, method: string): any { data = super.encodeData(data); if (['DELETE', 'GET', 'HEAD'].includes(method)) { @@ -21,11 +21,11 @@ class JSONAPI extends AbstractAPI return JSON.stringify(data); } - request (url, options = {}) { + request (url: string, options: any = {}) { options.contentType = 'application/vnd.api+json'; return super.request(url, options); } } export default JSONAPI; -export const jsonapi = new JSONAPI(); +export const jsonapi: JSONAPI = new JSONAPI(); diff --git a/resources/assets/javascripts/studip-ui.js b/resources/assets/javascripts/studip-ui.js index f581295..e611150 100644 --- a/resources/assets/javascripts/studip-ui.js +++ b/resources/assets/javascripts/studip-ui.js @@ -33,9 +33,9 @@ import eventBus from "./lib/event-bus.ts"; if (STUDIP.UI.restrictedDates[year] === undefined) { STUDIP.UI.restrictedDates[year] = {}; - STUDIP.jsonapi.GET('holidays', {data: { + STUDIP.jsonapi.withPromises().get('holidays', {data: { 'filter[year]': year - }}).done(response => { + }}).then(response => { // Since PHP will return an empty object as an array, // we need to check if (Array.isArray(response)) { diff --git a/resources/vue/components/MyCoursesTiles.vue b/resources/vue/components/MyCoursesTiles.vue index 73f8aad..12b2627 100644 --- a/resources/vue/components/MyCoursesTiles.vue +++ b/resources/vue/components/MyCoursesTiles.vue @@ -136,7 +136,7 @@ export default { return this.shownColorPicker === course.id; }, changeColor(course, index) { - STUDIP.jsonapi.PATCH(`course-memberships/${course.id}_${this.userid}`, { + STUDIP.jsonapi.withPromises().patch(`course-memberships/${course.id}_${this.userid}`, { data: { data: { type: 'course-memberships', @@ -145,9 +145,9 @@ export default { } } } - }).done(() => { + }).then(() => { course.group = index; - }).always(() => { + }).finally(() => { this.shownColorPicker = null; }); }, diff --git a/resources/vue/store/ContentModulesStore.js b/resources/vue/store/ContentModulesStore.js index 9dc4609..141a178 100644 --- a/resources/vue/store/ContentModulesStore.js +++ b/resources/vue/store/ContentModulesStore.js @@ -52,7 +52,7 @@ export default { attributes: { value: view === 'tiles' } }; - return STUDIP.jsonapi.PATCH(`config-values/${documentId}`, { data: { data } }) ; + return STUDIP.jsonapi.withPromises().patch(`config-values/${documentId}`, { data: { data } }) ; }, exchangeModules({ commit, state }, modules) { const order = modules.filter(module => module.active) diff --git a/resources/vue/store/MyCoursesStore.js b/resources/vue/store/MyCoursesStore.js index 08c0389..af8e0fc 100644 --- a/resources/vue/store/MyCoursesStore.js +++ b/resources/vue/store/MyCoursesStore.js @@ -71,7 +71,7 @@ export default { attributes: { value: configValue[configKey] } }; - return STUDIP.jsonapi.PATCH(`config-values/${documentId}`, { data: { data } }) + return STUDIP.jsonapi.withPromises().patch(`config-values/${documentId}`, { data: { data } }) }, toggleOpenGroup ({ state, dispatch }, group) { let open_groups = [ ...state.config.open_groups ]; |
