Files
raven-client/ayanova/src/api/gzapi.js
2020-09-14 17:38:30 +00:00

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
};