678 lines
18 KiB
JavaScript
678 lines
18 KiB
JavaScript
/* 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 <protocol>//<hostname>:<port>/
|
|
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
|
|
};
|