Skip to main content

capmonstercloud_client/requests/
recaptcha_v3_enterprise.rs

1use serde::Serialize;
2
3use super::recaptcha_v3::check_min_score;
4use super::{require_non_empty, CaptchaRequest};
5use crate::error::Result;
6use crate::timeouts::GetResultTimeouts;
7
8/// reCAPTCHA v3 Enterprise (`RecaptchaV3EnterpriseTask`). Proxies are not supported.
9#[derive(Debug, Clone, Serialize)]
10pub struct RecaptchaV3EnterpriseRequest {
11    /// Address of a webpage with Google reCAPTCHA.
12    #[serde(rename = "websiteURL")]
13    pub website_url: String,
14    /// The site key (`...enterprise.js?render=THIS_ONE`).
15    #[serde(rename = "websiteKey")]
16    pub website_key: String,
17    /// Minimum score of the token, `0.1..=0.9`.
18    #[serde(rename = "minScore", skip_serializing_if = "Option::is_none")]
19    pub min_score: Option<f64>,
20    /// Value of the `action` parameter, if it differs from the default `verify`.
21    #[serde(rename = "pageAction", skip_serializing_if = "Option::is_none")]
22    pub page_action: Option<String>,
23}
24
25impl RecaptchaV3EnterpriseRequest {
26    pub fn new(website_url: impl Into<String>, website_key: impl Into<String>) -> Self {
27        Self {
28            website_url: website_url.into(),
29            website_key: website_key.into(),
30            min_score: None,
31            page_action: None,
32        }
33    }
34
35    /// Minimum score, must be in `0.1..=0.9` (checked by `validate()`).
36    pub fn with_min_score(mut self, value: f64) -> Self {
37        self.min_score = Some(value);
38        self
39    }
40
41    pub fn with_page_action(mut self, value: impl Into<String>) -> Self {
42        self.page_action = Some(value.into());
43        self
44    }
45}
46
47impl CaptchaRequest for RecaptchaV3EnterpriseRequest {
48    const TASK_TYPE: &'static str = "RecaptchaV3EnterpriseTask";
49
50    /// Same schedule as the Python client actually uses for this type (`getRecaptchaV3Timeouts`,
51    /// 180 s), not the unused `getRecaptchaV3EnterpriseTimeouts` (150 s).
52    fn timeouts(&self) -> GetResultTimeouts {
53        GetResultTimeouts::secs(1.0, 10.0, 3.0, 180.0)
54    }
55
56    fn validate(&self) -> Result<()> {
57        require_non_empty("websiteURL", &self.website_url)?;
58        require_non_empty("websiteKey", &self.website_key)?;
59        check_min_score(self.min_score)
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use crate::error::Error;
67    use serde_json::json;
68
69    #[test]
70    fn minimal_task() {
71        let task = RecaptchaV3EnterpriseRequest::new("https://example.com", "KEY")
72            .to_task()
73            .unwrap();
74        assert_eq!(
75            task,
76            json!({"type": "RecaptchaV3EnterpriseTask", "websiteURL": "https://example.com", "websiteKey": "KEY"})
77        );
78    }
79
80    #[test]
81    fn full_task() {
82        let task = RecaptchaV3EnterpriseRequest::new("https://example.com", "KEY")
83            .with_min_score(0.9)
84            .with_page_action("login")
85            .to_task()
86            .unwrap();
87        assert_eq!(
88            task,
89            json!({"type": "RecaptchaV3EnterpriseTask", "websiteURL": "https://example.com",
90                   "websiteKey": "KEY", "minScore": 0.9, "pageAction": "login"})
91        );
92    }
93
94    #[test]
95    fn min_score_out_of_range() {
96        let err = RecaptchaV3EnterpriseRequest::new("u", "k")
97            .with_min_score(0.95)
98            .to_task()
99            .unwrap_err();
100        assert!(matches!(err, Error::Validation(_)));
101    }
102
103    #[test]
104    fn empty_required_strings_are_rejected() {
105        for (url, key, field) in [
106            ("", "KEY", "websiteURL"),
107            ("https://example.com", "", "websiteKey"),
108        ] {
109            let err = RecaptchaV3EnterpriseRequest::new(url, key)
110                .to_task()
111                .unwrap_err();
112            assert!(matches!(err, crate::error::Error::Validation(ref m) if m.contains(field)));
113        }
114    }
115}