From 53a7c69d25a350a1a033d0aaecaf5d1608e46535 Mon Sep 17 00:00:00 2001 From: ilslv Date: Wed, 8 Dec 2021 15:19:09 +0300 Subject: [PATCH 1/8] Forbid `__typename` on subscription root --- .../juniper_tests/src/issue_372.rs | 52 +++++++++++++------ juniper/src/types/subscriptions.rs | 11 ---- .../rules/fields_on_correct_type.rs | 22 ++++++++ 3 files changed, 59 insertions(+), 26 deletions(-) diff --git a/integration_tests/juniper_tests/src/issue_372.rs b/integration_tests/juniper_tests/src/issue_372.rs index 8d765176a..70472df6f 100644 --- a/integration_tests/juniper_tests/src/issue_372.rs +++ b/integration_tests/juniper_tests/src/issue_372.rs @@ -1,14 +1,12 @@ //! Checks that `__typename` field queries okay on root types. //! See [#372](https://github.com/graphql-rust/juniper/issues/372) for details. -use futures::{stream, FutureExt as _}; +use futures::stream; use juniper::{ execute, graphql_object, graphql_subscription, graphql_value, graphql_vars, resolve_into_stream, RootNode, }; -use crate::util::extract_next; - pub struct Query; #[graphql_object] @@ -98,28 +96,52 @@ async fn explicit_mutation_typename() { #[tokio::test] async fn subscription_typename() { + use juniper::GraphQLError; + let query = r#"subscription { __typename }"#; let schema = RootNode::new(Query, Mutation, Subscription); - assert_eq!( - resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()) - .then(|s| extract_next(s)) - .await, - Ok((graphql_value!({"__typename": "Subscription"}), vec![])), - ); + match resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()).await { + Err(GraphQLError::ValidationError(mut errors)) => { + assert_eq!(errors.len(), 1); + + let error = errors.pop().unwrap(); + assert_eq!( + error.message(), + "__typename may not be included as a root field in a \ + subscription operation" + ); + assert_eq!(error.locations()[0].index(), 15); + assert_eq!(error.locations()[0].line(), 0); + assert_eq!(error.locations()[0].column(), 15); + } + _ => panic!("Expected ValidationError"), + }; } #[tokio::test] async fn explicit_subscription_typename() { + use juniper::GraphQLError; + let query = r#"subscription Subscription { __typename }"#; let schema = RootNode::new(Query, Mutation, Subscription); - assert_eq!( - resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()) - .then(|s| extract_next(s)) - .await, - Ok((graphql_value!({"__typename": "Subscription"}), vec![])), - ); + match resolve_into_stream(query, None, &schema, &graphql_vars! {}, &()).await { + Err(GraphQLError::ValidationError(mut errors)) => { + assert_eq!(errors.len(), 1); + + let error = errors.pop().unwrap(); + assert_eq!( + error.message(), + "__typename may not be included as a root field in a \ + subscription operation" + ); + assert_eq!(error.locations()[0].index(), 28); + assert_eq!(error.locations()[0].line(), 0); + assert_eq!(error.locations()[0].column(), 28); + } + _ => panic!("Expected ValidationError"), + }; } diff --git a/juniper/src/types/subscriptions.rs b/juniper/src/types/subscriptions.rs index c8ac53af3..e99a5aafa 100644 --- a/juniper/src/types/subscriptions.rs +++ b/juniper/src/types/subscriptions.rs @@ -1,4 +1,3 @@ -use futures::{future, stream}; use serde::Serialize; use crate::{ @@ -293,16 +292,6 @@ where let response_name = f.alias.as_ref().unwrap_or(&f.name).item; - if f.name.item == "__typename" { - let typename = - Value::scalar(instance.concrete_type_name(executor.context(), info)); - object.add_field( - response_name, - Value::Scalar(Box::pin(stream::once(future::ok(typename)))), - ); - continue; - } - let meta_field = meta_type .field_by_name(f.name.item) .unwrap_or_else(|| { diff --git a/juniper/src/validation/rules/fields_on_correct_type.rs b/juniper/src/validation/rules/fields_on_correct_type.rs index c15379aac..8017a06f4 100644 --- a/juniper/src/validation/rules/fields_on_correct_type.rs +++ b/juniper/src/validation/rules/fields_on_correct_type.rs @@ -4,6 +4,7 @@ use crate::{ schema::meta::MetaType, validation::{ValidatorContext, Visitor}, value::ScalarValue, + Operation, OperationType, Selection, }; pub struct FieldsOnCorrectType; @@ -16,6 +17,27 @@ impl<'a, S> Visitor<'a, S> for FieldsOnCorrectType where S: ScalarValue, { + fn enter_operation_definition( + &mut self, + context: &mut ValidatorContext<'a, S>, + operation: &'a Spanning>, + ) { + // https://spec.graphql.org/October2021/#note-bc213 + if let OperationType::Subscription = operation.item.operation_type { + for selection in &operation.item.selection_set { + if let Selection::Field(field) = selection { + if field.item.name.item == "__typename" { + context.report_error( + "__typename may not be included as a root \ + field in a subscription operation", + &[field.item.name.start], + ); + } + } + } + } + } + fn enter_field( &mut self, context: &mut ValidatorContext<'a, S>, From c1d563164e1bb8448f83a4c010a916c2240bcf3f Mon Sep 17 00:00:00 2001 From: ilslv Date: Thu, 9 Dec 2021 15:24:25 +0300 Subject: [PATCH 2/8] CHANGELOG --- juniper/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 5c968de4b..118f46f7e 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -7,6 +7,7 @@ - `#[graphql_object]` and `#[graphql_subscription]` macros expansion now preserves defined `impl` blocks "as is" and reuses defined methods in opaque way. ([#971](https://github.com/graphql-rust/juniper/pull/971)) - `rename = ""` attribute's argument renamed to `rename_all = ""`. ([#971](https://github.com/graphql-rust/juniper/pull/971)) - Upgrade `bson` feature to [2.0 version of its crate](https://github.com/mongodb/bson-rust/releases/tag/v2.0.0). ([#979](https://github.com/graphql-rust/juniper/pull/979)) +- Forbid `__typename` field query on `subscription` [accordingly to the October 2021 spec](https://spec.graphql.org/October2021/#note-bc213). ([#1001](https://github.com/graphql-rust/juniper/pull/1001), [#1000](https://github.com/graphql-rust/juniper/pull/1000)) ## Features From 64f7f8ea852e3e8ef59802358921029b96abb559 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 9 Dec 2021 15:23:22 +0100 Subject: [PATCH 3/8] Minor corrections --- integration_tests/juniper_tests/src/issue_372.rs | 14 +++++--------- juniper/CHANGELOG.md | 2 +- .../src/validation/rules/fields_on_correct_type.rs | 4 ++-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/integration_tests/juniper_tests/src/issue_372.rs b/integration_tests/juniper_tests/src/issue_372.rs index 70472df6f..c92ef6aa3 100644 --- a/integration_tests/juniper_tests/src/issue_372.rs +++ b/integration_tests/juniper_tests/src/issue_372.rs @@ -1,10 +1,10 @@ -//! Checks that `__typename` field queries okay on root types. +//! Checks that `__typename` field queries okay (and not okay) on root types. //! See [#372](https://github.com/graphql-rust/juniper/issues/372) for details. use futures::stream; use juniper::{ execute, graphql_object, graphql_subscription, graphql_value, graphql_vars, - resolve_into_stream, RootNode, + resolve_into_stream, GraphQLError, RootNode, }; pub struct Query; @@ -96,8 +96,6 @@ async fn explicit_mutation_typename() { #[tokio::test] async fn subscription_typename() { - use juniper::GraphQLError; - let query = r#"subscription { __typename }"#; let schema = RootNode::new(Query, Mutation, Subscription); @@ -109,8 +107,8 @@ async fn subscription_typename() { let error = errors.pop().unwrap(); assert_eq!( error.message(), - "__typename may not be included as a root field in a \ - subscription operation" + "`__typename` may not be included as a root field in a \ + subscription operation", ); assert_eq!(error.locations()[0].index(), 15); assert_eq!(error.locations()[0].line(), 0); @@ -122,8 +120,6 @@ async fn subscription_typename() { #[tokio::test] async fn explicit_subscription_typename() { - use juniper::GraphQLError; - let query = r#"subscription Subscription { __typename }"#; let schema = RootNode::new(Query, Mutation, Subscription); @@ -135,7 +131,7 @@ async fn explicit_subscription_typename() { let error = errors.pop().unwrap(); assert_eq!( error.message(), - "__typename may not be included as a root field in a \ + "`__typename` may not be included as a root field in a \ subscription operation" ); assert_eq!(error.locations()[0].index(), 28); diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 118f46f7e..f773885ba 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -7,7 +7,7 @@ - `#[graphql_object]` and `#[graphql_subscription]` macros expansion now preserves defined `impl` blocks "as is" and reuses defined methods in opaque way. ([#971](https://github.com/graphql-rust/juniper/pull/971)) - `rename = ""` attribute's argument renamed to `rename_all = ""`. ([#971](https://github.com/graphql-rust/juniper/pull/971)) - Upgrade `bson` feature to [2.0 version of its crate](https://github.com/mongodb/bson-rust/releases/tag/v2.0.0). ([#979](https://github.com/graphql-rust/juniper/pull/979)) -- Forbid `__typename` field query on `subscription` [accordingly to the October 2021 spec](https://spec.graphql.org/October2021/#note-bc213). ([#1001](https://github.com/graphql-rust/juniper/pull/1001), [#1000](https://github.com/graphql-rust/juniper/pull/1000)) +- Forbid `__typename` field query on `subscription` operations [accordingly to October 2021 spec](https://spec.graphql.org/October2021/#note-bc213). ([#1001](https://github.com/graphql-rust/juniper/pull/1001), [#1000](https://github.com/graphql-rust/juniper/pull/1000)) ## Features diff --git a/juniper/src/validation/rules/fields_on_correct_type.rs b/juniper/src/validation/rules/fields_on_correct_type.rs index 8017a06f4..68e6cc281 100644 --- a/juniper/src/validation/rules/fields_on_correct_type.rs +++ b/juniper/src/validation/rules/fields_on_correct_type.rs @@ -28,8 +28,8 @@ where if let Selection::Field(field) = selection { if field.item.name.item == "__typename" { context.report_error( - "__typename may not be included as a root \ - field in a subscription operation", + "`__typename` may not be included as a root \ + field in a subscription operation", &[field.item.name.start], ); } From 3e438866556a955e6aa021231a2e5c025e2a0436 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 08:05:47 +0300 Subject: [PATCH 4/8] Add tests --- .../rules/fields_on_correct_type.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/juniper/src/validation/rules/fields_on_correct_type.rs b/juniper/src/validation/rules/fields_on_correct_type.rs index 68e6cc281..ecd9cf693 100644 --- a/juniper/src/validation/rules/fields_on_correct_type.rs +++ b/juniper/src/validation/rules/fields_on_correct_type.rs @@ -379,4 +379,28 @@ mod tests { "#, ); } + + #[test] + fn typename_on_subscription() { + expect_fails_rule::<_, _, DefaultScalarValue>( + factory, + r#"subscription { __typename }"#, + &[RuleError::new( + "`__typename` may not be included as a root field in a subscription operation", + &[SourcePosition::new(15, 0, 15)], + )], + ); + } + + #[test] + fn typename_on_explicit_subscription() { + expect_fails_rule::<_, _, DefaultScalarValue>( + factory, + r#"subscription SubscriptionRoot { __typename }"#, + &[RuleError::new( + "`__typename` may not be included as a root field in a subscription operation", + &[SourcePosition::new(32, 0, 32)], + )], + ); + } } From 0112c1adae539f21e4caea664a8427bff2362494 Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 10:20:46 +0300 Subject: [PATCH 5/8] Upgrade actix --- juniper_actix/Cargo.toml | 12 +++--- juniper_actix/src/lib.rs | 82 ++++++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/juniper_actix/Cargo.toml b/juniper_actix/Cargo.toml index d5730bd7c..f3f4fd27f 100644 --- a/juniper_actix/Cargo.toml +++ b/juniper_actix/Cargo.toml @@ -13,10 +13,10 @@ subscriptions = ["juniper_graphql_ws", "tokio"] [dependencies] actix = "0.12" -actix-http = "3.0.0-beta.13" +actix-http = "3.0.0-beta.15" http = "0.2.4" -actix-web = "4.0.0-beta.12" -actix-web-actors = "4.0.0-beta.7" +actix-web = "4.0.0-beta.14" +actix-web-actors = "4.0.0-beta.8" juniper = { version = "0.15.7", path = "../juniper", default-features = false } juniper_graphql_ws = { version = "0.3.0", path = "../juniper_graphql_ws", optional = true } @@ -30,11 +30,11 @@ tokio = { version = "1.0", features = ["sync"], optional = true } [dev-dependencies] actix-rt = "2" -actix-cors = "0.6.0-beta.4" -actix-identity = "0.4.0-beta.4" +actix-cors = "0.6.0-beta.6" +actix-identity = "0.4.0-beta.5" tokio = "1.0" async-stream = "0.3" -actix-test = "0.1.0-beta.6" +actix-test = "0.1.0-beta.8" juniper = { version = "0.15.7", path = "../juniper", features = ["expose-test-schema"] } diff --git a/juniper_actix/src/lib.rs b/juniper_actix/src/lib.rs index d1b89da5d..f22906b9f 100644 --- a/juniper_actix/src/lib.rs +++ b/juniper_actix/src/lib.rs @@ -466,7 +466,9 @@ pub mod subscriptions { #[cfg(test)] mod tests { - use actix_http::body::AnyBody; + use std::{future::Future, pin::Pin, task}; + + use actix_http::body::{BoxBody, MessageBody}; use actix_web::{ dev::ServiceResponse, http, @@ -475,6 +477,7 @@ mod tests { web::Data, App, }; + use bytes::Bytes; use juniper::{ http::tests::{run_http_test_suite, HttpIntegration, TestResponse}, tests::fixtures::starwars::schema::{Database, Query}, @@ -487,13 +490,27 @@ mod tests { type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>; - async fn take_response_body_string(resp: &mut ServiceResponse) -> String { - match resp.response().body() { - AnyBody::Bytes(body) => String::from_utf8(body.to_vec()).unwrap(), - _ => String::from(""), + struct BoxBodyFuture(BoxBody); + + impl Future for BoxBodyFuture { + type Output = Option>; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + Pin::new(&mut self.get_mut().0).poll_next(cx) } } + async fn take_response_body_string(resp: ServiceResponse) -> String { + String::from_utf8( + BoxBodyFuture(resp.into_body()) + .await + .unwrap() + .unwrap() + .to_vec(), + ) + .unwrap() + } + async fn index( req: HttpRequest, payload: actix_web::web::Payload, @@ -537,13 +554,13 @@ mod tests { .append_header((ACCEPT, "text/html")) .to_request(); - let mut resp = test::call_service(&mut app, req).await; - let body = take_response_body_string(&mut resp).await; + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( resp.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(), "text/html; charset=utf-8" ); + let body = take_response_body_string(resp).await; assert!(body.contains("")); assert!(body.contains( "" @@ -578,13 +595,13 @@ mod tests { .append_header((ACCEPT, "text/html")) .to_request(); - let mut resp = test::call_service(&mut app, req).await; - let body = take_response_body_string(&mut resp).await; + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); assert_eq!( resp.headers().get(CONTENT_TYPE).unwrap().to_str().unwrap(), "text/html; charset=utf-8" ); + let body = take_response_body_string(resp).await; assert!(body.contains("GraphQLPlayground.init(root, { endpoint: '/dogs-api/graphql', subscriptionEndpoint: '/dogs-api/subscriptions' })")); } @@ -611,17 +628,17 @@ mod tests { ) .await; - let mut resp = test::call_service(&mut app, req).await; - dbg!(take_response_body_string(&mut resp).await); + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); - assert_eq!( - take_response_body_string(&mut resp).await, - r#"{"data":{"hero":{"name":"R2-D2"}}}"# - ); assert_eq!( resp.headers().get("content-type").unwrap(), "application/json", ); + + assert_eq!( + take_response_body_string(resp).await, + r#"{"data":{"hero":{"name":"R2-D2"}}}"# + ); } #[actix_web::rt::test] @@ -644,17 +661,18 @@ mod tests { ) .await; - let mut resp = test::call_service(&mut app, req).await; + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); - assert_eq!( - take_response_body_string(&mut resp).await, - r#"{"data":{"hero":{"name":"R2-D2"}}}"# - ); assert_eq!( resp.headers().get("content-type").unwrap(), "application/json", ); + + assert_eq!( + take_response_body_string(resp).await, + r#"{"data":{"hero":{"name":"R2-D2"}}}"# + ); } #[actix_web::rt::test] @@ -688,17 +706,17 @@ mod tests { ) .await; - let mut resp = test::call_service(&mut app, req).await; + let resp = test::call_service(&mut app, req).await; assert_eq!(resp.status(), http::StatusCode::OK); - assert_eq!( - take_response_body_string(&mut resp).await, - r#"[{"data":{"hero":{"name":"R2-D2"}}},{"data":{"hero":{"id":"1000","name":"Luke Skywalker"}}}]"# - ); assert_eq!( resp.headers().get("content-type").unwrap(), "application/json", ); + assert_eq!( + take_response_body_string(resp).await, + r#"[{"data":{"hero":{"name":"R2-D2"}}},{"data":{"hero":{"id":"1000","name":"Luke Skywalker"}}}]"# + ); } #[test] @@ -757,14 +775,20 @@ mod tests { } } - async fn make_test_response(mut resp: ServiceResponse) -> TestResponse { - let body = take_response_body_string(&mut resp).await; + async fn make_test_response(resp: ServiceResponse) -> TestResponse { let status_code = resp.status().as_u16(); - let content_type = resp.headers().get(CONTENT_TYPE).unwrap(); + let content_type = resp + .headers() + .get(CONTENT_TYPE) + .unwrap() + .to_str() + .unwrap() + .to_string(); + let body = take_response_body_string(resp).await; TestResponse { status_code: status_code as i32, body: Some(body), - content_type: content_type.to_str().unwrap().to_string(), + content_type, } } From 7006d5838f6bf5ac4e3b27207bb7589b518aa14f Mon Sep 17 00:00:00 2001 From: ilslv Date: Mon, 13 Dec 2021 10:33:36 +0300 Subject: [PATCH 6/8] Use poll_fn() --- juniper_actix/src/lib.rs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/juniper_actix/src/lib.rs b/juniper_actix/src/lib.rs index f22906b9f..de870df80 100644 --- a/juniper_actix/src/lib.rs +++ b/juniper_actix/src/lib.rs @@ -466,9 +466,9 @@ pub mod subscriptions { #[cfg(test)] mod tests { - use std::{future::Future, pin::Pin, task}; + use std::pin::Pin; - use actix_http::body::{BoxBody, MessageBody}; + use actix_http::body::MessageBody; use actix_web::{ dev::ServiceResponse, http, @@ -477,7 +477,7 @@ mod tests { web::Data, App, }; - use bytes::Bytes; + use futures::future; use juniper::{ http::tests::{run_http_test_suite, HttpIntegration, TestResponse}, tests::fixtures::starwars::schema::{Database, Query}, @@ -490,19 +490,10 @@ mod tests { type Schema = juniper::RootNode<'static, Query, EmptyMutation, EmptySubscription>; - struct BoxBodyFuture(BoxBody); - - impl Future for BoxBodyFuture { - type Output = Option>; - - fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { - Pin::new(&mut self.get_mut().0).poll_next(cx) - } - } - async fn take_response_body_string(resp: ServiceResponse) -> String { + let mut body = resp.into_body(); String::from_utf8( - BoxBodyFuture(resp.into_body()) + future::poll_fn(|cx| Pin::new(&mut body).poll_next(cx)) .await .unwrap() .unwrap() From 6ded4a70b852c5e7224d9cad463ba1adcf471454 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 13 Dec 2021 12:18:47 +0100 Subject: [PATCH 7/8] Correction --- juniper_actix/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/juniper_actix/src/lib.rs b/juniper_actix/src/lib.rs index de870df80..935be89cf 100644 --- a/juniper_actix/src/lib.rs +++ b/juniper_actix/src/lib.rs @@ -625,7 +625,6 @@ mod tests { resp.headers().get("content-type").unwrap(), "application/json", ); - assert_eq!( take_response_body_string(resp).await, r#"{"data":{"hero":{"name":"R2-D2"}}}"# @@ -659,7 +658,6 @@ mod tests { resp.headers().get("content-type").unwrap(), "application/json", ); - assert_eq!( take_response_body_string(resp).await, r#"{"data":{"hero":{"name":"R2-D2"}}}"# From b81d81743f794c2cb20421a37f01be406d7f3295 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 13 Dec 2021 12:25:49 +0100 Subject: [PATCH 8/8] Corrections --- .../juniper_tests/src/issue_372.rs | 22 ++++++++++--------- juniper/CHANGELOG.md | 2 +- .../rules/fields_on_correct_type.rs | 10 +++++---- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/integration_tests/juniper_tests/src/issue_372.rs b/integration_tests/juniper_tests/src/issue_372.rs index c92ef6aa3..afe86a322 100644 --- a/integration_tests/juniper_tests/src/issue_372.rs +++ b/integration_tests/juniper_tests/src/issue_372.rs @@ -104,15 +104,16 @@ async fn subscription_typename() { Err(GraphQLError::ValidationError(mut errors)) => { assert_eq!(errors.len(), 1); - let error = errors.pop().unwrap(); + let err = errors.pop().unwrap(); + assert_eq!( - error.message(), + err.message(), "`__typename` may not be included as a root field in a \ subscription operation", ); - assert_eq!(error.locations()[0].index(), 15); - assert_eq!(error.locations()[0].line(), 0); - assert_eq!(error.locations()[0].column(), 15); + assert_eq!(err.locations()[0].index(), 15); + assert_eq!(err.locations()[0].line(), 0); + assert_eq!(err.locations()[0].column(), 15); } _ => panic!("Expected ValidationError"), }; @@ -128,15 +129,16 @@ async fn explicit_subscription_typename() { Err(GraphQLError::ValidationError(mut errors)) => { assert_eq!(errors.len(), 1); - let error = errors.pop().unwrap(); + let err = errors.pop().unwrap(); + assert_eq!( - error.message(), + err.message(), "`__typename` may not be included as a root field in a \ subscription operation" ); - assert_eq!(error.locations()[0].index(), 28); - assert_eq!(error.locations()[0].line(), 0); - assert_eq!(error.locations()[0].column(), 28); + assert_eq!(err.locations()[0].index(), 28); + assert_eq!(err.locations()[0].line(), 0); + assert_eq!(err.locations()[0].column(), 28); } _ => panic!("Expected ValidationError"), }; diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index f773885ba..914f4b911 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -7,7 +7,7 @@ - `#[graphql_object]` and `#[graphql_subscription]` macros expansion now preserves defined `impl` blocks "as is" and reuses defined methods in opaque way. ([#971](https://github.com/graphql-rust/juniper/pull/971)) - `rename = ""` attribute's argument renamed to `rename_all = ""`. ([#971](https://github.com/graphql-rust/juniper/pull/971)) - Upgrade `bson` feature to [2.0 version of its crate](https://github.com/mongodb/bson-rust/releases/tag/v2.0.0). ([#979](https://github.com/graphql-rust/juniper/pull/979)) -- Forbid `__typename` field query on `subscription` operations [accordingly to October 2021 spec](https://spec.graphql.org/October2021/#note-bc213). ([#1001](https://github.com/graphql-rust/juniper/pull/1001), [#1000](https://github.com/graphql-rust/juniper/pull/1000)) +- Forbid `__typename` field on `subscription` operations [accordingly to October 2021 spec](https://spec.graphql.org/October2021/#note-bc213). ([#1001](https://github.com/graphql-rust/juniper/pull/1001), [#1000](https://github.com/graphql-rust/juniper/pull/1000)) ## Features diff --git a/juniper/src/validation/rules/fields_on_correct_type.rs b/juniper/src/validation/rules/fields_on_correct_type.rs index ecd9cf693..ad90cb257 100644 --- a/juniper/src/validation/rules/fields_on_correct_type.rs +++ b/juniper/src/validation/rules/fields_on_correct_type.rs @@ -381,24 +381,26 @@ mod tests { } #[test] - fn typename_on_subscription() { + fn forbids_typename_on_subscription() { expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#"subscription { __typename }"#, &[RuleError::new( - "`__typename` may not be included as a root field in a subscription operation", + "`__typename` may not be included as a root field in a \ + subscription operation", &[SourcePosition::new(15, 0, 15)], )], ); } #[test] - fn typename_on_explicit_subscription() { + fn forbids_typename_on_explicit_subscription() { expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#"subscription SubscriptionRoot { __typename }"#, &[RuleError::new( - "`__typename` may not be included as a root field in a subscription operation", + "`__typename` may not be included as a root field in a \ + subscription operation", &[SourcePosition::new(32, 0, 32)], )], );