Skip to main content

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