juniper/juniper_hyper/src/lib.rs

401 lines
14 KiB
Rust
Raw Normal View History

#[macro_use]
extern crate futures;
extern crate hyper;
extern crate juniper;
#[macro_use]
extern crate serde_derive;
#[cfg(test)]
extern crate reqwest;
extern crate serde_json;
extern crate tokio;
extern crate tokio_threadpool;
extern crate url;
use futures::future::Either;
use hyper::header::HeaderValue;
use hyper::rt::Stream;
use hyper::{header, Body, Method, Request, Response, StatusCode};
use juniper::http::GraphQLRequest as JuniperGraphQLRequest;
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
use juniper::serde::Deserialize;
use juniper::{DefaultScalarValue, GraphQLType, InputValue, RootNode, ScalarRefValue, ScalarValue};
use serde_json::error::Error as SerdeError;
use std::error::Error;
use std::fmt;
use std::string::FromUtf8Error;
use std::sync::Arc;
use tokio::prelude::*;
use url::form_urlencoded;
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
pub fn graphql<CtxT, QueryT, MutationT, S>(
root_node: Arc<RootNode<'static, QueryT, MutationT, S>>,
context: Arc<CtxT>,
request: Request<Body>,
) -> impl Future<Item = Response<Body>, Error = hyper::Error>
where
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
S: ScalarValue + Send + Sync + 'static,
for<'b> &'b S: ScalarRefValue<'b>,
CtxT: Send + Sync + 'static,
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
QueryT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
MutationT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
QueryT::TypeInfo: Send + Sync,
MutationT::TypeInfo: Send + Sync,
{
match request.method() {
&Method::GET => Either::A(Either::A(
future::done(
request
.uri()
.query()
.map(|q| gql_request_from_get(q).map(GraphQLRequest::Single))
.unwrap_or(Err(GraphQLRequestError::Invalid(
"'query' parameter is missing".to_string(),
))),
)
.and_then(move |gql_req| {
execute_request(root_node, context, gql_req).map_err(|_| {
unreachable!("thread pool has shut down?!");
})
})
.or_else(|err| future::ok(render_error(err))),
)),
&Method::POST => Either::A(Either::B(
request
.into_body()
.concat2()
.or_else(|err| future::done(Err(GraphQLRequestError::BodyHyper(err))))
.and_then(move |chunk| {
future::done({
String::from_utf8(chunk.iter().cloned().collect::<Vec<u8>>())
.map_err(GraphQLRequestError::BodyUtf8)
.and_then(|input| {
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
serde_json::from_str::<GraphQLRequest<S>>(&input)
.map_err(GraphQLRequestError::BodyJSONError)
})
})
})
.and_then(move |gql_req| {
execute_request(root_node, context, gql_req).map_err(|_| {
unreachable!("thread pool has shut down?!");
})
})
.or_else(|err| future::ok(render_error(err))),
)),
_ => return Either::B(future::ok(new_response(StatusCode::METHOD_NOT_ALLOWED))),
}
}
pub fn graphiql(
graphql_endpoint: &str,
) -> impl Future<Item = Response<Body>, Error = hyper::Error> {
let mut resp = new_html_response(StatusCode::OK);
// XXX: is the call to graphiql_source blocking?
*resp.body_mut() = Body::from(juniper::graphiql::graphiql_source(graphql_endpoint));
future::ok(resp)
}
fn render_error(err: GraphQLRequestError) -> Response<Body> {
let message = format!("{}", err);
let mut resp = new_response(StatusCode::BAD_REQUEST);
*resp.body_mut() = Body::from(message);
resp
}
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
fn execute_request<CtxT, QueryT, MutationT, S>(
root_node: Arc<RootNode<'static, QueryT, MutationT, S>>,
context: Arc<CtxT>,
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
request: GraphQLRequest<S>,
) -> impl Future<Item = Response<Body>, Error = tokio_threadpool::BlockingError>
where
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
S: ScalarValue + Send + Sync + 'static,
for<'b> &'b S: ScalarRefValue<'b>,
CtxT: Send + Sync + 'static,
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
QueryT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
MutationT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
QueryT::TypeInfo: Send + Sync,
MutationT::TypeInfo: Send + Sync,
{
request.execute(root_node, context).map(|(is_ok, body)| {
let code = if is_ok {
StatusCode::OK
} else {
StatusCode::BAD_REQUEST
};
let mut resp = new_response(code);
resp.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
*resp.body_mut() = body;
resp
})
}
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
fn gql_request_from_get<S>(input: &str) -> Result<JuniperGraphQLRequest<S>, GraphQLRequestError>
where
S: ScalarValue,
{
let mut query = None;
let operation_name = None;
let mut variables = None;
for (key, value) in form_urlencoded::parse(input.as_bytes()).into_owned() {
match key.as_ref() {
"query" => {
if query.is_some() {
return Err(invalid_err("query"));
}
query = Some(value)
}
"operationName" => {
if operation_name.is_some() {
return Err(invalid_err("operationName"));
}
}
"variables" => {
if variables.is_some() {
return Err(invalid_err("variables"));
}
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
match serde_json::from_str::<InputValue<S>>(&value)
.map_err(GraphQLRequestError::Variables)
{
Ok(parsed_variables) => variables = Some(parsed_variables),
Err(e) => return Err(e),
}
}
_ => continue,
}
}
match query {
Some(query) => Ok(JuniperGraphQLRequest::new(query, operation_name, variables)),
None => Err(GraphQLRequestError::Invalid(
"'query' parameter is missing".to_string(),
)),
}
}
fn invalid_err(parameter_name: &str) -> GraphQLRequestError {
GraphQLRequestError::Invalid(format!(
"'{}' parameter is specified multiple times",
parameter_name
))
}
fn new_response(code: StatusCode) -> Response<Body> {
let mut r = Response::new(Body::empty());
*r.status_mut() = code;
r
}
fn new_html_response(code: StatusCode) -> Response<Body> {
let mut resp = new_response(code);
resp.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("text/html; charset=utf-8"),
);
resp
}
#[derive(Deserialize)]
#[serde(untagged)]
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
#[serde(bound = "InputValue<S>: Deserialize<'de>")]
enum GraphQLRequest<S = DefaultScalarValue>
where
S: ScalarValue,
{
Single(JuniperGraphQLRequest<S>),
Batch(Vec<JuniperGraphQLRequest<S>>),
}
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
impl<S> GraphQLRequest<S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn execute<'a, CtxT: 'a, QueryT, MutationT>(
self,
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
root_node: Arc<RootNode<'a, QueryT, MutationT, S>>,
context: Arc<CtxT>,
) -> impl Future<Item = (bool, hyper::Body), Error = tokio_threadpool::BlockingError> + 'a
where
Introduce an abstraction for scalar values (#251) Introduce an abstraction for scalar values Before this change, possible scalar values were hard coded to be representable by one of the following types: `i32`, `f64`, `String` or `bool`. This restricts the types of custom scalar values that can be defined. For example, it was not possible to define a scalar value that represents an `i64` without mapping it to a string (which would be inefficient). One solution to fix the example above would simply be to change the internal representation to allow it to represent an `i64`, but this would only fix the problem for one type (until someone wants to support `i128` for example). Also this would make juniper not follow the GraphQL standard closely. This commit takes another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only contains the types described in the standard whereas other crates could define custom scalars for their needs. To accomplish this we need to change several things in the current implementation: * Add some traits that abstract the behavior of such a scalar value representation * Change `Value` and `InputValue` to have a scalar variant (with a generic type) instead of hard coded variants for the standard types. This implies adding a generic parameter to both enums that needs to be added in the whole crate. * Change the parser to allow deciding between different types of scalar values. The problem is basically that the original parser implementation had no way to know whether a parsed integer number is a `i32` or a `i64` (for example). To fix this we added some knowledge of the existing schema to the parser. * Fix some macros and derives to follow the new behavior. This commit also contains an unrelated change about the way `juniper_codegen` resolves items from `juniper`. The `_internal` flag is removed and the resolution is replaced by a macro. The scalar parsing strategy is as follows: * Pass optional type information all the way down in the parser. If a field/type/… does note exist, just do not pass down the type information. * The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc. * If type information exists while parsing a scalar value, try the following: 1. Try parsing the value using that type information. 2. If that fails try parsing the value using the inferred type information from the lexer. * If no type information exists, try parsing the scalar value using the inferred type from the lexer, All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by `juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. Finally, we allow specifying the scalar value representations via an attribute (`#[graphql(scalar = "Type")]`). A default generic implementation is provided.
2018-10-22 22:40:14 -05:00
S: 'a,
QueryT: GraphQLType<S, Context = CtxT> + 'a,
MutationT: GraphQLType<S, Context = CtxT> + 'a,
{
match self {
GraphQLRequest::Single(request) => Either::A(future::poll_fn(move || {
let res = try_ready!(tokio_threadpool::blocking(
|| request.execute(&root_node, &context)
));
let is_ok = res.is_ok();
let body = Body::from(serde_json::to_string_pretty(&res).unwrap());
Ok(Async::Ready((is_ok, body)))
})),
GraphQLRequest::Batch(requests) => {
Either::B(
future::join_all(requests.into_iter().map(move |request| {
// TODO: these clones are sad
let root_node = root_node.clone();
let context = context.clone();
future::poll_fn(move || {
let res = try_ready!(tokio_threadpool::blocking(
|| request.execute(&root_node, &context)
));
let is_ok = res.is_ok();
let body = serde_json::to_string_pretty(&res).unwrap();
Ok(Async::Ready((is_ok, body)))
})
}))
.map(|results| {
let is_ok = results.iter().all(|&(is_ok, _)| is_ok);
// concatenate json bodies as array
// TODO: maybe use Body chunks instead?
let bodies: Vec<_> = results.into_iter().map(|(_, body)| body).collect();
let body = hyper::Body::from(format!("[{}]", bodies.join(",")));
(is_ok, body)
}),
)
}
}
}
}
#[derive(Debug)]
enum GraphQLRequestError {
BodyHyper(hyper::Error),
BodyUtf8(FromUtf8Error),
BodyJSONError(SerdeError),
Variables(SerdeError),
Invalid(String),
}
impl fmt::Display for GraphQLRequestError {
fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
match *self {
GraphQLRequestError::BodyHyper(ref err) => fmt::Display::fmt(err, &mut f),
GraphQLRequestError::BodyUtf8(ref err) => fmt::Display::fmt(err, &mut f),
GraphQLRequestError::BodyJSONError(ref err) => fmt::Display::fmt(err, &mut f),
GraphQLRequestError::Variables(ref err) => fmt::Display::fmt(err, &mut f),
GraphQLRequestError::Invalid(ref err) => fmt::Display::fmt(err, &mut f),
}
}
}
impl Error for GraphQLRequestError {
fn description(&self) -> &str {
match *self {
GraphQLRequestError::BodyHyper(ref err) => err.description(),
GraphQLRequestError::BodyUtf8(ref err) => err.description(),
GraphQLRequestError::BodyJSONError(ref err) => err.description(),
GraphQLRequestError::Variables(ref err) => err.description(),
GraphQLRequestError::Invalid(ref err) => err,
}
}
fn cause(&self) -> Option<&Error> {
match *self {
GraphQLRequestError::BodyHyper(ref err) => Some(err),
GraphQLRequestError::BodyUtf8(ref err) => Some(err),
GraphQLRequestError::BodyJSONError(ref err) => Some(err),
GraphQLRequestError::Variables(ref err) => Some(err),
GraphQLRequestError::Invalid(_) => None,
}
}
}
#[cfg(test)]
mod tests {
use futures::{future, future::Either, Future};
use hyper::service::service_fn;
use hyper::Method;
use hyper::{Body, Response, Server, StatusCode};
use juniper::http::tests as http_tests;
use juniper::tests::model::Database;
use juniper::EmptyMutation;
use juniper::RootNode;
use reqwest;
use reqwest::Response as ReqwestResponse;
use std::sync::Arc;
use std::thread;
use std::time;
use tokio::runtime::Runtime;
struct TestHyperIntegration;
impl http_tests::HTTPIntegration for TestHyperIntegration {
fn get(&self, url: &str) -> http_tests::TestResponse {
let url = format!("http://127.0.0.1:3001/graphql{}", url);
make_test_response(reqwest::get(&url).expect(&format!("failed GET {}", url)))
}
fn post(&self, url: &str, body: &str) -> http_tests::TestResponse {
let url = format!("http://127.0.0.1:3001/graphql{}", url);
let client = reqwest::Client::new();
let res = client
.post(&url)
.body(body.to_string())
.send()
.expect(&format!("failed POST {}", url));
make_test_response(res)
}
}
fn make_test_response(mut response: ReqwestResponse) -> http_tests::TestResponse {
let status_code = response.status().as_u16() as i32;
let body = response.text().unwrap();
let content_type_header = response.headers().get(reqwest::header::CONTENT_TYPE);
let content_type = if let Some(ct) = content_type_header {
format!("{}", ct.to_str().unwrap())
} else {
String::default()
};
http_tests::TestResponse {
status_code,
body: Some(body),
content_type,
}
}
#[test]
fn test_hyper_integration() {
let addr = ([127, 0, 0, 1], 3001).into();
let db = Arc::new(Database::new());
let root_node = Arc::new(RootNode::new(db.clone(), EmptyMutation::<Database>::new()));
let new_service = move || {
let root_node = root_node.clone();
let ctx = db.clone();
service_fn(move |req| {
let root_node = root_node.clone();
let ctx = ctx.clone();
let matches = {
let path = req.uri().path();
match req.method() {
&Method::POST | &Method::GET => path == "/graphql" || path == "/graphql/",
_ => false,
}
};
if matches {
Either::A(super::graphql(root_node, ctx, req))
} else {
let mut response = Response::new(Body::empty());
*response.status_mut() = StatusCode::NOT_FOUND;
Either::B(future::ok(response))
}
})
};
let server = Server::bind(&addr)
.serve(new_service)
.map_err(|e| eprintln!("server error: {}", e));
let mut runtime = Runtime::new().unwrap();
runtime.spawn(server);
thread::sleep(time::Duration::from_millis(10)); // wait 10ms for server to bind
let integration = TestHyperIntegration;
http_tests::run_http_test_suite(&integration);
runtime.shutdown_now().wait().unwrap();
}
}