tuwunel_service/profile/
remote.rs1use futures::TryStreamExt;
2use ruma::{
3 UserId,
4 api::federation::query::get_profile_information::v1::{Request, Response},
5 profile::ProfileFieldName,
6};
7use serde_json::Value;
8use tuwunel_core::{Result, implement, smallvec::SmallVec, utils::stream::TryReadyExt};
9
10use super::{Propagation, Service};
11
12type Removed = SmallVec<[ProfileFieldName; 1]>;
13
14type Fields = Vec<(ProfileFieldName, Option<Value>)>;
15
16#[implement(Service)]
23#[tracing::instrument(
24 level = "debug",
25 skip_all,
26 fields(
27 %user_id,
28 ),
29)]
30pub async fn mirror_remote_profile(&self, user_id: &UserId) -> Result<Removed> {
31 assert!(
32 !self.services.globals.user_is_local(user_id),
33 "mirror remote profile called with a local user"
34 );
35
36 let request = Request { user_id: user_id.to_owned(), field: None };
37 let response = self
38 .services
39 .federation
40 .execute(user_id.server_name(), request)
41 .await?;
42
43 self.mirror_profile(user_id, response).await
44}
45
46#[implement(Service)]
52pub(super) async fn mirror_profile(
53 &self,
54 user_id: &UserId,
55 response: Response,
56) -> Result<Removed> {
57 let profile_lock = self.mutex.lock(user_id).await;
58 let removed: Removed = self
59 .try_profile_field_names(user_id)
60 .ready_try_filter(|name| response.get(name.as_str()).is_none())
61 .try_collect()
62 .await?;
63
64 let fields: Fields = response
65 .into_iter()
66 .map(|(name, value)| (name.into(), Some(value)))
67 .chain(removed.iter().cloned().map(|name| (name, None)))
68 .collect();
69
70 self.set_profile_keys_locked(&profile_lock, user_id, &fields, Some(Propagation::None))
71 .await?;
72
73 Ok(removed)
74}