juniper
juniper copied to clipboard
`IntoFieldError` and `Into<FieldError>` have different behaviors
Describe the bug
FieldError::from
and error.into_field_error
have different behaviors. The former one has no extensions
field, but the latter one has.
To Reproduce
#[derive(thiserror::Error, Debug, Deserialize, Serialize)]
enum SomeError {
#[error("SomeError Item")]
Item,
}
impl IntoFieldError for SomeError {
fn into_field_error(self) -> juniper::FieldError {
juniper::FieldError::new(
"Get Some Error",
juniper::graphql_value!({
"type": "SomeError",
}),
)
}
}
#[test]
fn test_err() -> anyhow::Result<()> {
println!("--- FieldError::from ---");
println!("{:#?}", FieldError::<DefaultScalarValue>::from(SomeError::Item));
println!("--- into_field_error ---");
println!("{:#?}", SomeError::Item.into_field_error());
Ok(())
}
Expected behavior The result should be the same.
Actual bebavior
--- FieldError::from ---
FieldError {
message: "SomeError Item",
extensions: Null,
}
--- into_field_error ---
FieldError {
message: "Get Some Error",
extensions: Object(
Object {
key_value_list: [
(
"type",
Scalar(
String(
"SomeError",
),
),
),
],
},
),
}
So:
- I think
FieldError::from
should be a bug, - I have no idea why the same action(convert from
CustomError
toFieldError
) has two different way to achieve. Maybe one is enough...
They aren’t meant to do the same. FieldError::from is provided by From from std. It only requires that your type and be converted into a string. There is no way to provide custom extensions. It is mostly provided for ergonomic purposes.
Users of juniper wouldn’t be able to implement From<CustomError> for FieldError since neither the trait nor FieldError is defined in the users own crate. That is the orphan rule. So we need our own trait to do the conversion.
But it's a little redundant when returning Errors if I have to use IntoFieldError
.
Say a service:
pub struct Service;
impl juniper::Context for Service {}
impl Service {
async fn hello(&self) -> Result<String, SomeError> {
Err(SomeError::Item {
arg1: "arg1".to_string(),
arg2: 10i32,
})
}
}
If using into_field_error
, I should write code like:
pub struct Query;
#[juniper::graphql_object(Context=Service)]
impl Query {
pub async fn hello(ctx: &Service) -> FieldResult<String> {
Ok(ctx.hello().await.map_err(|err| err.into_field_error())?)
}
}
But the following code is far better:
pub struct Query;
#[juniper::graphql_object(Context=Service)]
impl Query {
pub async fn hello(ctx: &Service) -> FieldResult<String> {
Ok(ctx.hello().await?)
}
}
I don’t exactly remember how the conversion works but do you have to explicitly call “.into_field_error()”? Doesn’t the macro insert that call for you? I might be mistaking and on phone right now so I cannot check. If not I’ll investigate later.