diff options
author | bryan newbold <bnewbold@robocracy.org> | 2023-02-19 17:01:07 -0800 |
---|---|---|
committer | bryan newbold <bnewbold@robocracy.org> | 2023-02-19 17:19:39 -0800 |
commit | ec2bf0c54245cd84f492847d2a1e070919b14a53 (patch) | |
tree | dbeb5b28c8b7e06eb9ac192d14ea4fdec81bb1e7 /adenosine/src/xrpc.rs | |
parent | b8ba815b4cafdff48694d14c994e862738d342ef (diff) | |
download | adenosine-ec2bf0c54245cd84f492847d2a1e070919b14a53.tar.gz adenosine-ec2bf0c54245cd84f492847d2a1e070919b14a53.zip |
more refactoring of common code and APIs
Diffstat (limited to 'adenosine/src/xrpc.rs')
-rw-r--r-- | adenosine/src/xrpc.rs | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/adenosine/src/xrpc.rs b/adenosine/src/xrpc.rs new file mode 100644 index 0000000..382c7fb --- /dev/null +++ b/adenosine/src/xrpc.rs @@ -0,0 +1,186 @@ +use crate::identifiers::Nsid; +use anyhow::anyhow; +pub use anyhow::Result; +use reqwest::header; +use serde_json::Value; +use std::collections::HashMap; +use std::str::FromStr; +use std::time::Duration; + +static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum XrpcMethod { + Get, + Post, +} + +impl FromStr for XrpcMethod { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "get" => Ok(XrpcMethod::Get), + "post" => Ok(XrpcMethod::Post), + _ => Err(anyhow!("unknown method: {}", s)), + } + } +} + +pub struct XrpcClient { + http_client: reqwest::blocking::Client, + host: String, +} + +impl XrpcClient { + pub fn new(host: String, auth_token: Option<String>) -> Result<Self> { + let mut headers = header::HeaderMap::new(); + if let Some(token) = &auth_token { + let mut auth_value = header::HeaderValue::from_str(&format!("Bearer {}", token))?; + auth_value.set_sensitive(true); + headers.insert(header::AUTHORIZATION, auth_value); + }; + + let http_client = reqwest::blocking::Client::builder() + .default_headers(headers) + .user_agent(APP_USER_AGENT) + .timeout(Duration::from_secs(30)) + //.danger_accept_invalid_certs(true) + .build() + .expect("ERROR :: Could not build reqwest client"); + + Ok(XrpcClient { http_client, host }) + } + + pub fn get( + &self, + nsid: &Nsid, + params: Option<HashMap<String, String>>, + ) -> Result<Option<Value>> { + log::debug!("XRPC GET endpoint={} params={:?}", nsid, params); + let params: HashMap<String, String> = params.unwrap_or_default(); + let res = self + .http_client + .get(format!("{}/xrpc/{}", self.host, nsid)) + .query(¶ms) + .send()?; + // TODO: refactor this error handling stuff into single method + if res.status() == 400 { + let val: Value = res.json()?; + return Err(anyhow!( + "XRPC Bad Request (400): {}", + val["message"].as_str().unwrap_or("unknown") + )); + } else if res.status() == 500 { + let val: Value = res.json()?; + return Err(anyhow!( + "XRPC Internal Error (500): {}", + val["message"].as_str().unwrap_or("unknown") + )); + } + let res = res.error_for_status()?; + Ok(res.json()?) + } + + pub fn get_to_writer<W: std::io::Write>( + &self, + nsid: &Nsid, + params: Option<HashMap<String, String>>, + output: &mut W, + ) -> Result<u64> { + let params: HashMap<String, String> = params.unwrap_or_default(); + let res = self + .http_client + .get(format!("{}/xrpc/{}", self.host, nsid)) + .query(¶ms) + .send()?; + if res.status() == 400 { + let val: Value = res.json()?; + return Err(anyhow!( + "XRPC Bad Request (400): {}", + val["message"].as_str().unwrap_or("unknown") + )); + } else if res.status() == 500 { + let val: Value = res.json()?; + return Err(anyhow!( + "XRPC Internal Error (500): {}", + val["message"].as_str().unwrap_or("unknown") + )); + } + let mut res = res.error_for_status()?; + Ok(res.copy_to(output)?) + } + + pub fn post( + &self, + nsid: &Nsid, + params: Option<HashMap<String, String>>, + body: Option<Value>, + ) -> Result<Option<Value>> { + let params: HashMap<String, String> = params.unwrap_or_default(); + log::debug!( + "XRPC POST endpoint={} params={:?} body={:?}", + nsid, + params, + body + ); + let mut req = self + .http_client + .post(format!("{}/xrpc/{}", self.host, nsid)) + .query(¶ms); + req = if let Some(b) = body { + req.json(&b) + } else { + req + }; + let res = req.send()?; + if res.status() == 400 { + let val: Value = res.json()?; + return Err(anyhow!( + "XRPC Bad Request (400): {}", + val["message"].as_str().unwrap_or("unknown") + )); + } else if res.status() == 500 { + let val: Value = res.json()?; + return Err(anyhow!( + "XRPC Internal Error (500): {}", + val["message"].as_str().unwrap_or("unknown") + )); + } + let res = res.error_for_status()?; + if res.content_length() == Some(0) { + Ok(None) + } else { + Ok(res.json()?) + } + } + + pub fn post_cbor_from_reader<R: std::io::Read>( + &self, + nsid: &Nsid, + params: Option<HashMap<String, String>>, + input: &mut R, + ) -> Result<Option<Value>> { + let params: HashMap<String, String> = params.unwrap_or_default(); + let mut buf: Vec<u8> = Vec::new(); + input.read_to_end(&mut buf)?; + let res = self + .http_client + .post(format!("{}/xrpc/{}", self.host, nsid)) + .query(¶ms) + .header(reqwest::header::CONTENT_TYPE, "application/cbor") + .body(buf) + .send()?; + if res.status() == 400 { + let val: Value = res.json()?; + return Err(anyhow!( + "XRPC Bad Request: {}", + val["message"].as_str().unwrap_or("unknown") + )); + } + let res = res.error_for_status()?; + Ok(res.json()?) + } + + // reqwest::blocking::Body +} |