Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axum integration #1088

Merged
merged 35 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bc7f4a8
Add Error to Connection Stream
btielen Jul 15, 2022
11ecc25
Add post_with_variables test
btielen Jul 22, 2022
2932e6e
Decouple test and implementation
btielen Jul 23, 2022
719eb97
Add juniper_axum
btielen Jul 24, 2022
183302c
Clippy and format
btielen Jul 24, 2022
f9326e5
Merge branch 'master' into axum
ilslv Aug 3, 2022
59ab64a
Corrections
ilslv Aug 3, 2022
7414b4c
Update juniper_axum/README.md
LegNeato Aug 28, 2023
e1a1104
Update juniper_warp/src/lib.rs
LegNeato Aug 28, 2023
fbef4c0
Merge branch 'master' into axum
tyranron Nov 7, 2023
b5d5be8
Revert unrelevant changes
tyranron Nov 7, 2023
f5b0d70
Add to CI
tyranron Nov 7, 2023
2691dc4
Tune up deps
tyranron Nov 7, 2023
5f50474
Refactor solution, vol.1
tyranron Nov 7, 2023
224e30c
Refactor solution, vol.2 [skip ci]
tyranron Nov 7, 2023
072717e
Get rid of `GetQueryVariables`
tyranron Nov 7, 2023
aa03df3
Get rid of `JsonRequestBody` [skip ci]
tyranron Nov 7, 2023
baea7e6
Parametrize with `ScalarValue`
tyranron Nov 8, 2023
8961631
Fix `extract` unit tests
tyranron Nov 8, 2023
2a819fd
Rework `graphql-ws` subscriptions
tyranron Nov 8, 2023
ac97a3a
Fix doc tests
tyranron Nov 8, 2023
4600686
Add `graphql-transport-ws` impl
tyranron Nov 8, 2023
3b3e7e3
Impl auto-selection between new `graphql-transport-ws` and old `graph…
tyranron Nov 8, 2023
4d89d51
Add ready-to-go `graphql` handler
tyranron Nov 8, 2023
ccac700
Add ready-to-go `ws`, `graphql_ws` and `graphql_transport_ws` handlers
tyranron Nov 8, 2023
fa73e0b
Fix docs
tyranron Nov 8, 2023
5491556
Add `release.toml` [skip ci]
tyranron Nov 8, 2023
90bcc3c
Update workspace
tyranron Nov 8, 2023
cac125f
Rework `simple` example
tyranron Nov 8, 2023
afa9521
Rework `strawars` example as `custom`
tyranron Nov 8, 2023
e2561c7
Reworking integration tests, vol.1
tyranron Nov 8, 2023
8ba8de5
Reworking integration tests, vol.2
tyranron Nov 8, 2023
c44f5cd
Fill-up CHANGELOG
tyranron Nov 8, 2023
c9cf94e
Fix tests
tyranron Nov 8, 2023
bb6dcaa
Try fix MSRV
tyranron Nov 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ members = [
"juniper_graphql_ws",
"juniper_warp",
"juniper_actix",
"juniper_axum",
"tests/codegen",
"tests/integration",
]
39 changes: 38 additions & 1 deletion juniper/src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,9 @@ pub mod tests {
println!(" - test_get_with_variables");
test_get_with_variables(integration);

println!(" - test_post_with_variables");
test_post_with_variables(integration);

println!(" - test_simple_post");
test_simple_post(integration);

Expand Down Expand Up @@ -510,6 +513,40 @@ pub mod tests {
);
}

fn test_post_with_variables<T: HttpIntegration>(integration: &T) {
let response = integration.post_json(
"/",
r#"{
"query": "query($id: String!) { human(id: $id) { id, name, appearsIn, homePlanet } }",
"variables": { "id": "1000" }
}"#,
);

assert_eq!(response.status_code, 200);
assert_eq!(response.content_type, "application/json");

assert_eq!(
unwrap_json_response(&response),
serde_json::from_str::<Json>(
r#"{
"data": {
"human": {
"appearsIn": [
"NEW_HOPE",
"EMPIRE",
"JEDI"
],
"homePlanet": "Tatooine",
"name": "Luke Skywalker",
"id": "1000"
}
}
}"#
)
.expect("Invalid JSON constant in test")
);
}

fn test_simple_post<T: HttpIntegration>(integration: &T) {
let response = integration.post_json("/", r#"{"query": "{hero{name}}"}"#);

Expand Down Expand Up @@ -690,7 +727,7 @@ pub mod tests {
r#"{
"type":"connection_error",
"payload":{
"message":"serde error: expected value at line 1 column 1"
"message":"expected value at line 1 column 1"
}
}"#
.into(),
Expand Down
17 changes: 10 additions & 7 deletions juniper/src/schema/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl DeprecationStatus {
}

/// Scalar type metadata
#[derive(Clone)]
pub struct ScalarMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
Expand All @@ -61,7 +62,7 @@ pub type InputValueParseFn<S> = for<'b> fn(&'b InputValue<S>) -> Result<(), Fiel
pub type ScalarTokenParseFn<S> = for<'b> fn(ScalarToken<'b>) -> Result<S, ParseError>;

/// List type metadata
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ListMeta<'a> {
#[doc(hidden)]
pub of_type: Type<'a>,
Expand All @@ -71,14 +72,14 @@ pub struct ListMeta<'a> {
}

/// Nullable type metadata
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct NullableMeta<'a> {
#[doc(hidden)]
pub of_type: Type<'a>,
}

/// Object type metadata
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ObjectMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
Expand All @@ -91,6 +92,7 @@ pub struct ObjectMeta<'a, S> {
}

/// Enum type metadata
#[derive(Clone)]
pub struct EnumMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
Expand All @@ -102,7 +104,7 @@ pub struct EnumMeta<'a, S> {
}

/// Interface type metadata
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct InterfaceMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
Expand All @@ -115,7 +117,7 @@ pub struct InterfaceMeta<'a, S> {
}

/// Union type metadata
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct UnionMeta<'a> {
#[doc(hidden)]
pub name: Cow<'a, str>,
Expand All @@ -126,6 +128,7 @@ pub struct UnionMeta<'a> {
}

/// Input object metadata
#[derive(Clone)]
pub struct InputObjectMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
Expand All @@ -140,14 +143,14 @@ pub struct InputObjectMeta<'a, S> {
///
/// After a type's `meta` method has been called but before it has returned, a placeholder type
/// is inserted into a registry to indicate existence.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct PlaceholderMeta<'a> {
#[doc(hidden)]
pub of_type: Type<'a>,
}

/// Generic type metadata
#[derive(Debug)]
#[derive(Clone, Debug)]
pub enum MetaType<'a, S = DefaultScalarValue> {
#[doc(hidden)]
Scalar(ScalarMeta<'a, S>),
Expand Down
6 changes: 3 additions & 3 deletions juniper/src/schema/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::schema::translate::{graphql_parser::GraphQLParserTranslator, SchemaTr
///
/// This brings the mutation, subscription and query types together,
/// and provides the predefined metadata fields.
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct RootNode<
'a,
QueryT: GraphQLType<S>,
Expand All @@ -47,7 +47,7 @@ pub struct RootNode<
}

/// Metadata for a schema
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct SchemaType<'a, S> {
pub(crate) description: Option<Cow<'a, str>>,
pub(crate) types: FnvHashMap<Name, MetaType<'a, S>>,
Expand All @@ -66,7 +66,7 @@ pub enum TypeType<'a, S: 'a> {
List(Box<TypeType<'a, S>>, Option<usize>),
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct DirectiveType<'a, S> {
pub name: String,
pub description: Option<String>,
Expand Down
2 changes: 2 additions & 0 deletions juniper/src/tests/fixtures/starwars/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{collections::HashMap, pin::Pin};

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

#[derive(Clone, Copy, Debug)]
pub struct Query;

#[graphql_object(context = Database)]
Expand Down Expand Up @@ -33,6 +34,7 @@ impl Query {
}
}

#[derive(Clone, Copy, Debug)]
pub struct Subscription;

type HumanStream = Pin<Box<dyn futures::Stream<Item = Human> + Send>>;
Expand Down
17 changes: 17 additions & 0 deletions juniper/src/types/scalars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,14 @@ mod impl_float_scalar {
#[derive(Debug)]
pub struct EmptyMutation<T: ?Sized = ()>(PhantomData<JoinHandle<Box<T>>>);

impl<T> Clone for EmptyMutation<T> {
fn clone(&self) -> Self {
Self(PhantomData)
}
}

impl<T> Copy for EmptyMutation<T> {}

// `EmptyMutation` doesn't use `T`, so should be `Send` and `Sync` even when `T` is not.
crate::sa::assert_impl_all!(EmptyMutation<Rc<String>>: Send, Sync);

Expand Down Expand Up @@ -405,8 +413,17 @@ impl<T> Default for EmptyMutation<T> {
///
/// If you instantiate `RootNode` with this as the subscription,
/// no subscriptions will be generated for the schema.
#[derive(Debug)]
pub struct EmptySubscription<T: ?Sized = ()>(PhantomData<JoinHandle<Box<T>>>);

impl<T> Clone for EmptySubscription<T> {
fn clone(&self) -> Self {
Self(PhantomData)
}
}

impl<T> Copy for EmptySubscription<T> {}

// `EmptySubscription` doesn't use `T`, so should be `Send` and `Sync` even when `T` is not.
crate::sa::assert_impl_all!(EmptySubscription<Rc<String>>: Send, Sync);

Expand Down
2 changes: 1 addition & 1 deletion juniper_actix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ pub mod subscriptions {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Serde(e) => write!(f, "serde error: {e}"),
Self::Serde(e) => write!(f, "{e}"),
Self::UnexpectedClientMessage => {
write!(f, "unexpected message received from client")
}
Expand Down
10 changes: 10 additions & 0 deletions juniper_axum/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
`juniper_axum` changelog
========================

All user visible changes to `juniper_axum` crate will be documented in this file. This project uses [Semantic Versioning 2.0.0].




[`juniper` crate]: https://docs.rs/juniper
[Semantic Versioning 2.0.0]: https://semver.org
32 changes: 32 additions & 0 deletions juniper_axum/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "juniper_axum"
version = "0.1.0"
edition = "2021"
rust-version = "1.62"
description = "`juniper` GraphQL integration with `axum`."
license = "BSD-2-Clause"
authors = ["Benno Tielen <[email protected]>"]
documentation = "https://docs.rs/juniper_axum"
homepage = "https://github.com/graphql-rust/juniper/tree/master/juniper_axum"
repository = "https://github.com/graphql-rust/juniper"
readme = "README.md"
categories = ["asynchronous", "web-programming", "web-programming::http-server"]
keywords = ["graphql", "juniper", "axum", "websocket"]
exclude = ["/release.toml"]

[dependencies]
axum = { version = "0.5.11", features = ["ws"] }
juniper = { path = "../juniper" }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is a version required?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@btielen in the same way as other integrations have. The version bump is automated with cargo-release. See release.toml files for examples.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in the PR comments already, this should have a version and wired up to the release machinery.

serde = "1.0"
serde_json = "1.0"
juniper_graphql_ws = { path = "../juniper_graphql_ws" }
futures = "0.3"

[dev-dependencies]
tokio = { version = "1.20", features = ["full"] }
tokio-tungstenite = "0.17.2"
tokio-stream = "0.1.9"
tower = "0.4.13"
hyper = "0.14.20"
juniper = { path = "../juniper", features = ["expose-test-schema"] }
anyhow = "1.0"
25 changes: 25 additions & 0 deletions juniper_axum/LICENCE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BSD 2-Clause License

Copyright (c) 2022, Benno Tielen
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.
Loading