ChilliCream / ChilliCream/graphql-platform

Bug when deserializing multiple custom scalars to same type

Open
#6,378 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

🌶️ strawberry shake
Dominant language
C#
Stars
5.8k
Forks
810
Avg merge
15h 39m
Merged PRs (30d)
98

Description

Is there an existing issue for this?
  • I have searched the existing issues
Product

Strawberry Shake

Describe the bug

When multiple custom scalar deserializers are defined that deserialize to the same type, the Deserialize methods in the generated code will overlap and one of the custom deserializers will be used for both types.

Example query:

query TestMultipleScalar {
    a
    b
}
type Query { a: A, b: B }
scalar A
scalar B
extend scalar A
    @serializationType(name: "global::System.Text.Json.JsonElement")
    @runtimeType(name: "global::System.String")
extend scalar B
    @serializationType(name: "global::System.Text.Json.JsonElement")
    @runtimeType(name: "global::System.String")

Generated code:

    [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")]
    public partial class TestMultipleScalarBuilder : global::StrawberryShake.OperationResultBuilder<global::Foo.Bar.ITestMultipleScalarResult>
    {
        private readonly global::StrawberryShake.IEntityStore _entityStore;
        private readonly global::StrawberryShake.IEntityIdSerializer _idSerializer;
        private readonly global::StrawberryShake.Serialization.ILeafValueParser<global::System.Text.Json.JsonElement, global::System.String> _aParser;
        private readonly global::StrawberryShake.Serialization.ILeafValueParser<global::System.Text.Json.JsonElement, global::System.String> _bParser;
        public TestMultipleScalarBuilder(global::StrawberryShake.IEntityStore entityStore, global::StrawberryShake.IEntityIdSerializer idSerializer, global::StrawberryShake.IOperationResultDataFactory<global::Foo.Bar.ITestMultipleScalarResult> resultDataFactory, global::StrawberryShake.Serialization.ISerializerResolver serializerResolver)
        {
            _entityStore = entityStore ?? throw new global::System.ArgumentNullException(nameof(entityStore));
            _idSerializer = idSerializer ?? throw new global::System.ArgumentNullException(nameof(idSerializer));
            ResultDataFactory = resultDataFactory ?? throw new global::System.ArgumentNullException(nameof(resultDataFactory));
            _aParser = serializerResolver.GetLeafValueParser<global::System.Text.Json.JsonElement, global::System.String>("A") ?? throw new global::System.ArgumentException("No serializer for type `A` found.");
            _bParser = serializerResolver.GetLeafValueParser<global::System.Text.Json.JsonElement, global::System.String>("B") ?? throw new global::System.ArgumentException("No serializer for type `B` found.");
        }

        protected override global::StrawberryShake.IOperationResultDataFactory<global::Foo.Bar.ITestMultipleScalarResult> ResultDataFactory { get; }

        protected override global::StrawberryShake.IOperationResultDataInfo BuildData(global::System.Text.Json.JsonElement obj)
        {
            var entityIds = new global::System.Collections.Generic.HashSet<global::StrawberryShake.EntityId>();
            global::StrawberryShake.IEntityStoreSnapshot snapshot = default !;
            _entityStore.Update(session =>
            {
                snapshot = session.CurrentSnapshot;
            });
            return new TestMultipleScalarResultInfo(Deserialize_String(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "a")), Deserialize_String(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "b")), entityIds, snapshot.Version);
        }

        private global::System.String? Deserialize_String(global::System.Text.Json.JsonElement? obj)
        {
            if (!obj.HasValue)
            {
                return null;
            }

            return _aParser.Parse(obj.Value!);
        }
    }

Note how the "b" property is deserialized by Deserialize_String, which calls _aParser and not _bParser.

Steps to reproduce

See example query.

Relevant log output

No response

Additional Context?

No response

Version

latest source

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the generated TestMultipleScalarBuilder shown in the issue and reproduce the example query with scalar A and scalar B. Trace how the generated Deserialize_String method is selected and verify that each scalar property uses its own parser; done means the generated code no longer routes both properties through _aParser.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.