use {
crate::*,
serde::{
de::DeserializeOwned,
Deserialize,
Serialize,
},
std::collections::HashMap,
};
pub struct GraphqlClient {
requester: reqwest::Client,
url: String,
bearer_auth: Option<String>,
}
#[derive(Debug, Serialize)]
struct GraphqlRequest {
query: String,
}
#[derive(Debug, Deserialize)]
struct GraphqlResponse<D> {
data: Option<D>,
errors: Option<Vec<GraphqlError>>,
}
impl GraphqlClient {
pub fn new<S: Into<String>>(url: S) -> ByoResult<Self> {
let requester = reqwest::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 async fn raw<S: Into<String>>(
&self,
query: S,
) -> ByoResult<reqwest::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()
.await?
.error_for_status()?;
Ok(res)
}
pub async fn text<S: Into<String>>(
&self,
query: S,
) -> ByoResult<String> {
Ok(self.raw(query).await?.text().await?)
}
pub async fn get_data<S: Into<String>, Data: DeserializeOwned>(
&self,
query: S,
) -> ByoResult<Data> {
let res = self.raw(query).await?;
let response: GraphqlResponse<Data> = res.json().await?;
if let Some(errors) = response.errors {
Err(ByoError::Graphql(errors))
} else {
response.data.ok_or(ByoError::NoData)
}
}
pub async fn get_first_item<S: Into<String>, Item: DeserializeOwned>(
&self,
query: S,
) -> ByoResult<Item> {
let mut map: HashMap<String, Option<Item>> = self.get_data(query).await?;
let single = map.drain().next().and_then(|e| e.1);
single.ok_or(ByoError::NoData)
}
}