pub mod query_builder;
use reqwest;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::from_str;
use snafu::ResultExt;
use std::convert::From;
use crate::error::{self, Error, Result};
use crate::url;
use crate::QueryBuilder;
#[derive(Deserialize, Debug)]
struct MetaRaw {
#[serde(rename = "_id")]
id: String,
#[serde(rename = "_createdOn")]
created_on: String,
#[serde(rename = "_updatedOn", default)]
updated_on: Option<String>,
}
#[derive(Debug)]
pub struct Meta {
pub id: String,
pub created_on: String,
pub updated_on: String,
}
impl From<MetaRaw> for Meta {
fn from(meta: MetaRaw) -> Self {
let updated_on = match meta.updated_on {
Some(date) => date,
None => meta.created_on.clone(),
};
Meta {
id: meta.id.clone(),
created_on: meta.created_on.clone(),
updated_on,
}
}
}
#[derive(Deserialize, Debug)]
pub struct ErrorMessage {
pub message: String,
}
pub struct Client<'a> {
base_url: &'a str,
box_id: &'a str,
}
impl<'a> Client<'a> {
pub fn new(box_id: &'a str) -> Client<'a> {
Client {
base_url: url::BASE_URL,
box_id,
}
}
pub fn with_base_url(mut self, base_url: &'a str) -> Client<'a> {
self.base_url = base_url;
self
}
pub fn create<T>(&self, data: &T) -> Result<(T, Meta)>
where
T: Serialize + DeserializeOwned,
{
let client = reqwest::Client::new();
let mut res = client
.post(&url::of_box(&self.base_url, &self.box_id))
.json(&data)
.send()
.context(error::Network {})?;
if res.status().is_success() {
let raw = res.text().context(error::Network {})?;
let data: T = from_str(&raw).context(error::Json { reason: "data" })?;
let meta: MetaRaw = from_str(&raw).context(error::Json { reason: "meta" })?;
Ok((data, Meta::from(meta)))
} else {
let err: ErrorMessage = res.json().context(error::Network {})?;
Err(Error::General {
code: res.status().as_u16(),
message: err.message,
})
}
}
pub fn create_bulk<T>(&self, data: &Vec<T>) -> Result<Vec<(T, Meta)>>
where
T: Serialize + DeserializeOwned,
{
let client = reqwest::Client::new();
let mut res = client
.post(&url::of_box(&self.base_url, &self.box_id))
.json(&data)
.send()
.context(error::Network {})?;
if res.status().is_success() {
let raw = res.text().context(error::Network {})?;
let data: Vec<T> = from_str(&raw).context(error::Json { reason: "data" })?;
let meta: Vec<MetaRaw> = from_str(&raw).context(error::Json { reason: "meta" })?;
Ok(data
.into_iter()
.zip(meta.into_iter().map(|meta| Meta::from(meta)))
.collect())
} else {
let err: ErrorMessage = res.json().context(error::Network {})?;
Err(Error::General {
code: res.status().as_u16(),
message: err.message,
})
}
}
pub fn read(&self) -> QueryBuilder {
QueryBuilder::new(self)
}
fn read_by_id<T>(&self, id: &str) -> Result<(T, Meta)>
where
T: DeserializeOwned,
{
let url = url::of_record(&self.base_url, &self.box_id, id);
let mut res = reqwest::get(&url).context(error::Network {})?;
if res.status().is_success() {
let raw = res.text().context(error::Network {})?;
let data: T = from_str(&raw).context(error::Json { reason: "data" })?;
let meta: MetaRaw = from_str(&raw).context(error::Json { reason: "meta" })?;
Ok((data, Meta::from(meta)))
} else {
let err: ErrorMessage = res.json().context(error::Network {})?;
Err(Error::General {
code: res.status().as_u16(),
message: err.message,
})
}
}
fn read_by_query<T>(&self, query: &QueryBuilder) -> Result<Vec<(T, Meta)>>
where
T: DeserializeOwned,
{
let url = &url::of_query(&self.base_url, &self.box_id, &query.to_string());
let mut res = reqwest::get(url).context(error::Network {})?;
if res.status().is_success() {
let raw = res.text().context(error::Network {})?;
let data: Vec<T> = from_str(&raw).context(error::Json { reason: "data" })?;
let meta: Vec<MetaRaw> = from_str(&raw).context(error::Json { reason: "meta" })?;
Ok(data
.into_iter()
.zip(meta.into_iter().map(|meta| Meta::from(meta)))
.collect())
} else {
let err: ErrorMessage = res.json().context(error::Network {})?;
Err(Error::General {
code: res.status().as_u16(),
message: err.message,
})
}
}
pub fn update<T>(&self, id: &str, data: &T) -> Result<()>
where
T: Serialize,
{
let client = reqwest::Client::new();
let mut res = client
.put(&url::of_record(&self.base_url, &self.box_id, id))
.json(&data)
.send()
.context(error::Network {})?;
if res.status().is_success() {
Ok(())
} else {
let err: ErrorMessage = res.json().context(error::Network {})?;
Err(error::Error::General {
code: res.status().as_u16(),
message: err.message,
})
}
}
pub fn delete(&self, id: &str) -> Result<()> {
let client = reqwest::Client::new();
let mut res = client
.delete(&url::of_record(&self.base_url, &self.box_id, id))
.send()
.context(error::Network {})?;
if res.status().is_success() {
Ok(())
} else {
let err: ErrorMessage = res.json().context(error::Network {})?;
Err(Error::General {
code: res.status().as_u16(),
message: err.message,
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let client = Client::new("01234012340123401234");
assert_eq!(client.base_url, "https://jsonbox.io");
assert_eq!(client.box_id, "01234012340123401234");
}
#[test]
fn test_with_base_url() {
let client = Client::new("01234012340123401234").with_base_url("https://blog.endflow.net");
assert_eq!(client.base_url, "https://blog.endflow.net");
assert_eq!(client.box_id, "01234012340123401234");
}
}