1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9use super::common::UserId;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct GroupId(pub i64);
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub struct PermissionId(pub i64);
18
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21pub struct User {
22 pub id: UserId,
24
25 pub email: String,
27
28 pub first_name: String,
30
31 pub last_name: String,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub common_name: Option<String>,
37
38 #[serde(default)]
40 pub is_superuser: bool,
41
42 #[serde(default = "default_true")]
44 pub is_active: bool,
45
46 #[serde(default)]
48 pub is_qbnewb: bool,
49
50 pub date_joined: DateTime<Utc>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub last_login: Option<DateTime<Utc>>,
56
57 #[serde(default, skip_serializing_if = "Vec::is_empty")]
59 pub group_ids: Vec<GroupId>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub locale: Option<String>,
64
65 #[serde(default)]
67 pub google_auth: bool,
68
69 #[serde(default)]
71 pub ldap_auth: bool,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
76 pub login_attributes: Option<serde_json::Value>,
77
78 #[serde(default, skip_serializing_if = "Vec::is_empty")]
80 pub user_group_memberships: Vec<UserGroupMembership>,
81}
82
83#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub struct UserGroupMembership {
86 pub id: GroupId,
88
89 #[serde(default)]
91 pub is_default: bool,
92}
93
94#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct HealthStatus {
97 pub status: String,
99
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub version: Option<String>,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub database: Option<bool>,
107}
108
109#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
111pub struct Group {
112 pub id: GroupId,
114
115 pub name: String,
117
118 #[serde(default, skip_serializing_if = "Vec::is_empty")]
120 pub members: Vec<UserId>,
121}
122
123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub struct Permission {
126 pub id: PermissionId,
128
129 pub object: String,
131
132 pub group_id: GroupId,
134}
135
136#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
138pub struct PermissionGraph {
139 pub group_id: GroupId,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub native: Option<serde_json::Value>,
145
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub schemas: Option<serde_json::Value>,
149}
150
151#[derive(Debug, Clone, Serialize)]
153pub struct CreateUserRequest {
154 pub email: String,
156
157 pub first_name: String,
159
160 pub last_name: String,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub password: Option<String>,
166
167 #[serde(default, skip_serializing_if = "Vec::is_empty")]
169 pub group_ids: Vec<GroupId>,
170
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub locale: Option<String>,
174}
175
176#[derive(Debug, Clone, Default, Serialize)]
178pub struct UpdateUserRequest {
179 #[serde(skip_serializing_if = "Option::is_none")]
181 pub email: Option<String>,
182
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub first_name: Option<String>,
186
187 #[serde(skip_serializing_if = "Option::is_none")]
189 pub last_name: Option<String>,
190
191 #[serde(skip_serializing_if = "Option::is_none")]
193 pub password: Option<String>,
194
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub is_active: Option<bool>,
198
199 #[serde(skip_serializing_if = "Option::is_none")]
201 pub is_superuser: Option<bool>,
202
203 #[serde(skip_serializing_if = "Option::is_none")]
205 pub group_ids: Option<Vec<GroupId>>,
206
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub locale: Option<String>,
210}
211
212#[derive(Debug, Clone, Serialize)]
214pub struct CreateGroupRequest {
215 pub name: String,
217
218 #[serde(default, skip_serializing_if = "Vec::is_empty")]
220 pub members: Vec<UserId>,
221}
222
223#[derive(Debug, Clone, Default, Serialize)]
225pub struct UpdateGroupRequest {
226 #[serde(skip_serializing_if = "Option::is_none")]
228 pub name: Option<String>,
229
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub members: Option<Vec<UserId>>,
233}
234
235#[derive(Debug, Clone, Serialize)]
237pub struct MembershipRequest {
238 pub group_id: GroupId,
240
241 pub user_id: UserId,
243}
244
245fn default_true() -> bool {
246 true
247}
248
249impl User {
250 pub fn builder(
252 email: impl Into<String>,
253 first_name: impl Into<String>,
254 last_name: impl Into<String>,
255 ) -> UserBuilder {
256 UserBuilder::new(email, first_name, last_name)
257 }
258
259 pub fn full_name(&self) -> String {
261 format!("{} {}", self.first_name, self.last_name)
262 }
263}
264
265pub struct UserBuilder {
267 email: String,
268 first_name: String,
269 last_name: String,
270 password: Option<String>,
271 is_superuser: bool,
272 group_ids: Vec<GroupId>,
273 locale: Option<String>,
274}
275
276impl UserBuilder {
277 pub fn new(
279 email: impl Into<String>,
280 first_name: impl Into<String>,
281 last_name: impl Into<String>,
282 ) -> Self {
283 Self {
284 email: email.into(),
285 first_name: first_name.into(),
286 last_name: last_name.into(),
287 password: None,
288 is_superuser: false,
289 group_ids: Vec::new(),
290 locale: None,
291 }
292 }
293
294 pub fn password(mut self, password: impl Into<String>) -> Self {
296 self.password = Some(password.into());
297 self
298 }
299
300 pub fn superuser(mut self, is_superuser: bool) -> Self {
302 self.is_superuser = is_superuser;
303 self
304 }
305
306 pub fn add_group(mut self, group_id: GroupId) -> Self {
308 self.group_ids.push(group_id);
309 self
310 }
311
312 pub fn locale(mut self, locale: impl Into<String>) -> Self {
314 self.locale = Some(locale.into());
315 self
316 }
317
318 pub fn build(self) -> User {
320 User {
321 id: UserId(0), email: self.email,
323 first_name: self.first_name.clone(),
324 last_name: self.last_name.clone(),
325 common_name: Some(format!("{} {}", self.first_name, self.last_name)),
326 is_superuser: self.is_superuser,
327 is_active: true,
328 is_qbnewb: false,
329 date_joined: Utc::now(),
330 last_login: None,
331 group_ids: self.group_ids,
332 locale: self.locale,
333 google_auth: false,
334 ldap_auth: false,
335 login_attributes: None,
336 user_group_memberships: Vec::new(),
337 }
338 }
339
340 pub fn build_request(self) -> CreateUserRequest {
342 CreateUserRequest {
343 email: self.email,
344 first_name: self.first_name,
345 last_name: self.last_name,
346 password: self.password,
347 group_ids: self.group_ids,
348 locale: self.locale,
349 }
350 }
351}
352
353impl Group {
354 pub fn builder(name: impl Into<String>) -> GroupBuilder {
356 GroupBuilder::new(name)
357 }
358}
359
360pub struct GroupBuilder {
362 name: String,
363 members: Vec<UserId>,
364}
365
366impl GroupBuilder {
367 pub fn new(name: impl Into<String>) -> Self {
369 Self {
370 name: name.into(),
371 members: Vec::new(),
372 }
373 }
374
375 pub fn add_member(mut self, user_id: UserId) -> Self {
377 self.members.push(user_id);
378 self
379 }
380
381 pub fn members(mut self, members: Vec<UserId>) -> Self {
383 self.members = members;
384 self
385 }
386
387 pub fn build(self) -> Group {
389 Group {
390 id: GroupId(0), name: self.name,
392 members: self.members,
393 }
394 }
395
396 pub fn build_request(self) -> CreateGroupRequest {
398 CreateGroupRequest {
399 name: self.name,
400 members: self.members,
401 }
402 }
403}
404
405#[cfg(test)]
406mod tests {
407 use super::*;
408
409 #[test]
410 fn test_user_creation() {
411 let user = User::builder("john@example.com", "John", "Doe")
412 .password("secure123")
413 .superuser(false)
414 .add_group(GroupId(1))
415 .locale("en_US")
416 .build();
417
418 assert_eq!(user.email, "john@example.com");
419 assert_eq!(user.first_name, "John");
420 assert_eq!(user.last_name, "Doe");
421 assert_eq!(user.full_name(), "John Doe");
422 assert!(!user.is_superuser);
423 assert_eq!(user.group_ids.len(), 1);
424 }
425
426 #[test]
427 fn test_create_user_request() {
428 let request = User::builder("jane@example.com", "Jane", "Smith")
429 .password("password123")
430 .add_group(GroupId(2))
431 .build_request();
432
433 assert_eq!(request.email, "jane@example.com");
434 assert_eq!(request.first_name, "Jane");
435 assert_eq!(request.last_name, "Smith");
436 assert_eq!(request.password, Some("password123".to_string()));
437 assert_eq!(request.group_ids.len(), 1);
438 }
439
440 #[test]
441 fn test_group_creation() {
442 let group = Group::builder("Administrators")
443 .add_member(UserId(1))
444 .add_member(UserId(2))
445 .build();
446
447 assert_eq!(group.name, "Administrators");
448 assert_eq!(group.members.len(), 2);
449 }
450
451 #[test]
452 fn test_update_user_request() {
453 let request = UpdateUserRequest {
454 email: Some("newemail@example.com".to_string()),
455 is_active: Some(false),
456 ..Default::default()
457 };
458
459 assert_eq!(request.email, Some("newemail@example.com".to_string()));
460 assert_eq!(request.is_active, Some(false));
461 assert!(request.first_name.is_none());
462 }
463
464 #[test]
465 fn test_permission() {
466 let permission = Permission {
467 id: PermissionId(1),
468 object: "/db/1/schema/PUBLIC/".to_string(),
469 group_id: GroupId(1),
470 };
471
472 assert_eq!(permission.object, "/db/1/schema/PUBLIC/");
473 assert_eq!(permission.group_id, GroupId(1));
474 }
475}