Execution result is unable to convert values to Scalar Types
See original GitHub issueSummary
We’ve been using custom resolvers to resolve types/fields via http requests. Things have been working well but I encountered an error when trying to resolve Arrays of any Scalar Types during execution.
Here are some of the error messages I am seeing:
- “Unable to convert ‘1’ to the scalar type ‘Int’”
- “Unable to convert ‘Anna’ to the scalar type ‘String’”
- “Unable to convert ‘1.23’ to the scalar type ‘Float’”
- “Unable to convert ‘True’ to the scalar type ‘Boolean’”
Is there a configuration I missed or an extension I can write to create a generic way of converting values to their desired scalar types?
Just to note: The resolver is able to resolve arrays of object types without issue (I’ll provide more relevant info below), we just seem to be running into an issue with an array of a scalar type.
Relevant information
Sample Schema:
type Query {
users: [User]
}
type User {
id: String!
NAME: String!
favorite_numbers: [Int] # unable to convert
favorite_names: [String!]! # unable to convert
random_flags: [Boolean]! # unable to convert
previous_transactions: [Float] # unable to convert
relatives: [User] # converts successfully
}
Sample Query:
query {
users {
favorite_names
favorite_numbers
previous_transactions
random_flags
}
}
We register a visitor in the schema for each type/field in the schema with something like:
//Sample RegisterVistor impl is below
schema.RegisterVisitor(this.customResolverVisitor);
Create and execute Executor
var executor = new DocumentExecuter();
var executionResults = await executor.ExecuteAsync(_ =>
{
_.Schema = schema;
_.Query = query;
_.Variables = inputs;
_.CancellationToken = cancellation;
});
custom resolver visitor:
//
public override void VisitObjectFieldDefinition(FieldType field, IObjectGraphType type, ISchema _)
{
this.AttachResolver(type, field, field.ResolvedType);
}
public void AttachResolver(...)
{
if (fieldType is NonNullGraphType nonNullType)
{
this.AttachResolver(type, field, nonNullType.ResolvedType);
}
else if (fieldType is ListGraphType listGraphType)
{
field.Resolver = this.GraphQLResolver<JArray>(type, field);
}
else if (fieldType is ObjectGraphType)
{
field.Resolver = this.GraphQLResolver<JObject>(type, field);
}
// ... remaining scalar default scalar types like StringGraphType, IntGraphType, FloatGraphType, etc
}
T ChangeToType<T>(string content)
{
if (content == null || content.Length == 0)
{
return default(T);
}
T result;
if (typeof(T) == typeof(JObject))
{
result = (T)Convert.ChangeType(JObject.Parse(content), typeof(T));
}
else if (typeof(T) == typeof(JArray))
{
result = (T)Convert.ChangeType(JArray.Parse(content), typeof(T));
}
else
{
result = (T)Convert.ChangeType(content, typeof(T));
}
return result;
}
FuncFieldResolver<T> GraphQLResolver<T>(IObjectGraphType type, FieldType field)
{
return new FuncFieldResolver<T>(async context =>
{
// we have a flag to determine if an http call should be made to resolve this type/field
if (shouldUseHttpResolverConfigured) {
var jsonResponse = await someHttpGetCall();
return this.ChangeToType<T>(await jsonResponse.GetStringContent());
}
// otherwise we check to see if this is a nested resolver call for a field of a parent type
// and if the parent exists and has the field we are looking for already in its payload, we parse it and return it
else if (context.Source != null && context.Source is JObject obj) {
var token = obj.SelectToken(field.Name);
if (token != null) {
return this.ChangeToType<T>(token.ToString());
}
}
else { throwSomeError() }
}
}
Sample JSON Response from someHttpGetCall():
[
{
"id": "testId",
"NAME": "testName",
"favorite_numbers": [
1,
2,
3
],
"favorite_names": [
"Anna",
"Bob",
"Carl"
],
"random_flags": [
true,
true,
false
],
"previous_transactions": [
1.23,
4.56,
7.89
]
}
]
I am checking the code on myside to see if it’s something to do with the way we are parsing everything but thought I’d ask to see if perhaps there is something else we needed to do to configure the executor or extend anything to resolve the errors.
Thanks!
Environment (if relevant)
Versions:
GraphQL 5.0.0 GraphQL.NewtonSoftJson 5.0.0 GraphQL-Parser 8.0.0
Issue Analytics
- State:
- Created a year ago
- Comments:9 (6 by maintainers)
Ok so the issue is that the resolver for
favoriteNumbers
(which has a graph type ofListGraphType<IntGraphType>
) is returning aJArray
which inheirtsIList<JToken>
. GraphQL.NET understands this as an enumerable list, and iterates the list, findingJToken
instances, which the scalar doesn’t understand. At no point does the underlying JToken elements get parsed to integers.So for example, if you change like this:
Then it would work, because the resolver will return a list of integers.
There are probably many ways to solve this issue. One way would be to overwrite the built-in GraphQL scalars, so they also understand JToken objects. Then you can remove all your scalar-specific code from your sample above.
Another way would be something like this:
Either way is fine with me. These errors are “server errors” and should not normally returned to the end user, but rather used for debugging and logging. So extra information here is valuable.