ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.
https://chillicream.com
MIT License
5.28k stars 748 forks source link

StrawberryShake serialization of GeoJSONPointInput #6672

Open ChrisMH opened 1 year ago

ChrisMH commented 1 year ago

Is there an existing issue for this?

Product

Strawberry Shake

Describe the bug

I am sending a GeoJSONPointInput to a GraphQL server for reverse geocoding. I am using a custom serializer to serialize the Position part of the input type, which has been in place and working up through 13.5.1. When upgrading to 13.7.0, my server started returning errors. I have tracked down the prerelease versions and the issue was introduced in 13.6.0-preview.33. 13.6.0-preview.32 worked.

Steps to reproduce

Using this serializer, send a GeoJSONPointInput to a GraphQL server:

    public class PositionSerializer : ScalarSerializer<JsonElement, Array>
    {
        public PositionSerializer() : base("Position") { }

        public override Array Parse(JsonElement serializedValue)
        {
            if (serializedValue.ValueKind != JsonValueKind.Array)
                throw new ArgumentException($"expected ValueKind '{JsonValueKind.Array}', received '{serializedValue.ValueKind}", nameof(serializedValue));

            if (serializedValue.GetArrayLength() == 2)
                return new[] {serializedValue[0].GetDouble(), serializedValue[1].GetDouble(), 0.0};

            if (serializedValue.GetArrayLength() == 3)
                return new[] {serializedValue[0].GetDouble(), serializedValue[1].GetDouble(), serializedValue[2].GetDouble()};

            throw new ArgumentException($"expected 2 or 3 coordinate values, received {serializedValue.GetArrayLength()}", nameof(serializedValue));
        }

        protected override JsonElement Format(Array runtimeValue)
        {
            if (runtimeValue.Length == 2)
                return JsonDocument.Parse($"[{(double) runtimeValue.GetValue(0)!},{(double) runtimeValue.GetValue(1)!},0.0]").RootElement;

            if (runtimeValue.Length == 3)
                return JsonDocument.Parse($"[{(double) runtimeValue.GetValue(0)!},{(double) runtimeValue.GetValue(1)!},{(double) runtimeValue.GetValue(2)!}]").RootElement;

            throw new ArgumentException($"expected 2 or 3 coordinate values, received {runtimeValue.Length}", nameof(runtimeValue));
        }
    }

Relevant log output

Xunit.Sdk.EmptyException
Assert.Empty() Failure: Collection was not empty
Collection: [ClientError { Code = null, Exception = null, Extensions = [["field"] = "GeoJSONPointInput.coordinates", ["fieldType"] = "Position"], Locations = null, Message = "Unknown", ··· }]
   at GraphTest.Integration.GraphIntegrationTestBase.VerifyOperationResult[T](IOperationResult`1 result, ITestOutputHelper testOutput) in /Users/chogan/Dev/ovc/web-service/GraphTest/Integration/GraphIntegrationTestBase.cs:line 220
   at GraphTest.Integration.Details.Geocode.ReverseGeocodeTests.ReverseGeocodeTest(TestCase tc) in /Users/chogan/Dev/ovc/web-service/GraphTest/Integration/Details/Geocode/ReverseGeocodeTests.cs:line 62
   at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_0.<<InvokeTestMethodAsync>b__1>d.MoveNext() in /_/src/xunit.execution/Sdk/Frameworks/Runners/TestInvoker.cs:line 285
--- End of stack trace from previous location ---
   at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in /_/src/xunit.execution/Sdk/Frameworks/ExecutionTimer.cs:line 48
   at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in /_/src/xunit.core/Sdk/ExceptionAggregator.cs:line 90

Data: none
1. message:Unknown, code:
    Extensions:
    field: GeoJSONPointInput.coordinates
    fieldType: Position

Additional Context?

13.6.0-preview.32 - Working 13.6.0-preview.33 - NOT working

My server is 13.7.0. I am using 13.7.0 for code generation. The package that appears to cause the problem is StrawberryShake.Server. The above versions where changed for that package only.

I am able to send the operation directly to the server using Postman:

query($point: GeoJSONPointInput!) {
    reverseGeocode(point: $point) {
      formattedAddress
    }
}

{
    "point": {
        "type": "Point",
        "coordinates": [-85.113162,41.0766406,0]
    }
}

Version

13.7.0

anna-is-cute commented 1 year ago

I expect this issue is the same one I have been experiencing. I also had to downgrade to 13.5.1 because anything newer causes issues.

public class OptionsSerializer : ScalarSerializer<JsonElement, Dictionary<string, List<string>>> {
    public OptionsSerializer(string typeName = "Options") : base(typeName) {
    }

    public override Dictionary<string, List<string>> Parse(JsonElement serializedValue) {
        return serializedValue.Deserialize<Dictionary<string, List<string>>>()!;
    }

    protected override JsonElement Format(Dictionary<string, List<string>> runtimeValue) {
        return JsonSerializer.SerializeToElement(runtimeValue);
    }
}

Using this results in

StrawberryShake.GraphQLClientException: Failed to parse "Options": invalid type: string "{}", expected a map
   at StrawberryShake.OperationResultExtensions.EnsureNoErrors(IOperationResult result)

Rust backend is reporting that it received a string "{}" instead of an empty object {}.