Skip to content

Commit 6705830

Browse files
glen-84darren-clark
authored andcommitted
Fixed issue with parameter replacement in queryable field handlers (#8145)
Co-authored-by: Darren Clark <darren.clark@wheelsup.com>
1 parent 03e7804 commit 6705830

File tree

5 files changed

+259
-12
lines changed

5 files changed

+259
-12
lines changed

src/HotChocolate/Data/src/Data/Filters/Expressions/Handlers/QueryableDefaultFieldHandler.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ public override bool TryHandleEnter(
6565
}
6666

6767
nestedProperty = ReplaceVariableExpressionVisitor
68-
.ReplaceParameter(expression, expression.Parameters[0], context.GetInstance())
69-
.Body;
68+
.ReplaceParameter(expression.Body, expression.Parameters[0], context.GetInstance());
7069
}
7170
else
7271
{
@@ -172,12 +171,11 @@ protected override Expression VisitParameter(ParameterExpression node)
172171
return base.VisitParameter(node);
173172
}
174173

175-
public static LambdaExpression ReplaceParameter(
176-
LambdaExpression lambda,
174+
public static Expression ReplaceParameter(
175+
Expression lambdaBody,
177176
ParameterExpression parameter,
178177
Expression replacement)
179-
=> (LambdaExpression)
180-
new ReplaceVariableExpressionVisitor(replacement, parameter).Visit(lambda);
178+
=> new ReplaceVariableExpressionVisitor(replacement, parameter).Visit(lambdaBody);
181179
}
182180
}
183181

src/HotChocolate/Data/src/Data/Sorting/Expressions/Handlers/QueryableDefaultSortFieldHandler.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ public override bool TryHandleEnter(
5555
}
5656

5757
nextSelector = ReplaceVariableExpressionVisitor
58-
.ReplaceParameter(expression, expression.Parameters[0], lastSelector)
59-
.Body;
58+
.ReplaceParameter(expression.Body, expression.Parameters[0], lastSelector);
6059
}
6160
else
6261
{
@@ -127,11 +126,10 @@ protected override Expression VisitParameter(ParameterExpression node)
127126
return base.VisitParameter(node);
128127
}
129128

130-
public static LambdaExpression ReplaceParameter(
131-
LambdaExpression lambda,
129+
public static Expression ReplaceParameter(
130+
Expression lambdaBody,
132131
ParameterExpression parameter,
133132
Expression replacement)
134-
=> (LambdaExpression)
135-
new ReplaceVariableExpressionVisitor(replacement, parameter).Visit(lambda);
133+
=> new ReplaceVariableExpressionVisitor(replacement, parameter).Visit(lambdaBody);
136134
}
137135
}

src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/QueryableFilterVisitorObjectTests.cs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ public class QueryableFilterVisitorObjectTests
104104
},
105105
];
106106

107+
private static readonly Baz[] _bazEntities =
108+
_barEntities.Select(b => new Baz { Bar = b }).ToArray();
109+
107110
private readonly SchemaCache _cache = new();
108111

109112
[Fact]
@@ -620,6 +623,85 @@ await Snapshot
620623
.MatchAsync();
621624
}
622625

626+
[Fact]
627+
public async Task Create_ObjectStringEqual_Flattened()
628+
{
629+
// arrange
630+
var tester = _cache.CreateSchema<Bar, BarFilterInput>(_barEntities);
631+
632+
// act
633+
var res1 = await tester.ExecuteAsync(
634+
OperationRequestBuilder.New()
635+
.SetDocument(
636+
"{ root(where: { fooBarString: { eq: \"testatest\" } }) " +
637+
"{ foo { barString } } }")
638+
.Build());
639+
640+
var res2 = await tester.ExecuteAsync(
641+
OperationRequestBuilder.New()
642+
.SetDocument(
643+
"{ root(where: { fooBarString: { eq: \"testbtest\" } }) " +
644+
"{ foo { barString } } }")
645+
.Build());
646+
647+
var res3 = await tester.ExecuteAsync(
648+
OperationRequestBuilder.New()
649+
.SetDocument(
650+
"{ root(where: { fooBarString: { eq: null } }) { foo { barString } } }")
651+
.Build());
652+
653+
// assert
654+
Assert.Null(Assert.IsType<OperationResult>(res1).Errors);
655+
Assert.Null(Assert.IsType<OperationResult>(res2).Errors);
656+
Assert.Null(Assert.IsType<OperationResult>(res3).Errors);
657+
await Snapshot
658+
.Create()
659+
.AddResult(res1, "testatest")
660+
.AddResult(res2, "testbtest")
661+
.AddResult(res3, "null")
662+
.MatchAsync();
663+
}
664+
665+
[Fact]
666+
public async Task Create_ObjectStringEquals_Related_Flattened()
667+
{
668+
// arrange
669+
var tester = _cache.CreateSchema<Baz, BazFilterInput>(_bazEntities);
670+
671+
// act
672+
var res1 = await tester.ExecuteAsync(
673+
OperationRequestBuilder.New()
674+
.SetDocument(
675+
"{ root(where: { bar: { fooBarString: { eq: \"testatest\" } } }) " +
676+
"{ bar { foo { barString } } } }")
677+
.Build());
678+
679+
var res2 = await tester.ExecuteAsync(
680+
OperationRequestBuilder.New()
681+
.SetDocument(
682+
"{ root(where: { bar: { fooBarString: { eq: \"testbtest\" } } }) " +
683+
"{ bar { foo { barString } } } }")
684+
.Build());
685+
686+
var res3 = await tester.ExecuteAsync(
687+
OperationRequestBuilder.New()
688+
.SetDocument(
689+
"{ root(where: { bar: { fooBarString: { eq: null } } }) " +
690+
"{ bar { foo { barString } } } }")
691+
.Build());
692+
693+
// assert
694+
Assert.Null(Assert.IsType<OperationResult>(res1).Errors);
695+
Assert.Null(Assert.IsType<OperationResult>(res2).Errors);
696+
Assert.Null(Assert.IsType<OperationResult>(res3).Errors);
697+
await Snapshot
698+
.Create()
699+
.AddResult(res1, "testatest")
700+
.AddResult(res2, "testbtest")
701+
.AddResult(res3, "null")
702+
.MatchAsync();
703+
}
704+
623705
public class Foo
624706
{
625707
public int Id { get; set; }
@@ -664,14 +746,34 @@ public class BarNullable
664746
public FooNullable? Foo { get; set; }
665747
}
666748

749+
public class Baz
750+
{
751+
public int Id { get; set; }
752+
public Bar Bar { get; set; } = default!;
753+
}
754+
667755
public class BarFilterInput : FilterInputType<Bar>
668756
{
757+
protected override void Configure(IFilterInputTypeDescriptor<Bar> descriptor)
758+
{
759+
descriptor.Field(t => t.Foo.BarString)
760+
.Name("fooBarString");
761+
}
669762
}
670763

671764
public class BarNullableFilterInput : FilterInputType<BarNullable>
672765
{
673766
}
674767

768+
public class BazFilterInput : FilterInputType<Baz>
769+
{
770+
protected override void Configure(IFilterInputTypeDescriptor<Baz> descriptor)
771+
{
772+
descriptor.Field(b => b.Bar)
773+
.Type<BarFilterInput>();
774+
}
775+
}
776+
675777
public enum BarEnum
676778
{
677779
FOO,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
testatest Result:
2+
---------------
3+
{
4+
"data": {
5+
"root": [
6+
{
7+
"foo": {
8+
"barString": "testatest"
9+
}
10+
},
11+
{
12+
"foo": {
13+
"barString": "testatest"
14+
}
15+
}
16+
]
17+
}
18+
}
19+
---------------
20+
21+
testatest SQL:
22+
---------------
23+
.param set @__p_0 'testatest'
24+
25+
SELECT "d"."Id", "d"."FooId"
26+
FROM "Data" AS "d"
27+
INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id"
28+
WHERE "f"."BarString" = @__p_0
29+
---------------
30+
31+
testbtest Result:
32+
---------------
33+
{
34+
"data": {
35+
"root": [
36+
{
37+
"foo": {
38+
"barString": "testbtest"
39+
}
40+
},
41+
{
42+
"foo": {
43+
"barString": "testbtest"
44+
}
45+
}
46+
]
47+
}
48+
}
49+
---------------
50+
51+
testbtest SQL:
52+
---------------
53+
.param set @__p_0 'testbtest'
54+
55+
SELECT "d"."Id", "d"."FooId"
56+
FROM "Data" AS "d"
57+
INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id"
58+
WHERE "f"."BarString" = @__p_0
59+
---------------
60+
61+
null Result:
62+
---------------
63+
{
64+
"data": {
65+
"root": []
66+
}
67+
}
68+
---------------
69+
70+
null SQL:
71+
---------------
72+
SELECT "d"."Id", "d"."FooId"
73+
FROM "Data" AS "d"
74+
INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id"
75+
WHERE 0
76+
---------------
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
testatest Result:
2+
---------------
3+
{
4+
"data": {
5+
"root": [
6+
{
7+
"bar": {
8+
"foo": {
9+
"barString": "testatest"
10+
}
11+
}
12+
}
13+
]
14+
}
15+
}
16+
---------------
17+
18+
testatest SQL:
19+
---------------
20+
.param set @__p_0 'testatest'
21+
22+
SELECT "d"."Id", "d"."BarId"
23+
FROM "Data" AS "d"
24+
INNER JOIN "Bar" AS "b" ON "d"."BarId" = "b"."Id"
25+
INNER JOIN "Foo" AS "f" ON "b"."FooId" = "f"."Id"
26+
WHERE "f"."BarString" = @__p_0
27+
---------------
28+
29+
testbtest Result:
30+
---------------
31+
{
32+
"data": {
33+
"root": [
34+
{
35+
"bar": {
36+
"foo": {
37+
"barString": "testbtest"
38+
}
39+
}
40+
}
41+
]
42+
}
43+
}
44+
---------------
45+
46+
testbtest SQL:
47+
---------------
48+
.param set @__p_0 'testbtest'
49+
50+
SELECT "d"."Id", "d"."BarId"
51+
FROM "Data" AS "d"
52+
INNER JOIN "Bar" AS "b" ON "d"."BarId" = "b"."Id"
53+
INNER JOIN "Foo" AS "f" ON "b"."FooId" = "f"."Id"
54+
WHERE "f"."BarString" = @__p_0
55+
---------------
56+
57+
null Result:
58+
---------------
59+
{
60+
"data": {
61+
"root": []
62+
}
63+
}
64+
---------------
65+
66+
null SQL:
67+
---------------
68+
SELECT "d"."Id", "d"."BarId"
69+
FROM "Data" AS "d"
70+
INNER JOIN "Bar" AS "b" ON "d"."BarId" = "b"."Id"
71+
INNER JOIN "Foo" AS "f" ON "b"."FooId" = "f"."Id"
72+
WHERE 0
73+
---------------

0 commit comments

Comments
 (0)