viewpoint_core/context/api/
mod.rs

1//! Context-level API request functionality.
2
3use tracing::debug;
4
5use crate::api::{APIContextOptions, APIRequestContext};
6use crate::error::ContextError;
7
8use super::BrowserContext;
9
10impl BrowserContext {
11    /// Get an API request context associated with this browser context.
12    ///
13    /// The returned `APIRequestContext` can be used to make HTTP requests.
14    /// Cookies from the browser context are automatically synced to the API context.
15    ///
16    /// # Example
17    ///
18    /// ```no_run
19    /// use viewpoint_core::Browser;
20    ///
21    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
22    /// let browser = Browser::launch().headless(true).launch().await?;
23    /// let context = browser.new_context().await?;
24    ///
25    /// // Get API context (includes browser cookies)
26    /// let api = context.request().await?;
27    ///
28    /// // Make API requests with browser cookies
29    /// let response = api.get("https://api.example.com/data").send().await?;
30    /// # Ok(())
31    /// # }
32    /// ```
33    ///
34    /// # Errors
35    ///
36    /// Returns an error if the API context cannot be created.
37    pub async fn request(&self) -> Result<APIRequestContext, ContextError> {
38        if self.is_closed() {
39            return Err(ContextError::Closed);
40        }
41
42        debug!("Creating API request context for browser context");
43
44        // Build options from context settings
45        let mut options = APIContextOptions::new();
46
47        // Copy extra HTTP headers from context
48        if !self.options.extra_http_headers.is_empty() {
49            options = options.extra_http_headers(
50                self.options.extra_http_headers.iter()
51                    .map(|(k, v)| (k.clone(), v.clone()))
52            );
53        }
54
55        // Copy HTTP credentials if set
56        if let Some(ref creds) = self.options.http_credentials {
57            options = options.http_credentials(
58                crate::api::HttpCredentials::new(&creds.username, &creds.password)
59            );
60        }
61
62        // Create API context
63        let api = APIRequestContext::new(options)
64            .await
65            .map_err(|e| ContextError::Internal(e.to_string()))?;
66
67        // Sync cookies from browser to API context
68        let browser_cookies = self.cookies().await?;
69        crate::api::cookies::sync_to_jar(&browser_cookies, api.cookie_jar());
70        debug!("Synced {} browser cookies to API context", browser_cookies.len());
71
72        Ok(api)
73    }
74
75    /// Sync cookies from API responses back to the browser context.
76    ///
77    /// Call this after making API requests that may have set cookies
78    /// (e.g., login endpoints) to ensure the browser has the same cookies.
79    ///
80    /// # Example
81    ///
82    /// ```no_run
83    /// use viewpoint_core::Browser;
84    ///
85    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
86    /// let browser = Browser::launch().headless(true).launch().await?;
87    /// let context = browser.new_context().await?;
88    ///
89    /// // Login via API
90    /// let api = context.request().await?;
91    /// let response = api.post("https://api.example.com/login")
92    ///     .json(&serde_json::json!({"user": "admin", "pass": "secret"}))
93    ///     .send()
94    ///     .await?;
95    ///
96    /// // Sync cookies back to browser (e.g., session cookies from Set-Cookie)
97    /// context.sync_cookies_from_api(&api, "https://api.example.com").await?;
98    ///
99    /// // Now browser pages will have the session cookie
100    /// # Ok(())
101    /// # }
102    /// ```
103    ///
104    /// # Errors
105    ///
106    /// Returns an error if cookie syncing fails.
107    pub async fn sync_cookies_from_api(
108        &self,
109        api: &APIRequestContext,
110        url: &str,
111    ) -> Result<(), ContextError> {
112        if self.is_closed() {
113            return Err(ContextError::Closed);
114        }
115
116        let cookies = crate::api::cookies::extract_from_jar(api.cookie_jar(), url);
117        if !cookies.is_empty() {
118            debug!("Syncing {} cookies from API to browser", cookies.len());
119            self.add_cookies(cookies).await?;
120        }
121
122        Ok(())
123    }
124}