-
Notifications
You must be signed in to change notification settings - Fork 1.1k
CoerceInputValue() #1179
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
base: main
Are you sure you want to change the base?
CoerceInputValue() #1179
Conversation
✅ Deploy Preview for graphql-spec-draft ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
1f26f1c
to
5f8fa39
Compare
5f8fa39
to
4f40177
Compare
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 really like the introduction of coercion failure. This is a solid change, and we should consider extracting it and fast-track merging it as an editorial change.
I like the more algorithmic approach to validation, I think it should add significant clarity.
However, I'm struggling to actually comprehend this as it currently stands - see my second comment for details.
(As an aside, I think that it might behoove us to add "pseudotypes" to our algorithms to make it much clearer exactly what the type of each variable in an algorithm is.)
|
||
- Assert: {IsInputType(type)}. | ||
- If {type} is a Non-Null type: | ||
- If {value} is {null}, raise a _coercion failure_. |
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'm uncomfortable with how the CoerceInputValue
algorithm seems to be fed value
s which are a mixture of literals (AST nodes) from the document (required for things inside of input objects/lists within value
to be a "variable", right?) versus raw values from the client (required for its usage from CoerceVariableValues()
). This feels like it is essentially mixing together GraphQL.js' parseLiteral
and parseValue
and not being very explicit about it?
If this is not the case, please could you explain further what it means for something within value
to "be a variable" if it's also accepting raw values from CoerceVariableValues()
which could be essentially anything (due to custom scalars) - or maybe point out where literals and values are handled separately and I've misread the algorithms.
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.
You are correct. The spec groups literal values and externally provided values under the same umbrella when describing input coercion.
I share the unease around this, and maybe part of what we need is a better name for value
though to be fair we already have this "variable is a kind of value" here https://spec.graphql.org/draft/#Value
It's true that we are quite hand-wavy about how externally provided values are handled, and some of that is necessary because we need to support different runtimes and serialization. Perhaps we need some description in this "input coercion" section (or the variables coercion section?) where we explain how we expect a GraphQL service to provide a way to represent their internal values as those GraphQL expects.
An example were we do spell some detail here is enums https://spec.graphql.org/draft/#sec-Enums.Input-Coercion where we need to answer the question of whether an enum should input coerce a string? The answer is only if there isn't a better option. GraphQL literals has a better option, EDN has a better option, JSON does not.
@@ -1649,11 +1725,47 @@ type of an Object or Interface field. | |||
|
|||
**Input Coercion** | |||
|
|||
CoerceInputObjectValue(type, value, variableValues): |
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.
Noting this does not (yet) handle oneOf, so we'll need to decide which order we are merging these. Rewriting for oneOf may require a bit of refactoring due to the way ResolveVariable
works.
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.
oneof is, barring last few q's on the thread, ready to go - so I assume I'd be rebasing over it.
they are essentially different types, so I was thinking something like forking based on a directive conditional check to a CoerceInputOneOfValue()
algo
@@ -502,10 +542,17 @@ information on the serialization of scalars in common JSON and other formats. | |||
|
|||
**Input Coercion** | |||
|
|||
CoerceScalarValue(scalarType, value): |
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.
The JSON
scalar, discouraged though it may be, requires access to variableValues
.
I'm okay with omitting this from the spec, but it's worth noting that GraphQL.js does pass variableValues
to parseLiteral
/parseValue
.
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.
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.
yeah this is a reasonable compliance question - is it valid to consider variable values during scalar input coercion? I think it's okay to say yes, though I agree with you that this is a bit weird, it doesn't seem harmful.
The one consideration here is that we'd want to make sure we actually reference variableValues
in the prose, I think we'd do that sort of like `Return the result of calling the internal method provided by the type system for determining the “input coercion” of {scalarType} given the value {value} (which may consider {variableValues})."
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.
+1 to taking variableValues
into account: CoerceScalarValue(scalarType, value, variableValues)
It's late here and I don't have all the context any more but we might even want coercedVariableValues
?
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.
wordy but more accurate :)
|
||
ResolveVariable(type, variableName, coercedVariableValues): | ||
|
||
- If {coercedVariableValues} has an entry named {variableName}, let |
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.
- If {coercedVariableValues} has an entry named {variableName}, let | |
- If {coercedVariableValues} exists and has an entry named {variableName}, let |
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 think we could make this an assertion, right? The only time it doesn't exist is when we're coercing constant literals which do not have variables in them
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 may be different to your intention, but I figure we call this from validation too (e.g. in “values of correct type”) where variable values are unknown (null) so we need to assume they’re valid (by “all variables usages are allowed”) and thus we simply don’t access the variable when variableValues is explicitly null (which implies it is being called during validation?)
Co-authored-by: Benjie <benjie@jemjie.com>
Co-authored-by: Benjie <benjie@jemjie.com>
Co-authored-by: Benjie <benjie@jemjie.com>
Co-authored-by: Benjie <benjie@jemjie.com>
Co-authored-by: Benjie <benjie@jemjie.com>
Co-authored-by: Benjie <benjie@jemjie.com>
As an alternative to #1058 and #1178 - this is a more substantial overhaul of how input value coercion is defined in favor of a system of composed algorithms.
A quick summary of what this introduces:
Introduces the concept of a coercion failure which has a similar "raised error" behavior, but is caught and translated into specific errors based on each callsite. That removes the awkwardness of "a request error is actually an execution error here".
A new "Input Coercion" section which defines
CoerceInputValue()
. This is the point of reusability which handles nullability and then forks out to each specific input type.Algorithms for each kind of type's input coercion. Notably input objects and lists which have been under-defined (as an alt to [RFC] List coercion algorithm #1058).
A validation rule for default variable values, which I believe exists in graphql.js but was missing in spec.
Open Qs:
Is this the right place for the Input Coercion subsection?
Have I missed anything?