use serde::{Deserialize, Serialize};
use super::error::{CedarError, CedarResult};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PolicyStoreResponse {
#[serde(default)]
pub policy: String,
#[serde(default)]
pub schema: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
#[derive(Debug, Clone)]
pub struct PolicyStoreClient {
http: reqwest::Client,
url: String,
token: Option<String>,
}
impl PolicyStoreClient {
pub fn new(url: impl Into<String>, token: Option<String>) -> Self {
Self {
http: reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.unwrap_or_default(),
url: url.into(),
token,
}
}
pub async fn fetch(&self) -> CedarResult<PolicyStoreResponse> {
let mut req = self.http.get(&self.url);
if let Some(ref token) = self.token {
req = req.bearer_auth(token);
}
let resp = req
.send()
.await
.map_err(|e| CedarError::Config(format!("Policy store fetch failed: {}", e)))?;
if !resp.status().is_success() {
return Err(CedarError::Config(format!(
"Policy store returned HTTP {}",
resp.status()
)));
}
resp.json::<PolicyStoreResponse>()
.await
.map_err(|e| CedarError::Config(format!("Policy store JSON parse failed: {}", e)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_policy_store_response_deserialize() {
let json = r#"{
"policy": "permit(principal, action, resource);",
"schema": "entity User;",
"version": "v1"
}"#;
let resp: PolicyStoreResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.policy, "permit(principal, action, resource);");
assert_eq!(resp.schema, "entity User;");
assert_eq!(resp.version, Some("v1".to_string()));
}
#[test]
fn test_policy_store_response_defaults() {
let json = r#"{}"#;
let resp: PolicyStoreResponse = serde_json::from_str(json).unwrap();
assert!(resp.policy.is_empty());
assert!(resp.schema.is_empty());
assert!(resp.version.is_none());
}
}