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