-
-
Notifications
You must be signed in to change notification settings - Fork 749
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
HC14 Strict RFC 3339 DateTime parsing should be opt-in/out - able? #7402
Comments
I was able to work around this by modifying your private static scalar lookups using just the right amount of unholy reflection and replacing your datetime scalar with a child of your datetime scalar that doesn't call the DateTimeScalarRegex. This is a less-than-ideal solution. |
Why dont you use BindRuntimeType? |
Is there a way to replace the DateTime scalar type that HotChcoalte register? BindRuntimeType adds a new scalar type. That cannot have the same name. This makes client generation harder down the line. |
builder.Services
.AddGraphQLServer()
.BindRuntimeType<DateTime, CustomDateTimeType>()
.AddTypes();
|
Same as magahl, I get this error when trying to do the workaround:
|
Hey @randyridge could you tell me how you did this? My approach was to try and modify the static regex in DateTimeType, but newer versions of dotnet won't let me be that naughty (because it's static readonly and the class is initialised before I can get to it) |
In our startup something like so: var lookupFieldValue = typeof(Scalars).GetField("_lookup", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null);
if(lookupFieldValue == null) {
throw new InvalidOperationException("Could not find _lookup field on Scalars type.");
}
var lookup = (Dictionary<Type, Type>) lookupFieldValue;
lookup.Remove(typeof(DateTime));
lookup.Remove(typeof(DateTimeOffset));
lookup.Add(typeof(DateTime), typeof(LooseyGooseyDateTimeType));
lookup.Add(typeof(DateTimeOffset), typeof(LooseyGooseyDateTimeType));
var nameLookupFieldValue = typeof(Scalars).GetField("_nameLookup", BindingFlags.NonPublic | BindingFlags.Static)?.GetValue(null);
if(nameLookupFieldValue == null) {
throw new InvalidOperationException("Could not find _nameLookup field on Scalars type.");
}
var nameLookup = (Dictionary<string, Type>) nameLookupFieldValue;
nameLookup[ScalarNames.DateTime] = typeof(LooseyGooseyDateTimeType); and then... using System;
using System.Globalization;
using HotChocolate.Language;
using HotChocolate.Types;
public class LooseyGooseyDateTimeType : DateTimeType {
public LooseyGooseyDateTimeType() : base(ScalarNames.DateTime) {
}
public override bool TryDeserialize(object? resultValue, out object? runtimeValue) {
switch(resultValue) {
case null:
runtimeValue = null;
return true;
case string s when TryDeserializeFromString(s, out var d):
runtimeValue = d;
return true;
case DateTimeOffset dto:
runtimeValue = dto;
return true;
case DateTime dt:
runtimeValue = new DateTimeOffset(dt.ToUniversalTime(), TimeSpan.Zero);
return true;
default:
runtimeValue = null;
return false;
}
}
protected override DateTimeOffset ParseLiteral(StringValueNode valueSyntax) {
if(TryDeserializeFromString(valueSyntax.Value, out var value)) {
return value!.Value;
}
throw new SerializationException("LooseyGooseyDateTimeType: Cannot parse literal.", this);
}
private static bool TryDeserializeFromString(string? serialized, out DateTimeOffset? value) {
// No "Unknown Local Offset Convention" - https://www.graphql-scalars.com/date-time/#no-unknown-local-offset-convention
if(string.IsNullOrEmpty(serialized) || serialized.EndsWith("-00:00")) {
value = null;
return false;
}
if(serialized.EndsWith('Z') && DateTime.TryParse(serialized, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var zuluTime)) {
value = new DateTimeOffset(zuluTime.ToUniversalTime(), TimeSpan.Zero);
return true;
}
if(DateTimeOffset.TryParse(serialized, out var dt)) {
value = dt;
return true;
}
value = null;
return false;
}
} ymmv |
@randyridge thanks for that, I figured all that out but what I also needed to do was replace the references to it in the filtering. Because we have Here's how I did that:
|
Product
Hot Chocolate
Is your feature request related to a problem?
In updating from HC 13 -> 14 we encountered a runtime behavior change, with respect to dates, I see that this is documented explicitly in the migration notes and I agree that having a common datetime spec is beneficial and rfc 3339 is a reasonable choice. I also realize that 13->14 is a major semver and could be breaking. All that said, I have clients sending me dates that are not strictly compatible, and while I agree they're not doing the correct thing and can and should be fixed, it's unfortunate this new spec enforcement is coupled directly with upgrading to 14.
My proposal would be to allow the ability to opt-in to the legacy datetime parser similar to how I can do the same for the legacy node ID serializer. It gives me time to migrate clients while the server can go from 13 to 14. For dates though, I have published clients, who from their point of view are sending the same query to the same schema and are now broken just for me having updated the server from 13 to 14. I need some way to decouple the 13 -> 14 upgrade from the strict enforcement of the datetime spec the same way I can decouple the upgrade from the new ID serializer.
I'm left with few options, either the client has to change, our clients go through app approval processes on various platforms, which again I can't always force (some platforms won't force an update even if one is available), especially at an exact time (like when the hc14 server starts taking traffic), or I have to upgrade and deploy elsewhere so new clients that are sending rfc compatible dates can hit an hc 14 instance (without a schema change) and those that haven't updated can still point to the hc13 version (with the same schema) and run dual processes for some period of time.
Specifically the error was:
DateTime cannot parse the given literal of type StringValueNode
For this date (missing seconds)
"2024-08-23T00:00-04:00"
The solution you'd like
allow the ability to opt-in to the legacy datetime parser
The text was updated successfully, but these errors were encountered: