mod handlers;
pub mod from_request;
extern crate iron;
extern crate router;
extern crate bodyparser;
extern crate serde;
extern crate serde_json;
extern crate persistent;
use self::handlers::*;
use self::iron::mime::Mime;
use self::iron::prelude::*;
use self::iron::status;
use self::persistent::Read;
use FromRequest;
use error::JsonApiErrorArray;
use errors::FromRequestError;
use errors::QueryStringParseError;
use errors::RequestError;
use iron::handlers::BodyParserError;
use iron::router::Router;
use params::JsonApiResource;
use serde::Serialize;
use serde::de::Deserialize;
use service::*;
use service::JsonPatch;
use sort_order::SortOrder;
use status::Status;
use std::error::Error;
use std::fmt::Debug;
use std::str::FromStr;
use to_json::ToJson;
use try_from::TryFrom;
fn json_api_type() -> Mime {
"application/vnd.api+json".parse().unwrap()
}
#[derive(Debug)]
struct RequestResult<T, E, I>(Result<T, RequestError<E, I>>, Status)
where T: Serialize,
E: Send + Error,
I: FromStr + Debug,
<I as FromStr>::Err: Error;
impl<T, E, I> TryFrom<RequestResult<T, E, I>> for Response
where T: Serialize,
E: Send + Error + 'static,
I: FromStr + Debug,
<I as FromStr>::Err: Error
{
type Error = IronError;
fn try_from(request: RequestResult<T, E, I>) -> IronResult<Response> {
let result = request.0;
match result {
Ok(json) => {
match serde_json::to_string(&json) {
Ok(serialized) => {
let status = request.1;
match status {
Status::NoContent => Ok(Response::with((json_api_type(), status))),
_ => Ok(Response::with((json_api_type(), status, serialized))),
}
}
Err(e) => Err(IronError::new(e, Status::InternalServerError)),
}
}
Err(err) => err.into(),
}
}
}
impl<E, I> From<RequestError<E, I>> for IronResult<Response>
where E: Send + Error,
I: FromStr + Debug,
<I as FromStr>::Err: Error
{
fn from(err: RequestError<E, I>) -> IronResult<Response> {
let status = err.status();
let json = JsonApiErrorArray::new(&err, status);
match serde_json::to_string(&json) {
Ok(serialized) => Ok(Response::with((json_api_type(), status, serialized))),
Err(e) => Err(IronError::new(e, Status::InternalServerError)),
}
}
}
impl<T> From<FromRequestError<T>> for IronResult<Response>
where T: Error + Send
{
fn from(err: FromRequestError<T>) -> IronResult<Response> {
let status = Status::InternalServerError;
let json = JsonApiErrorArray::new(&err, status);
match serde_json::to_string(&json) {
Ok(serialized) => Ok(Response::with((json_api_type(), status, serialized))),
Err(e) => Err(IronError::new(e, status)),
}
}
}
impl From<BodyParserError> for IronResult<Response> {
fn from(err: BodyParserError) -> IronResult<Response> {
let status = Status::BadRequest;
let json = JsonApiErrorArray::new(&err, status);
match serde_json::to_string(&json) {
Ok(serialized) => Ok(Response::with((json_api_type(), status, serialized))),
Err(e) => Err(IronError::new(e, status)),
}
}
}
pub fn id<'a>(req: &'a Request) -> &'a str {
let router = req.extensions
.get::<Router>()
.expect("Expected to get a Router from the request extensions.");
router
.find("id")
.expect("No id param found in method that expects one!")
}
#[allow(missing_debug_implementations)] pub struct JsonApiRouterBuilder {
router: Router,
max_body_length: usize,
}
impl Default for JsonApiRouterBuilder {
fn default() -> Self {
Self::new(Router::new(), 10 * 1024 * 1024)
}
}
impl JsonApiRouterBuilder {
fn new(router: Router, max_body_length: usize) -> Self {
JsonApiRouterBuilder {
router: router,
max_body_length: max_body_length,
}
}
pub fn set_max_body_length(&mut self, max_body_length: usize) {
self.max_body_length = max_body_length;
}
pub fn jsonapi_index<'a, T>(&mut self)
where Status: for<'b> From<&'b T::Error>,
T: JsonIndex + JsonApiResource + ToJson + for<'b> IndexHandler<'b, T>,
T::Error: Send + 'static,
T::JsonApiIdType: FromStr,
T::SortField: for<'b> TryFrom<(&'b str, SortOrder), Error = QueryStringParseError>,
T::FilterField: for<'b> TryFrom<(&'b str, Vec<&'b str>),
Error = QueryStringParseError>,
<T::JsonApiIdType as FromStr>::Err: Send + Error + 'static,
<<T as JsonIndex>::Context as FromRequest>::Error: 'static
{
self.router
.get(format!("/{}", T::resource_name()),
move |r: &mut Request| T::get(r),
format!("index_{}", T::resource_name()));
}
pub fn jsonapi_get<'a, T>(&mut self)
where Status: for<'b> From<&'b T::Error>,
T: JsonGet + JsonApiResource + ToJson + for<'b> GetHandler<'b, T>,
T::Error: Send + 'static,
T::JsonApiIdType: FromStr,
T::SortField: for<'b> TryFrom<(&'b str, SortOrder), Error = QueryStringParseError>,
T::FilterField: for<'b> TryFrom<(&'b str, Vec<&'b str>),
Error = QueryStringParseError>,
<T::JsonApiIdType as FromStr>::Err: Send + Error + 'static,
<<T as JsonGet>::Context as FromRequest>::Error: 'static
{
self.router
.get(format!("/{}/:id", T::resource_name()),
move |r: &mut Request| T::get(r),
format!("get_{}", T::resource_name()));
}
pub fn jsonapi_delete<'a, T>(&mut self)
where Status: for<'b> From<&'b T::Error>,
T: JsonDelete + JsonApiResource + ToJson + for<'b> DeleteHandler<'b, T>,
T::Error: Send + 'static,
T::JsonApiIdType: FromStr,
<T::Context as FromRequest>::Error: 'static,
<T::JsonApiIdType as FromStr>::Err: Send + Error + 'static
{
self.router
.delete(format!("/{}/:id", T::resource_name()),
move |r: &mut Request| <T as DeleteHandler<T>>::delete(r),
format!("delete_{}", T::resource_name()));
}
pub fn jsonapi_post<'a, T>(&mut self)
where Status: for<'b> From<&'b T::Error>,
T: JsonPost + JsonApiResource + ToJson + for<'b> PostHandler<'b, T>,
T::Error: Send + 'static,
T::JsonApiIdType: FromStr,
T::Attrs: 'static + for<'b> Deserialize<'b>,
T::SortField: for<'b> TryFrom<(&'b str, SortOrder), Error = QueryStringParseError>,
T::FilterField: for<'b> TryFrom<(&'b str, Vec<&'b str>),
Error = QueryStringParseError>,
<T::Context as FromRequest>::Error: 'static,
<T::JsonApiIdType as FromStr>::Err: Send + Error + 'static
{
self.router
.post(format!("/{}", T::resource_name()),
move |r: &mut Request| T::post(r),
format!("create_{}", T::resource_name()));
}
pub fn jsonapi_patch<'a, T>(&mut self)
where Status: for<'b> From<&'b T::Error>,
T: JsonPatch + JsonApiResource + ToJson + for<'b> PatchHandler<'b, T>,
T::Error: Send + 'static,
T::JsonApiIdType: FromStr,
T::Attrs: 'static + for<'b> Deserialize<'b>,
T::SortField: for<'b> TryFrom<(&'b str, SortOrder), Error = QueryStringParseError>,
T::FilterField: for<'b> TryFrom<(&'b str, Vec<&'b str>),
Error = QueryStringParseError>,
<T::Context as FromRequest>::Error: 'static,
<T::JsonApiIdType as FromStr>::Err: Send + Error + 'static
{
self.router
.patch(format!("/{}/:id", T::resource_name()),
move |r: &mut Request| T::patch(r),
format!("update_{}", T::resource_name()));
}
pub fn build(self) -> Chain {
let mut chain = iron::Chain::new(self.router);
chain.link_before(Read::<bodyparser::MaxBodyLength>::one(self.max_body_length));
chain
}
}
#[cfg(test)]
mod tests {
extern crate iron_test;
use self::iron_test::response;
use super::*;
use super::iron::headers::ContentType;
use error::JsonApiError;
use std::string::ParseError;
use try_from::TryInto;
#[derive(Serialize, Deserialize)]
struct Test {
foo: String,
}
#[test]
fn test_200_ok_response() {
let test = Test { foo: "bar".to_string() };
let req: RequestResult<Test, ParseError, String> = RequestResult(Ok(test), Status::Ok);
let resp: IronResult<Response> = req.try_into();
let result = resp.expect("Invalid response!");
let headers = result.headers.clone();
let content_type = headers
.get::<ContentType>()
.expect("no content type found!");
assert_eq!("application/vnd.api+json", content_type.to_string());
let json = response::extract_body_to_string(result);
assert_eq!("{\"foo\":\"bar\"}", json);
}
#[test]
fn test_201_created() {
let test = Test { foo: "bar".to_string() };
let req: RequestResult<Test, ParseError, String> = RequestResult(Ok(test),
Status::NoContent);
let resp: IronResult<Response> = req.try_into();
let result = resp.expect("Invalid response!");
let headers = result.headers.clone();
let content_type = headers
.get::<ContentType>()
.expect("no content type found!");
assert_eq!("application/vnd.api+json", content_type.to_string());
let json = response::extract_body_to_string(result);
assert_eq!("", json);
}
#[test]
fn test_204_no_content() {
let test = Test { foo: "bar".to_string() };
let req: RequestResult<Test, ParseError, String> = RequestResult(Ok(test),
Status::NoContent);
let resp: IronResult<Response> = req.try_into();
let result = resp.expect("Invalid response!");
let headers = result.headers.clone();
let content_type = headers
.get::<ContentType>()
.expect("no content type found!");
assert_eq!("application/vnd.api+json", content_type.to_string());
let json = response::extract_body_to_string(result);
assert_eq!("", json);
}
#[test]
fn test_error_json() {
let req: RequestResult<Test, RequestError<ParseError, String>, String> =
RequestResult(Err(RequestError::NoBody), Status::NoContent);
let resp: IronResult<Response> = req.try_into();
let result = resp.expect("Invalid response!");
let headers = result.headers.clone();
let content_type = headers
.get::<ContentType>()
.expect("no content type found!");
assert_eq!("application/vnd.api+json", content_type.to_string());
let json = response::extract_body_to_string(result);
let expected = JsonApiErrorArray {
errors: vec![JsonApiError {
detail: "No body".to_string(),
status: "400".to_string(),
title: "No body".to_string(),
}],
};
let record: JsonApiErrorArray = serde_json::from_str(&json).unwrap();
assert_eq!(expected, record);
}
}