Skip to main content

zlayer_types/api/
users.rs

1//! User management DTOs.
2
3use serde::{Deserialize, Serialize};
4
5use crate::storage::UserRole;
6
7/// Body for `POST /api/v1/users`.
8#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
9pub struct CreateUserRequest {
10    pub email: String,
11    pub password: String,
12    #[serde(default)]
13    pub display_name: Option<String>,
14    pub role: UserRole,
15}
16
17/// Body for `PATCH /api/v1/users/{id}`. All fields are optional so a caller
18/// can update a single attribute without touching the others.
19#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
20pub struct UpdateUserRequest {
21    #[serde(default)]
22    pub display_name: Option<String>,
23    #[serde(default)]
24    pub role: Option<UserRole>,
25    #[serde(default)]
26    pub is_active: Option<bool>,
27}
28
29/// Body for `POST /api/v1/users/{id}/password`.
30#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
31pub struct SetPasswordRequest {
32    /// New password.
33    pub new_password: String,
34    /// Required when the caller is setting their own password (non-admin
35    /// path). Admins changing someone else's password may omit this.
36    #[serde(default)]
37    pub current_password: Option<String>,
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_create_user_request_deserialize() {
46        let json = r#"{"email": "a@b.com", "password": "pw", "role": "user"}"#;
47        let req: CreateUserRequest = serde_json::from_str(json).unwrap();
48        assert_eq!(req.email, "a@b.com");
49        assert_eq!(req.role, UserRole::User);
50        assert!(req.display_name.is_none());
51    }
52
53    #[test]
54    fn test_create_user_request_admin_role() {
55        let json = r#"{"email":"a@b.com","password":"pw","role":"admin","display_name":"A"}"#;
56        let req: CreateUserRequest = serde_json::from_str(json).unwrap();
57        assert_eq!(req.role, UserRole::Admin);
58        assert_eq!(req.display_name.as_deref(), Some("A"));
59    }
60
61    #[test]
62    fn test_update_user_request_partial() {
63        // All fields optional — empty body is valid
64        let req: UpdateUserRequest = serde_json::from_str("{}").unwrap();
65        assert!(req.display_name.is_none());
66        assert!(req.role.is_none());
67        assert!(req.is_active.is_none());
68    }
69
70    #[test]
71    fn test_set_password_request_deserialize() {
72        let req: SetPasswordRequest =
73            serde_json::from_str(r#"{"new_password":"np","current_password":"cp"}"#).unwrap();
74        assert_eq!(req.new_password, "np");
75        assert_eq!(req.current_password.as_deref(), Some("cp"));
76
77        let req_admin: SetPasswordRequest =
78            serde_json::from_str(r#"{"new_password":"np"}"#).unwrap();
79        assert!(req_admin.current_password.is_none());
80    }
81}