Initial commit: immich-sdk v1.137.0
This commit is contained in:
232
src/client.rs
Normal file
232
src/client.rs
Normal file
@@ -0,0 +1,232 @@
|
||||
//! Client for interacting with the Immich API
|
||||
|
||||
use crate::apis::{AlbumsApi, AssetsApi, ServerApi, TimelineApi};
|
||||
use crate::error::{ImmichError, Result};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Configuration for the Immich client
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
/// Base URL of the Immich server
|
||||
pub base_url: String,
|
||||
/// API key for authentication
|
||||
pub api_key: Option<String>,
|
||||
/// Request timeout
|
||||
pub timeout: Duration,
|
||||
/// User agent string
|
||||
pub user_agent: String,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
base_url: String::new(),
|
||||
api_key: None,
|
||||
timeout: Duration::from_secs(30),
|
||||
user_agent: format!("immich-sdk/{} (Rust)", env!("CARGO_PKG_VERSION")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Create a new config with the given base URL
|
||||
pub fn new(base_url: impl Into<String>) -> Self {
|
||||
Self {
|
||||
base_url: base_url.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the API key
|
||||
pub fn with_api_key(mut self, api_key: impl Into<String>) -> Self {
|
||||
self.api_key = Some(api_key.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the timeout
|
||||
pub fn with_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.timeout = timeout;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a custom user agent
|
||||
pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
|
||||
self.user_agent = user_agent.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Client for making requests to the Immich API
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Client {
|
||||
config: Config,
|
||||
http: reqwest::Client,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Create a new client with the given configuration
|
||||
pub fn new(config: Config) -> Result<Self> {
|
||||
// Validate base URL
|
||||
let base_url = if config.base_url.ends_with('/') {
|
||||
config.base_url.trim_end_matches('/').to_string()
|
||||
} else {
|
||||
config.base_url.clone()
|
||||
};
|
||||
|
||||
if base_url.is_empty() {
|
||||
return Err(ImmichError::Config("Base URL cannot be empty".to_string()));
|
||||
}
|
||||
|
||||
// Parse to validate URL
|
||||
let _ = url::Url::parse(&base_url)?;
|
||||
|
||||
let http = reqwest::Client::builder()
|
||||
.timeout(config.timeout)
|
||||
.user_agent(&config.user_agent)
|
||||
.build()?;
|
||||
|
||||
Ok(Self {
|
||||
config: Config { base_url, ..config },
|
||||
http,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a client from a base URL string
|
||||
pub fn from_url(base_url: impl Into<String>) -> Result<Self> {
|
||||
Self::new(Config::new(base_url))
|
||||
}
|
||||
|
||||
/// Set the API key
|
||||
pub fn with_api_key(mut self, api_key: impl Into<String>) -> Self {
|
||||
self.config.api_key = Some(api_key.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the configuration
|
||||
pub fn config(&self) -> &Config {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// Get the base URL
|
||||
pub fn base_url(&self) -> &str {
|
||||
&self.config.base_url
|
||||
}
|
||||
|
||||
/// Check if the client has an API key configured
|
||||
pub fn has_api_key(&self) -> bool {
|
||||
self.config.api_key.is_some()
|
||||
}
|
||||
|
||||
/// Get the underlying HTTP client
|
||||
pub fn http(&self) -> &reqwest::Client {
|
||||
&self.http
|
||||
}
|
||||
|
||||
/// Create an authenticated request builder
|
||||
pub fn request(&self, method: reqwest::Method, path: &str) -> reqwest::RequestBuilder {
|
||||
let url = format!("{}/api{}", self.config.base_url, path);
|
||||
let mut builder = self.http.request(method, &url);
|
||||
|
||||
// Add API key authentication
|
||||
if let Some(ref api_key) = self.config.api_key {
|
||||
builder = builder.header("x-api-key", api_key);
|
||||
}
|
||||
|
||||
builder
|
||||
}
|
||||
|
||||
/// Execute a request and handle common error cases
|
||||
pub async fn execute(&self, request: reqwest::Request) -> Result<reqwest::Response> {
|
||||
let response = self.http.execute(request).await?;
|
||||
|
||||
if response.status().is_success() {
|
||||
Ok(response)
|
||||
} else {
|
||||
Err(ImmichError::from_response(response).await)
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a GET request
|
||||
pub fn get(&self, path: &str) -> reqwest::RequestBuilder {
|
||||
self.request(reqwest::Method::GET, path)
|
||||
}
|
||||
|
||||
/// Make a POST request
|
||||
pub fn post(&self, path: &str) -> reqwest::RequestBuilder {
|
||||
self.request(reqwest::Method::POST, path)
|
||||
}
|
||||
|
||||
/// Make a PUT request
|
||||
pub fn put(&self, path: &str) -> reqwest::RequestBuilder {
|
||||
self.request(reqwest::Method::PUT, path)
|
||||
}
|
||||
|
||||
/// Make a DELETE request
|
||||
pub fn delete(&self, path: &str) -> reqwest::RequestBuilder {
|
||||
self.request(reqwest::Method::DELETE, path)
|
||||
}
|
||||
|
||||
/// Make a PATCH request
|
||||
pub fn patch(&self, path: &str) -> reqwest::RequestBuilder {
|
||||
self.request(reqwest::Method::PATCH, path)
|
||||
}
|
||||
|
||||
/// Access the albums API
|
||||
pub fn albums(&self) -> AlbumsApi {
|
||||
AlbumsApi::new(self.clone())
|
||||
}
|
||||
|
||||
/// Access the assets API
|
||||
pub fn assets(&self) -> AssetsApi {
|
||||
AssetsApi::new(self.clone())
|
||||
}
|
||||
|
||||
/// Access the server API
|
||||
pub fn server(&self) -> ServerApi {
|
||||
ServerApi::new(self.clone())
|
||||
}
|
||||
|
||||
/// Access the timeline API
|
||||
pub fn timeline(&self) -> TimelineApi {
|
||||
TimelineApi::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_config_builder() {
|
||||
let config = Config::new("https://example.com")
|
||||
.with_api_key("test-key")
|
||||
.with_timeout(Duration::from_secs(60));
|
||||
|
||||
assert_eq!(config.base_url, "https://example.com");
|
||||
assert_eq!(config.api_key, Some("test-key".to_string()));
|
||||
assert_eq!(config.timeout, Duration::from_secs(60));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_client_creation() {
|
||||
let client = Client::from_url("https://example.com").unwrap();
|
||||
assert_eq!(client.base_url(), "https://example.com");
|
||||
assert!(!client.has_api_key());
|
||||
|
||||
let config = Config::new("https://example.com").with_api_key("test-key");
|
||||
let client = Client::new(config).unwrap();
|
||||
assert!(client.has_api_key());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_url_normalization() {
|
||||
let client = Client::from_url("https://example.com/").unwrap();
|
||||
assert_eq!(client.base_url(), "https://example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_url_error() {
|
||||
let result = Client::from_url("");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user