Remove iron
integration (#1210)
This commit is contained in:
parent
e75cf26995
commit
d7103e2ef2
20 changed files with 6 additions and 754 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -152,7 +152,6 @@ jobs:
|
||||||
- juniper_actix
|
- juniper_actix
|
||||||
- juniper_axum
|
- juniper_axum
|
||||||
- juniper_hyper
|
- juniper_hyper
|
||||||
#- juniper_iron
|
|
||||||
- juniper_rocket
|
- juniper_rocket
|
||||||
- juniper_warp
|
- juniper_warp
|
||||||
os:
|
os:
|
||||||
|
@ -205,7 +204,6 @@ jobs:
|
||||||
- juniper_actix
|
- juniper_actix
|
||||||
- juniper_axum
|
- juniper_axum
|
||||||
- juniper_hyper
|
- juniper_hyper
|
||||||
- juniper_iron
|
|
||||||
- juniper_rocket
|
- juniper_rocket
|
||||||
- juniper_warp
|
- juniper_warp
|
||||||
os:
|
os:
|
||||||
|
@ -332,7 +330,6 @@ jobs:
|
||||||
- juniper_actix
|
- juniper_actix
|
||||||
- juniper_axum
|
- juniper_axum
|
||||||
- juniper_hyper
|
- juniper_hyper
|
||||||
- juniper_iron
|
|
||||||
- juniper_rocket
|
- juniper_rocket
|
||||||
- juniper_warp
|
- juniper_warp
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -5,7 +5,6 @@ members = [
|
||||||
"juniper_codegen",
|
"juniper_codegen",
|
||||||
"juniper",
|
"juniper",
|
||||||
"juniper_hyper",
|
"juniper_hyper",
|
||||||
"juniper_iron",
|
|
||||||
"juniper_rocket",
|
"juniper_rocket",
|
||||||
"juniper_subscriptions",
|
"juniper_subscriptions",
|
||||||
"juniper_graphql_ws",
|
"juniper_graphql_ws",
|
||||||
|
|
|
@ -18,7 +18,7 @@ GraphQL schemas as convenient as Rust will allow.
|
||||||
|
|
||||||
Juniper does not include a web server - instead it provides building blocks to
|
Juniper does not include a web server - instead it provides building blocks to
|
||||||
make integration with existing servers straightforward. It optionally provides a
|
make integration with existing servers straightforward. It optionally provides a
|
||||||
pre-built integration for the [Actix][actix], [Hyper][hyper], [Iron][iron], [Rocket], and [Warp][warp] frameworks, including
|
pre-built integration for the [Actix][actix], [Hyper][hyper], [Rocket], and [Warp][warp] frameworks, including
|
||||||
embedded [Graphiql][graphiql] and [GraphQL Playground][playground] for easy debugging.
|
embedded [Graphiql][graphiql] and [GraphQL Playground][playground] for easy debugging.
|
||||||
|
|
||||||
- [Cargo crate](https://crates.io/crates/juniper)
|
- [Cargo crate](https://crates.io/crates/juniper)
|
||||||
|
@ -42,7 +42,7 @@ For specific information about macros, types and the Juniper api, the
|
||||||
You can also check out the [Star Wars schema][test_schema_rs] to see a complex
|
You can also check out the [Star Wars schema][test_schema_rs] to see a complex
|
||||||
example including polymorphism with traits and interfaces.
|
example including polymorphism with traits and interfaces.
|
||||||
For an example of web framework integration,
|
For an example of web framework integration,
|
||||||
see the [actix][actix_examples], [hyper][hyper_examples], [rocket][rocket_examples], [iron][iron_examples], and [warp][warp_examples] examples folders.
|
see the [actix][actix_examples], [axum][axum_examples], [hyper][hyper_examples], [rocket][rocket_examples], and [warp][warp_examples] examples folders.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
@ -82,7 +82,6 @@ your Schemas automatically.
|
||||||
- [axum][axum]
|
- [axum][axum]
|
||||||
- [hyper][hyper]
|
- [hyper][hyper]
|
||||||
- [rocket][rocket]
|
- [rocket][rocket]
|
||||||
- [iron][iron]
|
|
||||||
- [warp][warp]
|
- [warp][warp]
|
||||||
|
|
||||||
## Guides & Examples
|
## Guides & Examples
|
||||||
|
@ -98,16 +97,15 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
|
||||||
[graphql]: http://graphql.org
|
[graphql]: http://graphql.org
|
||||||
[graphiql]: https://github.com/graphql/graphiql
|
[graphiql]: https://github.com/graphql/graphiql
|
||||||
[playground]: https://github.com/prisma/graphql-playground
|
[playground]: https://github.com/prisma/graphql-playground
|
||||||
[iron]: https://github.com/iron/iron
|
|
||||||
[graphql_spec]: https://spec.graphql.org/October2021
|
[graphql_spec]: https://spec.graphql.org/October2021
|
||||||
[schema_language]: https://graphql.org/learn/schema/#type-language
|
[schema_language]: https://graphql.org/learn/schema/#type-language
|
||||||
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
|
[schema_approach]: https://blog.logrocket.com/code-first-vs-schema-first-development-graphql/
|
||||||
[test_schema_rs]: https://github.com/graphql-rust/juniper/blob/master/juniper/src/tests/fixtures/starwars/schema.rs
|
[test_schema_rs]: https://github.com/graphql-rust/juniper/blob/master/juniper/src/tests/fixtures/starwars/schema.rs
|
||||||
[tokio]: https://github.com/tokio-rs/tokio
|
[tokio]: https://github.com/tokio-rs/tokio
|
||||||
[actix_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_actix/examples
|
[actix_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_actix/examples
|
||||||
|
[axum_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_axum/examples
|
||||||
[hyper_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_hyper/examples
|
[hyper_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_hyper/examples
|
||||||
[rocket_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_rocket/examples
|
[rocket_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_rocket/examples
|
||||||
[iron_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_iron/examples
|
|
||||||
[hyper]: https://hyper.rs
|
[hyper]: https://hyper.rs
|
||||||
[rocket]: https://rocket.rs
|
[rocket]: https://rocket.rs
|
||||||
[book]: https://graphql-rust.github.io
|
[book]: https://graphql-rust.github.io
|
||||||
|
|
|
@ -12,7 +12,7 @@ GraphQL schemas as convenient as possible as Rust will allow.
|
||||||
|
|
||||||
Juniper does not include a web server - instead it provides building blocks to
|
Juniper does not include a web server - instead it provides building blocks to
|
||||||
make integration with existing servers straightforward. It optionally provides a
|
make integration with existing servers straightforward. It optionally provides a
|
||||||
pre-built integration for the [Hyper][hyper], [Iron][iron], [Rocket], and [Warp][warp] frameworks, including
|
pre-built integration for the [Hyper][hyper], [Rocket], and [Warp][warp] frameworks, including
|
||||||
embedded [Graphiql][graphiql] for easy debugging.
|
embedded [Graphiql][graphiql] for easy debugging.
|
||||||
|
|
||||||
- [Cargo crate](https://crates.io/crates/juniper)
|
- [Cargo crate](https://crates.io/crates/juniper)
|
||||||
|
@ -47,7 +47,6 @@ your Schemas automatically.
|
||||||
|
|
||||||
- [hyper][hyper]
|
- [hyper][hyper]
|
||||||
- [rocket][rocket]
|
- [rocket][rocket]
|
||||||
- [iron][iron]
|
|
||||||
- [warp][warp]
|
- [warp][warp]
|
||||||
|
|
||||||
## API Stability
|
## API Stability
|
||||||
|
@ -56,13 +55,11 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
|
||||||
|
|
||||||
[graphql]: http://graphql.org
|
[graphql]: http://graphql.org
|
||||||
[graphiql]: https://github.com/graphql/graphiql
|
[graphiql]: https://github.com/graphql/graphiql
|
||||||
[iron]: https://github.com/iron/iron
|
|
||||||
[graphql_spec]: https://spec.graphql.org/October2021
|
[graphql_spec]: https://spec.graphql.org/October2021
|
||||||
[test_schema_rs]: https://github.com/graphql-rust/juniper/blob/master/juniper/src/tests/schema.rs
|
[test_schema_rs]: https://github.com/graphql-rust/juniper/blob/master/juniper/src/tests/schema.rs
|
||||||
[tokio]: https://github.com/tokio-rs/tokio
|
[tokio]: https://github.com/tokio-rs/tokio
|
||||||
[hyper_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_hyper/examples
|
[hyper_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_hyper/examples
|
||||||
[rocket_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_rocket/examples
|
[rocket_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_rocket/examples
|
||||||
[iron_examples]: https://github.com/graphql-rust/juniper/tree/master/juniper_iron/examples
|
|
||||||
[hyper]: https://hyper.rs
|
[hyper]: https://hyper.rs
|
||||||
[rocket]: https://rocket.rs
|
[rocket]: https://rocket.rs
|
||||||
[book]: https://graphql-rust.github.io
|
[book]: https://graphql-rust.github.io
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
- [Official Server Integrations](servers/official.md) - [Hyper](servers/hyper.md)
|
- [Official Server Integrations](servers/official.md) - [Hyper](servers/hyper.md)
|
||||||
- [Warp](servers/warp.md)
|
- [Warp](servers/warp.md)
|
||||||
- [Rocket](servers/rocket.md)
|
- [Rocket](servers/rocket.md)
|
||||||
- [Iron](servers/iron.md)
|
|
||||||
- [Hyper](servers/hyper.md)
|
- [Hyper](servers/hyper.md)
|
||||||
- [Third Party Integrations](servers/third-party.md)
|
- [Third Party Integrations](servers/third-party.md)
|
||||||
|
|
||||||
|
|
|
@ -205,6 +205,5 @@ fn main() {
|
||||||
[hyper]: servers/hyper.md
|
[hyper]: servers/hyper.md
|
||||||
[warp]: servers/warp.md
|
[warp]: servers/warp.md
|
||||||
[rocket]: servers/rocket.md
|
[rocket]: servers/rocket.md
|
||||||
[iron]: servers/iron.md
|
|
||||||
[tutorial]: ./tutorial.html
|
[tutorial]: ./tutorial.html
|
||||||
[graphql_object]: https://docs.rs/juniper/latest/juniper/macro.graphql_object.html
|
[graphql_object]: https://docs.rs/juniper/latest/juniper/macro.graphql_object.html
|
||||||
|
|
|
@ -9,7 +9,6 @@ third-party integration crates that will get you there.
|
||||||
- [Official Server Integrations](official.md)
|
- [Official Server Integrations](official.md)
|
||||||
- [Warp](warp.md)
|
- [Warp](warp.md)
|
||||||
- [Rocket](rocket.md)
|
- [Rocket](rocket.md)
|
||||||
- [Iron](iron.md)
|
|
||||||
- [Hyper](hyper.md)
|
- [Hyper](hyper.md)
|
||||||
- [Third Party Integrations](third-party.md)
|
- [Third Party Integrations](third-party.md)
|
||||||
- [Actix-Web](https://github.com/actix/examples/tree/master/graphql/juniper)
|
- [Actix-Web](https://github.com/actix/examples/tree/master/graphql/juniper)
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
# Integrating with Iron
|
|
||||||
|
|
||||||
[Iron] is a library that's been around for a while in the Rust sphere but lately
|
|
||||||
hasn't seen much of development. Nevertheless, it's still a solid library with a
|
|
||||||
familiar request/response/middleware architecture that works on Rust's stable
|
|
||||||
channel.
|
|
||||||
|
|
||||||
Juniper's Iron integration is contained in the `juniper_iron` crate:
|
|
||||||
|
|
||||||
!FILENAME Cargo.toml
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
juniper = "0.16.0"
|
|
||||||
juniper_iron = "0.8.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
Included in the source is a [small
|
|
||||||
example](https://github.com/graphql-rust/juniper_iron/blob/master/examples/iron_server.rs)
|
|
||||||
which sets up a basic GraphQL and [GraphiQL] handler.
|
|
||||||
|
|
||||||
## Basic integration
|
|
||||||
|
|
||||||
Let's start with a minimal schema and just get a GraphQL endpoint up and
|
|
||||||
running. We use [mount] to attach the GraphQL handler at `/graphql`.
|
|
||||||
|
|
||||||
The `context_factory` function will be executed on every request and can be used
|
|
||||||
to set up database connections, read session token information from cookies, and
|
|
||||||
set up other global data that the schema might require.
|
|
||||||
|
|
||||||
In this example, we won't use any global data so we just return an empty value.
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
extern crate juniper;
|
|
||||||
extern crate juniper_iron;
|
|
||||||
extern crate iron;
|
|
||||||
extern crate mount;
|
|
||||||
|
|
||||||
use mount::Mount;
|
|
||||||
use iron::prelude::*;
|
|
||||||
use juniper::EmptyMutation;
|
|
||||||
use juniper_iron::GraphQLHandler;
|
|
||||||
|
|
||||||
fn context_factory(_: &mut Request) -> IronResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Root;
|
|
||||||
|
|
||||||
#[juniper::graphql_object]
|
|
||||||
impl Root {
|
|
||||||
fn foo() -> String {
|
|
||||||
"Bar".into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# #[allow(unreachable_code, unused_variables)]
|
|
||||||
fn main() {
|
|
||||||
let mut mount = Mount::new();
|
|
||||||
|
|
||||||
let graphql_endpoint = GraphQLHandler::new(
|
|
||||||
context_factory,
|
|
||||||
Root,
|
|
||||||
EmptyMutation::<()>::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
mount.mount("/graphql", graphql_endpoint);
|
|
||||||
|
|
||||||
let chain = Chain::new(mount);
|
|
||||||
|
|
||||||
# return;
|
|
||||||
Iron::new(chain).http("0.0.0.0:8080").unwrap();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Accessing data from the request
|
|
||||||
|
|
||||||
If you want to access e.g. the source IP address of the request from a field
|
|
||||||
resolver, you need to pass this data using Juniper's [context feature](../types/objects/using_contexts.md).
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
# extern crate juniper;
|
|
||||||
# extern crate juniper_iron;
|
|
||||||
# extern crate iron;
|
|
||||||
# use iron::prelude::*;
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
|
|
||||||
struct Context {
|
|
||||||
remote_addr: SocketAddr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl juniper::Context for Context {}
|
|
||||||
|
|
||||||
fn context_factory(req: &mut Request) -> IronResult<Context> {
|
|
||||||
Ok(Context {
|
|
||||||
remote_addr: req.remote_addr
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Root;
|
|
||||||
|
|
||||||
#[juniper::graphql_object(
|
|
||||||
Context = Context,
|
|
||||||
)]
|
|
||||||
impl Root {
|
|
||||||
field my_addr(context: &Context) -> String {
|
|
||||||
format!("Hello, you're coming from {}", context.remote_addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# fn main() {
|
|
||||||
# let _graphql_endpoint = juniper_iron::GraphQLHandler::new(
|
|
||||||
# context_factory,
|
|
||||||
# Root,
|
|
||||||
# juniper::EmptyMutation::<Context>::new(),
|
|
||||||
# );
|
|
||||||
# }
|
|
||||||
```
|
|
||||||
|
|
||||||
[iron]: https://github.com/iron/iron
|
|
||||||
[graphiql]: https://github.com/graphql/graphiql
|
|
||||||
[mount]: https://github.com/iron/mount
|
|
|
@ -5,5 +5,4 @@ libraries.
|
||||||
|
|
||||||
- [Warp](warp.md)
|
- [Warp](warp.md)
|
||||||
- [Rocket](rocket.md)
|
- [Rocket](rocket.md)
|
||||||
- [Iron](iron.md)
|
|
||||||
- [Hyper](hyper.md)
|
- [Hyper](hyper.md)
|
||||||
|
|
|
@ -4,8 +4,7 @@ The context type is a feature in Juniper that lets field resolvers access global
|
||||||
data, most commonly database connections or authentication information. The
|
data, most commonly database connections or authentication information. The
|
||||||
context is usually created from a _context factory_. How this is defined is
|
context is usually created from a _context factory_. How this is defined is
|
||||||
specific to the framework integration you're using, so check out the
|
specific to the framework integration you're using, so check out the
|
||||||
documentation for either the [Iron](../../servers/iron.md) or [Rocket](../../servers/rocket.md)
|
documentation for [Rocket](../../servers/rocket.md) integration.
|
||||||
integration.
|
|
||||||
|
|
||||||
In this chapter, we'll show you how to define a context type and use it in field
|
In this chapter, we'll show you how to define a context type and use it in field
|
||||||
resolvers. Let's say that we have a simple user database in a `HashMap`:
|
resolvers. Let's say that we have a simple user database in a `HashMap`:
|
||||||
|
|
|
@ -60,7 +60,6 @@ As an exception to other [GraphQL] libraries for other languages, [Juniper] buil
|
||||||
- [`actix-web`] ([`juniper_actix`] crate)
|
- [`actix-web`] ([`juniper_actix`] crate)
|
||||||
- [`axum`] ([`juniper_axum`] crate)
|
- [`axum`] ([`juniper_axum`] crate)
|
||||||
- [`hyper`] ([`juniper_hyper`] crate)
|
- [`hyper`] ([`juniper_hyper`] crate)
|
||||||
- [`iron`] ([`juniper_iron`] crate)
|
|
||||||
- [`rocket`] ([`juniper_rocket`] crate)
|
- [`rocket`] ([`juniper_rocket`] crate)
|
||||||
- [`warp`] ([`juniper_warp`] crate)
|
- [`warp`] ([`juniper_warp`] crate)
|
||||||
|
|
||||||
|
@ -90,11 +89,9 @@ This project is licensed under [BSD 2-Clause License](https://github.com/graphql
|
||||||
[`juniper_actix`]: https://docs.rs/juniper_actix
|
[`juniper_actix`]: https://docs.rs/juniper_actix
|
||||||
[`juniper_axum`]: https://docs.rs/juniper_axum
|
[`juniper_axum`]: https://docs.rs/juniper_axum
|
||||||
[`juniper_hyper`]: https://docs.rs/juniper_hyper
|
[`juniper_hyper`]: https://docs.rs/juniper_hyper
|
||||||
[`juniper_iron`]: https://docs.rs/juniper_iron
|
|
||||||
[`juniper_rocket`]: https://docs.rs/juniper_rocket
|
[`juniper_rocket`]: https://docs.rs/juniper_rocket
|
||||||
[`juniper_warp`]: https://docs.rs/juniper_warp
|
[`juniper_warp`]: https://docs.rs/juniper_warp
|
||||||
[`hyper`]: https://docs.rs/hyper
|
[`hyper`]: https://docs.rs/hyper
|
||||||
[`iron`]: https://docs.rs/iron
|
|
||||||
[`rocket`]: https://docs.rs/rocket
|
[`rocket`]: https://docs.rs/rocket
|
||||||
[`rust_decimal`]: https://docs.rs/rust_decimal
|
[`rust_decimal`]: https://docs.rs/rust_decimal
|
||||||
[`time`]: https://docs.rs/time
|
[`time`]: https://docs.rs/time
|
||||||
|
|
|
@ -19,11 +19,6 @@ exactly = 1
|
||||||
search = "juniper = \"[^\"]+\""
|
search = "juniper = \"[^\"]+\""
|
||||||
replace = "juniper = \"{{version}}\""
|
replace = "juniper = \"{{version}}\""
|
||||||
[[pre-release-replacements]]
|
[[pre-release-replacements]]
|
||||||
file = "../book/src/servers/iron.md"
|
|
||||||
exactly = 1
|
|
||||||
search = "juniper = \"[^\"]+\""
|
|
||||||
replace = "juniper = \"{{version}}\""
|
|
||||||
[[pre-release-replacements]]
|
|
||||||
file = "../book/src/servers/rocket.md"
|
file = "../book/src/servers/rocket.md"
|
||||||
exactly = 1
|
exactly = 1
|
||||||
search = "juniper = \"[^\"]+\""
|
search = "juniper = \"[^\"]+\""
|
||||||
|
@ -58,12 +53,6 @@ exactly = 2
|
||||||
search = "juniper = \\{ version = \"[^\"]+\""
|
search = "juniper = \\{ version = \"[^\"]+\""
|
||||||
replace = "juniper = { version = \"{{version}}\""
|
replace = "juniper = { version = \"{{version}}\""
|
||||||
|
|
||||||
[[pre-release-replacements]]
|
|
||||||
file = "../juniper_iron/Cargo.toml"
|
|
||||||
exactly = 2
|
|
||||||
search = "juniper = \\{ version = \"[^\"]+\""
|
|
||||||
replace = "juniper = { version = \"{{version}}\""
|
|
||||||
|
|
||||||
[[pre-release-replacements]]
|
[[pre-release-replacements]]
|
||||||
file = "../juniper_rocket/Cargo.toml"
|
file = "../juniper_rocket/Cargo.toml"
|
||||||
exactly = 2
|
exactly = 2
|
||||||
|
|
|
@ -626,7 +626,7 @@ where
|
||||||
/// Access the current context
|
/// Access the current context
|
||||||
///
|
///
|
||||||
/// You usually provide the context when calling the top-level `execute`
|
/// You usually provide the context when calling the top-level `execute`
|
||||||
/// function, or using the context factory in the Iron integration.
|
/// function, or using the context factory.
|
||||||
pub fn context(&self) -> &'r CtxT {
|
pub fn context(&self) -> &'r CtxT {
|
||||||
self.context
|
self.context
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
`juniper_iron` changelog
|
|
||||||
========================
|
|
||||||
|
|
||||||
All user visible changes to `juniper_iron` crate will be documented in this file. This project uses [Semantic Versioning 2.0.0].
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## master
|
|
||||||
|
|
||||||
### BC Breaks
|
|
||||||
|
|
||||||
- Switched to 0.16 version of [`juniper` crate].
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Previous releases
|
|
||||||
|
|
||||||
See [old CHANGELOG](/../../blob/juniper_iron-v0.7.6/juniper_iron/CHANGELOG.md).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[`juniper` crate]: https://docs.rs/juniper
|
|
||||||
[Semantic Versioning 2.0.0]: https://semver.org
|
|
|
@ -1,34 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "juniper_iron"
|
|
||||||
version = "0.8.0-dev"
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.73"
|
|
||||||
description = "`juniper` GraphQL integration with `iron`."
|
|
||||||
license = "BSD-2-Clause"
|
|
||||||
authors = [
|
|
||||||
"Magnus Hallin <mhallin@fastmail.com>",
|
|
||||||
"Christoph Herzog <chris@theduke.at>",
|
|
||||||
]
|
|
||||||
documentation = "https://docs.rs/juniper_iron"
|
|
||||||
homepage = "https://github.com/graphql-rust/juniper/tree/master/juniper_iron"
|
|
||||||
repository = "https://github.com/graphql-rust/juniper"
|
|
||||||
readme = "README.md"
|
|
||||||
categories = ["web-programming", "web-programming::http-server"]
|
|
||||||
keywords = ["apollo", "graphql", "iron", "juniper"]
|
|
||||||
exclude = ["/examples/", "/release.toml"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
futures = "0.3.22"
|
|
||||||
iron = ">= 0.5, < 0.7"
|
|
||||||
juniper = { version = "0.16.0-dev", path = "../juniper", default-features = false }
|
|
||||||
serde_json = "1.0.18"
|
|
||||||
urlencoded = ">= 0.5, < 0.7"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
iron-test = "0.6"
|
|
||||||
juniper = { version = "0.16.0-dev", path = "../juniper", features = ["expose-test-schema"] }
|
|
||||||
logger = "0.4"
|
|
||||||
mount = "0.4"
|
|
||||||
percent-encoding = "2.0"
|
|
||||||
router = "0.6"
|
|
||||||
url = "2.0"
|
|
|
@ -1,25 +0,0 @@
|
||||||
BSD 2-Clause License
|
|
||||||
|
|
||||||
Copyright (c) 2016-2022, Magnus Hallin
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,46 +0,0 @@
|
||||||
`juniper_iron` crate
|
|
||||||
====================
|
|
||||||
|
|
||||||
[![Crates.io](https://img.shields.io/crates/v/juniper_iron.svg?maxAge=2592000)](https://crates.io/crates/juniper_iron)
|
|
||||||
[![Documentation](https://docs.rs/juniper_iron/badge.svg)](https://docs.rs/juniper_iron)
|
|
||||||
[![CI](https://github.com/graphql-rust/juniper/workflows/CI/badge.svg?branch=master "CI")](https://github.com/graphql-rust/juniper/actions?query=workflow%3ACI+branch%3Amaster)
|
|
||||||
[![Rust 1.73+](https://img.shields.io/badge/rustc-1.73+-lightgray.svg "Rust 1.73+")](https://blog.rust-lang.org/2023/10/05/Rust-1.73.0.html)
|
|
||||||
|
|
||||||
- [Changelog](https://github.com/graphql-rust/juniper/blob/master/juniper_iron/CHANGELOG.md)
|
|
||||||
|
|
||||||
[`iron`] web framework integration for [`juniper`] ([GraphQL] implementation for [Rust]).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
For documentation, including guides and examples, check out [Juniper Book].
|
|
||||||
|
|
||||||
A basic usage example can also be found in the [API docs][`juniper_iron`].
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
Check [`examples/iron_server.rs`][1] for example code of a working [`iron`] server with [GraphQL] handlers.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is licensed under [BSD 2-Clause License](https://github.com/graphql-rust/juniper/blob/master/juniper_iron/LICENSE).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[`iron`]: https://docs.rs/iron
|
|
||||||
[`juniper`]: https://docs.rs/juniper
|
|
||||||
[`juniper_iron`]: https://docs.rs/juniper_iron
|
|
||||||
[GraphQL]: http://graphql.org
|
|
||||||
[Juniper Book]: https://graphql-rust.github.io
|
|
||||||
[Rust]: https://www.rust-lang.org
|
|
||||||
|
|
||||||
[1]: https://github.com/graphql-rust/juniper/blob/master/juniper_iron/examples/iron_server.rs
|
|
|
@ -1,39 +0,0 @@
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use iron::prelude::*;
|
|
||||||
use juniper::{
|
|
||||||
tests::fixtures::starwars::schema::{Database, Query},
|
|
||||||
DefaultScalarValue, EmptyMutation, EmptySubscription,
|
|
||||||
};
|
|
||||||
use juniper_iron::{GraphQLHandler, GraphiQLHandler};
|
|
||||||
use logger::Logger;
|
|
||||||
use mount::Mount;
|
|
||||||
|
|
||||||
fn context_factory(_: &mut Request) -> IronResult<Database> {
|
|
||||||
Ok(Database::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut mount = Mount::new();
|
|
||||||
|
|
||||||
let graphql_endpoint = <GraphQLHandler<_, _, _, _, _, DefaultScalarValue>>::new(
|
|
||||||
context_factory,
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<Database>::new(),
|
|
||||||
EmptySubscription::<Database>::new(),
|
|
||||||
);
|
|
||||||
let graphiql_endpoint = GraphiQLHandler::new("/graphql", None);
|
|
||||||
|
|
||||||
mount.mount("/", graphiql_endpoint);
|
|
||||||
mount.mount("/graphql", graphql_endpoint);
|
|
||||||
|
|
||||||
let (logger_before, logger_after) = Logger::new(None);
|
|
||||||
|
|
||||||
let mut chain = Chain::new(mount);
|
|
||||||
chain.link_before(logger_before);
|
|
||||||
chain.link_after(logger_after);
|
|
||||||
|
|
||||||
let host = env::var("LISTEN").unwrap_or_else(|_| "0.0.0.0:8080".into());
|
|
||||||
println!("GraphQL server started on {host}");
|
|
||||||
Iron::new(chain).http(host.as_str()).unwrap();
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
[[pre-release-replacements]]
|
|
||||||
file = "../book/src/servers/iron.md"
|
|
||||||
exactly = 1
|
|
||||||
search = "juniper_iron = \"[^\"]+\""
|
|
||||||
replace = "juniper_iron = \"{{version}}\""
|
|
||||||
|
|
||||||
[[pre-release-replacements]]
|
|
||||||
file = "CHANGELOG.md"
|
|
||||||
max = 1
|
|
||||||
min = 0
|
|
||||||
search = "## master"
|
|
||||||
replace = "## [{{version}}] · {{date}}\n[{{version}}]: /../../tree/{{crate_name}}-v{{version}}/{{crate_name}}"
|
|
||||||
|
|
||||||
[[pre-release-replacements]]
|
|
||||||
file = "README.md"
|
|
||||||
exactly = 3
|
|
||||||
search = "graphql-rust/juniper/blob/[^/]+/"
|
|
||||||
replace = "graphql-rust/juniper/blob/{{crate_name}}-v{{version}}/"
|
|
|
@ -1,410 +0,0 @@
|
||||||
#![doc = include_str!("../README.md")]
|
|
||||||
|
|
||||||
use std::{error::Error, fmt, io::Read, ops::Deref as _};
|
|
||||||
|
|
||||||
use iron::{
|
|
||||||
headers::ContentType,
|
|
||||||
itry, method,
|
|
||||||
middleware::Handler,
|
|
||||||
mime::{Mime, TopLevel},
|
|
||||||
prelude::*,
|
|
||||||
status,
|
|
||||||
};
|
|
||||||
use juniper::{
|
|
||||||
http, http::GraphQLBatchRequest, DefaultScalarValue, GraphQLType, InputValue, RootNode,
|
|
||||||
ScalarValue,
|
|
||||||
};
|
|
||||||
use serde_json::error::Error as SerdeError;
|
|
||||||
use urlencoded::{UrlDecodingError, UrlEncodedQuery};
|
|
||||||
|
|
||||||
/// Handler that executes `GraphQL` queries in the given schema
|
|
||||||
///
|
|
||||||
/// The handler responds to GET requests and POST requests only. In GET
|
|
||||||
/// requests, the query should be supplied in the `query` URL parameter, e.g.
|
|
||||||
/// `http://localhost:3000/graphql?query={hero{name}}`.
|
|
||||||
///
|
|
||||||
/// POST requests support both queries and variables. POST a JSON document to
|
|
||||||
/// this endpoint containing the field `"query"` and optionally `"variables"`.
|
|
||||||
/// The variables should be a JSON object containing the variable to value
|
|
||||||
/// mapping.
|
|
||||||
pub struct GraphQLHandler<
|
|
||||||
'a,
|
|
||||||
CtxFactory,
|
|
||||||
Query,
|
|
||||||
Mutation,
|
|
||||||
Subscription,
|
|
||||||
CtxT,
|
|
||||||
S = DefaultScalarValue,
|
|
||||||
> where
|
|
||||||
S: ScalarValue,
|
|
||||||
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
|
|
||||||
CtxT: 'static,
|
|
||||||
Query: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
|
|
||||||
Mutation: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
|
|
||||||
Subscription: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
context_factory: CtxFactory,
|
|
||||||
root_node: RootNode<'a, Query, Mutation, Subscription, S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler that renders `GraphiQL` - a graphical query editor interface
|
|
||||||
pub struct GraphiQLHandler {
|
|
||||||
graphql_url: String,
|
|
||||||
subscription_url: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler that renders `GraphQL Playground` - a graphical query editor interface
|
|
||||||
pub struct PlaygroundHandler {
|
|
||||||
graphql_url: String,
|
|
||||||
subscription_url: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_single_value<T>(mut values: Vec<T>) -> IronResult<T> {
|
|
||||||
if values.len() == 1 {
|
|
||||||
Ok(values.remove(0))
|
|
||||||
} else {
|
|
||||||
Err(GraphQLIronError::InvalidData("Duplicate URL query parameter").into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_url_param(params: Option<Vec<String>>) -> IronResult<Option<String>> {
|
|
||||||
if let Some(values) = params {
|
|
||||||
get_single_value(values).map(Some)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_variable_param<S>(params: Option<Vec<String>>) -> IronResult<Option<InputValue<S>>>
|
|
||||||
where
|
|
||||||
S: ScalarValue,
|
|
||||||
{
|
|
||||||
params
|
|
||||||
.map(|vals| {
|
|
||||||
serde_json::from_str::<InputValue<S>>(get_single_value(vals)?.as_ref())
|
|
||||||
.map_err(|e| GraphQLIronError::Serde(e).into())
|
|
||||||
})
|
|
||||||
.transpose()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, CtxFactory, Query, Mutation, Subscription, CtxT, S>
|
|
||||||
GraphQLHandler<'a, CtxFactory, Query, Mutation, Subscription, CtxT, S>
|
|
||||||
where
|
|
||||||
S: ScalarValue + Send + Sync + 'static,
|
|
||||||
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
|
|
||||||
CtxT: Send + Sync + 'static,
|
|
||||||
Query: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
|
|
||||||
Mutation: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
|
|
||||||
Subscription: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
/// Build a new GraphQL handler
|
|
||||||
///
|
|
||||||
/// The context factory will receive the Iron request object and is
|
|
||||||
/// expected to construct a context object for the given schema. This can
|
|
||||||
/// be used to construct e.g. database connections or similar data that
|
|
||||||
/// the schema needs to execute the query.
|
|
||||||
pub fn new(
|
|
||||||
context_factory: CtxFactory,
|
|
||||||
query: Query,
|
|
||||||
mutation: Mutation,
|
|
||||||
subscription: Subscription,
|
|
||||||
) -> Self {
|
|
||||||
GraphQLHandler {
|
|
||||||
context_factory,
|
|
||||||
root_node: RootNode::new_with_scalar_value(query, mutation, subscription),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_get(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest<S>> {
|
|
||||||
let url_query = req
|
|
||||||
.get_mut::<UrlEncodedQuery>()
|
|
||||||
.map_err(GraphQLIronError::Url)?;
|
|
||||||
|
|
||||||
let query = parse_url_param(url_query.remove("query"))?
|
|
||||||
.ok_or(GraphQLIronError::InvalidData("No query provided"))?;
|
|
||||||
let operation_name = parse_url_param(url_query.remove("operationName"))?;
|
|
||||||
let variables = parse_variable_param(url_query.remove("variables"))?;
|
|
||||||
|
|
||||||
Ok(GraphQLBatchRequest::Single(http::GraphQLRequest::new(
|
|
||||||
query,
|
|
||||||
operation_name,
|
|
||||||
variables,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_post_json(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest<S>> {
|
|
||||||
let mut payload = String::new();
|
|
||||||
itry!(req.body.read_to_string(&mut payload));
|
|
||||||
|
|
||||||
Ok(
|
|
||||||
serde_json::from_str::<GraphQLBatchRequest<S>>(payload.as_str())
|
|
||||||
.map_err(GraphQLIronError::Serde)?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_post_graphql(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest<S>> {
|
|
||||||
let mut payload = String::new();
|
|
||||||
itry!(req.body.read_to_string(&mut payload));
|
|
||||||
|
|
||||||
Ok(GraphQLBatchRequest::Single(http::GraphQLRequest::new(
|
|
||||||
payload, None, None,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute_sync(
|
|
||||||
&self,
|
|
||||||
context: &CtxT,
|
|
||||||
request: GraphQLBatchRequest<S>,
|
|
||||||
) -> IronResult<Response> {
|
|
||||||
let response = request.execute_sync(&self.root_node, context);
|
|
||||||
let content_type = "application/json".parse::<Mime>().unwrap();
|
|
||||||
let json = serde_json::to_string_pretty(&response).unwrap();
|
|
||||||
let status = if response.is_ok() {
|
|
||||||
status::Ok
|
|
||||||
} else {
|
|
||||||
status::BadRequest
|
|
||||||
};
|
|
||||||
Ok(Response::with((content_type, status, json)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GraphiQLHandler {
|
|
||||||
/// Build a new GraphiQL handler targeting the specified URL.
|
|
||||||
///
|
|
||||||
/// The provided URL should point to the URL of the attached `GraphQLHandler`. It can be
|
|
||||||
/// relative, so a common value could be `"/graphql"`.
|
|
||||||
pub fn new(graphql_url: &str, subscription_url: Option<&str>) -> GraphiQLHandler {
|
|
||||||
GraphiQLHandler {
|
|
||||||
graphql_url: graphql_url.into(),
|
|
||||||
subscription_url: subscription_url.map(Into::into),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlaygroundHandler {
|
|
||||||
/// Build a new GraphQL Playground handler targeting the specified URL.
|
|
||||||
///
|
|
||||||
/// The provided URL should point to the URL of the attached `GraphQLHandler`. It can be
|
|
||||||
/// relative, so a common value could be `"/graphql"`.
|
|
||||||
pub fn new(graphql_url: &str, subscription_url: Option<&str>) -> PlaygroundHandler {
|
|
||||||
PlaygroundHandler {
|
|
||||||
graphql_url: graphql_url.into(),
|
|
||||||
subscription_url: subscription_url.map(Into::into),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<CtxFactory, Query, Mutation, Subscription, CtxT, S> Handler
|
|
||||||
for GraphQLHandler<'static, CtxFactory, Query, Mutation, Subscription, CtxT, S>
|
|
||||||
where
|
|
||||||
S: ScalarValue + Sync + Send + 'static,
|
|
||||||
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
|
|
||||||
CtxT: Send + Sync + 'static,
|
|
||||||
Query: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
|
|
||||||
Mutation: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
|
|
||||||
Subscription: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
fn handle(&self, req: &mut Request) -> IronResult<Response> {
|
|
||||||
let context = (self.context_factory)(req)?;
|
|
||||||
|
|
||||||
let graphql_request = match req.method {
|
|
||||||
method::Get => self.handle_get(req)?,
|
|
||||||
method::Post => match req.headers.get::<ContentType>().map(ContentType::deref) {
|
|
||||||
Some(Mime(TopLevel::Application, sub_lvl, _)) => match sub_lvl.as_str() {
|
|
||||||
"json" => self.handle_post_json(req)?,
|
|
||||||
"graphql" => self.handle_post_graphql(req)?,
|
|
||||||
_ => return Ok(Response::with(status::BadRequest)),
|
|
||||||
},
|
|
||||||
_ => return Ok(Response::with(status::BadRequest)),
|
|
||||||
},
|
|
||||||
_ => return Ok(Response::with(status::MethodNotAllowed)),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.execute_sync(&context, graphql_request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler for GraphiQLHandler {
|
|
||||||
fn handle(&self, _: &mut Request) -> IronResult<Response> {
|
|
||||||
let content_type = "text/html; charset=utf-8".parse::<Mime>().unwrap();
|
|
||||||
|
|
||||||
Ok(Response::with((
|
|
||||||
content_type,
|
|
||||||
status::Ok,
|
|
||||||
juniper::http::graphiql::graphiql_source(
|
|
||||||
&self.graphql_url,
|
|
||||||
self.subscription_url.as_deref(),
|
|
||||||
),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler for PlaygroundHandler {
|
|
||||||
fn handle(&self, _: &mut Request) -> IronResult<Response> {
|
|
||||||
let content_type = "text/html; charset=utf-8".parse::<Mime>().unwrap();
|
|
||||||
|
|
||||||
Ok(Response::with((
|
|
||||||
content_type,
|
|
||||||
status::Ok,
|
|
||||||
juniper::http::playground::playground_source(
|
|
||||||
&self.graphql_url,
|
|
||||||
self.subscription_url.as_deref(),
|
|
||||||
),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum GraphQLIronError {
|
|
||||||
Serde(SerdeError),
|
|
||||||
Url(UrlDecodingError),
|
|
||||||
InvalidData(&'static str),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for GraphQLIronError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
GraphQLIronError::Serde(err) => fmt::Display::fmt(err, f),
|
|
||||||
GraphQLIronError::Url(err) => fmt::Display::fmt(err, f),
|
|
||||||
GraphQLIronError::InvalidData(err) => fmt::Display::fmt(err, f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for GraphQLIronError {
|
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
|
||||||
match *self {
|
|
||||||
GraphQLIronError::Serde(ref err) => Some(err),
|
|
||||||
GraphQLIronError::Url(ref err) => Some(err),
|
|
||||||
GraphQLIronError::InvalidData(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GraphQLIronError> for IronError {
|
|
||||||
fn from(err: GraphQLIronError) -> IronError {
|
|
||||||
let message = err.to_string();
|
|
||||||
IronError::new(err, (status::BadRequest, message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use iron::{
|
|
||||||
headers::ContentType,
|
|
||||||
mime::{Mime, SubLevel, TopLevel},
|
|
||||||
Handler, Headers, Url,
|
|
||||||
};
|
|
||||||
use iron_test::{request, response};
|
|
||||||
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
|
|
||||||
|
|
||||||
use juniper::{
|
|
||||||
http::tests as http_tests,
|
|
||||||
tests::fixtures::starwars::schema::{Database, Query},
|
|
||||||
DefaultScalarValue, EmptyMutation, EmptySubscription,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::GraphQLHandler;
|
|
||||||
|
|
||||||
/// https://url.spec.whatwg.org/#query-state
|
|
||||||
const QUERY_ENCODE_SET: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
|
|
||||||
|
|
||||||
// This is ugly but it works. `iron_test` just dumps the path/url in headers
|
|
||||||
// and newer `hyper` doesn't allow unescaped "{" or "}".
|
|
||||||
fn fixup_url(url: &str) -> String {
|
|
||||||
let url = Url::parse(&format!("http://localhost:3000{url}")).expect("url to parse");
|
|
||||||
let path: String = url
|
|
||||||
.path()
|
|
||||||
.iter()
|
|
||||||
.map(|x| x.to_string())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("/");
|
|
||||||
format!(
|
|
||||||
"http://localhost:3000{path}?{}",
|
|
||||||
utf8_percent_encode(url.query().unwrap_or(""), QUERY_ENCODE_SET),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestIronIntegration;
|
|
||||||
|
|
||||||
impl http_tests::HttpIntegration for TestIronIntegration {
|
|
||||||
fn get(&self, url: &str) -> http_tests::TestResponse {
|
|
||||||
request::get(&fixup_url(url), Headers::new(), &make_handler())
|
|
||||||
.map(make_test_response)
|
|
||||||
.unwrap_or_else(make_test_error_response)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_json(&self, url: &str, body: &str) -> http_tests::TestResponse {
|
|
||||||
let mut headers = Headers::new();
|
|
||||||
headers.set(ContentType::json());
|
|
||||||
request::post(&fixup_url(url), headers, body, &make_handler())
|
|
||||||
.map(make_test_response)
|
|
||||||
.unwrap_or_else(make_test_error_response)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_graphql(&self, url: &str, body: &str) -> http_tests::TestResponse {
|
|
||||||
let mut headers = Headers::new();
|
|
||||||
headers.set(ContentType(Mime(
|
|
||||||
TopLevel::Application,
|
|
||||||
SubLevel::Ext("graphql".into()),
|
|
||||||
vec![],
|
|
||||||
)));
|
|
||||||
request::post(&fixup_url(url), headers, body, &make_handler())
|
|
||||||
.map(make_test_response)
|
|
||||||
.unwrap_or_else(make_test_error_response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_iron_integration() {
|
|
||||||
let integration = TestIronIntegration;
|
|
||||||
|
|
||||||
http_tests::run_http_test_suite(&integration);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn context_factory(_: &mut Request) -> IronResult<Database> {
|
|
||||||
Ok(Database::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_test_error_response(_: IronError) -> http_tests::TestResponse {
|
|
||||||
// For now all errors return the same status code.
|
|
||||||
// `juniper_iron` users can choose to do something different if desired.
|
|
||||||
http_tests::TestResponse {
|
|
||||||
status_code: 400,
|
|
||||||
body: None,
|
|
||||||
content_type: "application/json".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_test_response(response: Response) -> http_tests::TestResponse {
|
|
||||||
let status_code = response
|
|
||||||
.status
|
|
||||||
.expect("No status code returned from handler")
|
|
||||||
.to_u16() as i32;
|
|
||||||
let content_type = String::from_utf8(
|
|
||||||
response
|
|
||||||
.headers
|
|
||||||
.get_raw("content-type")
|
|
||||||
.expect("No content type header from handler")[0]
|
|
||||||
.clone(),
|
|
||||||
)
|
|
||||||
.expect("Content-type header invalid UTF-8");
|
|
||||||
let body = response::extract_body_to_string(response);
|
|
||||||
|
|
||||||
http_tests::TestResponse {
|
|
||||||
status_code,
|
|
||||||
body: Some(body),
|
|
||||||
content_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_handler() -> Box<dyn Handler> {
|
|
||||||
Box::new(<GraphQLHandler<_, _, _, _, _, DefaultScalarValue>>::new(
|
|
||||||
context_factory,
|
|
||||||
Query,
|
|
||||||
EmptyMutation::<Database>::new(),
|
|
||||||
EmptySubscription::<Database>::new(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue