Skip to content
This repository has been archived by the owner on Jan 18, 2020. It is now read-only.

reform the error representation based on failure #214

Merged
merged 1 commit into from
Jan 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion examples/logging/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,11 @@ mod logging {
//
let response = result
.map(|response| response.map(Into::into))
.unwrap_or_else(|e| e.into_response(input.request).map(Into::into));
.unwrap_or_else(|e| {
e.into_response(input.request)
.expect("never fails")
.map(Into::into)
});

let log_level = match response.status().as_u16() {
400...599 => log::Level::Error,
Expand Down
2 changes: 1 addition & 1 deletion examples/template-tera/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ mod support_tera {
.expect("should be a valid response")
.into()
})
.map_err(tsukuyomi::error::internal_server_error)
.map_err(|e| tsukuyomi::error::internal_server_error(e.to_string()))
}
}
}
9 changes: 2 additions & 7 deletions tsukuyomi-cors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,13 +579,8 @@ impl From<CORSErrorKind> for CORSError {
}

impl HttpError for CORSError {
type Body = String;

fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
Response::builder()
.status(StatusCode::FORBIDDEN)
.body(self.to_string())
.expect("should be a valid response")
fn status_code(&self) -> StatusCode {
StatusCode::FORBIDDEN
}
}

Expand Down
1 change: 1 addition & 0 deletions tsukuyomi-juniper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ izanami-util = "0.1.0-preview.1"
juniper = "0.11.1"

bytes = "0.4"
failure = "0.1.5"
futures = "0.1"
http = "0.1"
mime = "0.3"
Expand Down
143 changes: 54 additions & 89 deletions tsukuyomi-juniper/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,91 +1,37 @@
use {
http::{Request, Response, StatusCode},
http::{Response, StatusCode},
serde_json::json,
std::fmt,
tsukuyomi::{
error::{Error, HttpError}, //
future::{Poll, TryFuture},
future::{Async, Poll, TryFuture},
handler::{AllowedMethods, Handler, ModifyHandler},
input::Input,
output::ResponseBody,
output::IntoResponse,
util::Either,
},
};

#[derive(Debug)]
#[derive(Debug, failure::Fail)]
pub enum GraphQLParseError {
#[fail(display = "the request method is invalid")]
InvalidRequestMethod,
#[fail(display = "missing query")]
MissingQuery,
#[fail(display = "missing content-type")]
MissingMime,
#[fail(display = "the content type is invalid.")]
InvalidMime,
ParseJson(serde_json::Error),
ParseQuery(serde_urlencoded::de::Error),
DecodeUtf8(std::str::Utf8Error),
}

impl fmt::Display for GraphQLParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GraphQLParseError::InvalidRequestMethod => f.write_str("the request method is invalid"),
GraphQLParseError::MissingQuery => f.write_str("missing query"),
GraphQLParseError::MissingMime => f.write_str("missing content-type"),
GraphQLParseError::InvalidMime => f.write_str("the content type is invalid."),
GraphQLParseError::ParseJson(ref e) => e.fmt(f),
GraphQLParseError::ParseQuery(ref e) => e.fmt(f),
GraphQLParseError::DecodeUtf8(ref e) => e.fmt(f),
}
}
#[fail(display = "failed to parse input as a JSON object")]
ParseJson(#[fail(cause)] serde_json::Error),
#[fail(display = "failed to parse HTTP query")]
ParseQuery(#[fail(cause)] serde_urlencoded::de::Error),
#[fail(display = "failed to decode input as a UTF-8 sequence")]
DecodeUtf8(#[fail(cause)] std::str::Utf8Error),
}

impl HttpError for GraphQLParseError {
type Body = String;

fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
let body = json!({
"errors": [
{
"message": self.to_string(),
}
],
})
.to_string();
Response::builder()
.status(StatusCode::BAD_REQUEST)
.header("content-type", "application/json")
.body(body)
.expect("should be a valid response")
}
}

#[derive(Debug)]
pub struct GraphQLError(Error);

impl fmt::Display for GraphQLError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

impl HttpError for GraphQLError {
type Body = ResponseBody;

fn into_response(self, request: &Request<()>) -> Response<Self::Body> {
let body = json!({
"errors": [
{
"message": self.to_string(),
}
],
})
.to_string();

let mut response = self.0.into_response(request).map(|_| body.into());

response.headers_mut().insert(
http::header::CONTENT_TYPE,
http::header::HeaderValue::from_static("application/json"),
);

response
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}

Expand All @@ -103,58 +49,77 @@ impl<H> ModifyHandler<H> for CaptureErrors
where
H: Handler,
{
type Output = H::Output;
type Handler = GraphQLHandler<H>; // private;
type Output = Either<Response<String>, H::Output>;
type Handler = CaptureErrorsHandler<H>; // private;

fn modify(&self, inner: H) -> Self::Handler {
GraphQLHandler { inner }
CaptureErrorsHandler { inner }
}
}

#[derive(Debug)]
pub struct GraphQLHandler<H> {
pub struct CaptureErrorsHandler<H> {
inner: H,
}

impl<H> Handler for GraphQLHandler<H>
impl<H> Handler for CaptureErrorsHandler<H>
where
H: Handler,
{
type Output = H::Output;
type Output = Either<Response<String>, H::Output>;
type Error = Error;
type Handle = GraphQLHandle<H::Handle>;
type Handle = CaptureErrorsHandle<H::Handle>;

fn allowed_methods(&self) -> Option<&AllowedMethods> {
self.inner.allowed_methods()
}

fn handle(&self) -> Self::Handle {
GraphQLHandle {
CaptureErrorsHandle {
inner: self.inner.handle(),
}
}
}

#[derive(Debug)]
pub struct GraphQLHandle<H> {
pub struct CaptureErrorsHandle<H> {
inner: H,
}

impl<H> TryFuture for GraphQLHandle<H>
impl<H> TryFuture for CaptureErrorsHandle<H>
where
H: TryFuture,
{
type Ok = H::Ok;
type Ok = Either<Response<String>, H::Ok>;
type Error = Error;

#[inline]
fn poll_ready(&mut self, input: &mut Input<'_>) -> Poll<Self::Ok, Self::Error> {
self.inner.poll_ready(input).map_err(|err| {
let err = err.into();
if err.is::<GraphQLParseError>() || err.is::<GraphQLError>() {
err
} else {
GraphQLError(err).into()
match self.inner.poll_ready(input) {
Ok(Async::Ready(ok)) => Ok(Async::Ready(Either::Right(ok))),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(err) => {
let err = err.into();
let body = json!({
"errors": [
{
"message": err.to_string(),
}
],
})
.to_string();

let mut response = err
.into_response(input.request)
.expect("never fails")
.map(|_| body);
response.headers_mut().insert(
http::header::CONTENT_TYPE,
http::header::HeaderValue::from_static("application/json"),
);

Ok(Async::Ready(Either::Left(response)))
}
})
}
}
}
11 changes: 3 additions & 8 deletions tsukuyomi-tungstenite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ mod imp {
SEC_WEBSOCKET_VERSION,
UPGRADE,
},
Request, Response, StatusCode,
Response, StatusCode,
},
izanami_util::rt::{DefaultExecutor, Executor},
sha1::{Digest, Sha1},
Expand Down Expand Up @@ -161,13 +161,8 @@ mod imp {
}

impl HttpError for HandshakeError {
type Body = String;

fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(self.to_string())
.expect("should be a valid response")
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}

Expand Down
4 changes: 2 additions & 2 deletions tsukuyomi/src/app/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use {
param::Params,
Cookies, Input,
},
output::ResponseBody,
output::{IntoResponse, ResponseBody},
util::Never,
},
cookie::CookieJar,
Expand Down Expand Up @@ -184,7 +184,7 @@ impl<C: Concurrency> Future for AppFuture<C> {

let mut output = match polled {
Ok(output) => output,
Err(err) => err.into_response(&self.request),
Err(err) => err.into_response(&self.request)?,
};

self.process_before_reply(&mut output);
Expand Down
Loading