redmine_api/api/
queries.rs

1//! Queries Rest API Endpoint definitions
2//!
3//! [Redmine Documentation](https://www.redmine.org/projects/redmine/wiki/Rest_Queries)
4//!
5//! - [x] all (visible) custom queries endpoint
6
7use derive_builder::Builder;
8use reqwest::Method;
9use std::borrow::Cow;
10
11use crate::api::{Endpoint, Pageable, ReturnsJsonResponse};
12
13/// a type for query to use as an API return type
14///
15/// alternatively you can use your own type limited to the fields you need
16#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17pub struct Query {
18    /// numeric id
19    pub id: u64,
20    /// display name
21    pub name: String,
22    /// is this query public
23    pub is_public: bool,
24    /// the project for project-specific queries
25    pub project_id: Option<u64>,
26}
27
28/// The endpoint to retrieve all queries visible to the current user
29///
30/// to actually use them pass the query_id to the ListIssues endpoint
31#[derive(Debug, Clone, Builder)]
32#[builder(setter(strip_option))]
33pub struct ListQueries {}
34
35impl ReturnsJsonResponse for ListQueries {}
36impl Pageable for ListQueries {
37    fn response_wrapper_key(&self) -> String {
38        "queries".to_string()
39    }
40}
41
42impl ListQueries {
43    /// Create a builder for the endpoint.
44    #[must_use]
45    pub fn builder() -> ListQueriesBuilder {
46        ListQueriesBuilder::default()
47    }
48}
49
50impl Endpoint for ListQueries {
51    fn method(&self) -> Method {
52        Method::GET
53    }
54
55    fn endpoint(&self) -> Cow<'static, str> {
56        "queries.json".into()
57    }
58}
59
60/// helper struct for outer layers with a queries field holding the inner data
61#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
62pub struct QueriesWrapper<T> {
63    /// to parse JSON with queries key
64    pub queries: Vec<T>,
65}
66
67#[cfg(test)]
68mod test {
69    use super::*;
70    use pretty_assertions::assert_eq;
71    use std::error::Error;
72    use tracing_test::traced_test;
73
74    #[traced_test]
75    #[test]
76    fn test_list_queries_no_pagination() -> Result<(), Box<dyn Error>> {
77        dotenvy::dotenv()?;
78        let redmine = crate::api::Redmine::from_env()?;
79        let endpoint = ListQueries::builder().build()?;
80        redmine.json_response_body::<_, QueriesWrapper<Query>>(&endpoint)?;
81        Ok(())
82    }
83
84    #[traced_test]
85    #[test]
86    fn test_list_queries_first_page() -> Result<(), Box<dyn Error>> {
87        dotenvy::dotenv()?;
88        let redmine = crate::api::Redmine::from_env()?;
89        let endpoint = ListQueries::builder().build()?;
90        redmine.json_response_body_page::<_, Query>(&endpoint, 0, 25)?;
91        Ok(())
92    }
93
94    #[traced_test]
95    #[test]
96    fn test_list_queries_all_pages() -> Result<(), Box<dyn Error>> {
97        dotenvy::dotenv()?;
98        let redmine = crate::api::Redmine::from_env()?;
99        let endpoint = ListQueries::builder().build()?;
100        redmine.json_response_body_all_pages::<_, Query>(&endpoint)?;
101        Ok(())
102    }
103
104    /// this tests if any of the results contain a field we are not deserializing
105    ///
106    /// this will only catch fields we missed if they are part of the response but
107    /// it is better than nothing
108    #[traced_test]
109    #[test]
110    fn test_completeness_query_type() -> Result<(), Box<dyn Error>> {
111        dotenvy::dotenv()?;
112        let redmine = crate::api::Redmine::from_env()?;
113        let endpoint = ListQueries::builder().build()?;
114        let QueriesWrapper { queries: values } =
115            redmine.json_response_body::<_, QueriesWrapper<serde_json::Value>>(&endpoint)?;
116        for value in values {
117            let o: Query = serde_json::from_value(value.clone())?;
118            let reserialized = serde_json::to_value(o)?;
119            assert_eq!(value, reserialized);
120        }
121        Ok(())
122    }
123}