Skip to main content

faucet_source_xml/
config.rs

1//! XML source configuration.
2
3use reqwest::header::HeaderMap;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7/// Authentication for XML API endpoints.
8#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
9#[serde(tag = "type")]
10pub enum XmlAuth {
11    /// No authentication.
12    None,
13    /// Bearer token.
14    Bearer(String),
15    /// Basic authentication.
16    Basic { username: String, password: String },
17    /// Custom headers (e.g. SOAP action headers, API keys).
18    #[serde(skip)]
19    Custom(HeaderMap),
20}
21
22/// Pagination configuration for XML APIs.
23#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
24#[serde(tag = "type")]
25pub enum XmlPagination {
26    /// Page-number pagination with a query parameter.
27    PageNumber {
28        param_name: String,
29        start_page: usize,
30        page_size: Option<usize>,
31        page_size_param: Option<String>,
32    },
33    /// Offset/limit pagination.
34    Offset {
35        offset_param: String,
36        limit_param: String,
37        limit: usize,
38    },
39}
40
41/// Configuration for the XML source.
42#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
43pub struct XmlStreamConfig {
44    /// Base URL of the API.
45    pub base_url: String,
46    /// Request path (appended to base_url).
47    pub path: String,
48    /// HTTP method (GET or POST for SOAP).
49    #[serde(with = "crate::serde_helpers::http_method")]
50    #[schemars(with = "String")]
51    pub method: reqwest::Method,
52    /// Authentication method.
53    pub auth: XmlAuth,
54    /// Additional request headers.
55    #[serde(skip, default)]
56    pub headers: HeaderMap,
57    /// Optional request body (e.g. SOAP envelope).
58    pub body: Option<String>,
59    /// Dot-separated path to the repeating element in the XML response
60    /// (e.g. `"Envelope.Body.GetUsersResponse.Users.User"`).
61    pub records_element_path: Option<String>,
62    /// Pagination configuration.
63    pub pagination: Option<XmlPagination>,
64    /// Maximum number of pages to fetch.
65    pub max_pages: Option<usize>,
66    /// Query parameters to include in every request.
67    pub query_params: std::collections::HashMap<String, String>,
68}
69
70impl XmlStreamConfig {
71    /// Create a new config with required fields.
72    pub fn new(base_url: impl Into<String>, path: impl Into<String>) -> Self {
73        Self {
74            base_url: base_url.into(),
75            path: path.into(),
76            method: reqwest::Method::GET,
77            auth: XmlAuth::None,
78            headers: HeaderMap::new(),
79            body: None,
80            records_element_path: None,
81            pagination: None,
82            max_pages: None,
83            query_params: std::collections::HashMap::new(),
84        }
85    }
86
87    /// Set the HTTP method (default: GET).
88    pub fn method(mut self, method: reqwest::Method) -> Self {
89        self.method = method;
90        self
91    }
92
93    /// Set the authentication method.
94    pub fn auth(mut self, auth: XmlAuth) -> Self {
95        self.auth = auth;
96        self
97    }
98
99    /// Set additional headers.
100    pub fn headers(mut self, headers: HeaderMap) -> Self {
101        self.headers = headers;
102        self
103    }
104
105    /// Set a SOAP or XML request body.
106    pub fn body(mut self, body: impl Into<String>) -> Self {
107        self.body = Some(body.into());
108        self
109    }
110
111    /// Set the dot-separated path to the repeating element.
112    pub fn records_element_path(mut self, path: impl Into<String>) -> Self {
113        self.records_element_path = Some(path.into());
114        self
115    }
116
117    /// Set pagination configuration.
118    pub fn pagination(mut self, pagination: XmlPagination) -> Self {
119        self.pagination = Some(pagination);
120        self
121    }
122
123    /// Set the maximum number of pages.
124    pub fn max_pages(mut self, max: usize) -> Self {
125        self.max_pages = Some(max);
126        self
127    }
128
129    /// Add a query parameter.
130    pub fn query_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
131        self.query_params.insert(key.into(), value.into());
132        self
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[test]
141    fn default_config() {
142        let config = XmlStreamConfig::new("https://api.example.com", "/users");
143        assert_eq!(config.base_url, "https://api.example.com");
144        assert_eq!(config.path, "/users");
145        assert_eq!(config.method, reqwest::Method::GET);
146        assert!(config.records_element_path.is_none());
147    }
148
149    #[test]
150    fn soap_config() {
151        let config = XmlStreamConfig::new("https://api.example.com", "/soap")
152            .method(reqwest::Method::POST)
153            .body("<Envelope><Body><GetUsers/></Body></Envelope>")
154            .records_element_path("Envelope.Body.GetUsersResponse.Users.User");
155        assert_eq!(config.method, reqwest::Method::POST);
156        assert!(config.body.is_some());
157        assert_eq!(
158            config.records_element_path.unwrap(),
159            "Envelope.Body.GetUsersResponse.Users.User"
160        );
161    }
162}