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 ruma_common::owned_mxc_uri;
139    use serde_json::{
140        Value as JsonValue, from_slice as from_json_slice, json, to_vec as to_json_vec,
141    };
142
143    use super::v3::Response;
144
145    #[test]
146    #[cfg(feature = "server")]
147    fn serialize_response() {
148        use ruma_common::api::OutgoingResponse;
149
150        use crate::profile::ProfileFieldValue;
151
152        let response = [
153            ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef")),
154            ProfileFieldValue::DisplayName("Alice".to_owned()),
155            ProfileFieldValue::new("custom_field", "value".into()).unwrap(),
156        ]
157        .into_iter()
158        .collect::<Response>();
159
160        let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
161
162        assert_eq!(
163            from_json_slice::<JsonValue>(http_response.body().as_ref()).unwrap(),
164            json!({
165                "avatar_url": "mxc://localhost/abcdef",
166                "displayname": "Alice",
167                "custom_field": "value",
168            })
169        );
170    }
171
172    #[test]
173    #[cfg(feature = "client")]
174    fn deserialize_response() {
175        use ruma_common::api::IncomingResponse;
176
177        use crate::profile::{AvatarUrl, DisplayName};
178
179        // Values are set.
180        let body = to_json_vec(&json!({
181            "avatar_url": "mxc://localhost/abcdef",
182            "displayname": "Alice",
183            "custom_field": "value",
184        }))
185        .unwrap();
186
187        let response = Response::try_from_http_response(http::Response::new(body)).unwrap();
188        assert_eq!(response.get_static::<AvatarUrl>().unwrap().unwrap(), "mxc://localhost/abcdef");
189        assert_eq!(response.get_static::<DisplayName>().unwrap().unwrap(), "Alice");
190        assert_eq!(response.get("custom_field").unwrap().as_str().unwrap(), "value");
191
192        // Values are missing or null.
193        let body = to_json_vec(&json!({
194            "custom_field": null,
195        }))
196        .unwrap();
197
198        let response = Response::try_from_http_response(http::Response::new(body)).unwrap();
199        assert_eq!(response.get_static::<AvatarUrl>().unwrap(), None);
200        assert_eq!(response.get_static::<DisplayName>().unwrap(), None);
201        assert!(response.get("custom_field").unwrap().is_null());
202    }
203}