vantage_api_client/
api.rs1use indexmap::IndexMap;
2use vantage_core::error;
3use vantage_dataset::traits::Result;
4use vantage_types::Record;
5
6#[derive(Clone, Debug)]
14pub struct RestApi {
15 base_url: String,
16 client: reqwest::Client,
17 pub(crate) auth_header: Option<String>,
18}
19
20impl RestApi {
21 pub fn new(base_url: impl Into<String>) -> Self {
23 Self {
24 base_url: base_url.into(),
25 client: reqwest::Client::new(),
26 auth_header: None,
27 }
28 }
29
30 pub fn with_auth(mut self, auth: impl Into<String>) -> Self {
32 self.auth_header = Some(auth.into());
33 self
34 }
35
36 fn endpoint_url(&self, table_name: &str) -> String {
38 format!("{}/{}", self.base_url, table_name)
39 }
40
41 pub(crate) async fn fetch_records(
46 &self,
47 table_name: &str,
48 id_field: Option<&str>,
49 ) -> Result<IndexMap<String, Record<serde_json::Value>>> {
50 let url = self.endpoint_url(table_name);
51
52 let mut request = self.client.get(&url);
53 if let Some(ref auth) = self.auth_header {
54 request = request.header("Authorization", auth);
55 }
56
57 let response = request
58 .send()
59 .await
60 .map_err(|e| error!("API request failed", url = url, detail = e))?;
61
62 if !response.status().is_success() {
63 return Err(error!(
64 "API returned error status",
65 url = url,
66 status = response.status().as_u16()
67 ));
68 }
69
70 let body: serde_json::Value = response
71 .json()
72 .await
73 .map_err(|e| error!("Failed to parse API response as JSON", detail = e))?;
74
75 let data = body["data"]
76 .as_array()
77 .ok_or_else(|| error!("API response missing 'data' array", url = url))?;
78
79 let mut records = IndexMap::new();
80
81 for (row_idx, item) in data.iter().enumerate() {
82 let obj = item
83 .as_object()
84 .ok_or_else(|| error!("API data item is not an object", index = row_idx))?;
85
86 let id = id_field
88 .and_then(|field| obj.get(field))
89 .and_then(|v| match v {
90 serde_json::Value::String(s) => Some(s.clone()),
91 serde_json::Value::Number(n) => Some(n.to_string()),
92 _ => None,
93 })
94 .unwrap_or_else(|| row_idx.to_string());
95
96 let record: Record<serde_json::Value> =
97 obj.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
98
99 records.insert(id, record);
100 }
101
102 Ok(records)
103 }
104}