1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use time::OffsetDateTime;
5use url::Url;
6
7use crate::Hateoas;
8
9#[cfg(feature = "serde")]
10use super::utils;
11
12#[cfg_attr(
13 feature = "serde",
14 derive(Serialize, Deserialize),
15 serde(rename_all = "camelCase")
16)]
17#[derive(Debug, Clone, PartialEq)]
18#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
19pub struct UserPreferences {
20 pub visibility_days: u32,
21 pub min_elevation: f32,
22 pub max_elevation: f32,
23 pub min_duration: f32,
24 pub elevation_tolerance: f32,
25 pub duration_tolerance: f32,
26 pub notify_via_email: bool,
27 pub notify_via_text: bool,
28}
29
30#[cfg_attr(
32 feature = "serde",
33 derive(Serialize, Deserialize),
34 serde(rename_all = "camelCase")
35)]
36#[derive(Debug, Clone, PartialEq)]
37#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
38pub struct WhoAmI {
39 pub id: i32,
41 pub first_name: String,
43 pub last_name: String,
45 pub email: String,
47 pub machine_service: bool,
49 pub ui_role: String,
51 pub ui_privilege: String,
53 #[cfg_attr(
54 feature = "serde",
55 serde(rename = "_links", with = "utils::links::serde", default)
56 )]
57 pub links: HashMap<String, Url>,
58}
59
60impl Hateoas for WhoAmI {
61 fn get_links(&self) -> &HashMap<String, url::Url> {
62 &self.links
63 }
64
65 fn get_links_mut(&mut self) -> &mut HashMap<String, url::Url> {
66 &mut self.links
67 }
68}
69
70#[cfg_attr(
72 feature = "serde",
73 derive(Serialize, Deserialize),
74 serde(rename_all = "camelCase")
75)]
76#[derive(Debug, Clone, PartialEq)]
77#[cfg_attr(not(feature = "unstable"), non_exhaustive)]
78pub struct User {
79 #[cfg_attr(feature = "serde", serde(with = "time::serde::iso8601"))]
80 pub created: OffsetDateTime,
81 #[cfg_attr(
82 feature = "serde",
83 serde(default, with = "time::serde::iso8601::option")
84 )]
85 pub modified: Option<OffsetDateTime>,
86 pub first_name: String,
87 pub last_name: String,
88 pub verified: bool,
89 pub email: String,
90 pub preferences: UserPreferences,
91 #[cfg_attr(feature = "serde", serde(default))]
93 pub internal_meta_data: Option<HashMap<String, String>>,
94 #[cfg_attr(feature = "serde", serde(default))]
96 pub deleted: Option<bool>,
97 pub api_access_enabled: bool,
98 #[cfg_attr(
99 feature = "serde",
100 serde(rename = "_links", with = "utils::links::serde", default)
101 )]
102 pub links: HashMap<String, Url>,
103}
104
105impl Hateoas for User {
106 fn get_links(&self) -> &HashMap<String, url::Url> {
107 &self.links
108 }
109
110 fn get_links_mut(&mut self) -> &mut HashMap<String, url::Url> {
111 &mut self.links
112 }
113}
114
115#[cfg(all(feature = "serde", test))]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn whoami_deserializes_full_response() {
121 let json = r#"{
122 "uiRole": "ROLE_OPERATOR",
123 "uiPrivilege": "UI_PRIVILEGE_VIEW_TASKS,UI_PRIVILEGE_VIEW_SATELLITES",
124 "id": 42,
125 "email": "test.user@example.com",
126 "firstName": "Test",
127 "lastName": "User",
128 "machineService": false,
129 "_links": {
130 "self": { "href": "https://api.example.com/api/users/42" },
131 "users": { "href": "https://api.example.com/api/users/42" }
132 }
133 }"#;
134
135 let whoami: WhoAmI = serde_json::from_str(json).unwrap();
136
137 assert_eq!(whoami.id, 42);
138 assert_eq!(whoami.email, "test.user@example.com");
139 assert_eq!(whoami.first_name, "Test");
140 assert_eq!(whoami.last_name, "User");
141 assert!(!whoami.machine_service);
142 assert_eq!(whoami.ui_role, "ROLE_OPERATOR");
143 assert_eq!(
144 whoami.ui_privilege,
145 "UI_PRIVILEGE_VIEW_TASKS,UI_PRIVILEGE_VIEW_SATELLITES"
146 );
147 }
148
149 #[test]
150 fn whoami_deserializes_links() {
151 let json = r#"{
152 "uiRole": "ROLE_ADMIN",
153 "uiPrivilege": "UI_PRIVILEGE_VIEW_ALL",
154 "id": 1,
155 "email": "admin@example.com",
156 "firstName": "Admin",
157 "lastName": "User",
158 "machineService": false,
159 "_links": {
160 "self": { "href": "https://api.example.com/api/users/1" },
161 "users": { "href": "https://api.example.com/api/users/1" }
162 }
163 }"#;
164
165 let whoami: WhoAmI = serde_json::from_str(json).unwrap();
166
167 let self_link = whoami.get_links().get("self").unwrap();
168 assert_eq!(self_link.as_str(), "https://api.example.com/api/users/1");
169 assert!(whoami.get_links().contains_key("users"));
170 }
171
172 #[test]
173 fn whoami_deserializes_without_links() {
174 let json = r#"{
175 "uiRole": "ROLE_USER",
176 "uiPrivilege": "UI_PRIVILEGE_VIEW_TASKS",
177 "id": 99,
178 "email": "nolinks@example.com",
179 "firstName": "No",
180 "lastName": "Links",
181 "machineService": false
182 }"#;
183
184 let whoami: WhoAmI = serde_json::from_str(json).unwrap();
185 assert!(whoami.links.is_empty());
186 }
187
188 #[test]
189 fn whoami_deserializes_machine_service_user() {
190 let json = r#"{
191 "uiRole": "ROLE_SERVICE",
192 "uiPrivilege": "UI_PRIVILEGE_MANAGE_TASKS",
193 "id": 200,
194 "email": "machine@example.com",
195 "firstName": "Machine",
196 "lastName": "Service",
197 "machineService": true
198 }"#;
199
200 let whoami: WhoAmI = serde_json::from_str(json).unwrap();
201 assert!(whoami.machine_service);
202 }
203
204 #[test]
205 fn whoami_serializes_links_with_href_wrapper() {
206 let json = r#"{
207 "uiRole": "ROLE_USER",
208 "uiPrivilege": "UI_PRIVILEGE_VIEW_TASKS",
209 "id": 10,
210 "email": "roundtrip@example.com",
211 "firstName": "Round",
212 "lastName": "Trip",
213 "machineService": false,
214 "_links": {
215 "self": { "href": "https://api.example.com/api/users/10" }
216 }
217 }"#;
218
219 let whoami: WhoAmI = serde_json::from_str(json).unwrap();
220 let serialized = serde_json::to_value(&whoami).unwrap();
221
222 assert_eq!(
223 serialized["_links"]["self"]["href"],
224 "https://api.example.com/api/users/10"
225 );
226 }
227
228 #[test]
229 fn whoami_get_links_mut_allows_insertion() {
230 let json = r#"{
231 "uiRole": "ROLE_USER",
232 "uiPrivilege": "UI_PRIVILEGE_VIEW_TASKS",
233 "id": 5,
234 "email": "mut@example.com",
235 "firstName": "Mut",
236 "lastName": "User",
237 "machineService": false
238 }"#;
239
240 let mut whoami: WhoAmI = serde_json::from_str(json).unwrap();
241 whoami.get_links_mut().insert(
242 "self".into(),
243 "https://api.example.com/api/users/5".parse().unwrap(),
244 );
245
246 assert!(whoami.get_links().contains_key("self"));
247 }
248}