graphql-platform icon indicating copy to clipboard operation
graphql-platform copied to clipboard

FilterType<T> has incorrect field definitions

Open wietlol opened this issue 7 months ago • 4 comments

Product

Hot Chocolate

Version

13.9.6

Link to minimal reproduction

https://github.com/wietlol-org/HotChocolateIssues/tree/main/HotChocolateIssues.InvalidFilterType

Steps to reproduce

Run the unit tests in the HotChocolateIssues.FilterInputType project. The test DeleteBooks will fail. After running the tests, the output is written to the TestFiles folder where the erroneous response is presented.

What is expected?

When creating a query/mutation with input according to the following snippet.

[GraphQLType(typeof(FilterInputType<Book>))]
IDictionary<string, object?> filter

The dictionary should only contain the filter as it was specified by the query/mutation input.

What is actually happening?

Instead of only setting the properties that were provided by the input, the current implementation will incorrectly fill almost all fields with the same data.

Given the following input:

mutation {
    deleteBooks(
        filter: {
            title: {eq: "Test"}
        }
    )
}

The dictionary should only contain an entry for "title", which should be a dictionary only containing an entry for "eq".

Looking at HotChocolate.Types.InputParser.cs line 255, the data seems correct: image

But the resulting value is definitely not: image

The reason for this is that the field definitions of the filter type do not have correct index values: (HotChocolate.Types.InputObjectType.Initialization.cs line 126) image

While their values are being loaded by index: image

This means that the resulting dictionary, instead of the following data:

{
	"title": {
		"eq": "Test"
	}
}

will contain the following data:

{
	"and": {
		"and": "Test",
		"or": null,
		"eq": "Test",
		"neq": "Test",
		"contains": "Test",
		"ncontains": "Test",
		"in": "Test",
		"nin": "Test",
		"startsWith": "Test",
		"nstartsWith": "Test",
		"endsWith": "Test",
		"nendsWith": "Test"
	},
	"or": null,
	"title": {
		"and": "Test",
		"or": null,
		"eq": "Test",
		"neq": "Test",
		"contains": "Test",
		"ncontains": "Test",
		"in": "Test",
		"nin": "Test",
		"startsWith": "Test",
		"nstartsWith": "Test",
		"endsWith": "Test",
		"nendsWith": "Test"
	},
	"author": {
		"and": "Test",
		"or": null,
		"eq": "Test",
		"neq": "Test",
		"contains": "Test",
		"ncontains": "Test",
		"in": "Test",
		"nin": "Test",
		"startsWith": "Test",
		"nstartsWith": "Test",
		"endsWith": "Test",
		"nendsWith": "Test"
	}
}

Relevant log output

No response

Additional context

The use of [UseFiltering] is not a valid alternative as these filter fields need to be used in mutations in different places in the input structure.

A good alternative is if a different type than an IDictionary<string, object?> can be used that is deserialized properly that can be used to filter data (IEnumerables/Lists, IQueryable, etc) But to the best of my knowledge, there isn't.

wietlol avatar Jul 04 '24 11:07 wietlol