Skip to main content

wme_client/
lib.rs

1//! HTTP client for the Wikimedia Enterprise API.
2//!
3//! This crate provides a robust, production-ready client for accessing the Wikimedia Enterprise API.
4//! It includes automatic authentication management, retry logic with circuit breakers, and
5//! support for all API endpoints including metadata, on-demand, snapshots, and realtime streaming.
6//!
7//! # Quick Start
8//!
9//! ```rust,no_run
10//! use wme_client::WmeClient;
11//!
12//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
13//! // Create a client with authentication
14//! let client = WmeClient::builder()
15//!     .credentials("username", "password")
16//!     .build()
17//!     .await?;
18//!
19//! // List available projects
20//! let projects = client.metadata().list_projects().await?;
21//! # Ok(())
22//! # }
23//! ```
24//!
25//! # Architecture
26//!
27//! The client is organized into sub-clients for different API areas:
28//!
29//! - [`WmeClient`] - Main entry point and builder
30//! - [`MetadataClient`] - Discovery endpoints (projects, languages, namespaces)
31//! - [`OnDemandClient`] - Single article lookups
32//! - [`SnapshotClient`] - Bulk data downloads
33//! - [`RealtimeClient`] - Streaming and batch endpoints
34//!
35//! # Authentication
36//!
37//! Authentication is handled automatically via the [`TokenManager`]. Tokens are refreshed
38//! automatically before expiration. See the [`auth`] module for details.
39//!
40//! # Resilience
41//!
42//! The client includes several resilience features:
43//!
44//! - **Retry Logic**: Configurable exponential backoff with jitter ([`RetryConfig`])
45//! - **Circuit Breaker**: Prevents cascading failures ([`CircuitState`])
46//! - **Timeout Handling**: Configurable request timeouts
47//!
48//! # Error Handling
49//!
50//! All operations return a [`Result<T>`] which uses [`ClientError`] for error cases.
51//! The error type includes specific variants for common failure modes like authentication
52//! failures, rate limiting, and not-found errors.
53
54pub mod auth;
55pub mod client;
56pub mod error;
57pub mod metadata;
58pub mod ondemand;
59pub mod realtime;
60pub mod retry;
61pub mod retry_transport;
62pub mod snapshot;
63pub mod transport;
64
65pub use auth::{TokenManager, Tokens};
66pub use client::{ClientConfig, WmeClient, WmeClientBuilder};
67pub use error::ClientError;
68pub use metadata::MetadataClient;
69pub use ondemand::OnDemandClient;
70pub use realtime::{RealtimeClient, RealtimeConnectOptions};
71pub use retry::RetryConfig;
72pub use retry_transport::{CircuitState, RetryTransport};
73pub use snapshot::SnapshotClient;
74pub use transport::{BoxStream, HttpTransport, ReqwestTransport};
75pub use wme_models::RequestParams;
76
77/// Authentication tokens returned from login.
78///
79/// These tokens are used to authenticate API requests. The access token is
80/// sent with each request, while the refresh token is used to obtain new
81/// access tokens when the current one expires.
82#[derive(Debug, Clone)]
83pub struct AuthTokens {
84    /// Access token for API requests (JWT format)
85    pub access_token: String,
86    /// Refresh token for obtaining new access tokens
87    pub refresh_token: String,
88    /// Token expiration time (UTC)
89    pub expires_at: chrono::DateTime<chrono::Utc>,
90}
91
92/// Base URL configuration for API endpoints.
93///
94/// Allows customization of the API endpoints for testing or private deployments.
95/// Use [`BaseUrls::default()`] for production Wikimedia Enterprise endpoints.
96#[derive(Debug, Clone)]
97pub struct BaseUrls {
98    /// Base URL for API requests (metadata, snapshots, ondemand)
99    /// Default: `https://api.enterprise.wikimedia.com`
100    pub api: String,
101    /// URL for authentication
102    /// Default: `https://auth.enterprise.wikimedia.com`
103    pub auth: String,
104    /// Base URL for realtime streaming API
105    /// Default: `https://realtime.enterprise.wikimedia.com`
106    pub realtime: String,
107}
108
109impl Default for BaseUrls {
110    fn default() -> Self {
111        Self {
112            api: "https://api.enterprise.wikimedia.com".to_string(),
113            auth: "https://auth.enterprise.wikimedia.com".to_string(),
114            realtime: "https://realtime.enterprise.wikimedia.com".to_string(),
115        }
116    }
117}
118
119/// Result type alias for client operations.
120///
121/// All client operations that can fail return this type, which is a
122/// [`std::result::Result`] with [`ClientError`] as the error type.
123///
124/// # Example
125///
126/// ```rust
127/// use wme_client::{Result, ClientError};
128///
129/// fn operation() -> Result<u32> {
130///     Ok(42)
131/// }
132/// ```
133pub type Result<T> = std::result::Result<T, ClientError>;
134
135/// Parse JSON response body with debug logging.
136///
137/// This helper function deserializes JSON response text and logs debug information
138/// about the response and any parsing errors. Used internally by API clients.
139///
140/// # Type Parameters
141///
142/// * `T` - The target type to deserialize into. Must implement `DeserializeOwned`.
143///
144/// # Arguments
145///
146/// * `body_text` - Raw JSON response body as a string
147///
148/// # Errors
149///
150/// Returns a `ClientError::JsonParse` if deserialization fails.
151///
152/// # Example
153///
154/// ```rust,no_run
155/// use wme_client::parse_response;
156/// use wme_models::ProjectInfo;
157///
158/// # async fn example() -> Result<(), wme_client::ClientError> {
159/// let body_text = r#"{"identifier": "enwiki", "name": "English Wikipedia"}"#;
160/// let project: ProjectInfo = parse_response(body_text)?;
161/// # Ok(())
162/// # }
163/// ```
164pub fn parse_response<T>(body_text: &str) -> Result<T>
165where
166    T: serde::de::DeserializeOwned,
167{
168    use tracing::debug;
169    debug!("Raw response body:\n{}", body_text);
170
171    serde_json::from_str(body_text).map_err(|e| {
172        debug!("JSON parse error: {}", e);
173        ClientError::JsonParse(e.to_string())
174    })
175}