Skip to content

Allow post-validation FromInputValue errors #987

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

Merged
merged 32 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0cb9248
Change `FromInputValue` signature and boostrap refactoring
tyranron Oct 15, 2021
584f9ae
Refactor primitive types, vol.1
tyranron Oct 15, 2021
e5481a0
Update actix beta
ilslv Nov 23, 2021
fdac7c7
Merge branch 'update-actix-beta' into allow-from-input-value-resolve-…
ilslv Nov 23, 2021
1c0ce20
Merge branch 'master' into allow-from-input-value-resolve-errors
ilslv Nov 29, 2021
acbdf97
Support FromInputValue for arrays
ilslv Nov 29, 2021
7092bf2
Fix existing tests
ilslv Dec 1, 2021
111e464
Remove more unwraps and panics
ilslv Dec 1, 2021
555db09
Fix ui codegen tests
ilslv Dec 1, 2021
1350245
Fix book example and docs
ilslv Dec 1, 2021
3692c19
Merge branch 'master' into allow-from-input-value-resolve-errors
ilslv Dec 1, 2021
ea2fe64
Corrections
ilslv Dec 1, 2021
115c55f
CHANGELOG
ilslv Dec 1, 2021
06e71c3
Correction
ilslv Dec 1, 2021
53a7c69
Forbid `__typename` on subscription root
ilslv Dec 8, 2021
c1d5631
CHANGELOG
ilslv Dec 9, 2021
64f7f8e
Minor corrections
tyranron Dec 9, 2021
ff7c3d7
Merge branch 'master' into allow-from-input-value-resolve-errors
tyranron Dec 10, 2021
5cc7555
Corrections
tyranron Dec 10, 2021
3e43886
Add tests
ilslv Dec 13, 2021
0a7b7d7
Merge branch 'master' into forbid_typename_on_subscription_root
ilslv Dec 13, 2021
efca2c0
Tests fixes
ilslv Dec 13, 2021
0112c1a
Upgrade actix
ilslv Dec 13, 2021
7006d58
Use poll_fn()
ilslv Dec 13, 2021
dd3a43c
Merge branch 'forbid_typename_on_subscription_root' into allow-from-i…
ilslv Dec 13, 2021
f2dbec3
Add boxed_field_err_fut
ilslv Dec 13, 2021
91a91a1
Corrections
ilslv Dec 13, 2021
dd80735
Corrections
ilslv Dec 13, 2021
00ff1df
Corrections
ilslv Dec 14, 2021
1f69939
Merge branch 'master' into allow-from-input-value-resolve-errors
ilslv Dec 14, 2021
982ec42
Corrections
ilslv Dec 14, 2021
afd607e
Polishing
tyranron Dec 14, 2021
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
13 changes: 7 additions & 6 deletions docs/book/content/types/scalars.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ The example below is used just for illustration.
# }
# }
# }

#
use juniper::{Value, ParseScalarResult, ParseScalarValue};
use date::Date;

Expand All @@ -128,17 +128,18 @@ where
}

// Define how to parse a primitive type into your custom scalar.
fn from_input_value(v: &InputValue) -> Option<Date> {
v.as_scalar_value()
.and_then(|v| v.as_str())
.and_then(|s| s.parse().ok())
// NOTE: The error type should implement `IntoFieldError<S>`.
fn from_input_value(v: &InputValue) -> Result<Date, String> {
v.as_string_value()
.ok_or_else(|| format!("Expected `String`, found: {}", v))
.and_then(|s| s.parse().map_err(|e| format!("Failed to parse `Date`: {}", e)))
}

// Define how to parse a string value.
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
<String as ParseScalarValue<S>>::from_str(value)
}
}

#
# fn main() {}
```
139 changes: 71 additions & 68 deletions integration_tests/async_await/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,18 @@ impl Query {
}
}

#[tokio::test]
async fn async_simple() {
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
let doc = r#"
query {
fn main() {}

#[cfg(test)]
mod tests {
use juniper::{graphql_value, EmptyMutation, EmptySubscription, GraphQLError, RootNode, Value};

use super::Query;

#[tokio::test]
async fn async_simple() {
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
let doc = r#"query {
fieldSync
fieldAsyncPlain
delayed
Expand All @@ -83,39 +90,37 @@ async fn async_simple() {
name
delayed
}
}
"#;
}"#;

let vars = Default::default();
let (res, errs) = juniper::execute(doc, None, &schema, &vars, &())
.await
.unwrap();
let vars = Default::default();
let (res, errs) = juniper::execute(doc, None, &schema, &vars, &())
.await
.unwrap();

assert!(errs.is_empty());
assert!(errs.is_empty());

let obj = res.into_object().unwrap();
let value = Value::Object(obj);
let obj = res.into_object().unwrap();
let value = Value::Object(obj);

assert_eq!(
value,
graphql_value!({
"delayed": true,
"fieldAsyncPlain": "field_async_plain",
"fieldSync": "field_sync",
"user": {
assert_eq!(
value,
graphql_value!({
"delayed": true,
"kind": "USER",
"name": "user1",
},
}),
);
}
"fieldAsyncPlain": "field_async_plain",
"fieldSync": "field_sync",
"user": {
"delayed": true,
"kind": "USER",
"name": "user1",
},
}),
);
}

#[tokio::test]
async fn async_field_validation_error() {
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
let doc = r#"
query {
#[tokio::test]
async fn async_field_validation_error() {
let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
let doc = r#"query {
nonExistentField
fieldSync
fieldAsyncPlain
Expand All @@ -125,40 +130,38 @@ async fn async_field_validation_error() {
name
delayed
}
}
"#;

let vars = Default::default();
let result = juniper::execute(doc, None, &schema, &vars, &()).await;
assert!(result.is_err());

let error = result.err().unwrap();
let is_validation_error = match error {
GraphQLError::ValidationError(_) => true,
_ => false,
};
assert!(is_validation_error);
}

// FIXME: test seems broken by design, re-enable later
// #[tokio::test]
// async fn resolve_into_stream_validation_error() {
// let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
// let doc = r#"
// subscription {
// nonExistent
// }
// "#;
// let vars = Default::default();
// let result = juniper::resolve_into_stream(doc, None, &schema, &vars, &()).await;
// assert!(result.is_err());

// let error = result.err().unwrap();
// let is_validation_error = match error {
// GraphQLError::ValidationError(_) => true,
// _ => false,
// };
// assert!(is_validation_error);
// }
}"#;

let vars = Default::default();
let result = juniper::execute(doc, None, &schema, &vars, &()).await;
assert!(result.is_err());

let error = result.err().unwrap();
let is_validation_error = match error {
GraphQLError::ValidationError(_) => true,
_ => false,
};
assert!(is_validation_error);
}

fn main() {}
// FIXME: test seems broken by design, re-enable later
// #[tokio::test]
// async fn resolve_into_stream_validation_error() {
// let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
// let doc = r#"
// subscription {
// nonExistent
// }
// "#;
// let vars = Default::default();
// let result = juniper::resolve_into_stream(doc, None, &schema, &vars, &()).await;
// assert!(result.is_err());

// let error = result.err().unwrap();
// let is_validation_error = match error {
// GraphQLError::ValidationError(_) => true,
// _ => false,
// };
// assert!(is_validation_error);
// }
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
error: GraphQL enum expects at least one field
--> $DIR/derive_no_fields.rs:2:1

= note: https://spec.graphql.org/June2018/#sec-Enums

--> fail/enum/derive_no_fields.rs:2:1
|
2 | pub enum Test {}
| ^^^^^^^^^^^^^^^^
|
= note: https://spec.graphql.org/June2018/#sec-Enums
| ^^^
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
error[E0277]: the trait bound `ObjectA: IsInputType<__S>` is not satisfied
--> $DIR/derive_incompatible_object.rs:6:10
|
6 | #[derive(juniper::GraphQLInputObject)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjectA`
|
= note: required by `juniper::marker::IsInputType::mark`
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)
--> fail/input-object/derive_incompatible_object.rs:6:10
|
6 | #[derive(juniper::GraphQLInputObject)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjectA`
|
note: required by `juniper::marker::IsInputType::mark`
--> $WORKSPACE/juniper/src/types/marker.rs
|
| fn mark() {}
| ^^^^^^^^^
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `ObjectA: FromInputValue<__S>` is not satisfied
--> $DIR/derive_incompatible_object.rs:6:10
--> fail/input-object/derive_incompatible_object.rs:6:10
|
6 | #[derive(juniper::GraphQLInputObject)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjectA`
|
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `ObjectA: FromInputValue<__S>` is not satisfied
--> $DIR/derive_incompatible_object.rs:6:10
|
6 | #[derive(juniper::GraphQLInputObject)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjectA`
|
= note: required by `from_input_value`
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)
--> fail/input-object/derive_incompatible_object.rs:6:10
|
6 | #[derive(juniper::GraphQLInputObject)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjectA`
|
note: required by `from_input_value`
--> $WORKSPACE/juniper/src/ast.rs
|
| fn from_input_value(v: &InputValue<S>) -> Result<Self, Self::Error>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the derive macro `juniper::GraphQLInputObject` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0599]: no method named `to_input_value` found for struct `ObjectA` in the current scope
--> $DIR/derive_incompatible_object.rs:6:10
--> fail/input-object/derive_incompatible_object.rs:6:10
|
2 | struct ObjectA {
| -------------- method `to_input_value` not found for this
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
error: GraphQL input object expects at least one field
--> $DIR/derive_no_fields.rs:2:1

= note: https://spec.graphql.org/June2018/#sec-Input-Objects

--> fail/input-object/derive_no_fields.rs:2:1
|
2 | struct Object {}
| ^^^^^^^^^^^^^^^^
|
= note: https://spec.graphql.org/June2018/#sec-Input-Objects
| ^^^^^^
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.
--> $DIR/derive_no_underscore.rs:3:15

= note: https://spec.graphql.org/June2018/#sec-Schema

--> fail/input-object/derive_no_underscore.rs:3:15
|
3 | #[graphql(name = "__test")]
| ^^^^
|
= note: https://spec.graphql.org/June2018/#sec-Schema
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
error: GraphQL input object does not allow fields with the same name
--> $DIR/derive_unique_name.rs:4:5
|
4 | / #[graphql(name = "test")]
5 | | test2: String,
| |_________________^
|

= help: There is at least one other field with the same name `test`, possibly renamed via the #[graphql] attribute
= note: https://spec.graphql.org/June2018/#sec-Input-Objects

--> fail/input-object/derive_unique_name.rs:4:5
|
4 | #[graphql(name = "test")]
| ^
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
error: All types and directives defined within a schema must not have a name which begins with `__` (two underscores), as this is used exclusively by GraphQL’s introspection system.

= note: https://spec.graphql.org/June2018/#sec-Schema

--> $DIR/argument_double_underscored.rs:14:18
|
14 | fn id(&self, __num: i32) -> &str {
| ^^^^^
|
= note: https://spec.graphql.org/June2018/#sec-Schema

error[E0412]: cannot find type `CharacterValue` in this scope
--> $DIR/argument_double_underscored.rs:4:18
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
error[E0277]: the trait bound `ObjA: IsInputType<__S>` is not satisfied
--> $DIR/argument_non_input_type.rs:16:1
|
16 | #[graphql_interface(for = ObjA)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
= note: required by `juniper::marker::IsInputType::mark`
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)
--> fail/interface/argument_non_input_type.rs:16:1
|
16 | #[graphql_interface(for = ObjA)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `ObjA`
|
note: required by `juniper::marker::IsInputType::mark`
--> $WORKSPACE/juniper/src/types/marker.rs
|
| fn mark() {}
| ^^^^^^^^^
= note: this error originates in the attribute macro `graphql_interface` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `ObjA: FromInputValue<__S>` is not satisfied
--> $DIR/argument_non_input_type.rs:16:1
--> fail/interface/argument_non_input_type.rs:16:1
|
16 | #[graphql_interface(for = ObjA)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `ObjA`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
error: GraphQL interface trait method `as_obja` conflicts with the external downcast function `downcast_obja` declared on the trait to downcast into the implementer type `ObjA`

= note: https://spec.graphql.org/June2018/#sec-Interfaces
= note: use `#[graphql(ignore)]` attribute argument to ignore this trait method for interface implementers downcasting

--> $DIR/downcast_method_conflicts_with_external_downcast_fn.rs:26:5
|
26 | fn as_obja(&self) -> Option<&ObjA>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: https://spec.graphql.org/June2018/#sec-Interfaces
= note: use `#[graphql(ignore)]` attribute argument to ignore this trait method for interface implementers downcasting
| ^^

error[E0412]: cannot find type `CharacterValue` in this scope
--> $DIR/downcast_method_conflicts_with_external_downcast_fn.rs:4:18
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
error: GraphQL interface expects trait method to accept `&self` only and, optionally, `&Context`

= note: https://spec.graphql.org/June2018/#sec-Interfaces

--> $DIR/downcast_method_wrong_input_args.rs:10:10
|
10 | fn a(&self, ctx: &(), rand: u8) -> Option<&Human> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: https://spec.graphql.org/June2018/#sec-Interfaces
| ^

error[E0412]: cannot find type `CharacterValue` in this scope
--> $DIR/downcast_method_wrong_input_args.rs:16:18
Expand Down
Loading