Files
my-notes-viewer/src/api.ts
2021-03-22 16:54:31 +01:00

166 lines
4.7 KiB
TypeScript

import { apiBaseURL } from "./config"
import * as sentry from "./sentry"
import * as SSR from "../api/hooks/lib/utils.js"
// [MIT License](LICENSE.md) © [Jason Miller](https://jasonformat.com/)
const _f = function (url, options): Promise<Response> {
if (typeof XMLHttpRequest === "undefined") {
return Promise.resolve(null)
}
options = options || {}
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest()
const keys = []
const all = []
const headers = {}
const response = (): Response => ({
ok: ((request.status / 100) | 0) == 2, // 200-299
statusText: request.statusText,
status: request.status,
url: request.responseURL,
text: () => Promise.resolve(request.responseText),
json: () => Promise.resolve(request.responseText).then(JSON.parse),
blob: () => Promise.resolve(new Blob([request.response])),
clone: response,
headers: {
// @ts-ignore
keys: () => keys,
// @ts-ignore
entries: () => all,
get: (n) => headers[n.toLowerCase()],
has: (n) => n.toLowerCase() in headers,
},
})
request.open(options.method || "get", url, true)
request.onload = () => {
request
.getAllResponseHeaders()
// @ts-ignore
.replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm, (m, key, value) => {
keys.push((key = key.toLowerCase()))
all.push([key, value])
headers[key] = headers[key]
? `${headers[key]},${value}`
: value
})
resolve(response())
}
request.onerror = reject
request.withCredentials = options.credentials == "include"
for (const i in options.headers) {
request.setRequestHeader(i, options.headers[i])
}
request.send(options.body || null)
})
}
const _fetch =
typeof fetch === "undefined"
? typeof window === "undefined"
? _f
: window.fetch || _f
: fetch
export const api = async <T>(
endpoint: string,
options?: {
method?: string
filter?: any
sort?: string
limit?: number
offset?: number
projection?: string
headers?: {
[key: string]: string
}
params?: {
[key: string]: string
}
}
): Promise<{ data: T; count: number }> => {
if (typeof window === "undefined") {
// ssr
// @ts-ignore
return context.ssrFetch(endpoint, options)
}
// try ssr cache
const key = SSR.obj2str({ endpoint, options })
// @ts-ignore
if (window.__SSR_CACHE__) {
// @ts-ignore
const ssrVal = window.__SSR_CACHE__[key]
console.log("SSR:", key, ssrVal)
if (ssrVal) {
return ssrVal
}
}
let method = "GET"
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?.params) {
Object.keys(options.params).forEach((p) => {
query += "&" + p + "=" + encodeURIComponent(options.params[p])
})
}
let headers: any = {
"Content-Type": "application/json",
}
if (options?.headers) headers = { ...headers, ...options.headers }
let url = apiBaseURL + 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
}
const response = await _fetch(url, {
method,
mode: "cors",
headers,
})
span?.finish()
// @ts-ignore
let data = (await response?.json()) || null
if (response?.status < 200 || response?.status >= 400)
throw { response, data }
// @ts-ignore
return { data, count: response?.headers?.get("x-results-count") || 0 }
}
export const getContent = async (path: string): Promise<Content> => {
const c = await api<Content[]>("content", { limit: 1, filter: { path } })
if (c?.data?.length) {
return c.data[0]
}
return null
}