oramacore_client/
client.rs1use std::collections::HashMap;
4use std::sync::Arc;
5
6use reqwest::{Client as ReqwestClient, Method, Response};
7use serde::de::DeserializeOwned;
8use serde::Serialize;
9use url::Url;
10
11use crate::auth::{Auth, Target};
12use crate::error::{OramaError, Result};
13
14#[derive(Debug, Clone, PartialEq)]
16pub enum ApiKeyPosition {
17 Header,
18 QueryParams,
19}
20
21#[derive(Debug)]
23pub struct ClientRequest<T> {
24 pub target: Target,
25 pub method: Method,
26 pub path: String,
27 pub api_key_position: ApiKeyPosition,
28 pub body: Option<T>,
29 pub params: Option<HashMap<String, String>>,
30}
31
32impl<T> ClientRequest<T> {
33 pub fn get(path: String, target: Target, api_key_position: ApiKeyPosition) -> Self {
35 Self {
36 target,
37 method: Method::GET,
38 path,
39 api_key_position,
40 body: None,
41 params: None,
42 }
43 }
44
45 pub fn post(path: String, target: Target, api_key_position: ApiKeyPosition, body: T) -> Self {
47 Self {
48 target,
49 method: Method::POST,
50 path,
51 api_key_position,
52 body: Some(body),
53 params: None,
54 }
55 }
56
57 pub fn with_params(mut self, params: HashMap<String, String>) -> Self {
59 self.params = Some(params);
60 self
61 }
62
63 pub fn with_param<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
65 let mut params = self.params.unwrap_or_default();
66 params.insert(key.into(), value.into());
67 self.params = Some(params);
68 self
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct OramaClient {
75 client: Arc<ReqwestClient>,
76 auth: Auth,
77}
78
79impl OramaClient {
80 pub fn new(auth: Auth) -> Result<Self> {
82 let client = ReqwestClient::builder()
83 .user_agent("oramacore-client-rust/1.2.0")
84 .build()?;
85
86 Ok(Self {
87 client: Arc::new(client),
88 auth,
89 })
90 }
91
92 pub async fn request<T, R>(&self, req: ClientRequest<T>) -> Result<R>
94 where
95 T: Serialize,
96 R: DeserializeOwned,
97 {
98 let response = self.get_response(req).await?;
99
100 if !response.status().is_success() {
101 let status = response.status().as_u16();
102 let text = response.text().await.unwrap_or_default();
103
104 return Err(match status {
105 401 => OramaError::auth("Unauthorized: are you using the correct API Key?"),
106 400 => OramaError::api(status, format!("Bad Request: {text}")),
107 _ => OramaError::api(status, text),
108 });
109 }
110
111 let text = response.text().await?;
113 let result = crate::utils::safe_json_parse::<R>(&text)
114 .map_err(|e| OramaError::generic(format!("Failed to parse API response: {e}")))?;
115 Ok(result)
116 }
117
118 pub async fn get_response<T>(&self, req: ClientRequest<T>) -> Result<Response>
120 where
121 T: Serialize,
122 {
123 let auth_ref = self.auth.get_ref(req.target).await?;
124 let base_url = Url::parse(&auth_ref.base_url)?;
125 let url = base_url.join(&req.path)?;
126
127 let mut request_builder = self.client.request(req.method, url);
128
129 request_builder = request_builder.header("Content-Type", "application/json");
131
132 if req.api_key_position == ApiKeyPosition::Header {
133 request_builder =
134 request_builder.header("Authorization", format!("Bearer {}", auth_ref.bearer));
135 }
136
137 let mut query_params = req.params.unwrap_or_default();
139 if req.api_key_position == ApiKeyPosition::QueryParams {
140 query_params.insert("api-key".to_string(), auth_ref.bearer);
141 }
142
143 if !query_params.is_empty() {
144 request_builder = request_builder.query(&query_params);
145 }
146
147 if let Some(body) = req.body {
149 request_builder = request_builder.json(&body);
150 }
151
152 let response = request_builder.send().await?;
153 Ok(response)
154 }
155
156 pub fn inner(&self) -> &ReqwestClient {
158 &self.client
159 }
160
161 pub async fn get_auth_ref(&self, target: Target) -> Result<crate::auth::AuthRef> {
163 self.auth.get_ref(target).await
164 }
165}