Extract general HTTP/JSON functions from Iron integration
This commit is contained in:
parent
cac5dd17c3
commit
a7a6778b18
5 changed files with 237 additions and 271 deletions
64
src/graphiql.rs
Normal file
64
src/graphiql.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
pub fn graphiql_source(graphql_endpoint_url: &str) -> String {
|
||||
let stylesheet_source = r#"
|
||||
<style>
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
"#;
|
||||
let fetcher_source = r#"
|
||||
<script>
|
||||
function graphQLFetcher(params) {
|
||||
return fetch(GRAPHQL_URL, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
}).then(function (response) {
|
||||
return response.text();
|
||||
}).then(function (body) {
|
||||
try {
|
||||
return JSON.parse(body);
|
||||
} catch (error) {
|
||||
return body;
|
||||
}
|
||||
});
|
||||
}
|
||||
ReactDOM.render(
|
||||
React.createElement(GraphiQL, {
|
||||
fetcher: graphQLFetcher,
|
||||
}),
|
||||
document.querySelector('#app'));
|
||||
</script>
|
||||
"#;
|
||||
|
||||
format!(r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GraphQL</title>
|
||||
{stylesheet_source}
|
||||
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/graphiql/0.8.1/graphiql.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/fetch/2.0.1/fetch.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/graphiql/0.8.1/graphiql.js"></script>
|
||||
<script>var GRAPHQL_URL = '{graphql_url}';</script>
|
||||
{fetcher_source}
|
||||
</body>
|
||||
</html>
|
||||
"#,
|
||||
graphql_url = graphql_endpoint_url,
|
||||
stylesheet_source = stylesheet_source,
|
||||
fetcher_source = fetcher_source)
|
||||
|
||||
}
|
92
src/http.rs
Normal file
92
src/http.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
use serde::ser;
|
||||
use serde::ser::SerializeMap;
|
||||
|
||||
use ::{GraphQLError, Value, Variables, GraphQLType, RootNode};
|
||||
use ast::InputValue;
|
||||
use executor::ExecutionError;
|
||||
|
||||
/// The expected structure of the decoded JSON Document for either Post or Get requests.
|
||||
#[derive(Deserialize)]
|
||||
pub struct GraphQLRequest {
|
||||
query: String,
|
||||
#[serde(rename = "operationName")]
|
||||
operation_name: Option<String>,
|
||||
variables: Option<InputValue>
|
||||
}
|
||||
|
||||
impl GraphQLRequest {
|
||||
fn operation_name(&self) -> Option<&str> {
|
||||
self.operation_name.as_ref().map(|oper_name| &**oper_name)
|
||||
}
|
||||
|
||||
fn variables(&self) -> Variables {
|
||||
self.variables.as_ref().and_then(|iv| {
|
||||
iv.to_object_value().map(|o| {
|
||||
o.into_iter().map(|(k, v)| (k.to_owned(), v.clone())).collect()
|
||||
})
|
||||
}).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn new(query: String, operation_name: Option<String>, variables: Option<InputValue>) -> GraphQLRequest {
|
||||
GraphQLRequest {
|
||||
query: query,
|
||||
operation_name: operation_name,
|
||||
variables: variables,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute<'a, CtxT, QueryT, MutationT>(
|
||||
&'a self,
|
||||
root_node: &RootNode<QueryT, MutationT>,
|
||||
context: &CtxT,
|
||||
)
|
||||
-> GraphQLResponse<'a>
|
||||
where QueryT: GraphQLType<Context=CtxT>,
|
||||
MutationT: GraphQLType<Context=CtxT>,
|
||||
{
|
||||
GraphQLResponse(::execute(
|
||||
&self.query,
|
||||
self.operation_name(),
|
||||
root_node,
|
||||
&self.variables(),
|
||||
context,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct GraphQLResponse<'a>(Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>);
|
||||
|
||||
impl<'a> GraphQLResponse<'a> {
|
||||
pub fn is_ok(&self) -> bool {
|
||||
self.0.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::Serialize for GraphQLResponse<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer,
|
||||
{
|
||||
match self.0 {
|
||||
Ok((ref res, ref err)) => {
|
||||
let mut map = try!(serializer.serialize_map(None));
|
||||
|
||||
try!(map.serialize_key("data"));
|
||||
try!(map.serialize_value(res));
|
||||
|
||||
if !err.is_empty() {
|
||||
try!(map.serialize_key("errors"));
|
||||
try!(map.serialize_value(err));
|
||||
}
|
||||
|
||||
map.end()
|
||||
},
|
||||
Err(ref err) => {
|
||||
let mut map = try!(serializer.serialize_map(Some(1)));
|
||||
try!(map.serialize_key("errors"));
|
||||
try!(map.serialize_value(err));
|
||||
map.end()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,17 +7,14 @@ use iron::method;
|
|||
use urlencoded::{UrlEncodedQuery, UrlDecodingError};
|
||||
|
||||
use std::io::Read;
|
||||
use std::io::Error as IoError;
|
||||
use std::io::ErrorKind;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::boxed::Box;
|
||||
|
||||
use serde_json;
|
||||
use serde_json::error::Error as SerdeError;
|
||||
|
||||
use ::{InputValue, GraphQLType, RootNode, execute};
|
||||
use super::serde::{WrappedGraphQLResult, GraphQLQuery};
|
||||
use ::{InputValue, GraphQLType, RootNode};
|
||||
use ::http;
|
||||
|
||||
/// Handler that executes GraphQL queries in the given schema
|
||||
///
|
||||
|
@ -45,45 +42,29 @@ pub struct GraphiQLHandler {
|
|||
}
|
||||
|
||||
|
||||
/// Get queries are allowed to repeat the same key more than once.
|
||||
fn check_for_repeat_keys(params: &Vec<String>) -> Result<(), IronError> {
|
||||
if params.len() > 1 {
|
||||
let error = IronError::new(
|
||||
Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
|
||||
"Was able parse a query string \
|
||||
but a duplicate uri key was \
|
||||
found."))),
|
||||
(status::BadRequest, "Duplicate uri key was found."));
|
||||
Err(error)
|
||||
fn get_single_value<T>(mut values: Vec<T>) -> IronResult<T> {
|
||||
if values.len() == 1 {
|
||||
Ok(values.remove(0))
|
||||
}
|
||||
else {
|
||||
Ok(())
|
||||
Err(GraphQLIronError::InvalidData("Duplicate URL query parameter").into())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_url_param(param: Option<Vec<String>>) -> Result<Option<String>, IronError> {
|
||||
if let Some(values) = param {
|
||||
check_for_repeat_keys(&values)?;
|
||||
Ok(Some(values[0].to_owned()))
|
||||
fn parse_url_param(params: Option<Vec<String>>) -> IronResult<Option<String>> {
|
||||
if let Some(values) = params {
|
||||
get_single_value(values).map(Some)
|
||||
}
|
||||
else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_variable_param(param: Option<Vec<String>>) -> Result<Option<InputValue>, IronError> {
|
||||
if let Some(values) = param {
|
||||
check_for_repeat_keys(&values)?;
|
||||
match serde_json::from_str::<InputValue>(values[0].as_ref()) {
|
||||
Ok(input_values) => {
|
||||
Ok(Some(input_values))
|
||||
}
|
||||
Err(err) => {
|
||||
Err(IronError::new(
|
||||
Box::new(GraphQlIronError::Serde(err)),
|
||||
(status::BadRequest, "No JSON object was decoded.")))
|
||||
}
|
||||
}
|
||||
fn parse_variable_param(params: Option<Vec<String>>) -> IronResult<Option<InputValue>> {
|
||||
if let Some(values) = params {
|
||||
Ok(serde_json::from_str::<InputValue>(get_single_value(values)?.as_ref())
|
||||
.map(Some)
|
||||
.map_err(GraphQLIronError::Serde)?)
|
||||
}
|
||||
else {
|
||||
Ok(None)
|
||||
|
@ -112,62 +93,36 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
|
|||
}
|
||||
|
||||
|
||||
fn handle_get(&self, req: &mut Request) -> IronResult<GraphQLQuery> {
|
||||
match req.get_mut::<UrlEncodedQuery>() {
|
||||
Ok(ref mut query_string) => {
|
||||
let input_query = parse_url_param(query_string.remove("query").to_owned())?;
|
||||
if let Some(query) = input_query {
|
||||
let operation_name =
|
||||
parse_url_param(query_string.remove("operationName"))?;
|
||||
let input_variables =
|
||||
parse_variable_param(query_string.remove("variables"))?;
|
||||
Ok(GraphQLQuery::new(query,operation_name,input_variables))
|
||||
} else {
|
||||
Err(IronError::new(
|
||||
Box::new(GraphQlIronError::IO(IoError::new(ErrorKind::InvalidData,
|
||||
"No query key was found in \
|
||||
the Get request."))),
|
||||
(status::BadRequest, "No query was provided.")))
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
Err(IronError::new(
|
||||
Box::new(GraphQlIronError::Url(err)),
|
||||
(status::BadRequest, "No JSON object was decoded.")))
|
||||
}
|
||||
}
|
||||
fn handle_get(&self, req: &mut Request) -> IronResult<http::GraphQLRequest> {
|
||||
let url_query_string = req.get_mut::<UrlEncodedQuery>()
|
||||
.map_err(|e| GraphQLIronError::Url(e))?;
|
||||
|
||||
let input_query = parse_url_param(url_query_string.remove("query"))?
|
||||
.ok_or_else(|| GraphQLIronError::InvalidData("No query provided"))?;
|
||||
let operation_name = parse_url_param(url_query_string.remove("operationName"))?;
|
||||
let variables = parse_variable_param(url_query_string.remove("variables"))?;
|
||||
|
||||
Ok(http::GraphQLRequest::new(input_query, operation_name, variables))
|
||||
}
|
||||
|
||||
fn handle_post(&self, req: &mut Request) -> IronResult<GraphQLQuery> {
|
||||
fn handle_post(&self, req: &mut Request) -> IronResult<http::GraphQLRequest> {
|
||||
let mut request_payload = String::new();
|
||||
itry!(req.body.read_to_string(&mut request_payload));
|
||||
let graphql_query = serde_json::from_str::<GraphQLQuery>(request_payload.as_str()).map_err(|err|{
|
||||
IronError::new(
|
||||
Box::new(GraphQlIronError::Serde(err)),
|
||||
(status::BadRequest, "No JSON object was decoded."))
|
||||
});
|
||||
graphql_query
|
||||
|
||||
Ok(serde_json::from_str::<http::GraphQLRequest>(request_payload.as_str())
|
||||
.map_err(|err| GraphQLIronError::Serde(err))?)
|
||||
}
|
||||
|
||||
fn respond(&self, req: &mut Request, graphql: GraphQLQuery) -> IronResult<Response> {
|
||||
let context = (self.context_factory)(req);
|
||||
let variables = graphql.variables();
|
||||
let result = execute(graphql.query(),
|
||||
graphql.operation_name(),
|
||||
&self.root_node,
|
||||
&variables,
|
||||
&context);
|
||||
let content_type = "application/json".parse::<Mime>().unwrap();
|
||||
if result.is_ok() {
|
||||
let response = WrappedGraphQLResult::new(result);
|
||||
let json = serde_json::to_string_pretty(&response).unwrap();
|
||||
Ok(Response::with((content_type, status::Ok, json)))
|
||||
} else {
|
||||
let response = WrappedGraphQLResult::new(result);
|
||||
let json = serde_json::to_string_pretty(&response).unwrap();
|
||||
Ok(Response::with((content_type, status::BadRequest, json)))
|
||||
}
|
||||
}
|
||||
fn execute(&self, context: &CtxT, request: http::GraphQLRequest) -> IronResult<Response> {
|
||||
let response = request.execute(
|
||||
&self.root_node,
|
||||
context,
|
||||
);
|
||||
let content_type = "application/json".parse::<Mime>().unwrap();
|
||||
let json = serde_json::to_string_pretty(&response).unwrap();
|
||||
let status = if response.is_ok() { status::Ok } else { status::BadRequest };
|
||||
Ok(Response::with((content_type, status, json)))
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphiQLHandler {
|
||||
|
@ -191,17 +146,15 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
|
|||
Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static, 'a: 'static,
|
||||
{
|
||||
fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
|
||||
match req.method {
|
||||
method::Get => {
|
||||
let graphql_query = self.handle_get(&mut req)?;
|
||||
self.respond(&mut req, graphql_query)
|
||||
}
|
||||
method::Post => {
|
||||
let graphql_query = self.handle_post(&mut req)?;
|
||||
self.respond(&mut req, graphql_query)
|
||||
},
|
||||
_ => Ok(Response::with((status::MethodNotAllowed)))
|
||||
}
|
||||
let context = (self.context_factory)(req);
|
||||
|
||||
let graphql_request = match req.method {
|
||||
method::Get => self.handle_get(&mut req)?,
|
||||
method::Post => self.handle_post(&mut req)?,
|
||||
_ => return Ok(Response::with((status::MethodNotAllowed)))
|
||||
};
|
||||
|
||||
self.execute(&context, graphql_request)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,121 +162,54 @@ impl Handler for GraphiQLHandler {
|
|||
fn handle(&self, _: &mut Request) -> IronResult<Response> {
|
||||
let content_type = "text/html".parse::<Mime>().unwrap();
|
||||
|
||||
let stylesheet_source = r#"
|
||||
<style>
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
"#;
|
||||
let fetcher_source = r#"
|
||||
<script>
|
||||
function graphQLFetcher(params) {
|
||||
return fetch(GRAPHQL_URL, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(params)
|
||||
}).then(function (response) {
|
||||
return response.text();
|
||||
}).then(function (body) {
|
||||
try {
|
||||
return JSON.parse(body);
|
||||
} catch (error) {
|
||||
return body;
|
||||
}
|
||||
});
|
||||
}
|
||||
ReactDOM.render(
|
||||
React.createElement(GraphiQL, {
|
||||
fetcher: graphQLFetcher,
|
||||
}),
|
||||
document.querySelector('#app'));
|
||||
</script>
|
||||
"#;
|
||||
|
||||
let source = format!(r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GraphQL</title>
|
||||
{stylesheet_source}
|
||||
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/graphiql/0.8.1/graphiql.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/fetch/2.0.1/fetch.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/graphiql/0.8.1/graphiql.js"></script>
|
||||
<script>var GRAPHQL_URL = '{graphql_url}';</script>
|
||||
{fetcher_source}
|
||||
</body>
|
||||
</html>
|
||||
"#,
|
||||
graphql_url = self.graphql_url,
|
||||
stylesheet_source = stylesheet_source,
|
||||
fetcher_source = fetcher_source);
|
||||
|
||||
Ok(Response::with((content_type, status::Ok, source)))
|
||||
Ok(Response::with((
|
||||
content_type,
|
||||
status::Ok,
|
||||
::graphiql::graphiql_source(&self.graphql_url),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A general error allowing the developer to see the underlying issue.
|
||||
#[derive(Debug)]
|
||||
pub enum GraphQlIronError {
|
||||
///Captures any errors that were caused by Serde.
|
||||
enum GraphQLIronError {
|
||||
Serde(SerdeError),
|
||||
/// Captures any error related the IO.
|
||||
IO(IoError),
|
||||
/// Captures any error related to Url Decoding,
|
||||
Url(UrlDecodingError)
|
||||
Url(UrlDecodingError),
|
||||
InvalidData(&'static str),
|
||||
}
|
||||
|
||||
impl fmt::Display for GraphQlIronError {
|
||||
impl fmt::Display for GraphQLIronError {
|
||||
fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
GraphQlIronError::Serde(ref err) => fmt::Display::fmt(err, &mut f),
|
||||
GraphQlIronError::IO(ref err) => fmt::Display::fmt(err, &mut f),
|
||||
GraphQlIronError::Url(ref err) => fmt::Display::fmt(err, &mut f),
|
||||
GraphQLIronError::Serde(ref err) => fmt::Display::fmt(err, &mut f),
|
||||
GraphQLIronError::Url(ref err) => fmt::Display::fmt(err, &mut f),
|
||||
GraphQLIronError::InvalidData(ref err) => fmt::Display::fmt(err, &mut f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for GraphQlIronError {
|
||||
impl Error for GraphQLIronError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
GraphQlIronError::Serde(ref err) => {
|
||||
err.description()
|
||||
},
|
||||
GraphQlIronError::IO(ref err) => {
|
||||
err.description()
|
||||
}
|
||||
GraphQlIronError::Url(ref err) => {
|
||||
err.description()
|
||||
}
|
||||
GraphQLIronError::Serde(ref err) => err.description(),
|
||||
GraphQLIronError::Url(ref err) => err.description(),
|
||||
GraphQLIronError::InvalidData(ref err) => err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
match *self {
|
||||
GraphQlIronError::Serde(ref err) => {
|
||||
err.cause()
|
||||
}
|
||||
GraphQlIronError::IO(ref err) => {
|
||||
err.cause()
|
||||
}
|
||||
GraphQlIronError::Url(ref err) => {
|
||||
err.cause()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
match *self {
|
||||
GraphQLIronError::Serde(ref err) => Some(err),
|
||||
GraphQLIronError::Url(ref err) => Some(err),
|
||||
GraphQLIronError::InvalidData(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GraphQLIronError> for IronError {
|
||||
fn from(err: GraphQLIronError) -> IronError {
|
||||
let message = format!("{}", err);
|
||||
IronError::new(err, (status::BadRequest, message))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use serde::ser::SerializeMap;
|
|||
use std::fmt;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ::{GraphQLError, Value, Variables};
|
||||
use ::{GraphQLError, Value};
|
||||
use ast::InputValue;
|
||||
use executor::ExecutionError;
|
||||
use parser::{ParseError, Spanning, SourcePosition};
|
||||
|
@ -223,81 +223,3 @@ impl ser::Serialize for Value {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The expected structure of the decoded JSON Document for either Post or Get requests.
|
||||
#[derive(Deserialize)]
|
||||
pub struct GraphQLQuery {
|
||||
query: String,
|
||||
#[serde(rename = "operationName")]
|
||||
operation_name: Option<String>,
|
||||
variables: Option<InputValue>
|
||||
}
|
||||
|
||||
impl GraphQLQuery {
|
||||
pub fn new(query: String,
|
||||
operation_name: Option<String>,
|
||||
variables: Option<InputValue>
|
||||
) -> GraphQLQuery {
|
||||
GraphQLQuery {
|
||||
query: query,
|
||||
operation_name: operation_name,
|
||||
variables: variables
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query(&self) -> &str {
|
||||
self.query.as_str()
|
||||
}
|
||||
|
||||
pub fn operation_name(&self) -> Option<&str> {
|
||||
self.operation_name.as_ref().map(|oper_name| &**oper_name)
|
||||
}
|
||||
|
||||
pub fn variables(&self) -> Variables {
|
||||
self.variables.as_ref().and_then(|iv| {
|
||||
iv.to_object_value().map(|o| {
|
||||
o.into_iter().map(|(k, v)| (k.to_owned(), v.clone())).collect()
|
||||
})
|
||||
}).unwrap_or_default()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
pub struct WrappedGraphQLResult<'a>(Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>);
|
||||
|
||||
impl<'a> WrappedGraphQLResult<'a> {
|
||||
pub fn new(result: Result<(Value, Vec<ExecutionError>),
|
||||
GraphQLError<'a>>
|
||||
) -> WrappedGraphQLResult<'a> {
|
||||
WrappedGraphQLResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ser::Serialize for WrappedGraphQLResult<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer,
|
||||
{
|
||||
match self.0 {
|
||||
Ok((ref res, ref err)) => {
|
||||
let mut map = try!(serializer.serialize_map(None));
|
||||
|
||||
try!(map.serialize_key("data"));
|
||||
try!(map.serialize_value(res));
|
||||
|
||||
if !err.is_empty() {
|
||||
try!(map.serialize_key("errors"));
|
||||
try!(map.serialize_value(err));
|
||||
}
|
||||
|
||||
map.end()
|
||||
},
|
||||
Err(ref err) => {
|
||||
let mut map = try!(serializer.serialize_map(Some(1)));
|
||||
try!(map.serialize_key("errors"));
|
||||
try!(map.serialize_value(err));
|
||||
map.end()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,6 +203,8 @@ mod schema;
|
|||
mod validation;
|
||||
mod executor;
|
||||
mod integrations;
|
||||
pub mod graphiql;
|
||||
pub mod http;
|
||||
#[macro_use] mod result_ext;
|
||||
|
||||
#[cfg(all(test, not(feature="expose-test-schema")))] mod tests;
|
||||
|
|
Loading…
Reference in a new issue