ruma_client_api/profile/
get_profile.rs

1//! `GET /_matrix/client/*/profile/{userId}`
2//!
3//! Get all profile information of a user.
4
5pub mod v3 {
6    //! `/v3/` ([spec])
7    //!
8    //! [spec]: https://spec.matrix.org/latest/client-server-api/#get_matrixclientv3profileuserid
9
10    use std::collections::{BTreeMap, btree_map};
11
12    use ruma_common::{
13        OwnedUserId,
14        api::{auth_scheme::NoAuthentication, request, response},
15        metadata,
16    };
17    use serde_json::Value as JsonValue;
18
19    use crate::profile::{ProfileFieldName, ProfileFieldValue, StaticProfileField};
20
21    metadata! {
22        method: GET,
23        rate_limited: false,
24        authentication: NoAuthentication,
25        history: {
26            1.0 => "/_matrix/client/r0/profile/{user_id}",
27            1.1 => "/_matrix/client/v3/profile/{user_id}",
28        }
29    }
30
31    /// Request type for the `get_profile` endpoint.
32    #[request(error = crate::Error)]
33    pub struct Request {
34        /// The user whose profile will be retrieved.
35        #[ruma_api(path)]
36        pub user_id: OwnedUserId,
37    }
38
39    /// Response type for the `get_profile` endpoint.
40    #[response(error = crate::Error)]
41    #[derive(Default)]
42    pub struct Response {
43        /// The profile data.
44        #[ruma_api(body)]
45        data: BTreeMap<String, JsonValue>,
46    }
47
48    impl Request {
49        /// Creates a new `Request` with the given user ID.
50        pub fn new(user_id: OwnedUserId) -> Self {
51            Self { user_id }
52        }
53    }
54
55    impl Response {
56        /// Creates a new empty `Response`.
57        pub fn new() -> Self {
58            Self::default()
59        }
60
61        /// Returns the value of the given capability.
62        pub fn get(&self, field: &str) -> Option<&JsonValue> {
63            self.data.get(field)
64        }
65
66        /// Returns the value of the given [`StaticProfileField`].
67        ///
68        /// Returns `Ok(Some(_))` if the field is present and the value was deserialized
69        /// successfully, `Ok(None)` if the field is not set, or an error if deserialization of the
70        /// value failed.
71        pub fn get_static<F: StaticProfileField>(
72            &self,
73        ) -> Result<Option<F::Value>, serde_json::Error> {
74            self.data.get(F::NAME).map(|value| serde_json::from_value(value.clone())).transpose()
75        }
76
77        /// Gets an iterator over the fields of the profile.
78        pub fn iter(&self) -> btree_map::Iter<'_, String, JsonValue> {
79            self.data.iter()
80        }
81
82        /// Sets a field to the given value.
83        pub fn set(&mut self, field: String, value: JsonValue) {
84            self.data.insert(field, value);
85        }
86    }
87
88    impl FromIterator<(String, JsonValue)> for Response {
89        fn from_iter<T: IntoIterator<Item = (String, JsonValue)>>(iter: T) -> Self {
90            Self { data: iter.into_iter().collect() }
91        }
92    }
93
94    impl FromIterator<(ProfileFieldName, JsonValue)> for Response {
95        fn from_iter<T: IntoIterator<Item = (ProfileFieldName, JsonValue)>>(iter: T) -> Self {
96            iter.into_iter().map(|(field, value)| (field.as_str().to_owned(), value)).collect()
97        }
98    }
99
100    impl FromIterator<ProfileFieldValue> for Response {
101        fn from_iter<T: IntoIterator<Item = ProfileFieldValue>>(iter: T) -> Self {
102            iter.into_iter().map(|value| (value.field_name(), value.value().into_owned())).collect()
103        }
104    }
105
106    impl Extend<(String, JsonValue)> for Response {
107        fn extend<T: IntoIterator<Item = (String, JsonValue)>>(&mut self, iter: T) {
108            self.data.extend(iter);
109        }
110    }
111
112    impl Extend<(ProfileFieldName, JsonValue)> for Response {
113        fn extend<T: IntoIterator<Item = (ProfileFieldName, JsonValue)>>(&mut self, iter: T) {
114            self.extend(iter.into_iter().map(|(field, value)| (field.as_str().to_owned(), value)));
115        }
116    }
117
118    impl Extend<ProfileFieldValue> for Response {
119        fn extend<T: IntoIterator<Item = ProfileFieldValue>>(&mut self, iter: T) {
120            self.extend(
121                iter.into_iter().map(|value| (value.field_name(), value.value().into_owned())),
122            );
123        }
124    }
125
126    impl IntoIterator for Response {
127        type Item = (String, JsonValue);
128        type IntoIter = btree_map::IntoIter<String, JsonValue>;
129
130        fn into_iter(self) -> Self::IntoIter {
131            self.data.into_iter()
132        }
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use serde_json::json;
139
140    use super::v3::Response;
141
142    #[test]
143    #[cfg(feature = "server")]
144    fn serialize_response() {
145        use ruma_common::{api::OutgoingResponse, owned_mxc_uri};
146        use serde_json::{Value as JsonValue, from_slice as from_json_slice};
147
148        use crate::profile::ProfileFieldValue;
149
150        let response = [
151            ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef")),
152            ProfileFieldValue::DisplayName("Alice".to_owned()),
153            ProfileFieldValue::new("custom_field", "value".into()).unwrap(),
154        ]
155        .into_iter()
156        .collect::<Response>();
157
158        let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
159
160        assert_eq!(
161            from_json_slice::<JsonValue>(http_response.body().as_ref()).unwrap(),
162            json!({
163                "avatar_url": "mxc://localhost/abcdef",
164                "displayname": "Alice",
165                "custom_field": "value",
166            })
167        );
168    }
169
170    #[test]
171    #[cfg(feature = "client")]
172    fn deserialize_response() {
173        use ruma_common::api::IncomingResponse;
174        use serde_json::to_vec as to_json_vec;
175
176        use crate::profile::{AvatarUrl, DisplayName};
177
178        // Values are set.
179        let body = to_json_vec(&json!({
180            "avatar_url": "mxc://localhost/abcdef",
181            "displayname": "Alice",
182            "custom_field": "value",
183        }))
184        .unwrap();
185
186        let response = Response::try_from_http_response(http::Response::new(body)).unwrap();
187        assert_eq!(response.get_static::<AvatarUrl>().unwrap().unwrap(), "mxc://localhost/abcdef");
188        assert_eq!(response.get_static::<DisplayName>().unwrap().unwrap(), "Alice");
189        assert_eq!(response.get("custom_field").unwrap().as_str().unwrap(), "value");
190
191        // Values are missing or null.
192        let body = to_json_vec(&json!({
193            "custom_field": null,
194        }))
195        .unwrap();
196
197        let response = Response::try_from_http_response(http::Response::new(body)).unwrap();
198        assert_eq!(response.get_static::<AvatarUrl>().unwrap(), None);
199        assert_eq!(response.get_static::<DisplayName>().unwrap(), None);
200        assert!(response.get("custom_field").unwrap().is_null());
201    }
202}