Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

74 linesCodeBlameRaw
1//! An account purged (`user.deleted`, identity's `account_deletion.rs`):
2//! what it wrote stays where it is and shows as `ghost`, and it is taken
3//! off what it was asked to do.
4//!
5//! Its work items, pull requests, comments and reviews, plans and messages
6//! to agents keep their place and their words; their author becomes
7//! `ghost` (`usr_ghost`), and so does whoever asked g1t for a work item or
8//! pull request. It is no longer assigned to anything or asked to review, and
9//! review requests that reached it through a team are dropped. Stored
10//! rather than mapped when read, so every reader agrees.
11
12use g1t_contracts::account_deletion::{GHOST_ID, GHOST_USERNAME};
13
14/// Each statement takes the account's id as `?1` and its username as `?2`
15/// (`g1t_kit::user_deleted`). `ghost`'s id and name are written in.
16pub(crate) fn statements() -> Vec<String> {
17 // Its work items (items/sql.rs).
18 let mut sql = crate::items::sql::user_deleted();
19 for table in ["pulls", "comments", "plans", "agent_messages", "commit_comments"] {
20 sql.push(format!(
21 "UPDATE {table} SET author_id = '{GHOST_ID}', author_name = '{GHOST_USERNAME}' WHERE author_id = ?1"
22 ));
23 }
24 sql.push(format!(
25 "UPDATE pulls SET requested_by_id = '{GHOST_ID}', requested_by_name = '{GHOST_USERNAME}' WHERE requested_by_id = ?1"
26 ));
27 for (table, column) in [("pulls", "assignees"), ("pulls", "reviewers")] {
28 sql.push(format!(
29 "UPDATE {table} SET {column} = (SELECT json_group_array(value) FROM json_each({table}.{column}) WHERE value <> ?2)
30 WHERE ?1 IS NOT NULL AND json_valid({column}) AND EXISTS (SELECT 1 FROM json_each({table}.{column}) WHERE value = ?2)"
31 ));
32 }
33 // What it reacted with goes: a reaction is nobody's without its person.
34 sql.push(crate::reactions::USER_DELETED.to_owned());
35 sql.push("DELETE FROM team_review_requests WHERE ?1 IS NOT NULL AND username = ?2".to_owned());
36 // Its sessions and claims (coordination/) keep their place as ghost's,
37 // and a hand-off to it is to anyone now.
38 // A session's harness id is unique per person, so it goes.
39 sql.push(format!(
40 "UPDATE sessions SET actor_id = '{GHOST_ID}', actor_name = '{GHOST_USERNAME}', harness_session_id = NULL WHERE actor_id = ?1"
41 ));
42 sql.push(format!("UPDATE claims SET actor_id = '{GHOST_ID}', actor_name = '{GHOST_USERNAME}' WHERE actor_id = ?1"));
43 sql.push("UPDATE claims SET handoff_to = NULL WHERE ?1 IS NOT NULL AND handoff_to = ?2".to_owned());
44 sql
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn everything_it_wrote_becomes_ghosts() {
53 let sql = statements();
54 for table in ["work_items", "pulls", "comments", "plans", "agent_messages"] {
55 assert!(
56 sql.iter().any(|s| s.starts_with(&format!("UPDATE {table} SET author_id = 'usr_ghost', author_name = 'ghost'"))),
57 "{table}"
58 );
59 }
60 assert!(sql.iter().any(|s| s.contains("UPDATE pulls SET requested_by_id = 'usr_ghost'")));
61 assert!(sql.iter().any(|s| s.contains("UPDATE pulls SET reviewers")));
62 assert!(sql.iter().any(|s| s.contains("team_review_requests")));
63 }
64
65 #[test]
66 fn every_statement_takes_both_binds() {
67 // D1 refuses a bind a statement does not name: each names ?1, and
68 // the ones that need the username name ?2 as well.
69 for s in statements() {
70 assert!(s.contains("?1"), "{s}");
71 assert_eq!(g1t_kit::user_deleted::binds(&s, "usr_1", "ada").len(), if s.contains("?2") { 2 } else { 1 });
72 }
73 }
74}