Skip to main content

gog_forms/
responses.rs

1// responses.rs - List form responses from the Google Forms API.
2// Google Forms API v1: https://developers.google.com/forms/api/reference/rest/v1/forms.responses/list
3
4use reqwest::Client;
5
6use crate::types::{FormsError, ResponseList};
7
8const FORMS_BASE: &str = "https://forms.googleapis.com/v1/forms";
9
10// ---------------------------------------------------------------------------
11// ListResponsesParams
12// ---------------------------------------------------------------------------
13
14/// Optional query parameters for listing form responses.
15#[derive(Debug, Clone, Default)]
16pub struct ListResponsesParams {
17    /// Maximum number of responses to return per page (default: 5000, max: 5000).
18    pub page_size: Option<u32>,
19
20    /// Page token returned by a previous call.
21    pub page_token: Option<String>,
22
23    /// RFC 3339 timestamp; only responses submitted after this time are returned.
24    pub filter: Option<String>,
25}
26
27// ---------------------------------------------------------------------------
28// list_responses
29// ---------------------------------------------------------------------------
30
31/// List all responses for a form, with optional pagination and filtering.
32///
33/// Uses: `GET https://forms.googleapis.com/v1/forms/{formId}/responses`
34pub async fn list_responses(
35    client: &Client,
36    access_token: &str,
37    form_id: &str,
38    params: &ListResponsesParams,
39) -> Result<ResponseList, FormsError> {
40    let url = format!("{FORMS_BASE}/{form_id}/responses");
41
42    let mut query: Vec<(&str, String)> = Vec::new();
43
44    if let Some(size) = params.page_size {
45        query.push(("pageSize", size.to_string()));
46    }
47    if let Some(token) = &params.page_token {
48        query.push(("pageToken", token.clone()));
49    }
50    if let Some(filter) = &params.filter {
51        query.push(("filter", filter.clone()));
52    }
53
54    let resp = client
55        .get(&url)
56        .bearer_auth(access_token)
57        .query(&query)
58        .send()
59        .await?;
60
61    if !resp.status().is_success() {
62        let status = resp.status().as_u16();
63        let message = resp.text().await.unwrap_or_default();
64        return Err(FormsError::Api { status, message });
65    }
66
67    let list: ResponseList = resp.json().await?;
68    Ok(list)
69}
70
71// ---------------------------------------------------------------------------
72// Tests
73// ---------------------------------------------------------------------------
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_list_responses_url_construction() {
81        let form_id = "form_abc";
82        let url = format!("{FORMS_BASE}/{form_id}/responses");
83        assert_eq!(url, "https://forms.googleapis.com/v1/forms/form_abc/responses");
84    }
85
86    #[test]
87    fn test_list_responses_params_default() {
88        let params = ListResponsesParams::default();
89        assert!(params.page_size.is_none());
90        assert!(params.page_token.is_none());
91        assert!(params.filter.is_none());
92    }
93
94    #[test]
95    fn test_list_responses_params_with_values() {
96        let params = ListResponsesParams {
97            page_size: Some(100),
98            page_token: Some("tok_abc".to_string()),
99            filter: Some("timestamp > 2024-01-01T00:00:00Z".to_string()),
100        };
101        assert_eq!(params.page_size, Some(100));
102        assert_eq!(params.page_token.as_deref(), Some("tok_abc"));
103        assert!(params.filter.is_some());
104    }
105
106    #[test]
107    fn test_query_params_built_correctly() {
108        // Verify query parameter construction logic mirrors what list_responses does.
109        let params = ListResponsesParams {
110            page_size: Some(50),
111            page_token: Some("next_page".to_string()),
112            filter: None,
113        };
114
115        let mut query: Vec<(&str, String)> = Vec::new();
116        if let Some(size) = params.page_size {
117            query.push(("pageSize", size.to_string()));
118        }
119        if let Some(token) = &params.page_token {
120            query.push(("pageToken", token.clone()));
121        }
122        if let Some(filter) = &params.filter {
123            query.push(("filter", filter.clone()));
124        }
125
126        assert_eq!(query.len(), 2);
127        assert_eq!(query[0], ("pageSize", "50".to_string()));
128        assert_eq!(query[1], ("pageToken", "next_page".to_string()));
129    }
130
131    #[test]
132    fn test_query_params_empty_when_all_none() {
133        let params = ListResponsesParams::default();
134
135        let mut query: Vec<(&str, String)> = Vec::new();
136        if let Some(size) = params.page_size {
137            query.push(("pageSize", size.to_string()));
138        }
139        if let Some(token) = &params.page_token {
140            query.push(("pageToken", token.clone()));
141        }
142        if let Some(filter) = &params.filter {
143            query.push(("filter", filter.clone()));
144        }
145
146        assert!(query.is_empty());
147    }
148}