-
Notifications
You must be signed in to change notification settings - Fork 427
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
Allow post-validation FromInputValue
errors
#987
Conversation
cc @ilslv Before continuing this, make sure |
# Conflicts: # juniper/src/ast.rs # juniper_actix/src/lib.rs
@tyranron PR is ready for review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilslv I've refactored base machinery in juniper
:
- more errors propagation;
- simplifying code with using
String
in implementations rather thanFieldError<S>
.
Codegen needs some updates, though, described below.
juniper/src/types/containers.rs
Outdated
NotEnough(usize), | ||
|
||
/// Too many elements. Contains __all__ [`InputValue`] elements. | ||
TooMuch(Vec<T>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think keeping Vec
gives no actual use here, while complicates relative unsafe
code (and so reasoning) a lot.
<Vec<i32>>::from_input_value(&V::list(vec![V::scalar(1), V::scalar(2), V::scalar(3)])), | ||
Ok(vec![1, 2, 3]), | ||
); | ||
// TODO: all examples |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilslv we should provide here a complete test suite. But let's do it in a separate PR after merging this.
juniper/src/schema/meta.rs
Outdated
@@ -743,5 +740,5 @@ fn try_parse_fn<S, T>(v: &InputValue<S>) -> bool | |||
where | |||
T: FromInputValue<S>, | |||
{ | |||
<T as FromInputValue<S>>::from_input_value(v).is_some() | |||
T::from_input_value(v).is_ok() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't actually propagate any errors here, only silence them as we did before. Let's do propagation.
juniper/src/types/scalars.rs
Outdated
.map(ID), | ||
_ => None, | ||
} | ||
fn from_input_value(v: &InputValue) -> Result<ID, FieldError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be more accurate here and use FieldError<S>
to omit redundant scalar conversions.
} | ||
} | ||
None => return Box::pin(::juniper::futures::future::ready( | ||
Err(::juniper::FieldError::from("This GraphQLType has no name")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilslv to DRYing the generated code a bit, let's move such things to helper functions inside juniper
crate, and reuse them here.
…nput-value-resolve-errors # Conflicts: # juniper/CHANGELOG.md
# Conflicts: # integration_tests/juniper_tests/src/issue_372.rs # juniper/CHANGELOG.md # juniper/src/validation/rules/fields_on_correct_type.rs # juniper_actix/src/lib.rs
@tyranron this PR is ready for review |
}, | ||
Ok, | ||
) | ||
})? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilslv I don't follow the logic here.
It was:
args.get::<#ty>(#name)
returnsOption<T>
.- If it's
None
, then::juniper::FromInputValue::<#scalar>::from_implicit_null
returnsOption<T>
. - If it's
None
we panic.
Now it is:
args.get::<#ty>(#name)
returnsResult<Option<T>, FieldError>
.- If it's
Ok
, we check the inneropt
. - If it
Some
we map it toSome(Ok(v))
and so haveResult<Option<Result<T>, FieldError>, FieldError>
.
Why?
It seems the more natural way is:
- Unpack the
Result
early:args.get::<#ty>(#name)?
.transpose()
the inner error inOption
and return it with?
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh... understood. .map_or_else
doesn't map an inner Option
value, but rather maps the whole Option
into something.
.ok_or_else
will do better here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meh... .ok_or_else()
cannot map absense to Result::Ok
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ilslv LGTM
let e = ::juniper::IntoFieldError::into_field_error(e); | ||
::juniper::FieldError::new( | ||
format!(#err_text, e.message()), | ||
e.extensions().clone(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mess is better be replaced with .map_message
method on FieldError
.
}, | ||
Ok, | ||
) | ||
})? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meh... .ok_or_else()
cannot map absense to Result::Ok
.
Required for #975
Synopsis
At the moment,
FromInputValue
fallibility is considered during input operation validation only. Also, the real error is erased, due toOption<T>
usage, thus argument absence is unobviously mixed with convertion errors over the code base.Being applicable to most cases, still there are some situations where the error may occur only during resolution after passing the validation (from #975 it's deserializing
json::Value
intoJson<T: Deserialize>
). At the moment, we're ending up with runtime panics in such situation, which is not good.Solution
Make
FromInputValue
returningResult
with the actual error preservation. Require this error beIntoFieldError
convertible, so it may naturally bubble-up during operation execution.Checklist