systemprompt_users/services/user/
merge.rs1use systemprompt_identifiers::UserId;
7
8use super::UserService;
9use crate::error::{Result, UserError};
10use crate::models::UserRole;
11use crate::repository::MergeResult;
12
13impl UserService {
14 pub async fn merge_users(&self, source_id: &UserId, target_id: &UserId) -> Result<MergeResult> {
15 if self.owner_reassignments.is_empty() {
16 return Err(UserError::MergeUnavailable);
17 }
18 if source_id == target_id {
19 return Err(UserError::Validation(
20 "cannot merge a user into itself".to_owned(),
21 ));
22 }
23 for id in [source_id, target_id] {
24 if self.repository.find_by_id(id).await?.is_none() {
25 return Err(UserError::NotFound(id.clone()));
26 }
27 }
28
29 let mut tasks = 0;
30 let mut total_rows = 0;
31 for reassignment in self.owner_reassignments.iter() {
32 let moved = reassignment
33 .reassign_owner(source_id, target_id)
34 .await
35 .map_err(|source| UserError::OwnerReassignment {
36 domain: reassignment.domain(),
37 source,
38 })?;
39 tasks += moved
40 .tables
41 .iter()
42 .filter(|(table, _)| *table == "agent_tasks")
43 .map(|(_, rows)| rows)
44 .sum::<u64>();
45 total_rows += moved.total();
46 }
47
48 let sessions = self.repository.complete_merge(source_id, target_id).await?;
49 Ok(MergeResult {
50 sessions,
51 tasks,
52 total_rows: total_rows + sessions,
53 })
54 }
55
56 pub async fn promote_anonymous(
57 &self,
58 source_id: &UserId,
59 target_id: &UserId,
60 ) -> Result<MergeResult> {
61 if source_id == target_id {
62 return Err(UserError::Validation(
63 "cannot promote a user onto itself".to_owned(),
64 ));
65 }
66 let source = self
67 .repository
68 .find_by_id(source_id)
69 .await?
70 .ok_or_else(|| UserError::NotFound(source_id.clone()))?;
71 if !source.has_role(UserRole::Anonymous) {
72 return Err(UserError::Validation(format!(
73 "user {} is not anonymous; use an explicit admin merge instead",
74 source_id
75 )));
76 }
77 self.merge_users(source_id, target_id).await
78 }
79}