2018-09-07 16:28:56 -05:00
|
|
|
/*!
|
|
|
|
|
|
|
|
# juniper_warp
|
|
|
|
|
|
|
|
This repository contains the [warp][warp] web server integration for
|
|
|
|
[Juniper][Juniper], a [GraphQL][GraphQL] implementation for Rust.
|
|
|
|
|
|
|
|
## Documentation
|
|
|
|
|
|
|
|
For documentation, including guides and examples, check out [Juniper][Juniper].
|
|
|
|
|
|
|
|
A basic usage example can also be found in the [Api documentation][documentation].
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
Check [examples/warp_server][example] for example code of a working warp
|
|
|
|
server with GraphQL handlers.
|
|
|
|
|
|
|
|
## Links
|
|
|
|
|
|
|
|
* [Juniper][Juniper]
|
|
|
|
* [Api Reference][documentation]
|
|
|
|
* [warp][warp]
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
This project is under the BSD-2 license.
|
|
|
|
|
|
|
|
Check the LICENSE file for details.
|
|
|
|
|
|
|
|
[warp]: https://github.com/seanmonstar/warp
|
|
|
|
[Juniper]: https://github.com/graphql-rust/juniper
|
|
|
|
[GraphQL]: http://graphql.org
|
|
|
|
[documentation]: https://docs.rs/juniper_warp
|
2018-12-30 08:12:58 -06:00
|
|
|
[example]: https://github.com/graphql-rust/juniper/blob/master/juniper_warp/examples/warp_server
|
2018-09-07 16:28:56 -05:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#![deny(missing_docs)]
|
|
|
|
#![deny(warnings)]
|
2018-09-24 05:07:44 -05:00
|
|
|
#![doc(html_root_url = "https://docs.rs/juniper_warp/0.2.0")]
|
2018-09-07 16:28:56 -05:00
|
|
|
|
2018-10-20 02:59:35 -05:00
|
|
|
use futures::{future::poll_fn, Future};
|
|
|
|
use juniper::{DefaultScalarValue, InputValue, ScalarRefValue, ScalarValue};
|
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 serde::Deserialize;
|
2018-09-07 16:28:56 -05:00
|
|
|
use std::sync::Arc;
|
|
|
|
use warp::{filters::BoxedFilter, Filter};
|
|
|
|
|
2019-04-22 19:19:06 -05:00
|
|
|
#[derive(Debug, serde_derive::Deserialize, PartialEq)]
|
2018-09-07 16:28:56 -05:00
|
|
|
#[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 GraphQLBatchRequest<S = DefaultScalarValue>
|
|
|
|
where
|
|
|
|
S: ScalarValue,
|
|
|
|
{
|
|
|
|
Single(juniper::http::GraphQLRequest<S>),
|
|
|
|
Batch(Vec<juniper::http::GraphQLRequest<S>>),
|
2018-09-07 16:28:56 -05:00
|
|
|
}
|
|
|
|
|
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> GraphQLBatchRequest<S>
|
|
|
|
where
|
|
|
|
S: ScalarValue,
|
|
|
|
for<'b> &'b S: ScalarRefValue<'b>,
|
|
|
|
{
|
2018-09-07 16:28:56 -05:00
|
|
|
pub fn execute<'a, CtxT, QueryT, MutationT>(
|
|
|
|
&'a 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: &'a juniper::RootNode<QueryT, MutationT, S>,
|
2018-09-07 16:28:56 -05:00
|
|
|
context: &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
|
|
|
) -> GraphQLBatchResponse<'a, S>
|
2018-09-07 16:28:56 -05:00
|
|
|
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
|
|
|
QueryT: juniper::GraphQLType<S, Context = CtxT>,
|
|
|
|
MutationT: juniper::GraphQLType<S, Context = CtxT>,
|
2018-09-07 16:28:56 -05:00
|
|
|
{
|
|
|
|
match self {
|
|
|
|
&GraphQLBatchRequest::Single(ref request) => {
|
|
|
|
GraphQLBatchResponse::Single(request.execute(root_node, context))
|
|
|
|
}
|
|
|
|
&GraphQLBatchRequest::Batch(ref requests) => GraphQLBatchResponse::Batch(
|
|
|
|
requests
|
|
|
|
.iter()
|
|
|
|
.map(|request| request.execute(root_node, context))
|
|
|
|
.collect(),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-22 19:19:06 -05:00
|
|
|
#[derive(serde_derive::Serialize)]
|
2018-09-07 16:28:56 -05:00
|
|
|
#[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
|
|
|
enum GraphQLBatchResponse<'a, S = DefaultScalarValue>
|
|
|
|
where
|
|
|
|
S: ScalarValue,
|
|
|
|
{
|
|
|
|
Single(juniper::http::GraphQLResponse<'a, S>),
|
|
|
|
Batch(Vec<juniper::http::GraphQLResponse<'a, S>>),
|
2018-09-07 16:28:56 -05:00
|
|
|
}
|
|
|
|
|
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<'a, S> GraphQLBatchResponse<'a, S>
|
|
|
|
where
|
|
|
|
S: ScalarValue,
|
|
|
|
{
|
2018-09-07 16:28:56 -05:00
|
|
|
fn is_ok(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
GraphQLBatchResponse::Single(res) => res.is_ok(),
|
|
|
|
GraphQLBatchResponse::Batch(reses) => reses.iter().all(|res| res.is_ok()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Make a filter for graphql endpoint.
|
|
|
|
///
|
|
|
|
/// The `schema` argument is your juniper schema.
|
|
|
|
///
|
|
|
|
/// The `context_extractor` argument should be a filter that provides the GraphQL context required by the schema.
|
|
|
|
///
|
2018-10-20 02:59:35 -05:00
|
|
|
/// In order to avoid blocking, this helper will use the `tokio_threadpool` threadpool created by hyper to resolve GraphQL requests.
|
2018-09-07 16:28:56 -05:00
|
|
|
///
|
|
|
|
/// Example:
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # extern crate juniper_warp;
|
|
|
|
/// # extern crate juniper;
|
|
|
|
/// # extern crate warp;
|
|
|
|
/// #
|
|
|
|
/// # use std::sync::Arc;
|
|
|
|
/// # use warp::Filter;
|
|
|
|
/// # use juniper::{EmptyMutation, RootNode};
|
|
|
|
/// # use juniper_warp::make_graphql_filter;
|
|
|
|
/// #
|
|
|
|
/// # fn main() {
|
|
|
|
/// type UserId = String;
|
|
|
|
/// # #[derive(Debug)]
|
|
|
|
/// struct AppState(Vec<i64>);
|
|
|
|
/// struct ExampleContext(Arc<AppState>, UserId);
|
|
|
|
///
|
|
|
|
/// struct QueryRoot;
|
|
|
|
///
|
2019-05-13 11:33:45 -05:00
|
|
|
/// #[juniper::object(
|
2019-05-07 03:56:33 -05:00
|
|
|
/// Context = ExampleContext
|
|
|
|
/// )]
|
|
|
|
/// impl QueryRoot {
|
|
|
|
/// fn say_hello(context: &ExampleContext) -> String {
|
|
|
|
/// format!(
|
|
|
|
/// "good morning {}, the app state is {:?}",
|
|
|
|
/// context.1,
|
|
|
|
/// context.0
|
|
|
|
/// )
|
2018-09-07 16:28:56 -05:00
|
|
|
/// }
|
2019-05-07 03:56:33 -05:00
|
|
|
/// }
|
2018-09-07 16:28:56 -05:00
|
|
|
///
|
|
|
|
/// let schema = RootNode::new(QueryRoot, EmptyMutation::new());
|
|
|
|
///
|
|
|
|
/// let app_state = Arc::new(AppState(vec![3, 4, 5]));
|
|
|
|
/// let app_state = warp::any().map(move || app_state.clone());
|
|
|
|
///
|
|
|
|
/// let context_extractor = warp::any()
|
|
|
|
/// .and(warp::header::<String>("authorization"))
|
|
|
|
/// .and(app_state)
|
|
|
|
/// .map(|auth_header: String, app_state: Arc<AppState>| {
|
|
|
|
/// let user_id = auth_header; // we believe them
|
|
|
|
/// ExampleContext(app_state, user_id)
|
|
|
|
/// })
|
|
|
|
/// .boxed();
|
|
|
|
///
|
|
|
|
/// let graphql_filter = make_graphql_filter(schema, context_extractor);
|
|
|
|
///
|
|
|
|
/// let graphql_endpoint = warp::path("graphql")
|
|
|
|
/// .and(warp::post2())
|
|
|
|
/// .and(graphql_filter);
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2018-10-20 02:59:35 -05:00
|
|
|
pub fn make_graphql_filter<Query, Mutation, Context, 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
|
|
|
schema: juniper::RootNode<'static, Query, Mutation, S>,
|
2018-09-07 16:28:56 -05:00
|
|
|
context_extractor: BoxedFilter<(Context,)>,
|
|
|
|
) -> BoxedFilter<(warp::http::Response<Vec<u8>>,)>
|
|
|
|
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>,
|
2018-09-07 16:28:56 -05:00
|
|
|
Context: Send + '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
|
|
|
Query: juniper::GraphQLType<S, Context = Context, TypeInfo = ()> + Send + Sync + 'static,
|
|
|
|
Mutation: juniper::GraphQLType<S, Context = Context, TypeInfo = ()> + Send + Sync + 'static,
|
2018-09-07 16:28:56 -05:00
|
|
|
{
|
|
|
|
let schema = Arc::new(schema);
|
|
|
|
let post_schema = schema.clone();
|
|
|
|
|
|
|
|
let handle_post_request =
|
2018-10-20 02:59:35 -05:00
|
|
|
move |context: Context, request: GraphQLBatchRequest<S>| -> Response {
|
2018-09-07 16:28:56 -05:00
|
|
|
let schema = post_schema.clone();
|
|
|
|
Box::new(
|
2018-10-20 02:59:35 -05:00
|
|
|
poll_fn(move || {
|
|
|
|
tokio_threadpool::blocking(|| {
|
|
|
|
let response = request.execute(&schema, &context);
|
|
|
|
Ok((serde_json::to_vec(&response)?, response.is_ok()))
|
|
|
|
})
|
2018-12-19 12:27:49 -06:00
|
|
|
})
|
|
|
|
.and_then(|result| ::futures::future::done(Ok(build_response(result))))
|
2018-10-27 20:45:47 -05:00
|
|
|
.map_err(|e: tokio_threadpool::BlockingError| warp::reject::custom(e)),
|
2018-09-07 16:28:56 -05:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
let post_filter = warp::post2()
|
|
|
|
.and(context_extractor.clone())
|
|
|
|
.and(warp::body::json())
|
|
|
|
.and_then(handle_post_request);
|
|
|
|
|
|
|
|
let handle_get_request = move |context: Context,
|
2018-10-20 02:59:35 -05:00
|
|
|
mut request: std::collections::HashMap<String, String>|
|
2018-09-07 16:28:56 -05:00
|
|
|
-> Response {
|
|
|
|
let schema = schema.clone();
|
|
|
|
Box::new(
|
2018-10-20 02:59:35 -05:00
|
|
|
poll_fn(move || {
|
|
|
|
tokio_threadpool::blocking(|| {
|
|
|
|
let variables = match request.remove("variables") {
|
|
|
|
None => None,
|
|
|
|
Some(vs) => serde_json::from_str(&vs)?,
|
|
|
|
};
|
|
|
|
|
|
|
|
let graphql_request = juniper::http::GraphQLRequest::new(
|
|
|
|
request.remove("query").ok_or_else(|| {
|
2019-04-22 19:19:06 -05:00
|
|
|
failure::format_err!("Missing GraphQL query string in query parameters")
|
2018-10-20 02:59:35 -05:00
|
|
|
})?,
|
|
|
|
request.get("operation_name").map(|s| s.to_owned()),
|
|
|
|
variables,
|
|
|
|
);
|
|
|
|
|
|
|
|
let response = graphql_request.execute(&schema, &context);
|
|
|
|
Ok((serde_json::to_vec(&response)?, response.is_ok()))
|
|
|
|
})
|
2018-12-19 12:27:49 -06:00
|
|
|
})
|
|
|
|
.and_then(|result| ::futures::future::done(Ok(build_response(result))))
|
2018-10-27 20:45:47 -05:00
|
|
|
.map_err(|e: tokio_threadpool::BlockingError| warp::reject::custom(e)),
|
2018-09-07 16:28:56 -05:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
let get_filter = warp::get2()
|
|
|
|
.and(context_extractor.clone())
|
|
|
|
.and(warp::filters::query::query())
|
|
|
|
.and_then(handle_get_request);
|
|
|
|
|
|
|
|
get_filter.or(post_filter).unify().boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn build_response(
|
|
|
|
response: Result<(Vec<u8>, bool), failure::Error>,
|
|
|
|
) -> warp::http::Response<Vec<u8>> {
|
|
|
|
match response {
|
|
|
|
Ok((body, is_ok)) => warp::http::Response::builder()
|
|
|
|
.status(if is_ok { 200 } else { 400 })
|
|
|
|
.header("content-type", "application/json")
|
|
|
|
.body(body)
|
|
|
|
.expect("response is valid"),
|
|
|
|
Err(_) => warp::http::Response::builder()
|
|
|
|
.status(warp::http::StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
.body(Vec::new())
|
|
|
|
.expect("status code is valid"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-20 02:59:35 -05:00
|
|
|
type Response =
|
2019-06-18 22:12:13 -05:00
|
|
|
Box<dyn Future<Item = warp::http::Response<Vec<u8>>, Error = warp::reject::Rejection> + Send>;
|
2018-10-20 02:59:35 -05:00
|
|
|
|
2018-09-07 16:28:56 -05:00
|
|
|
/// Create a filter that replies with an HTML page containing GraphiQL. This does not handle routing, so you can mount it on any endpoint.
|
|
|
|
///
|
|
|
|
/// For example:
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # extern crate warp;
|
|
|
|
/// # extern crate juniper_warp;
|
|
|
|
/// #
|
|
|
|
/// # use warp::Filter;
|
2018-10-22 23:05:01 -05:00
|
|
|
/// # use juniper_warp::graphiql_filter;
|
2018-09-07 16:28:56 -05:00
|
|
|
/// #
|
|
|
|
/// # fn main() {
|
2018-10-22 23:05:01 -05:00
|
|
|
/// let graphiql_route = warp::path("graphiql").and(graphiql_filter("/graphql"));
|
2018-09-07 16:28:56 -05:00
|
|
|
/// # }
|
|
|
|
/// ```
|
2018-10-22 23:05:01 -05:00
|
|
|
pub fn graphiql_filter(
|
2018-09-07 16:28:56 -05:00
|
|
|
graphql_endpoint_url: &'static str,
|
|
|
|
) -> warp::filters::BoxedFilter<(warp::http::Response<Vec<u8>>,)> {
|
|
|
|
warp::any()
|
|
|
|
.map(move || graphiql_response(graphql_endpoint_url))
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn graphiql_response(graphql_endpoint_url: &'static str) -> warp::http::Response<Vec<u8>> {
|
|
|
|
warp::http::Response::builder()
|
|
|
|
.header("content-type", "text/html;charset=utf-8")
|
|
|
|
.body(juniper::graphiql::graphiql_source(graphql_endpoint_url).into_bytes())
|
|
|
|
.expect("response is valid")
|
|
|
|
}
|
|
|
|
|
2019-01-25 22:58:01 -06:00
|
|
|
/// Create a filter that replies with an HTML page containing GraphQL Playground. This does not handle routing, so you can mount it on any endpoint.
|
|
|
|
pub fn playground_filter(
|
|
|
|
graphql_endpoint_url: &'static str,
|
|
|
|
) -> warp::filters::BoxedFilter<(warp::http::Response<Vec<u8>>,)> {
|
|
|
|
warp::any()
|
|
|
|
.map(move || playground_response(graphql_endpoint_url))
|
|
|
|
.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn playground_response(graphql_endpoint_url: &'static str) -> warp::http::Response<Vec<u8>> {
|
|
|
|
warp::http::Response::builder()
|
|
|
|
.header("content-type", "text/html;charset=utf-8")
|
|
|
|
.body(juniper::http::playground::playground_source(graphql_endpoint_url).into_bytes())
|
|
|
|
.expect("response is valid")
|
|
|
|
}
|
|
|
|
|
2018-09-07 16:28:56 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use warp::http;
|
|
|
|
use warp::test::request;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn graphiql_response_does_not_panic() {
|
|
|
|
graphiql_response("/abcd");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn graphiql_endpoint_matches() {
|
|
|
|
let filter = warp::get2()
|
|
|
|
.and(warp::path("graphiql"))
|
2018-10-22 23:05:01 -05:00
|
|
|
.and(graphiql_filter("/graphql"));
|
2018-09-07 16:28:56 -05:00
|
|
|
let result = request()
|
|
|
|
.method("GET")
|
|
|
|
.path("/graphiql")
|
|
|
|
.header("accept", "text/html")
|
|
|
|
.filter(&filter);
|
|
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn graphiql_endpoint_returns_graphiql_source() {
|
|
|
|
let filter = warp::get2()
|
|
|
|
.and(warp::path("dogs-api"))
|
|
|
|
.and(warp::path("graphiql"))
|
2018-10-22 23:05:01 -05:00
|
|
|
.and(graphiql_filter("/dogs-api/graphql"));
|
2018-09-07 16:28:56 -05:00
|
|
|
let response = request()
|
|
|
|
.method("GET")
|
|
|
|
.path("/dogs-api/graphiql")
|
|
|
|
.header("accept", "text/html")
|
|
|
|
.reply(&filter);
|
|
|
|
|
|
|
|
assert_eq!(response.status(), http::StatusCode::OK);
|
|
|
|
assert_eq!(
|
|
|
|
response.headers().get("content-type").unwrap(),
|
|
|
|
"text/html;charset=utf-8"
|
|
|
|
);
|
|
|
|
let body = String::from_utf8(response.body().to_vec()).unwrap();
|
|
|
|
|
|
|
|
assert!(body.contains("<script>var GRAPHQL_URL = '/dogs-api/graphql';</script>"));
|
|
|
|
}
|
|
|
|
|
2019-01-25 22:58:01 -06:00
|
|
|
#[test]
|
|
|
|
fn playground_endpoint_matches() {
|
|
|
|
let filter = warp::get2()
|
|
|
|
.and(warp::path("playground"))
|
|
|
|
.and(playground_filter("/graphql"));
|
|
|
|
let result = request()
|
|
|
|
.method("GET")
|
|
|
|
.path("/playground")
|
|
|
|
.header("accept", "text/html")
|
|
|
|
.filter(&filter);
|
|
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn playground_endpoint_returns_playground_source() {
|
|
|
|
let filter = warp::get2()
|
|
|
|
.and(warp::path("dogs-api"))
|
|
|
|
.and(warp::path("playground"))
|
|
|
|
.and(playground_filter("/dogs-api/graphql"));
|
|
|
|
let response = request()
|
|
|
|
.method("GET")
|
|
|
|
.path("/dogs-api/playground")
|
|
|
|
.header("accept", "text/html")
|
|
|
|
.reply(&filter);
|
|
|
|
|
|
|
|
assert_eq!(response.status(), http::StatusCode::OK);
|
|
|
|
assert_eq!(
|
|
|
|
response.headers().get("content-type").unwrap(),
|
|
|
|
"text/html;charset=utf-8"
|
|
|
|
);
|
|
|
|
let body = String::from_utf8(response.body().to_vec()).unwrap();
|
|
|
|
|
|
|
|
assert!(body.contains("GraphQLPlayground.init(root, { endpoint: '/dogs-api/graphql' })"));
|
|
|
|
}
|
|
|
|
|
2018-09-07 16:28:56 -05:00
|
|
|
#[test]
|
|
|
|
fn graphql_handler_works_json_post() {
|
|
|
|
use juniper::tests::model::Database;
|
2019-05-18 11:07:51 -05:00
|
|
|
use juniper::tests::schema::Query;
|
2018-09-07 16:28:56 -05:00
|
|
|
use juniper::{EmptyMutation, RootNode};
|
|
|
|
|
2019-05-18 11:07:51 -05:00
|
|
|
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Database>>;
|
2018-09-07 16:28:56 -05:00
|
|
|
|
2019-05-18 11:07:51 -05:00
|
|
|
let schema: Schema = RootNode::new(Query, EmptyMutation::<Database>::new());
|
2018-09-07 16:28:56 -05:00
|
|
|
|
|
|
|
let state = warp::any().map(move || Database::new());
|
|
|
|
let filter = warp::path("graphql2").and(make_graphql_filter(schema, state.boxed()));
|
|
|
|
|
|
|
|
let response = request()
|
|
|
|
.method("POST")
|
|
|
|
.path("/graphql2")
|
|
|
|
.header("accept", "application/json")
|
|
|
|
.header("content-type", "application/json")
|
|
|
|
.body(r##"{ "variables": null, "query": "{ hero(episode: NEW_HOPE) { name } }" }"##)
|
|
|
|
.reply(&filter);
|
|
|
|
|
|
|
|
assert_eq!(response.status(), http::StatusCode::OK);
|
|
|
|
assert_eq!(
|
|
|
|
response.headers().get("content-type").unwrap(),
|
|
|
|
"application/json",
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
String::from_utf8(response.body().to_vec()).unwrap(),
|
|
|
|
r#"{"data":{"hero":{"name":"R2-D2"}}}"#
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn batch_requests_work() {
|
|
|
|
use juniper::tests::model::Database;
|
2019-05-18 11:07:51 -05:00
|
|
|
use juniper::tests::schema::Query;
|
2018-09-07 16:28:56 -05:00
|
|
|
use juniper::{EmptyMutation, RootNode};
|
|
|
|
|
2019-05-18 11:07:51 -05:00
|
|
|
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Database>>;
|
2018-09-07 16:28:56 -05:00
|
|
|
|
2019-05-18 11:07:51 -05:00
|
|
|
let schema: Schema = RootNode::new(Query, EmptyMutation::<Database>::new());
|
2018-09-07 16:28:56 -05:00
|
|
|
|
|
|
|
let state = warp::any().map(move || Database::new());
|
|
|
|
let filter = warp::path("graphql2").and(make_graphql_filter(schema, state.boxed()));
|
|
|
|
|
|
|
|
let response = request()
|
|
|
|
.method("POST")
|
|
|
|
.path("/graphql2")
|
|
|
|
.header("accept", "application/json")
|
|
|
|
.header("content-type", "application/json")
|
|
|
|
.body(
|
|
|
|
r##"[
|
|
|
|
{ "variables": null, "query": "{ hero(episode: NEW_HOPE) { name } }" },
|
|
|
|
{ "variables": null, "query": "{ hero(episode: EMPIRE) { id name } }" }
|
|
|
|
]"##,
|
2018-12-19 12:27:49 -06:00
|
|
|
)
|
|
|
|
.reply(&filter);
|
2018-09-07 16:28:56 -05:00
|
|
|
|
|
|
|
assert_eq!(response.status(), http::StatusCode::OK);
|
|
|
|
assert_eq!(
|
|
|
|
String::from_utf8(response.body().to_vec()).unwrap(),
|
|
|
|
r#"[{"data":{"hero":{"name":"R2-D2"}}},{"data":{"hero":{"id":"1000","name":"Luke Skywalker"}}}]"#
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
response.headers().get("content-type").unwrap(),
|
|
|
|
"application/json",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn batch_request_deserialization_can_fail() {
|
|
|
|
let json = r#"blah"#;
|
|
|
|
let result: Result<GraphQLBatchRequest, _> = serde_json::from_str(json);
|
|
|
|
|
|
|
|
assert!(result.is_err());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests_http_harness {
|
|
|
|
use super::*;
|
|
|
|
use juniper::http::tests::{run_http_test_suite, HTTPIntegration, TestResponse};
|
|
|
|
use juniper::tests::model::Database;
|
2019-05-18 11:07:51 -05:00
|
|
|
use juniper::tests::schema::Query;
|
2018-09-07 16:28:56 -05:00
|
|
|
use juniper::EmptyMutation;
|
|
|
|
use juniper::RootNode;
|
|
|
|
use warp;
|
|
|
|
use warp::Filter;
|
|
|
|
|
2019-05-18 11:07:51 -05:00
|
|
|
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Database>>;
|
2018-09-07 16:28:56 -05:00
|
|
|
|
|
|
|
fn warp_server() -> warp::filters::BoxedFilter<(warp::http::Response<Vec<u8>>,)> {
|
2019-05-18 11:07:51 -05:00
|
|
|
let schema: Schema = RootNode::new(Query, EmptyMutation::<Database>::new());
|
2018-09-07 16:28:56 -05:00
|
|
|
|
|
|
|
let state = warp::any().map(move || Database::new());
|
2018-10-27 20:45:47 -05:00
|
|
|
let filter = warp::filters::path::end().and(make_graphql_filter(schema, state.boxed()));
|
2018-09-07 16:28:56 -05:00
|
|
|
|
|
|
|
filter.boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TestWarpIntegration {
|
|
|
|
filter: warp::filters::BoxedFilter<(warp::http::Response<Vec<u8>>,)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// This can't be implemented with the From trait since TestResponse is not defined in this crate.
|
|
|
|
fn test_response_from_http_response(response: warp::http::Response<Vec<u8>>) -> TestResponse {
|
|
|
|
TestResponse {
|
|
|
|
status_code: response.status().as_u16() as i32,
|
|
|
|
body: Some(String::from_utf8(response.body().to_owned()).unwrap()),
|
|
|
|
content_type: response
|
|
|
|
.headers()
|
|
|
|
.get("content-type")
|
|
|
|
.expect("missing content-type header in warp response")
|
|
|
|
.to_str()
|
|
|
|
.expect("invalid content-type string")
|
|
|
|
.to_owned(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HTTPIntegration for TestWarpIntegration {
|
|
|
|
fn get(&self, url: &str) -> TestResponse {
|
|
|
|
use percent_encoding::{percent_encode, DEFAULT_ENCODE_SET};
|
|
|
|
let url: String = percent_encode(url.replace("/?", "").as_bytes(), DEFAULT_ENCODE_SET)
|
|
|
|
.into_iter()
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join("");
|
|
|
|
|
|
|
|
let response = warp::test::request()
|
|
|
|
.method("GET")
|
|
|
|
.path(&format!("/?{}", url))
|
|
|
|
.filter(&self.filter)
|
|
|
|
.unwrap_or_else(|rejection| {
|
|
|
|
warp::http::Response::builder()
|
|
|
|
.status(rejection.status())
|
|
|
|
.header("content-type", "application/json")
|
|
|
|
.body(Vec::new())
|
|
|
|
.unwrap()
|
|
|
|
});
|
|
|
|
test_response_from_http_response(response)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn post(&self, url: &str, body: &str) -> TestResponse {
|
|
|
|
let response = warp::test::request()
|
|
|
|
.method("POST")
|
|
|
|
.header("content-type", "application/json")
|
|
|
|
.path(url)
|
|
|
|
.body(body)
|
|
|
|
.filter(&self.filter)
|
|
|
|
.unwrap_or_else(|rejection| {
|
|
|
|
warp::http::Response::builder()
|
|
|
|
.status(rejection.status())
|
|
|
|
.header("content-type", "application/json")
|
|
|
|
.body(Vec::new())
|
|
|
|
.unwrap()
|
|
|
|
});
|
|
|
|
test_response_from_http_response(response)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_warp_integration() {
|
|
|
|
let integration = TestWarpIntegration {
|
|
|
|
filter: warp_server(),
|
|
|
|
};
|
|
|
|
|
|
|
|
run_http_test_suite(&integration);
|
|
|
|
}
|
|
|
|
}
|