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