-
-
Notifications
You must be signed in to change notification settings - Fork 798
Open
Description
Product
Hot Chocolate
Is your feature request related to a problem?
Hello Community,
Thanks for your great work.
We'd like to reach out to and see if there are some gaps in feature support or we're not doing it correctly that caused the issue.
Problem
How to handle variables defined on client side and pass it correctly to HC for execution?
- Example, we have a front end that takes in the Variables that client side defines as a JSON to pass to backend.
- On backend side, we have a logic to parse the variables based on the type of the JSON element and then build the Variables dictionary and pass to HC for execution for filter.
Example of our code snippet
public Dictionary<string, object> GetGraphQLVariables()
{
Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
if (Variables == null || Variables.Count == 0)
{
return keyValuePairs;
}
foreach ((string variableName, JsonElement variable) in Variables)
{
keyValuePairs.Add(variableName, ConvertJsonElementToObject(variable));
}
return keyValuePairs;
}
private static object ConvertJsonElementToObject(JsonElement jsonElement)
{
switch (jsonElement.ValueKind)
{
case JsonValueKind.String:
return jsonElement.ToString();
case JsonValueKind.Number:
// Handle different number types (int, double, etc.)
if (jsonElement.TryGetInt16(out short shortValue))
{
return shortValue;
}
else if (jsonElement.TryGetInt32(out int intValue))
{
return intValue;
}
else if (jsonElement.TryGetDouble(out double floatValue))
{
return floatValue;
}
else if (jsonElement.TryGetInt64(out long longValue))
{
return longValue;
}
else
{
throw new InvalidArtifactMetadataRequestException($"Invalid {nameof(jsonElement.ValueKind)}.{nameof(Variables)} has invalid variable of type {jsonElement.ValueKind}");
}
case JsonValueKind.True or JsonValueKind.False:
return jsonElement.GetBoolean();
case JsonValueKind.Null:
return null;
case JsonValueKind.Array:
var list = new List<object>();
foreach (var item in jsonElement.EnumerateArray())
{
list.Add(ConvertJsonElementToObject(item));
}
return list;
case JsonValueKind.Object:
var dictionary = new Dictionary<string, object>();
foreach (JsonProperty property in jsonElement.EnumerateObject())
{
dictionary[property.Name] = ConvertJsonElementToObject(property.Value);
}
return dictionary;
default:
throw new Error;
}
}
This will have below problems
- When a column is DateTime, and client side defines a datetime in string, e.g. "2024-01-01", if we directly pass the string value into the Variable dictionary -
Dictionary<string, object>. HC will error out as DateTime Column can't parse the string automatically. - But if we always proactively TryParse DateTime from the string, then we're making an assumption that the Column type should be DateTime. Cuz if the column type happens to be String, then String can't parse the DateTime type automatically.
- Similarly for the Number types, I can see a problem that we're converting to int,long,double based on value provided in the variable instead of the actual column type.
Ask
- What should be the correct way of handling this?
- Is there an option we can directly pass the Raw string from JSON to HC and let it handle the problem for us instead of us trying to check on the column type?
Background about our Setup
- We're directing building a
IOperationRequestand send it toIRequestExecutorto execute the query directly through C# class instead of running a GraphQL server.
IOperationRequest request = OperationRequestBuilder.New()
.SetDocument(query)
.....
.SetVariableValues(variables)
.Build();
IRequestExecutor requestExecutor = await requestExecutorBuilder.AddGraphQLServer()
.ConfigureSchema((sB) =>
{
SchemaGenerator.GenerateSchemaBuilder(sB, schema);
})
.DisableIntrospection(!allowIntrosepctionQuery)
.AddMaxExecutionDepthRule(GraphQLConstants.GQLMaxQueryDepth)
.UseTimeout()
.ModifyRequestOptions(o =>
{
o.ExecutionTimeout = GraphQLConstants.HotChocolateTimeoutLimit;
})
.UseDefaultPipeline()
.UseCostAnalyzer()
.ModifyCostOptions(o =>
{
o.DefaultResolverCost = GraphQLConstants.GQLResolverComplexity;
o.MaxFieldCost = GraphQLConstants.GQLMaximumAllowedComplexity;
})
........
.TryAddTypeInterceptor(fetchMiddlewareFromContext ? new DabResolverTypeInterceptor() : new DabResolverTypeInterceptor(executionHelper, queryMiddlewareDefinition, mutationMiddlewareDefinition)) // TODO: Migrate to always using method that fetches the middleware from context
.BuildRequestExecutorAsync(cancellationToken: ct);
The solution you'd like
- HC provided a way that can take in variables that are in raw string from JSON
- Or if HC can suggest a better way that we should use to resolve the issue on our side