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/database/postgres/syncdoc-history.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
import { callback2 } from "@cocalc/util/async-utils";
7
import { trunc } from "@cocalc/util/misc";
8
import { PostgreSQL } from "./types";
9
10
export interface Patch {
11
time_utc: Date;
12
patch_length?: number;
13
patch?: string;
14
user?: string;
15
account_id?: string;
16
format?: number;
17
snapshot?: string;
18
}
19
20
type User = { account_id: string; user: string };
21
22
async function get_users(db: PostgreSQL, where): Promise<User[]> {
23
const query = "SELECT project_id, users FROM syncstrings";
24
// get the user_id --> account_id map
25
const results = await callback2(db._query, { query, where });
26
if (results.rows.length != 1) {
27
throw Error("no such syncstring");
28
}
29
const account_ids: string[] = results.rows[0].users
30
? results.rows[0].users
31
: []; // syncdoc exists, but not used yet.
32
const project_id: string = results.rows[0].project_id;
33
const project_title: string = trunc(
34
(
35
await callback2(db.get_project, {
36
columns: ["title"],
37
project_id,
38
})
39
).title,
40
80,
41
);
42
43
// get the names of the users
44
// TODO: this whole file should be in @cocalc/server, be an api endpoint for api/v2,
45
// and this code below should instead use @cocalc/server/accounts/get-name:getNames.
46
const names = await callback2(db.account_ids_to_usernames, { account_ids });
47
const users: User[] = [];
48
for (const account_id of account_ids) {
49
if (account_id == project_id) {
50
users.push({ account_id, user: `Project: ${project_title}` });
51
continue;
52
}
53
const name = names[account_id];
54
if (name == null) continue;
55
const user = trunc(`${name.first_name} ${name.last_name}`, 80);
56
users.push({ account_id, user });
57
}
58
return users;
59
}
60
61
export async function syncdoc_history(
62
db: PostgreSQL,
63
string_id: string,
64
include_patches: boolean = false,
65
): Promise<Patch[]> {
66
const where = { "string_id = $::CHAR(40)": string_id };
67
const users: User[] = await get_users(db, where);
68
69
const order_by = "time";
70
let query: string;
71
if (include_patches) {
72
query = "SELECT time, user_id, format, patch, snapshot FROM patches";
73
} else {
74
query =
75
"SELECT time, user_id, format, length(patch) as patch_length FROM patches";
76
}
77
const results = await callback2(db._query, {
78
query,
79
where,
80
order_by,
81
timeout_s: 300,
82
});
83
const patches: Patch[] = [];
84
function format_patch(row): Patch {
85
const patch: Patch = { time_utc: row.time, format: row.format };
86
const u = users[row.user_id];
87
if (u != null) {
88
for (const k in u) {
89
patch[k] = u[k];
90
}
91
}
92
if (include_patches) {
93
patch.patch = row.patch;
94
if (row.snapshot != null) {
95
patch.snapshot = row.snapshot;
96
}
97
} else {
98
patch.patch_length = row.patch_length;
99
}
100
return patch;
101
}
102
for (const row of results.rows) {
103
patches.push(format_patch(row));
104
}
105
return patches;
106
}
107
108