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.24k stars 744 forks source link

StrawberryShake generated formatter is checking null for non-nullable type #3678

Closed ChrisMH closed 2 years ago

ChrisMH commented 3 years ago

StrawberryShake generated formatter is checking null for non-nullable type 11.3.0-preview.6

The SS generated formatter. The bits that are causing an error are FormatBeforeDate and FormatSinceDate, both of which take a non-nullable Instant as input, but check if(input is null)

[global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.3.0.0")]
    public partial class AssetActivityReportInputInputValueFormatter : global::StrawberryShake.Serialization.IInputObjectFormatter
    {
        private global::StrawberryShake.Serialization.IInputValueFormatter _intFormatter = default !;
        private global::StrawberryShake.Serialization.IInputValueFormatter _instantFormatter = default !;
        public global::System.String TypeName => "AssetActivityReportInput";
        public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver)
        {
            _intFormatter = serializerResolver.GetInputValueFormatter("Int");
            _instantFormatter = serializerResolver.GetInputValueFormatter("Instant");
        }

        public global::System.Object? Format(global::System.Object? runtimeValue)
        {
            if (runtimeValue is null)
            {
                return null;
            }

            var input = runtimeValue as global::GraphTest.Graphs.AssetActivityReportInput;
            var inputInfo = runtimeValue as global::GraphTest.Graphs.State.IAssetActivityReportInputInfo;
            if (input is null || inputInfo is null)
            {
                throw new global::System.ArgumentException(nameof(runtimeValue));
            }

            var fields = new global::System.Collections.Generic.List<global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>>();
            if (inputInfo.IsAssetIdsSet)
            {
                fields.Add(new global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>("assetIds", FormatAssetIds(input.AssetIds)));
            }

            if (inputInfo.IsBeforeDateSet)
            {
                fields.Add(new global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>("beforeDate", FormatBeforeDate(input.BeforeDate)));
            }

            if (inputInfo.IsSinceDateSet)
            {
                fields.Add(new global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>("sinceDate", FormatSinceDate(input.SinceDate)));
            }

            if (inputInfo.IsExtraParamSet)
            {
                fields.Add(new global::System.Collections.Generic.KeyValuePair<global::System.String, global::System.Object?>("extraParam", FormatExtraParam(input.ExtraParam)));
            }

            return fields;
        }

        private global::System.Object? FormatAssetIds(global::System.Collections.Generic.IReadOnlyList<global::System.Int32> input)
        {
            var input_list = new global::System.Collections.Generic.List<global::System.Object?>();
            foreach (var input_elm in input)
            {
                input_list.Add(_intFormatter.Format(input_elm));
            }

            return input_list;
        }

        private global::System.Object? FormatBeforeDate(global::NodaTime.Instant input)
        {
            if (input is null)
            {
                throw new global::System.ArgumentNullException(nameof(input));
            }

            return _instantFormatter.Format(input);
        }

        private global::System.Object? FormatSinceDate(global::NodaTime.Instant input)
        {
            if (input is null)
            {
                throw new global::System.ArgumentNullException(nameof(input));
            }

            return _instantFormatter.Format(input);
        }
    }

Relevant object in my schema:

input AssetActivityReportInput {
  assetIds: [Int!]!
  beforeDate: Instant!
  sinceDate: Instant!
}

I have a custom serializer registered for Instant in schema.extensions.graphql:

extend scalar Instant
    @runtimeType(name: "global::NodaTime.Instant")
    @serializationType(name: "global::System.String")

Here is the code for the serializer:

    public class InstantSerializer : ScalarSerializer<string, Instant>
    {
        private readonly IPattern<Instant> pattern;

        public InstantSerializer() : this(InstantPattern.ExtendedIso) { }

        public InstantSerializer(IPattern<Instant> pattern) : base(nameof(Instant))
        {
            this.pattern = pattern;
        }

        public override Instant Parse(string serializedValue)
        {
            var result = pattern.Parse(serializedValue);
            if (!result.Success) throw new ArgumentException($"Parse '{serializedValue}' as '{TypeName}' failed", nameof(serializedValue));
            return result.Value;
        }

        protected override string Format(Instant runtimeValue) =>
            pattern.Format(runtimeValue);
    }

And it is registered like:

services .AddSingleton<ISerializer, InstantSerializer>()

Expected behavior I would expect that the generated code NOT check is null since the input is not nullable.

michaelstaib commented 2 years ago

This one is now fixed.

ChrisMH commented 2 years ago

@michaelstaib This is still happening. Here is the SS generated code for a non-nullable instant, it's still checking for null:

private global::System.Object? FormatAlertTime(global::NodaTime.Instant input)
        {
            if (input is null)
            {
                throw new global::System.ArgumentNullException(nameof(input));
            }

            return _instantFormatter.Format(input);
        }

12.5.0-preview.15

PascalSenn commented 2 years ago

@ChrisMH do you declare it as a value type?

 @runtimeType(name: "global::NodaTime.Instant", valueType: true)
ChrisMH commented 2 years ago

@PascalSenn No

From my schema.extensions.graphql:

extend scalar Instant
    @runtimeType(name: "global::NodaTime.Instant")
    @serializationType(name: "global::System.String")

I must have missed that, adding the valueType: true works.

Thanks!