/* Xeslint-disable */ import router from "../router"; function stringifyPrimitive(v) { switch (typeof v) { case "string": return v; case "boolean": return v ? "true" : "false"; case "number": return isFinite(v) ? v : ""; default: return ""; } } // ///////////////////////////////////////////////// // // Show unexpected errors during development // // // function devShowUnknownError(error) { // if (window.$gz.dev) { // // eslint-disable-next-line // console.error("gzapi::devShowUnknownError, error is:", error); // console.trace(); // debugger; // window.$gz.eventBus.$emit( // "notify-warning", // "DEV ERROR gzapi::devShowUnknownError - unexpected error during api operation see console " // ); // } // } //////////////////////////////////////////// // Try to handle an api error // return true if handled or false if not // function handleError(action, error, route) { let errorMessage = "API error: " + action + " route =" + route + ", message =" + error.message; window.$gz.store.commit("logItem", errorMessage); //Handle 403 not authorized //popup not authorized, log, then go to HOME //was going to go back one page, but realized most of the time a not authorized is in //reaction to directly entered or opened link, not application logic driving it, so home is safest choice // if (error.message && error.message.includes("NotAuthorized")) { window.$gz.store.commit("logItem", "Not authorized, redirecting to HOME"); window.$gz.eventBus.$emit( "notify-warning", window.$gz.translation.get("ErrorUserNotAuthorized") ); router.push(window.$gz.store.state.homePage); throw "[ErrorUserNotAuthorized]"; } //Handle 401 not authenticated if (error.message && error.message.includes("NotAuthenticated")) { window.$gz.store.commit( "logItem", "User is not authenticated, redirecting to LOGIN" ); window.$gz.eventBus.$emit( "notify-error", window.$gz.translation.get("ErrorUserNotAuthenticated") ); router.push("/login"); throw "[ErrorUserNotAuthenticated]"; } //is it a network error? //https://medium.com/@vinhlh/how-to-handle-networkerror-when-using-fetch-ff2663220435 if (error instanceof TypeError) { if ( error.message.includes("Failed to fetch") || error.message.includes("NetworkError") || error.message.includes("Network request failed") ) { window.$gz.store.commit("logItem", "Network error"); let msg = ""; if (window.$gz.store.state.authenticated) { msg = window.$gz.translation.get("ErrorServerUnresponsive"); } else { msg = "Could not connect to AyaNova server "; } msg += window.$gz.api.APIUrl("") + "\r\nError: " + error.message; window.$gz.eventBus.$emit("notify-error", msg); //note: using translation key in square brackets throw msg; } } //Ideally this should never get called because any issue should be addressed above window.$gz.errorHandler.handleFormError(error); // devShowUnknownError(error); } export default { status(response) { //Handle expected api errors if (response.status == 401) { throw new Error("[ErrorUserNotAuthenticated]"); } if (response.status == 403) { throw new Error("[ErrorUserNotAuthorized]"); } //404 not found is an expected status not worth logging allow to bubble up //for client code to deal with if (response.status == 404) { return Promise.resolve(response); } if (response.status == 405) { //Probably a development error throw new Error("Method Not Allowed (route issue?) " + response.url); } if (response.status >= 200 && response.status < 300) { return Promise.resolve(response); } else { //log unhandled api error window.$gz.store.commit( "logItem", "API error: status=" + response.status + ", statusText=" + response.statusText + ", url=" + response.url ); //let it float up for dealing with by caller(s) return Promise.resolve(response); } }, statusEx(response) { //Handle expected api errors if (response.status == 401) { throw new Error("[ErrorUserNotAuthenticated]"); } if (response.status == 403) { throw new Error("[ErrorUserNotAuthorized]"); } //404 not found is an expected status not worth logging allow to bubble up //for client code to deal with if (response.status == 404) { return; } if (response.status == 405) { //Probably a development error throw new Error("Method Not Allowed (route issue?) " + response.url); } if (response.status >= 200 && response.status < 300) { return; } else { //log unhandled api error window.$gz.store.commit( "logItem", "API error: status=" + response.status + ", statusText=" + response.statusText + ", url=" + response.url ); } }, async extractBodyEx(response) { if (response.status == 204) { //no content, nothing to process return response; } let contentType = response.headers.get("content-type"); if (!contentType) { return response; } if (contentType.includes("json")) { return await response.json(); } if (contentType.includes("text/plain")) { return await response.text(); } if (contentType.includes("application/pdf")) { return await response.blob(); } return response; }, extractBody(response) { if (response.status == 204) { //no content, nothing to process return response; } let contentType = response.headers.get("content-type"); if (!contentType) { return response; } if (contentType.includes("json")) { return response.json(); } if (contentType.includes("text/plain")) { return response.text(); } return response; }, apiErrorToHumanString(apiError) { //empty error object? if (!apiError) { return "(E18) - apiErrorToHumanString():: Empty API eror, unknown"; } //convert to readable string return "(E18) - " + JSON.stringify(apiError); }, patchAuthorizedHeaders() { return { //Accept: "application/json, text/plain, */*", Accept: "application/json", "Content-Type": "application/json-patch+json", Authorization: "Bearer " + window.$gz.store.state.apiToken }; }, postAuthorizedHeaders() { return { Accept: "application/json", "Content-Type": "application/json", Authorization: "Bearer " + window.$gz.store.state.apiToken }; }, postUnAuthorizedHeaders() { return { Accept: "application/json", "Content-Type": "application/json" }; }, fetchPostNoAuthOptions(data) { return { method: "post", mode: "cors", headers: this.postUnAuthorizedHeaders(), body: JSON.stringify(data) }; }, fetchPostOptions(data) { return { method: "post", mode: "cors", headers: this.postAuthorizedHeaders(), body: JSON.stringify(data) }; }, fetchPutOptions(data) { return { method: "put", mode: "cors", headers: this.postAuthorizedHeaders(), body: JSON.stringify(data) }; }, fetchGetOptions() { /* GET WITH AUTH */ return { method: "get", mode: "cors", headers: this.postAuthorizedHeaders() }; }, fetchRemoveOptions() { /* REMOVE WITH AUTH */ return { method: "delete", mode: "cors", headers: this.postAuthorizedHeaders() }; }, APIUrl(apiPath) { if ("" == window.$gz.store.state.apiUrl) { //construct the api url and store it //development location? if ( (window.location.hostname == "localhost" || window.location.hostname == "192.168.1.56") && window.location.port == "8080" ) { window.$gz.store.commit("setAPIURL", "http://localhost:7575/api/v8.0/"); window.$gz.store.commit("setHelpURL", "http://localhost:7575/docs/"); window.$gz.store.commit( "logItem", "gzapi::APIUrl -> setting to dev. mode: " + window.$gz.store.state.apiUrl ); } else { //production location //:/ window.$gz.store.commit( "setHelpURL", window.location.protocol + "//" + window.location.host + "/docs/" ); window.$gz.store.commit( "setAPIURL", window.location.protocol + "//" + window.location.host + "/api/v8.0/" ); window.$gz.store.commit( "logItem", "gzapi::APIUrl -> setting to: " + window.$gz.store.state.apiUrl ); } } return window.$gz.store.state.apiUrl + apiPath; }, ///////////////////////////// // Just the server itself // used by profiler etc // ServerBaseUrl() { return window.$gz.store.state.helpUrl.replace("/docs/", "/"); }, ///////////////////////////// // generic routed download URL // genericDownloadUrl(route) { //http://localhost:7575/api/v8/backup/download/100?t=sssss let url = route + "?t=" + window.$gz.store.state.downloadToken; return this.APIUrl(url); }, ///////////////////////////// // report file download URL // reportDownloadUrl(fileName) { //http://localhost:7575/api/v8/report/download/filename.pdf?t=sssss let url = "report/download/" + fileName + "?t=" + window.$gz.store.state.downloadToken; return this.APIUrl(url); }, ///////////////////////////// // backup file download URL // backupDownloadUrl(fileName) { //http://localhost:7575/api/v8/backup/download/100?t=sssss let url = "backup/download/" + fileName + "?t=" + window.$gz.store.state.downloadToken; return this.APIUrl(url); }, ///////////////////////////// // attachment download URL // attachmentDownloadUrl(fileId, ctype) { //http://localhost:7575/api/v8/attachment/download/100?t=sssss //Ctype is optional and is the MIME content type, used to detect image urls at client for drag and drop ops //in wiki but ignored by server let url = "attachment/download/" + fileId + "?t=" + window.$gz.store.state.downloadToken; if (ctype && ctype.includes("image")) { url += "&i=1"; } return this.APIUrl(url); }, ///////////////////////////// // logo download URL // (size= 'small', 'medium', 'large') logoUrl(size) { //http://localhost:7575/api/v8/logo/small let url = "logo/" + size; return this.APIUrl(url); }, ///////////////////////////// // REPLACE END OF URL // (used to change ID in url) replaceAfterLastSlash(theUrl, theReplacement) { return theUrl.substr(0, theUrl.lastIndexOf("\\") + 1) + theReplacement; }, ///////////////////////////// // ENCODE QUERY STRING // buildQuery(obj, sep, eq, name) { sep = sep || "&"; eq = eq || "="; if (obj === null) { obj = undefined; } if (typeof obj === "object") { return Object.keys(obj) .map(function(k) { let ks = encodeURIComponent(stringifyPrimitive(k)) + eq; if (Array.isArray(obj[k])) { return obj[k] .map(function(v) { return ks + encodeURIComponent(stringifyPrimitive(v)); }) .join(sep); } else { return ks + encodeURIComponent(stringifyPrimitive(obj[k])); } }) .filter(Boolean) .join(sep); } if (!name) return ""; return ( encodeURIComponent(stringifyPrimitive(name)) + eq + encodeURIComponent(stringifyPrimitive(obj)) ); }, /////////////////////////////////// // GET DATA FROM API SERVER // async get(route) { try { let that = this; let r = await fetch(that.APIUrl(route), that.fetchGetOptions()); that.statusEx(r); r = await that.extractBodyEx(r); return r; } catch (error) { //fundamental error, can't proceed with this call handleError("GET", error, route); } }, ////////////////////////////////////// // Test delay for troubleshooting // doDelayAsync: () => { // eslint-disable-next-line return new Promise((resolve) => { setTimeout(() => resolve("I did something"), 10000); }); }, /////////////////////////////////// // POST / PUT DATA TO API SERVER // async upsert(route, data, isLogin = false) { try { let that = this; //determine if this is a new or existing record let fetchOptions = undefined; //put? if (data && data.concurrency) { fetchOptions = that.fetchPutOptions(data); } else { //post //ensure the route doesn't end in /0 which will happen if it's a new record //since the edit forms just send the url here with the ID regardless if (window.$gz._.endsWith(route, "/0")) { route = route.slice(0, -2); } if (isLogin == false) { fetchOptions = that.fetchPostOptions(data); } else { fetchOptions = that.fetchPostNoAuthOptions(data); } } let r = await fetch(that.APIUrl(route), fetchOptions); that.statusEx(r); r = await that.extractBodyEx(r); return r; } catch (error) { if (isLogin == false) { handleError("UPSERT", error, route); } else { //specifically this is for the login page throw error; } } }, /////////////////////////////////// // DELETE DATA FROM API SERVER // async remove(route) { let that = this; try { let r = await fetch(that.APIUrl(route), that.fetchRemoveOptions()); that.statusEx(r); //delete will return a body if there is an error of some kind with the request r = await that.extractBodyEx(r); return r; } catch (error) { //fundamental error, can't proceed with this call handleError("DELETE", error, route); } }, /////////////////////////////////// // POST FILE ATTACHMENTS // @param {ayaId:objectid, ayaType:objectType, files:[array of files]} // async uploadAttachment(at) { let that = this; try { var files = at.files; var data = new FormData(); for (var i = 0; i < files.length; i++) { data.append(files[i].name, files[i]); } data.append("AttachToObjectType", at.ayaType); data.append("AttachToObjectId", at.ayaId); data.append("Notes", at.notes); data.append("FileData", at.fileData); //----------------- let fetchOptions = { method: "post", mode: "cors", headers: { Authorization: "Bearer " + window.$gz.store.state.apiToken }, body: data }; let r = await fetch(that.APIUrl("attachment"), fetchOptions); that.statusEx(r); r = await that.extractBodyEx(r); return r; } catch (error) { handleError("POSTATTACHMENT", error, route); } }, ////////////////////////////////////////////// // POST (UPLOAD) FILE TO ARBITRARY ROUTE // for various things that require an upload // e.g. translation import etc // // async upload(route, at) { let that = this; try { var files = at.files; var data = new FormData(); for (var i = 0; i < files.length; i++) { data.append(files[i].name, files[i]); } if (at.ayaType) { data.append("ObjectType", at.ayaType); } if (at.ayaId) { data.append("ObjectId", at.ayaId); } if (at.notes) { data.append("Notes", at.notes); } data.append("FileData", at.fileData); //----------------- let fetchOptions = { method: "post", mode: "cors", headers: { Authorization: "Bearer " + window.$gz.store.state.apiToken }, body: data }; let r = await fetch(that.APIUrl(route), fetchOptions); that.statusEx(r); r = await that.extractBodyEx(r); return r; } catch (error) { handleError("POSTATTACHMENT", error, route); } }, /////////////////////////////////// // POST LOGO // // async uploadLogo(fileData, size) { let that = this; try { let data = new FormData(); data.append(fileData.name, fileData); //----------------- let fetchOptions = { method: "post", mode: "cors", headers: { Authorization: "Bearer " + window.$gz.store.state.apiToken }, body: data }; let r = await fetch(that.APIUrl("logo/" + size), fetchOptions); that.statusEx(r); r = await that.extractBodyEx(r); return r; } catch (error) { handleError("uploadLogo", error, route); } }, /////////////////////////////////// // REPORT CLIENT META DATA // // reportClientMetaData() { return { UserName: window.$gz.store.state.userName, Authorization: "Bearer " + window.$gz.store.state.apiToken, //api token for using api methods as current user viewing report TimeZoneName: window.$gz.locale.getBrowserTimeZoneName(), LanguageName: window.$gz.locale.getBrowserLanguages(), Hour12: window.$gz.locale.getHour12(), CurrencyName: window.$gz.locale.getCurrencyName(), LanguageName: window.$gz.locale.getBrowserFirstLanguage(), DefaultLocale: window.$gz.locale .getBrowserFirstLanguage() .split("-", 1)[0] }; }, /////////////////////////////////// // RENDER REPORT DIRECTLY // // async renderReport(objectid, reportid) { let reportDataOptions = { ReportId: reportid, SelectedRowIds: [objectid], ClientMeta: this.reportClientMetaData() }; let res = await window.$gz.api.upsert("report/render", reportDataOptions); if (res.error) { throw res.error; } else { let reportUrl = window.$gz.api.reportDownloadUrl(res.data); if (window.open(reportUrl, "Report") == null) { throw "Problem displaying report in new window. Browser must allow pop-ups to view reports; check your browser setting"; } } } //--------------- //new functions above here };