Move juniper_iron crate back into the main repo.
This is done for a lower maintainance burden and combined testing.
This commit is contained in:
parent
bdb1dd34af
commit
bec5295827
8 changed files with 525 additions and 0 deletions
|
@ -3,4 +3,5 @@ members = [
|
||||||
"juniper",
|
"juniper",
|
||||||
"juniper_codegen",
|
"juniper_codegen",
|
||||||
"juniper_tests",
|
"juniper_tests",
|
||||||
|
"juniper_iron",
|
||||||
]
|
]
|
||||||
|
|
2
juniper_iron/.gitignore
vendored
Normal file
2
juniper_iron/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
Cargo.lock
|
0
juniper_iron/CHANGELOG.md
Normal file
0
juniper_iron/CHANGELOG.md
Normal file
26
juniper_iron/Cargo.toml
Normal file
26
juniper_iron/Cargo.toml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[package]
|
||||||
|
name = "juniper_iron"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Magnus Hallin <mhallin@fastmail.com>"]
|
||||||
|
description = "Iron integration for juniper"
|
||||||
|
license = "BSD-2-Clause"
|
||||||
|
documentation = "https://docs.rs/juniper_iron"
|
||||||
|
repository = "https://github.com/graphql-rust/juniper_iron"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0.2" }
|
||||||
|
serde_json = { version = "1.0.2" }
|
||||||
|
urlencoded = { version = "0.5.0" }
|
||||||
|
iron = "0.5.1"
|
||||||
|
juniper = { version = "0.8.1", git = "https://github.com/graphql-rust/juniper" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
iron-test = "^0.5.0"
|
||||||
|
router = "^0.5.0"
|
||||||
|
mount = "^0.3.0"
|
||||||
|
logger = "^0.3.0"
|
||||||
|
juniper = { version = "0.8.1", features = ["expose-test-schema", "serde_json"], git = "https://github.com/graphql-rust/juniper" }
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
travis-ci = { repository = "graphql-rust/juniper_iron" }
|
||||||
|
appveyor = { repository = "graphql-rust/juniper_iron" }
|
25
juniper_iron/LICENSE
Normal file
25
juniper_iron/LICENSE
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2016, Magnus Hallin
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
31
juniper_iron/README.md
Normal file
31
juniper_iron/README.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# juniper_iron
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/graphql-rust/juniper_iron.svg?branch=master)](https://travis-ci.org/graphql-rust/juniper_iron)
|
||||||
|
[![Build status](https://ci.appveyor.com/api/projects/status/rqguvfkl9m0g7hum?svg=true)](https://ci.appveyor.com/project/theduke/juniper-iron)
|
||||||
|
[![Crates.io](https://img.shields.io/crates/v/juniper_iron.svg?maxAge=2592000)](https://crates.io/crates/juniper_iron)
|
||||||
|
[![Gitter chat](https://badges.gitter.im/juniper-graphql/gitter.png)](https://gitter.im/juniper-graphql)
|
||||||
|
|
||||||
|
This repository contains the [Iron][Iron] web framework integration for [Juniper][Juniper], a [GraphQL][GraphQL]
|
||||||
|
implementation for Rust.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Once the crate is published, documentation will be on [docs.rs][documentation].
|
||||||
|
|
||||||
|
For now, please consult the documentation comments [here](https://github.com/graphql-rust/juniper_iron/blob/master/src/lib.rs).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Check [examples/iron_server.rs][example] for example code of a working Iron server with GraphQL handlers.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is under the BSD-2 license.
|
||||||
|
|
||||||
|
Check the LICENSE file for details.
|
||||||
|
|
||||||
|
[Iron]: https://github.com/iron/iron
|
||||||
|
[Juniper]: https://github.com/graphql-rust/juniper
|
||||||
|
[GraphQL]: http://graphql.org
|
||||||
|
[documentation]: https://docs.rs/juniper_iron
|
||||||
|
[example]: https://github.com/graphql-rust/juniper_iron/blob/master/examples/iron_server.rs
|
43
juniper_iron/examples/iron_server.rs
Normal file
43
juniper_iron/examples/iron_server.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
extern crate iron;
|
||||||
|
extern crate mount;
|
||||||
|
extern crate logger;
|
||||||
|
extern crate serde;
|
||||||
|
extern crate juniper;
|
||||||
|
extern crate juniper_iron;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use mount::Mount;
|
||||||
|
use logger::Logger;
|
||||||
|
use iron::prelude::*;
|
||||||
|
use juniper::EmptyMutation;
|
||||||
|
use juniper_iron::{GraphQLHandler, GraphiQLHandler};
|
||||||
|
use juniper::tests::model::Database;
|
||||||
|
|
||||||
|
fn context_factory(_: &mut Request) -> Database {
|
||||||
|
Database::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut mount = Mount::new();
|
||||||
|
|
||||||
|
let graphql_endpoint = GraphQLHandler::new(
|
||||||
|
context_factory,
|
||||||
|
Database::new(),
|
||||||
|
EmptyMutation::<Database>::new(),
|
||||||
|
);
|
||||||
|
let graphiql_endpoint = GraphiQLHandler::new("/graphql");
|
||||||
|
|
||||||
|
mount.mount("/", graphiql_endpoint);
|
||||||
|
mount.mount("/graphql", graphql_endpoint);
|
||||||
|
|
||||||
|
let (logger_before, logger_after) = Logger::new(None);
|
||||||
|
|
||||||
|
let mut chain = Chain::new(mount);
|
||||||
|
chain.link_before(logger_before);
|
||||||
|
chain.link_after(logger_after);
|
||||||
|
|
||||||
|
let host = env::var("LISTEN").unwrap_or("0.0.0.0:8080".to_owned());
|
||||||
|
println!("GraphQL server started on {}", host);
|
||||||
|
Iron::new(chain).http(host.as_str()).unwrap();
|
||||||
|
}
|
397
juniper_iron/src/lib.rs
Normal file
397
juniper_iron/src/lib.rs
Normal file
|
@ -0,0 +1,397 @@
|
||||||
|
/*!
|
||||||
|
|
||||||
|
[Juniper][1] handlers for the [Iron][2] framework.
|
||||||
|
|
||||||
|
## Integrating with Iron
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
For example, continuing from the schema created above and using Iron to expose
|
||||||
|
the schema on an HTTP endpoint supporting both GET and POST requests:
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
extern crate iron;
|
||||||
|
# #[macro_use] extern crate juniper;
|
||||||
|
# extern crate juniper_iron;
|
||||||
|
# use std::collections::HashMap;
|
||||||
|
|
||||||
|
use iron::prelude::*;
|
||||||
|
use juniper_iron::GraphQLHandler;
|
||||||
|
use juniper::{Context, EmptyMutation};
|
||||||
|
|
||||||
|
# use juniper::FieldResult;
|
||||||
|
#
|
||||||
|
# struct User { id: String, name: String, friend_ids: Vec<String> }
|
||||||
|
# struct QueryRoot;
|
||||||
|
# struct Database { users: HashMap<String, User> }
|
||||||
|
#
|
||||||
|
# graphql_object!(User: Database |&self| {
|
||||||
|
# field id() -> FieldResult<&String> {
|
||||||
|
# Ok(&self.id)
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# field name() -> FieldResult<&String> {
|
||||||
|
# Ok(&self.name)
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# field friends(&executor) -> FieldResult<Vec<&User>> {
|
||||||
|
# Ok(self.friend_ids.iter()
|
||||||
|
# .filter_map(|id| executor.context().users.get(id))
|
||||||
|
# .collect())
|
||||||
|
# }
|
||||||
|
# });
|
||||||
|
#
|
||||||
|
# graphql_object!(QueryRoot: Database |&self| {
|
||||||
|
# field user(&executor, id: String) -> FieldResult<Option<&User>> {
|
||||||
|
# Ok(executor.context().users.get(&id))
|
||||||
|
# }
|
||||||
|
# });
|
||||||
|
|
||||||
|
// This function is executed for every request. Here, we would realistically
|
||||||
|
// provide a database connection or similar. For this example, we'll be
|
||||||
|
// creating the database from scratch.
|
||||||
|
fn context_factory(_: &mut Request) -> Database {
|
||||||
|
Database {
|
||||||
|
users: vec![
|
||||||
|
( "1000".to_owned(), User {
|
||||||
|
id: "1000".to_owned(), name: "Robin".to_owned(),
|
||||||
|
friend_ids: vec!["1001".to_owned()] } ),
|
||||||
|
( "1001".to_owned(), User {
|
||||||
|
id: "1001".to_owned(), name: "Max".to_owned(),
|
||||||
|
friend_ids: vec!["1000".to_owned()] } ),
|
||||||
|
].into_iter().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context for Database {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// GraphQLHandler takes a context factory function, the root object,
|
||||||
|
// and the mutation object. If we don't have any mutations to expose, we
|
||||||
|
// can use the empty tuple () to indicate absence.
|
||||||
|
let graphql_endpoint = GraphQLHandler::new(
|
||||||
|
context_factory, QueryRoot, EmptyMutation::<Database>::new());
|
||||||
|
|
||||||
|
// Start serving the schema at the root on port 8080.
|
||||||
|
Iron::new(graphql_endpoint).http("localhost:8080").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [iron_server.rs][5]
|
||||||
|
example for more information on how to use these handlers.
|
||||||
|
|
||||||
|
See the the [`GraphQLHandler`][3] documentation for more information on what request methods are
|
||||||
|
supported.
|
||||||
|
There's also a built-in [GraphiQL][4] handler included.
|
||||||
|
|
||||||
|
[1]: https://github.com/mhallin/Juniper
|
||||||
|
[2]: http://ironframework.io
|
||||||
|
[3]: ./struct.GraphQLHandler.html
|
||||||
|
[4]: https://github.com/graphql/graphiql
|
||||||
|
[5]: https://github.com/mhallin/juniper/blob/master/juniper_iron/examples/iron_server.rs
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern crate serde_json;
|
||||||
|
extern crate juniper;
|
||||||
|
extern crate urlencoded;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate iron;
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate iron_test;
|
||||||
|
|
||||||
|
use iron::prelude::*;
|
||||||
|
use iron::middleware::Handler;
|
||||||
|
use iron::mime::Mime;
|
||||||
|
use iron::status;
|
||||||
|
use iron::method;
|
||||||
|
use urlencoded::{UrlDecodingError, UrlEncodedQuery};
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use serde_json::error::Error as SerdeError;
|
||||||
|
|
||||||
|
use juniper::{GraphQLType, InputValue, RootNode};
|
||||||
|
use juniper::http;
|
||||||
|
|
||||||
|
/// Handler that executes GraphQL queries in the given schema
|
||||||
|
///
|
||||||
|
/// The handler responds to GET requests and POST requests only. In GET
|
||||||
|
/// requests, the query should be supplied in the `query` URL parameter, e.g.
|
||||||
|
/// `http://localhost:3000/graphql?query={hero{name}}`.
|
||||||
|
///
|
||||||
|
/// POST requests support both queries and variables. POST a JSON document to
|
||||||
|
/// this endpoint containing the field `"query"` and optionally `"variables"`.
|
||||||
|
/// The variables should be a JSON object containing the variable to value
|
||||||
|
/// mapping.
|
||||||
|
pub struct GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
||||||
|
where
|
||||||
|
CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||||
|
CtxT: 'static,
|
||||||
|
Query: GraphQLType<Context = CtxT> + Send + Sync + 'static,
|
||||||
|
Mutation: GraphQLType<Context = CtxT> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
context_factory: CtxFactory,
|
||||||
|
root_node: RootNode<'a, Query, Mutation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handler that renders GraphiQL - a graphical query editor interface
|
||||||
|
pub struct GraphiQLHandler {
|
||||||
|
graphql_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_single_value<T>(mut values: Vec<T>) -> IronResult<T> {
|
||||||
|
if values.len() == 1 {
|
||||||
|
Ok(values.remove(0))
|
||||||
|
} else {
|
||||||
|
Err(
|
||||||
|
GraphQLIronError::InvalidData("Duplicate URL query parameter").into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a, CtxFactory, Query, Mutation, CtxT> GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
||||||
|
where
|
||||||
|
CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||||
|
CtxT: 'static,
|
||||||
|
Query: GraphQLType<Context = CtxT, TypeInfo=()> + Send + Sync + 'static,
|
||||||
|
Mutation: GraphQLType<Context = CtxT, TypeInfo=()> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
/// Build a new GraphQL handler
|
||||||
|
///
|
||||||
|
/// The context factory will receive the Iron request object and is
|
||||||
|
/// expected to construct a context object for the given schema. This can
|
||||||
|
/// be used to construct e.g. database connections or similar data that
|
||||||
|
/// the schema needs to execute the query.
|
||||||
|
pub fn new(context_factory: CtxFactory, query: Query, mutation: Mutation) -> Self {
|
||||||
|
GraphQLHandler {
|
||||||
|
context_factory: context_factory,
|
||||||
|
root_node: RootNode::new(query, mutation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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<http::GraphQLRequest> {
|
||||||
|
let mut request_payload = String::new();
|
||||||
|
itry!(req.body.read_to_string(&mut request_payload));
|
||||||
|
|
||||||
|
Ok(serde_json::from_str::<http::GraphQLRequest>(
|
||||||
|
request_payload.as_str(),
|
||||||
|
).map_err(|err| GraphQLIronError::Serde(err))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/// Build a new GraphiQL handler targeting the specified URL.
|
||||||
|
///
|
||||||
|
/// The provided URL should point to the URL of the attached `GraphQLHandler`. It can be
|
||||||
|
/// relative, so a common value could be `"/graphql"`.
|
||||||
|
pub fn new(graphql_url: &str) -> GraphiQLHandler {
|
||||||
|
GraphiQLHandler {
|
||||||
|
graphql_url: graphql_url.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, CtxFactory, Query, Mutation, CtxT> Handler
|
||||||
|
for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
||||||
|
where
|
||||||
|
CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||||
|
CtxT: 'static,
|
||||||
|
Query: GraphQLType<Context = CtxT, TypeInfo=()> + Send + Sync + 'static,
|
||||||
|
Mutation: GraphQLType<Context = CtxT, TypeInfo=()> + Send + Sync + 'static,
|
||||||
|
'a: 'static,
|
||||||
|
{
|
||||||
|
fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handler for GraphiQLHandler {
|
||||||
|
fn handle(&self, _: &mut Request) -> IronResult<Response> {
|
||||||
|
let content_type = "text/html".parse::<Mime>().unwrap();
|
||||||
|
|
||||||
|
Ok(Response::with((
|
||||||
|
content_type,
|
||||||
|
status::Ok,
|
||||||
|
juniper::graphiql::graphiql_source(&self.graphql_url),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum GraphQLIronError {
|
||||||
|
Serde(SerdeError),
|
||||||
|
Url(UrlDecodingError),
|
||||||
|
InvalidData(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
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::Url(ref err) => fmt::Display::fmt(err, &mut f),
|
||||||
|
GraphQLIronError::InvalidData(ref err) => fmt::Display::fmt(err, &mut f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for GraphQLIronError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
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) => 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use iron::prelude::*;
|
||||||
|
use iron_test::{request, response};
|
||||||
|
use iron::{Handler, Headers};
|
||||||
|
|
||||||
|
use juniper::tests::model::Database;
|
||||||
|
use juniper::http::tests as http_tests;
|
||||||
|
use juniper::EmptyMutation;
|
||||||
|
|
||||||
|
use super::GraphQLHandler;
|
||||||
|
|
||||||
|
struct TestIronIntegration;
|
||||||
|
|
||||||
|
impl http_tests::HTTPIntegration for TestIronIntegration {
|
||||||
|
fn get(&self, url: &str) -> http_tests::TestResponse {
|
||||||
|
make_test_response(request::get(
|
||||||
|
&("http://localhost:3000".to_owned() + url),
|
||||||
|
Headers::new(),
|
||||||
|
&make_handler(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post(&self, url: &str, body: &str) -> http_tests::TestResponse {
|
||||||
|
make_test_response(request::post(
|
||||||
|
&("http://localhost:3000".to_owned() + url),
|
||||||
|
Headers::new(),
|
||||||
|
body,
|
||||||
|
&make_handler(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iron_integration() {
|
||||||
|
let integration = TestIronIntegration;
|
||||||
|
|
||||||
|
http_tests::run_http_test_suite(&integration);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn context_factory(_: &mut Request) -> Database {
|
||||||
|
Database::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_test_response(response: IronResult<Response>) -> http_tests::TestResponse {
|
||||||
|
let response = response.expect("Error response from GraphQL handler");
|
||||||
|
let status_code = response
|
||||||
|
.status
|
||||||
|
.expect("No status code returned from handler")
|
||||||
|
.to_u16() as i32;
|
||||||
|
let content_type = String::from_utf8(
|
||||||
|
response
|
||||||
|
.headers
|
||||||
|
.get_raw("content-type")
|
||||||
|
.expect("No content type header from handler")[0]
|
||||||
|
.clone(),
|
||||||
|
).expect("Content-type header invalid UTF-8");
|
||||||
|
let body = response::extract_body_to_string(response);
|
||||||
|
|
||||||
|
http_tests::TestResponse {
|
||||||
|
status_code: status_code,
|
||||||
|
body: Some(body),
|
||||||
|
content_type: content_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_handler() -> Box<Handler> {
|
||||||
|
Box::new(GraphQLHandler::new(
|
||||||
|
context_factory,
|
||||||
|
Database::new(),
|
||||||
|
EmptyMutation::<Database>::new(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue