Skip to content

Adding fields to an input type via TypeInterceptor causes an exception in InputParser #8847

@bbarry

Description

@bbarry

Product

Hot Chocolate

Version

15.1.11

Link to minimal reproduction

https://github.com/ChilliCream/graphql-platform

Steps to reproduce

Add and run this test:

public class TypeInterceptor_InputParserTests
{
    public class TestingInterceptor : TypeInterceptor
    {
        public override void OnBeforeCompleteType(
            ITypeCompletionContext completionContext,
            DefinitionBase definition)
        {
            if (definition is InputObjectTypeDefinition objectDefinition)
            {
                // Problem reproduces whenever we modify an input type definition
                // and that input type is used in another input type as a list element type,
                // and an operation is executed providing a value of the modified input type in that list.
                if (objectDefinition.Name == "SecondClassInput")
                {
                    var field = new InputFieldDefinition
                    {
                        Name = "testInterceptorCalled",
                        Type = TypeReference.Parse("Boolean"),
                    };
                    objectDefinition.Fields.Add(field);
                }
            }
        }
    }

    public class Query
    {
        public int Foo => 0;
    }

    public class Mutation
    {
        public CreateTestClassPayload CreateTestClass(CreateTestClassInput input) => new(new(input.TestClassId, input.Second.Select(x => new SecondClass(x.SecondId)).ToList()));
    }

    public record CreateTestClassPayload(TestClass? TestClass);
    public record CreateTestClassInput(int TestClassId, List<SecondClassInput> Second);
    public record TestClass(int TestClassId, List<SecondClass> Second);

    public record SecondClass(int SecondId);
    public record SecondClassInput(int SecondId);

    [Fact]
    public async Task AddingInterceptor_ShouldNotBreakInputParser()
    {
        var result = await new ServiceCollection()
            .AddGraphQL()
            .AddQueryType<Query>()
            .AddMutationType<Mutation>()
            .TryAddTypeInterceptor<TestingInterceptor>()
            .ModifyRequestOptions(o => o.IncludeExceptionDetails = true)
            .ExecuteRequestAsync("""
                mutation {
                  createTestClass(input: {
                    testClassId: 123
                    second: [
                      { secondId: 124 }
                    ]
                  })
                  {
                    testClass {
                      testClassId
                      second {
                        secondId
                      }
                    }
                  }
                }
            """, cancellationToken: TestContext.Current.CancellationToken);


        result.MatchInlineSnapshot("""
            {
              "data": {
                "createTestClass": {
                  "testClass": {
                    "testClassId": 123,
                    "second": [
                      {
                        "secondId": 124
                      }
                    ]
                  }
                }
              }
            }
        """);
    }
}

What is expected?

Operation executes, test passes.

What is actually happening?

Test fails with exception.

Relevant log output

Extracted error from exception details:


The value "System.Collections.Generic.Dictionary`2[System.String,System.Object]" is not of type "TypeInterceptor_InputParserTests+SecondClassInput" and cannot be used in this generic collection. (Parameter 'value')
   at System.Collections.Generic.List`1.System.Collections.IList.Add(Object item)
   at HotChocolate.Types.InputParser.ParseList(IValueNode resultValue, ListType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
   at HotChocolate.Types.InputParser.ParseLiteralInternal(IValueNode value, IType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
   at HotChocolate.Types.InputParser.ParseLiteralInternal(IValueNode value, IType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
   at HotChocolate.Types.InputParser.ParseObject(IValueNode resultValue, InputObjectType type, Path path, Int32 stack, Boolean defaults)
   at HotChocolate.Types.InputParser.ParseLiteralInternal(IValueNode value, IType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
   at HotChocolate.Types.InputParser.ParseLiteralInternal(IValueNode value, IType type, Path path, Int32 stack, Boolean defaults, IInputFieldInfo field)
   at HotChocolate.Types.InputParser.ParseLiteral(IValueNode value, IInputFieldInfo field, Type targetType)
   at HotChocolate.Execution.Processing.MiddlewareContext.CoerceArgumentValue[T](ArgumentValue argument)
   at HotChocolate.Execution.Processing.MiddlewareContext.ArgumentValue[T](String name)
   at lambda_method3(Closure, IResolverContext)
   at HotChocolate.Types.Helpers.FieldMiddlewareCompiler.<>c__DisplayClass9_0.<<CreateResolverMiddleware>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)
   at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions