Extracted iron/rocket crates and updated README.

Those now live in separate repos.
This commit is contained in:
theduke 2017-08-07 08:12:18 +02:00
parent 0c7e39f14e
commit 2ca9baa441
11 changed files with 2 additions and 885 deletions

View file

@ -3,6 +3,4 @@ members = [
"juniper",
"juniper_codegen",
"juniper_tests",
"juniper_iron",
"juniper_rocket",
]

View file

@ -36,7 +36,7 @@ feature flag:
```toml
[dependencies]
juniper = { version = "0.8.1" }
juniper_iron = { git = "https://github.com/mhallin/juniper" }
juniper_iron = { git = "https://github.com/graphql-rust/juniper_iron" }
```
@ -45,7 +45,7 @@ If you want Rocket integration, you need to depend on the `juniper_rocket` crate
```toml
[dependencies]
juniper = { version = "0.8.1" }
juniper_rocket = { git = "https://github.com/mhallin/juniper" }
juniper_rocket = { git = "https://github.com/graphql-rustl/juniper_rocket" }
```
## Building schemas

View file

@ -51,7 +51,4 @@ build: false
test_script:
- cd juniper && cargo build --verbose && cargo test --verbose && cd ..
- cd juniper_codegen && cargo build --verbose && cargo test --verbose && cd ..
- cd juniper_iron && cargo build --verbose && cargo test --verbose && cd ..
- cd juniper_tests && cargo build --verbose && cargo test --verbose && cd ..
- IF NOT %TARGET% == %TARGET:msvc=% ( IF %CHANNEL% == "nightly" ( cd juniper_rocket && cargo test --verbose && cargo build --verbose && cd .. ) )

View file

@ -1,64 +0,0 @@
# Build and test on stable, beta, and nightly on Windows.
#
# Copied general structure from https://github.com/japaric/rust-everywhere/blob/master/appveyor.yml
environment:
matrix:
# Stable channel
- TARGET: i686-pc-windows-gnu
CHANNEL: stable
- TARGET: i686-pc-windows-msvc
CHANNEL: stable
- TARGET: x86_64-pc-windows-gnu
CHANNEL: stable
- TARGET: x86_64-pc-windows-msvc
CHANNEL: stable
# Beta channel
- TARGET: i686-pc-windows-gnu
CHANNEL: beta
- TARGET: i686-pc-windows-msvc
CHANNEL: beta
- TARGET: x86_64-pc-windows-gnu
CHANNEL: beta
- TARGET: x86_64-pc-windows-msvc
CHANNEL: beta
# Nightly channel
- TARGET: i686-pc-windows-gnu
CHANNEL: nightly
- TARGET: i686-pc-windows-msvc
CHANNEL: nightly
- TARGET: x86_64-pc-windows-gnu
CHANNEL: nightly
- TARGET: x86_64-pc-windows-msvc
CHANNEL: nightly
# Install Rust and Cargo
# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml)
install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs
- rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv
- cargo -V
# 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents
# the "directory does not contain a project or solution file" error.
# source: https://github.com/starkat99/appveyor-rust/blob/master/appveyor.yml#L113
build: false
test_script:
# Build library standalone, with Iron, and with Rocket integrations enabled, respectively
- cargo build --verbose
- cargo build --verbose --features iron-handlers
- IF "%CHANNEL%"=="nightly" (cargo build --verbose --features rocket-handlers)
# Build example binaries; first Iron, then Rocket examples
- cargo build --verbose --example server --features "iron-handlers expose-test-schema"
- IF "%CHANNEL%"=="nightly" (cargo build --verbose --example rocket-server --features "rocket-handlers expose-test-schema")
# Run all tests for the base library and available integrations
- set TEST_FEATURES=iron-handlers expose-test-schema
- IF "%CHANNEL%"=="nightly" (set TEST_FEATURES=%TEST_FEATURES% rocket-handlers rocket/testing)
- cargo test --verbose --features "%TEST_FEATURES%"

View file

@ -1,26 +0,0 @@
[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/mhallin/juniper"
[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", path = "../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", path = "../juniper" , features = ["expose-test-schema", "serde_json"] }
[badges]
travis-ci = { repository = "mhallin/juniper" }
appveyor = { repository = "mhallin/juniper" }

View file

@ -1,43 +0,0 @@
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();
}

View file

@ -1,397 +0,0 @@
/*!
[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> + Send + Sync + 'static,
Mutation: GraphQLType<Context = CtxT> + 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> + Send + Sync + 'static,
Mutation: GraphQLType<Context = CtxT> + 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 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(),
))
}
}

View file

@ -1,24 +0,0 @@
[package]
name = "juniper_rocket"
version = "0.0.1"
authors = ["Magnus Hallin <mhallin@fastmail.com>"]
description = "Juniper GraphQL integration with Rocket"
license = "BSD-2-Clause"
documentation = "https://docs.rs/juniper_rocket"
repository = "https://github.com/mhallin/juniper"
[dependencies]
serde = { version = "^1.0.8" }
serde_derive = {version="^1.0.8" }
serde_json = { version = "^1.0.2" }
rocket = { version = "^0.3.0" }
rocket_codegen = { version = "^0.3.0" }
juniper = { version = "0.8.1", path = "../juniper" }
juniper_codegen = { version = "0.8.1", path = "../juniper_codegen" }
[badges]
travis-ci = { repository = "mhallin/juniper" }
appveyor = { repository = "mhallin/juniper" }
[dev-dependencies]
juniper = { version = "0.8.1", path = "../juniper", features=["expose-test-schema", "serde_json"] }

View file

@ -1,41 +0,0 @@
[tasks.build-verbose]
command = "cargo"
args = ["build", "--verbose"]
condition_script = [
'''
if [ "$CARGO_MAKE_RUST_CHANNEL" = "nightly" ]; then
exit 0
else
exit 1
fi
'''
]
[tasks.build-verbose.windows]
command = "cargo"
args = ["build", "--verbose"]
condition_script = [
'''IF "%CARGO_MAKE_RUST_CHANNEL%"=="nightly" (exit 0) ELSE (exit 1)'''
]
[tasks.test-verbose]
command = "cargo"
args = ["test", "--verbose"]
dependencies = ["build-verbose"]
condition_script = [
'''
if [ "$CARGO_MAKE_RUST_CHANNEL" = "nightly" ]; then
exit 0
else
exit 1
fi
'''
]
[tasks.test-verbose.windows]
command = "cargo"
args = ["test", "--verbose"]
dependencies = ["build-verbose"]
condition_script = [
'''IF "%CARGO_MAKE_RUST_CHANNEL%"=="nightly" (exit 0) ELSE (exit 1)'''
]

View file

@ -1,51 +0,0 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate juniper;
extern crate juniper_rocket;
use rocket::response::content;
use rocket::State;
use juniper::tests::model::Database;
use juniper::{EmptyMutation, RootNode};
type Schema = RootNode<'static, Database, EmptyMutation<Database>>;
#[get("/")]
fn graphiql() -> content::Html<String> {
juniper_rocket::graphiql_source("/graphql")
}
#[get("/graphql?<request>")]
fn get_graphql_handler(
context: State<Database>,
request: juniper_rocket::GraphQLRequest,
schema: State<Schema>,
) -> juniper_rocket::GraphQLResponse {
request.execute(&schema, &context)
}
#[post("/graphql", data = "<request>")]
fn post_graphql_handler(
context: State<Database>,
request: juniper_rocket::GraphQLRequest,
schema: State<Schema>,
) -> juniper_rocket::GraphQLResponse {
request.execute(&schema, &context)
}
fn main() {
rocket::ignite()
.manage(Database::new())
.manage(Schema::new(
Database::new(),
EmptyMutation::<Database>::new(),
))
.mount(
"/",
routes![graphiql, get_graphql_handler, post_graphql_handler],
)
.launch();
}

View file

@ -1,232 +0,0 @@
#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate juniper;
extern crate serde_json;
extern crate rocket;
use std::io::{Cursor, Read};
use std::error::Error;
use rocket::Request;
use rocket::request::{FormItems, FromForm};
use rocket::data::{FromData, Outcome as FromDataOutcome};
use rocket::response::{content, Responder, Response};
use rocket::http::{ContentType, Status};
use rocket::Data;
use rocket::Outcome::{Failure, Forward, Success};
use juniper::InputValue;
use juniper::http;
use juniper::GraphQLType;
use juniper::RootNode;
/// Simple wrapper around an incoming GraphQL request
///
/// See the `http` module for more information. This type can be constructed
/// automatically from both GET and POST routes by implementing the `FromForm`
/// and `FromData` traits.
pub struct GraphQLRequest(http::GraphQLRequest);
/// Simple wrapper around the result of executing a GraphQL query
pub struct GraphQLResponse(Status, String);
/// Generate an HTML page containing GraphiQL
pub fn graphiql_source(graphql_endpoint_url: &str) -> content::Html<String> {
content::Html(juniper::graphiql::graphiql_source(graphql_endpoint_url))
}
impl GraphQLRequest {
/// Execute an incoming GraphQL query
pub fn execute<CtxT, QueryT, MutationT>(
&self,
root_node: &RootNode<QueryT, MutationT>,
context: &CtxT,
) -> GraphQLResponse
where
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
{
let response = self.0.execute(root_node, context);
let status = if response.is_ok() {
Status::Ok
} else {
Status::BadRequest
};
let json = serde_json::to_string_pretty(&response).unwrap();
GraphQLResponse(status, json)
}
}
impl<'f> FromForm<'f> for GraphQLRequest {
type Error = String;
fn from_form(form_items: &mut FormItems<'f>, strict: bool) -> Result<Self, String> {
let mut query = None;
let mut operation_name = None;
let mut variables = None;
for (key, value) in form_items {
match key.as_str() {
"query" => if query.is_some() {
return Err("Query parameter must not occur more than once".to_owned());
} else {
query = Some(value.as_str().to_string());
},
"operation_name" => if operation_name.is_some() {
return Err(
"Operation name parameter must not occur more than once".to_owned(),
);
} else {
operation_name = Some(value.as_str().to_string());
},
"variables" => if variables.is_some() {
return Err(
"Variables parameter must not occur more than once".to_owned(),
);
} else {
variables = Some(serde_json::from_str::<InputValue>(value.as_str())
.map_err(|err| err.description().to_owned())?);
},
_ => if strict {
return Err(format!("Prohibited extra field '{}'", key).to_owned());
},
}
}
if let Some(query) = query {
Ok(GraphQLRequest(
http::GraphQLRequest::new(query, operation_name, variables),
))
} else {
Err("Query parameter missing".to_owned())
}
}
}
impl FromData for GraphQLRequest {
type Error = String;
fn from_data(request: &Request, data: Data) -> FromDataOutcome<Self, Self::Error> {
if !request.content_type().map_or(false, |ct| ct.is_json()) {
return Forward(data);
}
let mut body = String::new();
if let Err(e) = data.open().read_to_string(&mut body) {
return Failure((Status::InternalServerError, format!("{:?}", e)));
}
match serde_json::from_str(&body) {
Ok(value) => Success(GraphQLRequest(value)),
Err(failure) => return Failure((Status::BadRequest, format!("{}", failure))),
}
}
}
impl<'r> Responder<'r> for GraphQLResponse {
fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
let GraphQLResponse(status, body) = self;
Ok(
Response::build()
.header(ContentType::new("application", "json"))
.status(status)
.sized_body(Cursor::new(body))
.finalize(),
)
}
}
#[cfg(test)]
mod tests {
use rocket;
use rocket::Rocket;
use rocket::http::{ContentType, Method};
use rocket::State;
use juniper::RootNode;
use juniper::tests::model::Database;
use juniper::http::tests as http_tests;
use juniper::EmptyMutation;
type Schema = RootNode<'static, Database, EmptyMutation<Database>>;
#[get("/?<request>")]
fn get_graphql_handler(
context: State<Database>,
request: super::GraphQLRequest,
schema: State<Schema>,
) -> super::GraphQLResponse {
request.execute(&schema, &context)
}
#[post("/", data = "<request>")]
fn post_graphql_handler(
context: State<Database>,
request: super::GraphQLRequest,
schema: State<Schema>,
) -> super::GraphQLResponse {
request.execute(&schema, &context)
}
struct TestRocketIntegration {
rocket: Rocket,
}
/*
impl http_tests::HTTPIntegration for TestRocketIntegration
{
fn get(&self, url: &str) -> http_tests::TestResponse {
make_test_response(&self.rocket, MockRequest::new(
Method::Get,
url))
}
fn post(&self, url: &str, body: &str) -> http_tests::TestResponse {
make_test_response(
&self.rocket,
MockRequest::new(
Method::Post,
url,
).header(ContentType::JSON).body(body))
}
}
#[test]
fn test_rocket_integration() {
let integration = TestRocketIntegration {
rocket: make_rocket(),
};
http_tests::run_http_test_suite(&integration);
}
fn make_rocket() -> Rocket {
rocket::ignite()
.manage(Database::new())
.manage(Schema::new(Database::new(), EmptyMutation::<Database>::new()))
.mount("/", routes![post_graphql_handler, get_graphql_handler])
}
fn make_test_response<'r>(rocket: &'r Rocket, mut request: MockRequest<'r>) -> http_tests::TestResponse {
let mut response = request.dispatch_with(&rocket);
let status_code = response.status().code as i32;
let content_type = response.header_values("content-type").collect::<Vec<_>>().into_iter().next()
.expect("No content type header from handler").to_owned();
let body = response.body().expect("No body returned from GraphQL handler").into_string();
http_tests::TestResponse {
status_code: status_code,
body: body,
content_type: content_type,
}
}
*/
}