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

Call scalar coerceUserInput before coerceOutput #993

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

filosganga
Copy link
Contributor

@filosganga filosganga commented Apr 14, 2023

This PR investigates a solution for #991

The scalar are always input coerced before coerced for output. This also mean that for any scalarType: ScalarType[T], scalarType.coerceUserInput must accept any T and return it as-is (It is already the case for all the builtin Scalars).

The coercion happens in two places, the default field resolver, and the FutureResolver, that is basically the base resolver. I could not find a better place to do it, probably something in between the input unmarshaller and the resolver would be the best place. Unfortunately, the unmarshaller does not receive any information about the ScalarType, so it can only leverage the underlying model.

Comment on lines +1408 to +1412
val corcedInput = scalar
.coerceUserInput(value)
// Do we need a specific exepction here?
.fold(e => throw new ValidationError(Vector(e), exceptionHandler), identity)
val coerced = scalar.coerceOutput(corcedInput, marshaller.capabilities)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Throwing an exception here because the whole block is protected by try/catch

@@ -34,6 +34,7 @@ class ResolverBasedAstSchemaBuilderSpec extends AnyWordSpec with Matchers with F
"UUID",
coerceOutput = (v, _) => v.toString,
coerceUserInput = {
case uuid: UUID => Right(uuid)
Copy link
Contributor

Choose a reason for hiding this comment

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

I try to understand:
coerceUserInput is now called twice:

  • the first time to parse the user input as String into a UUID
  • the second time to confirm that a UUID is a valid value
    ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No it is not called, twice, but I have spent a bit of time looking at Apollo implementation, and I think, I would just need to change the scalars rather than Sangria.

In Apollo the scalar are represented by 3 functions:

  • serialize it is the same as coerceOutput, it coerces the scalar to a JSON representation
  • parseLiteral it is the same as coerceInput that is bit misleading, it basically coerces and hardcoded value into the scalar type
  • parseValue it is the same as coerceUserInput that basically coerces the values coming from the users via not hardcoded variables.

When you have an untyped underlying model, like JSON, the serialize function should take care also of parsing the type from the underlying model.

I will investigate more about that and maybe drop this PR in favour of a documentation one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants