From 03cb27d79117a366764a5e1f91a5c39f9bc5b1a8 Mon Sep 17 00:00:00 2001 From: Pedro Amorim Date: Fri, 10 Feb 2023 11:19:08 -0100 Subject: [PATCH] Bug 32939: Have a generic fetch function for POST and PUT requests in vue modules In bug 32925 it's discussed the possibility of having a generic fetch function to handle basic CRUD creates and edits for vuejs modules in the future and move all common POST+PUT async requests workflow in there, like the 'Submitting...' loading behaviour. This POC illustrates this and it's intent is to start discussion, plan on iterating further to the rest of ERM if agreed upon. --- .../vue/components/ERM/AgreementsFormAdd.vue | 42 ++++++---- .../prog/js/vue/fetch/api-client.js | 21 +++++ .../intranet-tmpl/prog/js/vue/fetch/fetch.js | 26 ++++++ .../prog/js/vue/fetch/http-client.js | 82 +++++++++++++++++++ 4 files changed, 155 insertions(+), 16 deletions(-) create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/fetch/fetch.js create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/AgreementsFormAdd.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/AgreementsFormAdd.vue index d2330b754e..2e684dc681 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/AgreementsFormAdd.vue +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/AgreementsFormAdd.vue @@ -198,6 +198,8 @@ import { submitted, } from "../../messages" import { fetchAgreement } from "../../fetch/erm.js" +import { submitToAPI } from "../../fetch/fetch.js" +import { ApiClient } from "../../fetch/api-client.js"; import { storeToRefs } from "pinia" export default { @@ -340,6 +342,9 @@ export default { let apiUrl = "/api/v1/erm/agreements" + //This is just temporary for new api calls implementation + let a_id = agreement.agreement_id; + let method = "POST" if (agreement.agreement_id) { method = "PUT" @@ -390,23 +395,28 @@ export default { }, } - isSubmitting() - fetch(apiUrl, options) - .then(response => { - if (response.status == 200) { - this.$router.push("/cgi-bin/koha/erm/agreements") - setMessage(this.$__("Agreement updated")) - } else if (response.status == 201) { - this.$router.push("/cgi-bin/koha/erm/agreements") - setMessage(this.$__("Agreement created")) - } else { - setError(response.message || response.statusText) + const client = new ApiClient( + "/api/v1/erm/", + { + "Content-Type": "application/json;charset=utf-8", + } + ); + + (async () => { + try { + if ( this.$route.path.includes("edit") ) { + await client.agreements.update(agreement, a_id); + setMessage(this.$__("Agreement updated")); + }else{ + await client.agreements.create(agreement); + setMessage(this.$__("Agreement created")); } - }) - .catch(error => { - setError(error) - }) - .then(() => submitted()) + this.$router.push("/cgi-bin/koha/erm/agreements") + } catch (err) { + setError(err.message || err.statusText) + } + + })(); }, onStatusChanged(e) { if (e.authorised_value != "closed") { diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js new file mode 100644 index 0000000000..983a8bdeaa --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js @@ -0,0 +1,21 @@ +import HttpClient from "./http-client"; + +export class ApiClient extends HttpClient { + constructor(baseURL, headers) { + super({ + baseURL, + headers: headers + }); + } + + get agreements() { + return { + get: () => this.get("/agreements"), + delete: (id) => this.delete(`/agreements/${id}`), + create: (agreement) => this.post("/agreements", agreement), + update: (agreement, id) => this.put("/agreements/"+id, agreement) + }; + } +} + +export default ApiClient; diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/fetch.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/fetch.js new file mode 100644 index 0000000000..ba6aabdbe7 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/fetch.js @@ -0,0 +1,26 @@ +import { + setMessage, + setError, + isSubmitting, + submitted, +} from "../messages" + +export const submitToAPI = async function (args) { + isSubmitting() + await fetch(args.apiUrl, args.options) + .then(response => { + if (response.status == 200) { + args.router.push(args.success_redirect) + setMessage(args.updated_message) + } else if (response.status == 201) { + args.router.push(args.success_redirect) + setMessage(args.created_message) + } else { + setError(response.message || response.statusText) + } + }) + .catch(error => { + setError(error) + }) + .then(() => submitted()) +}; diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js new file mode 100644 index 0000000000..06e15e3d61 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js @@ -0,0 +1,82 @@ +class HttpClient { + constructor(options = {}) { + this._baseURL = options.baseURL || ""; + this._headers = options.headers || {}; + } + + async _fetchJSON(endpoint, options = {}) { + const res = await fetch(this._baseURL + endpoint, { + ...options, + headers: this._headers + }); + + if (!res.ok) throw new Error(res.statusText); + + if (options.parseResponse !== false && res.status !== 204) + return res.json(); + + return undefined; + } + + setHeader(key, value) { + this._headers[key] = value; + return this; + } + + getHeader(key) { + return this._headers[key]; + } + + setBasicAuth(username, password) { + this._headers.Authorization = `Basic ${btoa(`${username}:${password}`)}`; + return this; + } + + setBearerAuth(token) { + this._headers.Authorization = `Bearer ${token}`; + return this; + } + + get(endpoint, options = {}) { + return this._fetchJSON(endpoint, { + ...options, + method: "GET" + }); + } + + post(endpoint, body, options = {}) { + return this._fetchJSON(endpoint, { + ...options, + body: body ? JSON.stringify(body) : undefined, + method: "POST" + }); + } + + put(endpoint, body, options = {}) { + console.log('puting'); + return this._fetchJSON(endpoint, { + ...options, + body: body ? JSON.stringify(body) : undefined, + method: "PUT" + }); + } + + patch(endpoint, operations, options = {}) { + return this._fetchJSON(endpoint, { + parseResponse: false, + ...options, + body: JSON.stringify(operations), + method: "PATCH" + }); + } + + delete(endpoint, options = {}) { + return this._fetchJSON(endpoint, { + parseResponse: false, + ...options, + method: "DELETE" + }); + } + } + + export default HttpClient; \ No newline at end of file -- 2.30.2