Skip to content

Commit 7617102

Browse files
btielenilslvLegNeatotyranron
authored
Provide axum integration (#1088, #986, #1184)
- create `juniper_axum` crate in Cargo workspace - implement `graphql` default `axum` handler for processing GraphQL requests - implement `extract::JuniperRequest` and `response::JuniperResponse` for custom processing GraphQL requests - implement `subscriptions::graphql_transport_ws()` default `axum` handler for processing the new `graphql-transport-ws` GraphQL over WebSocket Protocol - implement `subscriptions::graphql_ws()` default `axum` handler for processing the legacy `graphql-ws` GraphQL over WebSocket Protocol - implement `subscriptions::serve_graphql_transport_ws()` function for custom processing the new `graphql-transport-ws` GraphQL over WebSocket Protocol - implement `subscriptions::serve_graphql_ws()` function for custom processing the legacy `graphql-ws` GraphQL over WebSocket Protocol - provide `examples/simple.rs` of default `juniper_axum` integration - provide `examples/custom.rs` of custom `juniper_axum` integration Additionally: - fix `junper_actix` crate MSRV to 1.73 - add `test_post_with_variables()` case to integration `juniper::http::tests` Co-authored-by: ilslv <[email protected]> Co-authored-by: Christian Legnitto <[email protected]> Co-authored-by: Kai Ren <[email protected]>
1 parent ba59c95 commit 7617102

File tree

25 files changed

+1854
-25
lines changed

25 files changed

+1854
-25
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ jobs:
115115
- { feature: graphql-ws, crate: juniper_graphql_ws }
116116
- { feature: <none>, crate: juniper_actix }
117117
- { feature: subscriptions, crate: juniper_actix }
118+
- { feature: <none>, crate: juniper_axum }
119+
- { feature: subscriptions, crate: juniper_axum }
118120
- { feature: <none>, crate: juniper_warp }
119121
- { feature: subscriptions, crate: juniper_warp }
120122
runs-on: ubuntu-latest
@@ -148,6 +150,7 @@ jobs:
148150
- juniper_subscriptions
149151
- juniper_graphql_ws
150152
- juniper_actix
153+
- juniper_axum
151154
- juniper_hyper
152155
#- juniper_iron
153156
- juniper_rocket
@@ -200,6 +203,7 @@ jobs:
200203
- juniper_integration_tests
201204
- juniper_codegen_tests
202205
- juniper_actix
206+
- juniper_axum
203207
- juniper_hyper
204208
- juniper_iron
205209
- juniper_rocket
@@ -326,6 +330,7 @@ jobs:
326330
- juniper_subscriptions
327331
- juniper_graphql_ws
328332
- juniper_actix
333+
- juniper_axum
329334
- juniper_hyper
330335
- juniper_iron
331336
- juniper_rocket

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
"juniper_graphql_ws",
1212
"juniper_warp",
1313
"juniper_actix",
14+
"juniper_axum",
1415
"tests/codegen",
1516
"tests/integration",
1617
]

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ your Schemas automatically.
7979
### Web Frameworks
8080

8181
- [actix][actix]
82+
- [axum][axum]
8283
- [hyper][hyper]
8384
- [rocket][rocket]
8485
- [iron][iron]
@@ -93,6 +94,7 @@ your Schemas automatically.
9394
Juniper has not reached 1.0 yet, thus some API instability should be expected.
9495

9596
[actix]: https://actix.rs/
97+
[axum]: https://docs.rs/axum
9698
[graphql]: http://graphql.org
9799
[graphiql]: https://github.com/graphql/graphiql
98100
[playground]: https://github.com/prisma/graphql-playground

juniper/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ As an exception to other [GraphQL] libraries for other languages, [Juniper] buil
5858
### Web servers
5959

6060
- [`actix-web`] ([`juniper_actix`] crate)
61+
- [`axum`] ([`juniper_axum`] crate)
6162
- [`hyper`] ([`juniper_hyper`] crate)
6263
- [`iron`] ([`juniper_iron`] crate)
6364
- [`rocket`] ([`juniper_rocket`] crate)
@@ -81,11 +82,13 @@ This project is licensed under [BSD 2-Clause License](https://github.com/graphql
8182

8283

8384
[`actix-web`]: https://docs.rs/actix-web
85+
[`axum`]: https://docs.rs/axum
8486
[`bigdecimal`]: https://docs.rs/bigdecimal
8587
[`bson`]: https://docs.rs/bson
8688
[`chrono`]: https://docs.rs/chrono
8789
[`chrono-tz`]: https://docs.rs/chrono-tz
8890
[`juniper_actix`]: https://docs.rs/juniper_actix
91+
[`juniper_axum`]: https://docs.rs/juniper_axum
8992
[`juniper_hyper`]: https://docs.rs/juniper_hyper
9093
[`juniper_iron`]: https://docs.rs/juniper_iron
9194
[`juniper_rocket`]: https://docs.rs/juniper_rocket

juniper/release.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ exactly = 2
4040
search = "juniper = \\{ version = \"[^\"]+\""
4141
replace = "juniper = { version = \"{{version}}\""
4242

43+
[[pre-release-replacements]]
44+
file = "../juniper_axum/Cargo.toml"
45+
exactly = 2
46+
search = "juniper = \\{ version = \"[^\"]+\""
47+
replace = "juniper = { version = \"{{version}}\""
48+
4349
[[pre-release-replacements]]
4450
file = "../juniper_graphql_ws/Cargo.toml"
4551
exactly = 1

juniper/src/http/mod.rs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ where
3737
pub operation_name: Option<String>,
3838

3939
/// Optional variables to execute the GraphQL operation with.
40+
// TODO: Use `Variables` instead of `InputValue`?
4041
#[serde(bound(
4142
deserialize = "InputValue<S>: Deserialize<'de>",
4243
serialize = "InputValue<S>: Serialize",
@@ -238,11 +239,11 @@ where
238239
/// A batch operation request.
239240
///
240241
/// Empty batch is considered as invalid value, so cannot be deserialized.
241-
#[serde(deserialize_with = "deserialize_non_empty_vec")]
242+
#[serde(deserialize_with = "deserialize_non_empty_batch")]
242243
Batch(Vec<GraphQLRequest<S>>),
243244
}
244245

245-
fn deserialize_non_empty_vec<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
246+
fn deserialize_non_empty_batch<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
246247
where
247248
D: de::Deserializer<'de>,
248249
T: Deserialize<'de>,
@@ -251,7 +252,10 @@ where
251252

252253
let v = Vec::<T>::deserialize(deserializer)?;
253254
if v.is_empty() {
254-
Err(D::Error::invalid_length(0, &"a positive integer"))
255+
Err(D::Error::invalid_length(
256+
0,
257+
&"non-empty batch of GraphQL requests",
258+
))
255259
} else {
256260
Ok(v)
257261
}
@@ -403,6 +407,9 @@ pub mod tests {
403407
println!(" - test_get_with_variables");
404408
test_get_with_variables(integration);
405409

410+
println!(" - test_post_with_variables");
411+
test_post_with_variables(integration);
412+
406413
println!(" - test_simple_post");
407414
test_simple_post(integration);
408415

@@ -501,13 +508,48 @@ pub mod tests {
501508
"NEW_HOPE",
502509
"EMPIRE",
503510
"JEDI"
504-
],
505-
"homePlanet": "Tatooine",
506-
"name": "Luke Skywalker",
507-
"id": "1000"
508-
}
511+
],
512+
"homePlanet": "Tatooine",
513+
"name": "Luke Skywalker",
514+
"id": "1000"
509515
}
510-
}"#
516+
}
517+
}"#
518+
)
519+
.expect("Invalid JSON constant in test")
520+
);
521+
}
522+
523+
fn test_post_with_variables<T: HttpIntegration>(integration: &T) {
524+
let response = integration.post_json(
525+
"/",
526+
r#"{
527+
"query":
528+
"query($id: String!) { human(id: $id) { id, name, appearsIn, homePlanet } }",
529+
"variables": {"id": "1000"}
530+
}"#,
531+
);
532+
533+
assert_eq!(response.status_code, 200);
534+
assert_eq!(response.content_type, "application/json");
535+
536+
assert_eq!(
537+
unwrap_json_response(&response),
538+
serde_json::from_str::<Json>(
539+
r#"{
540+
"data": {
541+
"human": {
542+
"appearsIn": [
543+
"NEW_HOPE",
544+
"EMPIRE",
545+
"JEDI"
546+
],
547+
"homePlanet": "Tatooine",
548+
"name": "Luke Skywalker",
549+
"id": "1000"
550+
}
551+
}
552+
}"#
511553
)
512554
.expect("Invalid JSON constant in test")
513555
);
@@ -752,7 +794,7 @@ pub mod tests {
752794

753795
#[allow(missing_docs)]
754796
pub async fn run_test_suite<T: WsIntegration>(integration: &T) {
755-
println!("Running `graphql-ws` test suite for integration");
797+
println!("Running `graphql-transport-ws` test suite for integration");
756798

757799
println!(" - graphql_ws::test_simple_subscription");
758800
test_simple_subscription(integration).await;

juniper/src/tests/fixtures/starwars/schema.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::{collections::HashMap, pin::Pin};
44

55
use crate::{graphql_interface, graphql_object, graphql_subscription, Context, GraphQLEnum};
66

7+
#[derive(Clone, Copy, Debug)]
78
pub struct Query;
89

910
#[graphql_object(context = Database)]
@@ -33,6 +34,7 @@ impl Query {
3334
}
3435
}
3536

37+
#[derive(Clone, Copy, Debug)]
3638
pub struct Subscription;
3739

3840
type HumanStream = Pin<Box<dyn futures::Stream<Item = Human> + Send>>;

juniper_actix/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "juniper_actix"
33
version = "0.5.0-dev"
44
edition = "2021"
5-
rust-version = "1.68"
5+
rust-version = "1.73"
66
description = "`juniper` GraphQL integration with `actix-web`."
77
license = "BSD-2-Clause"
88
authors = ["Jordao Rosario <[email protected]>"]
@@ -12,7 +12,7 @@ repository = "https://github.com/graphql-rust/juniper"
1212
readme = "README.md"
1313
categories = ["asynchronous", "web-programming", "web-programming::http-server"]
1414
keywords = ["actix-web", "apollo", "graphql", "juniper", "websocket"]
15-
exclude = ["/examples/", "/release.toml"]
15+
exclude = ["/release.toml"]
1616

1717
[package.metadata.docs.rs]
1818
all-features = true

juniper_actix/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![Crates.io](https://img.shields.io/crates/v/juniper_actix.svg?maxAge=2592000)](https://crates.io/crates/juniper_actix)
55
[![Documentation](https://docs.rs/juniper_actix/badge.svg)](https://docs.rs/juniper_actix)
66
[![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)
7-
[![Rust 1.68+](https://img.shields.io/badge/rustc-1.68+-lightgray.svg "Rust 1.68+")](https://blog.rust-lang.org/2023/03/09/Rust-1.68.0.html)
7+
[![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)
88

99
- [Changelog](https://github.com/graphql-rust/juniper/blob/master/juniper_actix/CHANGELOG.md)
1010

@@ -26,7 +26,7 @@ A basic usage example can also be found in the [API docs][`juniper_actix`].
2626

2727
## Examples
2828

29-
Check [`examples/actix_server.rs`][1] for example code of a working [`actix-web`] server with [GraphQL] handlers.
29+
Check [`examples/subscription.rs`][1] for example code of a working [`actix-web`] server with [GraphQL] handlers.
3030

3131

3232

@@ -46,5 +46,5 @@ This project is licensed under [BSD 2-Clause License](https://github.com/graphql
4646
[Juniper Book]: https://graphql-rust.github.io
4747
[Rust]: https://www.rust-lang.org
4848

49-
[1]: https://github.com/graphql-rust/juniper/blob/master/juniper_actix/examples/actix_server.rs
49+
[1]: https://github.com/graphql-rust/juniper/blob/master/juniper_actix/examples/subscription.rs
5050

juniper_actix/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ where
143143
/// let app = App::new()
144144
/// .route("/", web::get().to(|| graphiql_handler("/graphql", Some("/graphql/subscriptions"))));
145145
/// ```
146-
#[allow(dead_code)]
147146
pub async fn graphiql_handler(
148147
graphql_endpoint_url: &str,
149148
subscriptions_endpoint_url: Option<&'static str>,
@@ -419,7 +418,7 @@ pub mod subscriptions {
419418
/// Possible errors of serving an [`actix_ws`] connection.
420419
#[derive(Debug)]
421420
enum Error {
422-
/// Deserializing of a client or server message failed.
421+
/// Deserializing of a client [`actix_ws::Message`] failed.
423422
Serde(serde_json::Error),
424423

425424
/// Unexpected client [`actix_ws::Message`].

0 commit comments

Comments
 (0)