firefly_iii_rust/client.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
use crate::requests::*;
use crate::response::*;
use crate::error::Result;
use crate::http::Method;
use serde::de::DeserializeOwned;
/// The main entry point into this crate. Hold the access token
/// currently `base_url` needs to be post fixed with `/api` for
/// this library to actually work.
/// TODO: that should be fixed.
#[derive(Debug)]
pub struct Client {
base_url: String,
token: String,
client: ureq::Agent,
}
pub fn new(base_url: &str, token: &str) -> Client {
let client = ureq::Agent::new();
Client{base_url: base_url.into(), token: token.into(), client}
}
impl Client {
/// This is our generic calling function for the API, the error
/// handling is somewhat ass since it doesn't use https://docs.rs/ureq/latest/ureq/enum.Error.html#examples
/// because the error enum in this repository somehow caused an
/// inifinite loop with the transformations.
///
/// TODO: The error should properly parse the actually useful error
/// responses from the API.
pub fn call<R: Request>(&self, request: R) -> Result<R::Response> {
let url = format!("{}{}", self.base_url, request.endpoint());
let mut req = match R::METHOD {
Method::Head => self.client.head(&url),
Method::Get => self.client.get(&url),
Method::Post => self.client.post(&url),
Method::Put => self.client.put(&url),
Method::Delete => self.client.delete(&url),
};
req = req.set("Authorization", &format!("Bearer {}", self.token));
req = req.set("accept", "application/vnd.api+json");
req = req.set("Content-Type", "application/json");
for (k, v) in request.headers().iter() {
req = req.set(k,v);
}
let resp = match request.body() {
Some(body) => {
println!("{:#?}", body);
req.send_json(body)
},
None => req.call(),
};
match resp {
Ok(resp) => {
eprintln!("{}", url);
if resp.status() == 204 {
Ok(R::Response::default())
} else {
/*
let str_result = resp.into_string().unwrap();
println!("{:#?}", str_result);
Ok(R::Response::default())
*/
let result: R::Response = resp.into_json()
.expect("json conversion failed on us");
Ok(result)
}
},
Err(err) => {
if let Some(body) = request.body() {
eprintln!("Body: {:#?}", body);
}
Err(err.into())
},
}
}
/// Consumes all pages of the API in sequence and assembles a flat Vec of
/// all pages.
pub fn fetch_all<R, T>(&self, mut request: R) -> Result<Vec<T>>
where
R: Request<Response = PaginatedResponse<Vec<T>>> + Paginated + Clone,
T: DeserializeOwned,
{
let mut current_page = 1;
let mut all_items = Vec::new();
loop {
request.set_page(current_page);
let response = self.call(request.clone())?;
all_items.extend(response.data.into_iter());
if request.get_page() < request.max_page() {
current_page += 1;
} else {
break; // we're done
}
}
Ok(all_items)
}
}