use domain::{DataSource, DataSourcePort, Value}; pub struct HttpJsonAdapter { client: reqwest::Client, } #[derive(Debug, thiserror::Error)] pub enum HttpJsonError { #[error("request: {0}")] Request(#[from] reqwest::Error), #[error("no url configured")] NoUrl, #[error("parse: {0}")] Parse(String), } impl Default for HttpJsonAdapter { fn default() -> Self { Self { client: reqwest::Client::new(), } } } impl HttpJsonAdapter { pub fn new() -> Self { Self::default() } } fn json_to_value(json: serde_json::Value) -> Value { match json { serde_json::Value::Null => Value::Null, serde_json::Value::Bool(b) => Value::Bool(b), serde_json::Value::Number(n) => Value::Number(n.as_f64().unwrap_or(0.0)), serde_json::Value::String(s) => Value::String(s), serde_json::Value::Array(arr) => Value::Array(arr.into_iter().map(json_to_value).collect()), serde_json::Value::Object(map) => Value::Object( map.into_iter() .map(|(k, v)| (k, json_to_value(v))) .collect(), ), } } impl DataSourcePort for HttpJsonAdapter { type Error = HttpJsonError; async fn poll(&self, source: &DataSource) -> Result { let domain::DataSourceConfig::External { ref url, ref headers, ref api_key, } = source.config else { return Err(HttpJsonError::NoUrl); }; let url = url.as_ref().ok_or(HttpJsonError::NoUrl)?; let mut req = self.client.get(url); for (key, val) in headers { req = req.header(key, val); } if let Some(api_key) = api_key { req = req.header("Authorization", format!("Bearer {api_key}")); } let resp = req.send().await.map_err(HttpJsonError::Request)?; let json: serde_json::Value = resp.json().await.map_err(HttpJsonError::Request)?; Ok(json_to_value(json)) } }