CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/client/api.ts
Views: 687
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 = 2000;
35
const NUM_RETRIES = 3;
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
const url = join(appBasePath, "api", endpoint);
45
const resp = await fetch(url, {
46
method: "POST",
47
headers: {
48
"Content-Type": "application/json",
49
},
50
...(args != null ? { body: JSON.stringify(args) } : undefined),
51
});
52
const respClone = resp.clone();
53
let json: any = null;
54
try {
55
json = await resp.json();
56
} catch (e) {
57
console.log(e);
58
const r = await respClone.text();
59
console.log(trunc(r, 2000));
60
if (numRetriesOnFail != null && numRetriesOnFail == 0) {
61
throw Error("API server is down -- try again later");
62
}
63
numRetriesOnFail = numRetriesOnFail ?? NUM_RETRIES;
64
console.log(
65
`waiting 3s then trying again up to ${numRetriesOnFail} more times`,
66
);
67
await delay(RETRY_DELAY_MS);
68
return await callApi(endpoint, args, numRetriesOnFail - 1);
69
}
70
if (json == null) {
71
throw Error("timeout -- try again later");
72
}
73
if (typeof json == "object" && json.error) {
74
throw Error(json.error);
75
}
76
return json;
77
}
78
79