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_first_page() -> Result<(), Box<dyn Error>> {
77        dotenvy::dotenv()?;
78        let redmine = crate::api::Redmine::from_env(
79            reqwest::blocking::Client::builder()
80                .use_rustls_tls()
81                .build()?,
82        )?;
83        let endpoint = ListQueries::builder().build()?;
84        redmine.json_response_body_page::<_, Query>(&endpoint, 0, 25)?;
85        Ok(())
86    }
87
88    #[traced_test]
89    #[test]
90    fn test_list_queries_all_pages() -> Result<(), Box<dyn Error>> {
91        dotenvy::dotenv()?;
92        let redmine = crate::api::Redmine::from_env(
93            reqwest::blocking::Client::builder()
94                .use_rustls_tls()
95                .build()?,
96        )?;
97        let endpoint = ListQueries::builder().build()?;
98        redmine.json_response_body_all_pages::<_, Query>(&endpoint)?;
99        Ok(())
100    }
101
102    /// this tests if any of the results contain a field we are not deserializing
103    ///
104    /// this will only catch fields we missed if they are part of the response but
105    /// it is better than nothing
106    #[traced_test]
107    #[test]
108    fn test_completeness_query_type() -> Result<(), Box<dyn Error>> {
109        dotenvy::dotenv()?;
110        let redmine = crate::api::Redmine::from_env(
111            reqwest::blocking::Client::builder()
112                .use_rustls_tls()
113                .build()?,
114        )?;
115        let endpoint = ListQueries::builder().build()?;
116        let values: Vec<serde_json::Value> = redmine.json_response_body_all_pages(&endpoint)?;
117        for value in values {
118            let o: Query = serde_json::from_value(value.clone())?;
119            let reserialized = serde_json::to_value(o)?;
120            assert_eq!(value, reserialized);
121        }
122        Ok(())
123    }
124}