nodesty_api_library/
client.rs1use crate::models::RestClientOptions;
2use crate::ApiResponse;
3use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
4use reqwest::{Client, ClientBuilder, Error, Method};
5use serde::de::DeserializeOwned;
6use serde_json;
7use serde_json::Value;
8use std::sync::Arc;
9use std::time::Duration;
10
11pub struct NodestyApiClient {
12 client: Client,
13 base_url: String,
14}
15
16impl NodestyApiClient {
17 pub fn new(options: RestClientOptions) -> Result<Arc<Self>, Error> {
18 let auth_header = HeaderValue::from_str(&format!("PAT {}", options.access_token))
19 .expect("Invalid authorization token");
20
21 let timeout = Duration::from_millis(options.timeout_ms.unwrap_or(30_000));
22
23 let mut headers = HeaderMap::new();
24 headers.insert(AUTHORIZATION, auth_header);
25
26 let client = ClientBuilder::new()
27 .default_headers(headers)
28 .timeout(timeout)
29 .connect_timeout(timeout)
30 .build()?;
31
32 Ok(Arc::new(Self {
33 client,
34 base_url: options.base_url.clone(),
35 }))
36 }
37
38 pub async fn send_request<T: DeserializeOwned>(
39 &self,
40 method: Method,
41 path: &str,
42 body: Option<Value>,
43 ) -> Result<ApiResponse<T>, Error> {
44 let url = format!("{}{}", self.base_url, path);
45 let mut request = self.client.request(method, &url);
46
47 if let Some(body) = body {
48 request = request.json(&body);
49 }
50
51 let response = request.send().await?;
52 let status = response.status();
53 let raw_data: Value = response.json().await?;
54
55 println!("Ham API Yanıtı ({} {}): {:?}", status, path, raw_data);
56
57 let final_api_response: ApiResponse<T> = if status.is_success() {
58 match serde_json::from_value::<T>(raw_data.clone()) {
59 Ok(data) => {
60 ApiResponse {
61 success: true,
62 error: None,
63 data: Some(data),
64 }
65 },
66 Err(direct_deserialize_err) => {
67 match serde_json::from_value::<ApiResponse<T>>(raw_data.clone()) {
68 Ok(api_resp) => {
69 api_resp
70 },
71 Err(api_resp_deserialize_err) => {
72 ApiResponse {
73 success: false,
74 error: Some(format!(
75 "JSON ayrıştırma hatası (HTTP {}): Beklenen 'ApiResponse' veya direkt veri formatında değil. \
76 Yanıt: {:?} - Doğrudan T ayrıştırma hatası: {} - ApiResponse<T> ayrıştırma hatası: {}",
77 status.as_u16(), raw_data, direct_deserialize_err, api_resp_deserialize_err
78 )),
79 data: None,
80 }
81 }
82 }
83 }
84 }
85 } else {
86 match serde_json::from_value::<ApiResponse<T>>(raw_data.clone()) {
87 Ok(mut api_resp) => {
88 if api_resp.success {
89 api_resp.success = false;
90 api_resp.error = api_resp.error.or_else(|| {
91 raw_data["message"].as_str().map(|s| s.to_string())
92 }).or_else(|| {
93 Some(format!("API hatası (HTTP {}): API yanıtında 'success' true ancak HTTP durum kodu hata. Yanıt: {:?}", status.as_u16(), raw_data))
94 });
95 api_resp.data = None;
96 }
97 api_resp
98 },
99 Err(e) => {
100 let error_message = raw_data["error"].as_str()
101 .or(raw_data["message"].as_str())
102 .map(|s| s.to_string())
103 .unwrap_or_else(|| {
104 format!("API hatası (HTTP {}): JSON formatı beklenenden farklı veya ayrıştırma hatası. Yanıt: {:?} - Hata: {}",
105 status.as_u16(), raw_data, e)
106 });
107 ApiResponse {
108 success: false,
109 error: Some(error_message),
110 data: None,
111 }
112 }
113 }
114 };
115
116 Ok(final_api_response)
117 }
118}