use std::fmt;
use anyhow::Result;
use reqwest::Url;
use crate::{
fields::{
blog::BlogsEndpoint, book::BooksEndpoint, country::CountriesEndpoint,
disaster::DisastersEndpoint, job::JobsEndpoint, report::ReportsEndpoint,
source::SourcesEndpoint, training::TrainingsEndpoint,
},
params::QueryParams,
};
pub const RELIEFWEB_DOMAIN: &str = "api.reliefweb.int";
pub struct Client {
pub(crate) api_base: Url,
pub(crate) client: reqwest::Client,
pub(crate) app_name: String,
}
pub enum APIVersion {
V1,
V2,
}
impl fmt::Display for APIVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
APIVersion::V1 => write!(f, "v1"),
APIVersion::V2 => write!(f, "v2"),
}
}
}
impl Client {
pub fn new(domain: &str, app_name: &str, version: APIVersion) -> Result<Client> {
let api_base = Url::parse(format!("https://{domain}/{version}/").as_str())?;
let client = reqwest::Client::new();
Ok(Client {
api_base,
client,
app_name: app_name.to_string(),
})
}
pub fn new_with_scheme(
scheme: &str,
domain: &str,
app_name: &str,
version: APIVersion,
) -> Result<Client> {
let api_base = Url::parse(&format!("{scheme}://{domain}/{version}/"))?;
let client = reqwest::Client::new();
Ok(Client {
api_base,
client,
app_name: app_name.to_string(),
})
}
pub fn reports(&'_ self) -> ReportsEndpoint<'_> {
ReportsEndpoint::new(self, "reports")
}
pub fn disasters(&'_ self) -> DisastersEndpoint<'_> {
DisastersEndpoint::new(self, "disasters")
}
pub fn countries(&'_ self) -> CountriesEndpoint<'_> {
CountriesEndpoint::new(self, "countries")
}
pub fn jobs(&'_ self) -> JobsEndpoint<'_> {
JobsEndpoint::new(self, "jobs")
}
pub fn training(&'_ self) -> TrainingsEndpoint<'_> {
TrainingsEndpoint::new(self, "training")
}
pub fn sources(&'_ self) -> SourcesEndpoint<'_> {
SourcesEndpoint::new(self, "sources")
}
pub fn blog(&'_ self) -> BlogsEndpoint<'_> {
BlogsEndpoint::new(self, "blog")
}
pub fn book(&'_ self) -> BooksEndpoint<'_> {
BooksEndpoint::new(self, "book")
}
pub(crate) fn get_with_params(
&self,
mut endpoint: Url,
params: Option<&QueryParams>,
) -> reqwest::RequestBuilder {
endpoint
.query_pairs_mut()
.append_pair("appname", &self.app_name);
if let Some(p) = params {
p.apply_to_url(&mut endpoint);
}
self.client.get(endpoint)
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use crate::params::QueryQuery;
use super::*;
#[test]
fn client_init() {
let app_name = "reliefweb_rust_tests";
let c1 = Client::new(RELIEFWEB_DOMAIN, app_name, APIVersion::V1).unwrap();
assert_eq!(
c1.api_base.as_str(),
format!("https://{RELIEFWEB_DOMAIN}/v1/")
);
let c2 = Client::new(RELIEFWEB_DOMAIN, app_name, APIVersion::V2).unwrap();
assert_eq!(
c2.api_base.as_str(),
format!("https://{RELIEFWEB_DOMAIN}/v2/")
);
assert_eq!(c2.app_name, app_name);
let result = Client::new("not a url", "app", APIVersion::V2);
assert!(result.is_err());
}
#[test]
fn get_with_params_none() {
let client = Client::new(RELIEFWEB_DOMAIN, "app", APIVersion::V2).unwrap();
let request = client
.get_with_params(
Url::parse(&format!("{}/reports", client.api_base)).unwrap(),
None,
)
.build()
.unwrap();
let mut pairs = request.url().query_pairs();
assert_eq!(
pairs.next(),
Some((Cow::Borrowed("appname"), Cow::Borrowed("app")))
);
assert_eq!(pairs.next(), None);
}
#[test]
fn get_with_params() {
let client = Client::new(RELIEFWEB_DOMAIN, "reliefweb_rust_tests", APIVersion::V2).unwrap();
let query = QueryQuery {
value: "bar".to_string(),
fields: vec!["foo".to_string()],
operator: Some(crate::params::FilterOperator::AND),
};
let params = QueryParams::new().query(query);
let request = client
.get_with_params(
Url::parse(format!("{}reports", client.api_base).as_str()).unwrap(),
Some(¶ms),
)
.build()
.unwrap();
let mut pairs = request.url().query_pairs();
assert_eq!(
pairs.next(),
Some((
Cow::Borrowed("appname"),
Cow::Borrowed("reliefweb_rust_tests")
))
);
assert_eq!(
pairs.next(),
Some((Cow::Borrowed("query[value]"), Cow::Borrowed("bar")))
);
assert_eq!(
pairs.next(),
Some((Cow::Borrowed("query[fields][0]"), Cow::Borrowed("foo")))
);
assert_eq!(
pairs.next(),
Some((Cow::Borrowed("query[operator]"), Cow::Borrowed("AND")))
);
assert_eq!(pairs.next(), None);
}
#[test]
fn get_with_params_encoding() {
let client = Client::new(RELIEFWEB_DOMAIN, "app", APIVersion::V2).unwrap();
let query = QueryQuery {
value: "foo bar".to_string(),
fields: vec!["field+name".to_string()],
operator: None,
};
let params = QueryParams::new().query(query);
let request = client
.get_with_params(
Url::parse(&format!("{}reports", client.api_base)).unwrap(),
Some(¶ms),
)
.build()
.unwrap();
let url = request.url().as_str();
assert!(url.contains("foo+bar")); assert!(url.contains("field%2Bname")); }
#[test]
fn client_endpoints() {
let client = Client::new(RELIEFWEB_DOMAIN, "app", APIVersion::V2).unwrap();
let reports = client.reports();
let disasters = client.disasters();
let countries = client.countries();
let jobs = client.jobs();
let training = client.training();
let sources = client.sources();
let blog = client.blog();
let book = client.book();
assert_eq!(reports.resource(), "reports");
assert_eq!(disasters.resource(), "disasters");
assert_eq!(countries.resource(), "countries");
assert_eq!(jobs.resource(), "jobs");
assert_eq!(training.resource(), "training");
assert_eq!(sources.resource(), "sources");
assert_eq!(blog.resource(), "blog");
assert_eq!(book.resource(), "book");
}
}