use {
anyhow::*,
serde::{ de::DeserializeOwned, Deserialize, Serialize },
std:: collections::HashMap,
};
pub struct GraphqlClient {
requester: reqwest::blocking::Client,
url: String,
bearer_auth: Option<String>,
}
#[derive(Debug, Serialize)]
struct GraphqlRequest {
query: String,
}
#[derive(Debug, Deserialize)]
struct GraphqlResponse<D> {
data: D,
}
impl GraphqlClient {
pub fn new<S: Into<String>>(url: S) -> Result<Self> {
let requester = reqwest::blocking::Client::builder()
.user_agent("byo/0.1")
.build()?;
Ok(Self {
requester,
url: url.into(),
bearer_auth: None,
})
}
pub fn set_bearer_auth<S: Into<String>>(&mut self, auth: S) {
self.bearer_auth = Some(auth.into());
}
pub fn raw<S: Into<String>>(&self, query: S) -> Result<reqwest::blocking::Response> {
let mut builder = self.requester.post(&self.url);
if let Some(auth) = &self.bearer_auth {
builder = builder.bearer_auth(auth);
}
let res = builder
.json(&GraphqlRequest{ query: query.into() })
.send()?
.error_for_status()?;
Ok(res)
}
pub fn text<S: Into<String>>(&self, query: S) -> Result<String> {
Ok(self.raw(query)?.text()?)
}
pub fn get_data<S: Into<String>, Data: DeserializeOwned>(&self, query: S) -> Result<Data> {
let res = self.raw(query)?;
let response: GraphqlResponse<Data> = res.json()?;
Ok(response.data)
}
pub fn get_first_item<S: Into<String>, Item: DeserializeOwned>(&self, query: S) -> Result<Item> {
let mut map: HashMap<String, Item> = self.get_data(query)?;
let single = map.drain()
.next()
.map(|e| e.1)
.ok_or_else(|| format_err!("empty data in server response"));
single
}
}