redmine_api/api/
my_account.rs

1//! My Account Rest API Endpoint definitions
2//!
3//! [Redmine Documentation](https://www.redmine.org/projects/redmine/wiki/Rest_MyAccount)
4//!
5//! - [x] my account endpoint
6
7use derive_builder::Builder;
8use reqwest::Method;
9use std::borrow::Cow;
10
11use crate::api::custom_fields::CustomFieldEssentialsWithValue;
12use crate::api::{Endpoint, NoPagination, ReturnsJsonResponse};
13
14/// a type for my account to use as an API return type
15///
16/// alternatively you can use your own type limited to the fields you need
17#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
18pub struct MyAccount {
19    /// numeric id
20    pub id: u64,
21    /// login name
22    pub login: String,
23    /// is this user an admin
24    pub admin: bool,
25    /// user's firstname
26    pub firstname: String,
27    /// user's lastname
28    pub lastname: String,
29    /// primary email of the user
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub mail: Option<String>,
32    /// The time when this user was created
33    #[serde(
34        serialize_with = "crate::api::serialize_rfc3339",
35        deserialize_with = "crate::api::deserialize_rfc3339"
36    )]
37    pub created_on: time::OffsetDateTime,
38    /// the time when this user last logged in
39    #[serde(
40        serialize_with = "crate::api::serialize_optional_rfc3339",
41        deserialize_with = "crate::api::deserialize_optional_rfc3339"
42    )]
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub last_login_on: Option<time::OffsetDateTime>,
45    /// the user's API key
46    pub api_key: String,
47    /// custom fields with values
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub custom_fields: Option<Vec<CustomFieldEssentialsWithValue>>,
50}
51
52/// The endpoint to retrieve the current user's my account settings/data
53#[derive(Debug, Clone, Builder)]
54#[builder(setter(strip_option))]
55pub struct GetMyAccount {}
56
57impl ReturnsJsonResponse for GetMyAccount {}
58impl NoPagination for GetMyAccount {}
59
60impl GetMyAccount {
61    /// Create a builder for the endpoint.
62    #[must_use]
63    pub fn builder() -> GetMyAccountBuilder {
64        GetMyAccountBuilder::default()
65    }
66}
67
68impl Endpoint for GetMyAccount {
69    fn method(&self) -> Method {
70        Method::GET
71    }
72
73    fn endpoint(&self) -> Cow<'static, str> {
74        "my/account.json".into()
75    }
76}
77
78#[cfg(test)]
79mod test {
80    use super::*;
81    use crate::api::users::UserWrapper;
82    use pretty_assertions::assert_eq;
83    use std::error::Error;
84    use tracing_test::traced_test;
85
86    #[traced_test]
87    #[test]
88    fn test_get_my_account() -> Result<(), Box<dyn Error>> {
89        dotenvy::dotenv()?;
90        let redmine = crate::api::Redmine::from_env()?;
91        let endpoint = GetMyAccount::builder().build()?;
92        redmine.json_response_body::<_, UserWrapper<MyAccount>>(&endpoint)?;
93        Ok(())
94    }
95
96    /// this tests if any of the results contain a field we are not deserializing
97    ///
98    /// this will only catch fields we missed if they are part of the response but
99    /// it is better than nothing
100    #[traced_test]
101    #[test]
102    fn test_completeness_my_account_type() -> Result<(), Box<dyn Error>> {
103        dotenvy::dotenv()?;
104        let redmine = crate::api::Redmine::from_env()?;
105        let endpoint = GetMyAccount::builder().build()?;
106        let UserWrapper { user: value } =
107            redmine.json_response_body::<_, UserWrapper<serde_json::Value>>(&endpoint)?;
108        let o: MyAccount = serde_json::from_value(value.clone())?;
109        let reserialized = serde_json::to_value(o)?;
110        assert_eq!(value, reserialized);
111        Ok(())
112    }
113}