Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/client/api.ts
1503 views
1
/*
2
Use the api v2 endpoint from the app frontend. This is everything defined
3
in @cocalc/next/pages/api/v2
4
5
We always use POST requests here.
6
7
The v1 api is also exported here.
8
9
This doesn't know anything about types, etc.
10
*/
11
12
import { join } from "path";
13
import { appBasePath } from "@cocalc/frontend/customize/app-base-path";
14
import { delay } from "awaiting";
15
import { trunc } from "@cocalc/util/misc";
16
17
export default async function api(endpoint: string, args?: object) {
18
return await callApi(join("v2", endpoint), args);
19
}
20
21
// also the old v1 api
22
export async function v1(endpoint: string, args?: object) {
23
return await callApi(join("v1", endpoint), args);
24
}
25
26
// if api call fails (typically 5xx due to a temporary restart of
27
// backend servers e.g., in kubernetes) we wait RETRY_DELAY_MS, then give
28
// it NUM_RETRIES many ties before showing the user an error.
29
// Setting the third numRetriesOnFail argument to 0 below
30
// can be used to disable this behavior.
31
// This "api call fails" isn't where you get an error json
32
// back, but when actually making the request really is
33
// failing, e.g., due to network or server issues.
34
const RETRY_DELAY_MS = 3000;
35
const NUM_RETRIES = 2;
36
37
// NOTE: I made this complicated with respClone, so I can see
38
// what the response is if it is not JSON.
39
async function callApi(
40
endpoint: string,
41
args?: object,
42
numRetriesOnFail?: number,
43
) {
44
// console.log("callApi", { endpoint, args });
45
const url = join(appBasePath, "api", endpoint);
46
const resp = await fetch(url, {
47
method: "POST",
48
headers: {
49
"Content-Type": "application/json",
50
},
51
...(args != null ? { body: JSON.stringify(args) } : undefined),
52
});
53
const respClone = resp.clone();
54
let json: any = null;
55
try {
56
json = await resp.json();
57
} catch (e) {
58
console.log(e);
59
const r = await respClone.text();
60
console.log(trunc(r, 2000));
61
if (numRetriesOnFail != null && numRetriesOnFail == 0) {
62
throw Error("API server is down -- try again later");
63
}
64
numRetriesOnFail = numRetriesOnFail ?? NUM_RETRIES;
65
console.log(
66
`waiting ${RETRY_DELAY_MS}ms then trying again up to ${numRetriesOnFail} more times`,
67
);
68
await delay(RETRY_DELAY_MS);
69
return await callApi(endpoint, args, numRetriesOnFail - 1);
70
}
71
if (json == null) {
72
throw Error("timeout -- try again later");
73
}
74
if (typeof json == "object" && json.error) {
75
throw Error(json.error);
76
}
77
if (typeof json == "object" && json.errors) {
78
// This is what happens when the api request fails due to schema validation issues.
79
// I.e., this is soemthing we only see in dev mode since the schema stuff is disabled in production.
80
throw Error(
81
`API Schema Error: ${json.message} ${JSON.stringify(json.errors)}`,
82
);
83
}
84
// console.log("got ", json);
85
return json;
86
}
87
88