const { login, newNotification } = require("../../../frontend/src/lib/store") const { apiClientBaseURL } = require("../config-client") /** * convert object to string * @param {any} obj object * @returns {string} string */ function obj2str(obj) { if (Array.isArray(obj)) { return JSON.stringify( obj.map(function (idx) { return obj2str(idx) }) ) } else if (typeof obj === "object" && obj !== null) { var elements = Object.keys(obj) .sort() .map(function (key) { var val = obj2str(obj[key]) if (val) { return key + ":" + val } }) var elementsCleaned = [] for (var i = 0; i < elements.length; i++) { if (elements[i]) elementsCleaned.push(elements[i]) } return "{" + elementsCleaned.join("|") + "}" } if (obj) return obj } /** * api request via client or server * server function ssrRequest is called via context.ssrRequest, binded in ssr hook * * @param {string} endpoint * @param {ApiOptions} options * @param {any} body * @param {import("../../../frontend/src/sentry")} sentry * @param {typeof fetch} _fetch * @returns {Promise>} */ function apiRequest(endpoint, options, body, sentry, _fetch) { // first check cache if on client const cacheKey = obj2str({ endpoint: endpoint, options: options }) let method = options?.method || "GET" // @ts-ignore if ( typeof window !== "undefined" && window.__SSR_CACHE__ && method === "GET" && !location.pathname.includes("profile") ) { // @ts-ignore const cache = window.__SSR_CACHE__[cacheKey] if (cache) { return Promise.resolve(cache) } } let query = "&count=1" if (options?.filter) query += "&filter=" + encodeURIComponent(JSON.stringify(options.filter)) if (options?.sort) query += "&sort=" + options.sort + "&sort=_id" if (options?.limit) query += "&limit=" + options.limit if (options?.offset) query += "&offset=" + options.offset if (options?.projection) query += "&projection=" + options.projection if (options?.lookup) query += "&lookup=" + options.lookup if (options?.params) { Object.keys(options.params).forEach((p) => { query += "&" + p + "=" + encodeURIComponent(options.params[p]) }) } /** @type {{[key: string]: string}} */ let headers = { "Content-Type": "application/json", } if (options?.headers) headers = { ...headers, ...options.headers } if (typeof window === "undefined" && method === "GET") { // server // reference via context from get hook to tree shake in client // @ts-ignore const d = context.ssrRequest(cacheKey, endpoint, query, Object.assign({}, options, { method, headers })) return Promise.resolve(d) } else { // client let url = (endpoint.startsWith("/") ? "" : apiClientBaseURL) + endpoint + (query ? "?" + query : "") const span = sentry?.currentTransaction()?.startChild({ op: "fetch", description: method + " " + url, data: Object.assign({}, options, { url }), }) const trace_id = span?.toTraceparent() if (trace_id) { headers["sentry-trace"] = trace_id } /** @type {{[key: string]: any}} */ const requestOptions = { method, mode: "cors", headers, } if (method === "POST" || method === "PUT") { requestOptions.body = JSON.stringify(body) } const response = _fetch(url, requestOptions) .then((response) => { return response?.json().then((json) => { if (response?.status < 200 || response?.status >= 400) { if (response?.status === 401) { login.set(null) newNotification({ html: "Nicht autorisiert. Bitte melde dich sich erneut an.", class: "error", }) } return Promise.reject({ response, data: json }) } return Promise.resolve({ data: json || null, count: response.headers?.get("x-results-count") || 0 }) }) }) .catch((error) => { if (options.noError) { return Promise.resolve({ data: { ...error.data, }, count: 0, }) } if (error.status === 401) { login.set(null) newNotification({ html: "Nicht autorisiert. Bitte melde dich sich erneut an.", class: "error", }) } else throw error }) // @ts-ignore return response } } module.exports = { obj2str, apiRequest, }