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, Metadata},
14        metadata, OwnedUserId,
15    };
16    use serde_json::Value as JsonValue;
17
18    #[cfg(feature = "unstable-msc4133")]
19    use crate::profile::{ProfileFieldName, ProfileFieldValue, StaticProfileField};
20
21    const METADATA: Metadata = metadata! {
22        method: GET,
23        rate_limited: false,
24        authentication: None,
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        #[cfg(feature = "unstable-msc4133")]
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        #[cfg(feature = "unstable-msc4133")]
85        pub fn set(&mut self, field: &str, value: JsonValue) {
86            self.data.insert(field.to_owned(), value);
87        }
88    }
89
90    impl FromIterator<(String, JsonValue)> for Response {
91        fn from_iter<T: IntoIterator<Item = (String, JsonValue)>>(iter: T) -> Self {
92            Self { data: iter.into_iter().collect() }
93        }
94    }
95
96    #[cfg(feature = "unstable-msc4133")]
97    impl FromIterator<(ProfileFieldName, JsonValue)> for Response {
98        fn from_iter<T: IntoIterator<Item = (ProfileFieldName, JsonValue)>>(iter: T) -> Self {
99            iter.into_iter().map(|(field, value)| (field.as_str().to_owned(), value)).collect()
100        }
101    }
102
103    #[cfg(feature = "unstable-msc4133")]
104    impl FromIterator<ProfileFieldValue> for Response {
105        fn from_iter<T: IntoIterator<Item = ProfileFieldValue>>(iter: T) -> Self {
106            iter.into_iter().map(|value| (value.field_name(), value.value().into_owned())).collect()
107        }
108    }
109
110    impl Extend<(String, JsonValue)> for Response {
111        fn extend<T: IntoIterator<Item = (String, JsonValue)>>(&mut self, iter: T) {
112            self.data.extend(iter);
113        }
114    }
115
116    #[cfg(feature = "unstable-msc4133")]
117    impl Extend<(ProfileFieldName, JsonValue)> for Response {
118        fn extend<T: IntoIterator<Item = (ProfileFieldName, JsonValue)>>(&mut self, iter: T) {
119            self.extend(iter.into_iter().map(|(field, value)| (field.as_str().to_owned(), value)));
120        }
121    }
122
123    #[cfg(feature = "unstable-msc4133")]
124    impl Extend<ProfileFieldValue> for Response {
125        fn extend<T: IntoIterator<Item = ProfileFieldValue>>(&mut self, iter: T) {
126            self.extend(
127                iter.into_iter().map(|value| (value.field_name(), value.value().into_owned())),
128            );
129        }
130    }
131
132    impl IntoIterator for Response {
133        type Item = (String, JsonValue);
134        type IntoIter = btree_map::IntoIter<String, JsonValue>;
135
136        fn into_iter(self) -> Self::IntoIter {
137            self.data.into_iter()
138        }
139    }
140}
141
142#[cfg(all(test, feature = "unstable-msc4133"))]
143mod tests {
144    use ruma_common::owned_mxc_uri;
145    use serde_json::{
146        from_slice as from_json_slice, json, to_vec as to_json_vec, Value as JsonValue,
147    };
148
149    use super::v3::Response;
150
151    #[test]
152    #[cfg(feature = "server")]
153    fn serialize_response() {
154        use ruma_common::api::OutgoingResponse;
155
156        use crate::profile::ProfileFieldValue;
157
158        let response = [
159            ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef")),
160            ProfileFieldValue::DisplayName("Alice".to_owned()),
161            ProfileFieldValue::new("custom_field", "value".into()).unwrap(),
162        ]
163        .into_iter()
164        .collect::<Response>();
165
166        let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
167
168        assert_eq!(
169            from_json_slice::<JsonValue>(http_response.body().as_ref()).unwrap(),
170            json!({
171                "avatar_url": "mxc://localhost/abcdef",
172                "displayname": "Alice",
173                "custom_field": "value",
174            })
175        );
176    }
177
178    #[test]
179    #[cfg(feature = "client")]
180    fn deserialize_response() {
181        use ruma_common::api::IncomingResponse;
182
183        use crate::profile::{AvatarUrl, DisplayName};
184
185        // Values are set.
186        let body = to_json_vec(&json!({
187            "avatar_url": "mxc://localhost/abcdef",
188            "displayname": "Alice",
189            "custom_field": "value",
190        }))
191        .unwrap();
192
193        let response = Response::try_from_http_response(http::Response::new(body)).unwrap();
194        assert_eq!(response.get_static::<AvatarUrl>().unwrap().unwrap(), "mxc://localhost/abcdef");
195        assert_eq!(response.get_static::<DisplayName>().unwrap().unwrap(), "Alice");
196        assert_eq!(response.get("custom_field").unwrap().as_str().unwrap(), "value");
197
198        // Values are missing or null.
199        let body = to_json_vec(&json!({
200            "custom_field": null,
201        }))
202        .unwrap();
203
204        let response = Response::try_from_http_response(http::Response::new(body)).unwrap();
205        assert_eq!(response.get_static::<AvatarUrl>().unwrap(), None);
206        assert_eq!(response.get_static::<DisplayName>().unwrap(), None);
207        assert!(response.get("custom_field").unwrap().is_null());
208    }
209}