From 2ab9c51120b3c9c27f8625bd0aa33c8709908a67 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 23 Sep 2022 17:06:04 +1000 Subject: [PATCH 1/4] . --- .../Where/ArgumentProcessor_List.cs | 6 + ...ionTests.Parent_child_with_id.verified.txt | 27 +++++ ...ionTests.Parent_with_id_child.verified.txt | 31 +++++ ....Parent_with_id_child_with_id.verified.txt | 28 +++++ .../IntegrationTests/IntegrationTests.cs | 113 ++++++++++++++++++ 5 files changed, 205 insertions(+) create mode 100644 src/Tests/IntegrationTests/IntegrationTests.Parent_child_with_id.verified.txt create mode 100644 src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child.verified.txt create mode 100644 src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child_with_id.verified.txt diff --git a/src/GraphQL.EntityFramework/Where/ArgumentProcessor_List.cs b/src/GraphQL.EntityFramework/Where/ArgumentProcessor_List.cs index 3aa08a403..1716e41ad 100644 --- a/src/GraphQL.EntityFramework/Where/ArgumentProcessor_List.cs +++ b/src/GraphQL.EntityFramework/Where/ArgumentProcessor_List.cs @@ -14,6 +14,12 @@ static IEnumerable ApplyToAll(this IEnumerable items, bool var predicate = ExpressionBuilder.BuildPredicate("Id", Comparison.In, values); items = items.Where(predicate.Compile()); } + + if (ArgumentReader.TryReadId(getArguments, out var value)) + { + var predicate = ExpressionBuilder.BuildPredicate("Id", Comparison.Equal, new[] {value}); + items = items.Where(predicate.Compile()); + } } if (ArgumentReader.TryReadWhere(getArguments, out var wheres)) diff --git a/src/Tests/IntegrationTests/IntegrationTests.Parent_child_with_id.verified.txt b/src/Tests/IntegrationTests/IntegrationTests.Parent_child_with_id.verified.txt new file mode 100644 index 000000000..5b6da8bc3 --- /dev/null +++ b/src/Tests/IntegrationTests/IntegrationTests.Parent_child_with_id.verified.txt @@ -0,0 +1,27 @@ +{ + target: +{ + "data": { + "parentEntities": [ + { + "property": "Value1", + "children": [ + { + "property": "Child1" + } + ] + } + ] + } +}, + sql: [ + { + HasTransaction: false, + Text: +SELECT [p].[Id], [p].[Property], [c].[Id], [c].[Nullable], [c].[ParentId], [c].[Property] +FROM [ParentEntities] AS [p] +LEFT JOIN [ChildEntities] AS [c] ON [p].[Id] = [c].[ParentId] +ORDER BY [p].[Id] + } + ] +} \ No newline at end of file diff --git a/src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child.verified.txt b/src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child.verified.txt new file mode 100644 index 000000000..377b7d2c1 --- /dev/null +++ b/src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child.verified.txt @@ -0,0 +1,31 @@ +{ + target: +{ + "data": { + "parentEntities": [ + { + "property": "Value1", + "children": [ + { + "property": "Child2" + }, + { + "property": "Child1" + } + ] + } + ] + } +}, + sql: [ + { + HasTransaction: false, + Text: +SELECT [p].[Id], [p].[Property], [c].[Id], [c].[Nullable], [c].[ParentId], [c].[Property] +FROM [ParentEntities] AS [p] +LEFT JOIN [ChildEntities] AS [c] ON [p].[Id] = [c].[ParentId] +WHERE [p].[Id] = 'Guid_1' +ORDER BY [p].[Id] + } + ] +} \ No newline at end of file diff --git a/src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child_with_id.verified.txt b/src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child_with_id.verified.txt new file mode 100644 index 000000000..dc8bba1bb --- /dev/null +++ b/src/Tests/IntegrationTests/IntegrationTests.Parent_with_id_child_with_id.verified.txt @@ -0,0 +1,28 @@ +{ + target: +{ + "data": { + "parentEntities": [ + { + "property": "Value1", + "children": [ + { + "property": "Child1" + } + ] + } + ] + } +}, + sql: [ + { + HasTransaction: false, + Text: +SELECT [p].[Id], [p].[Property], [c].[Id], [c].[Nullable], [c].[ParentId], [c].[Property] +FROM [ParentEntities] AS [p] +LEFT JOIN [ChildEntities] AS [c] ON [p].[Id] = [c].[ParentId] +WHERE [p].[Id] = 'Guid_1' +ORDER BY [p].[Id] + } + ] +} \ No newline at end of file diff --git a/src/Tests/IntegrationTests/IntegrationTests.cs b/src/Tests/IntegrationTests/IntegrationTests.cs index 776c871e9..134e5163f 100644 --- a/src/Tests/IntegrationTests/IntegrationTests.cs +++ b/src/Tests/IntegrationTests/IntegrationTests.cs @@ -1487,6 +1487,119 @@ public async Task Parent_child() await RunQuery(database, query, null, null, false, new object[] { entity1, entity2, entity3, entity4, entity5 }); } + [Fact] + public async Task Parent_child_with_id() + { + var parent = new ParentEntity + { + Property = "Value1" + }; + var child1 = new ChildEntity + { + Property = "Child1", + Parent = parent + }; + parent.Children.Add(child1); + var child2 = new ChildEntity + { + Property = "Child2", + Parent = parent + }; + parent.Children.Add(child2); + + var query = $@" +{{ + parentEntities + {{ + property + children(id:'{child1.Id}' ) + {{ + property + }} + }} +}}"; + await using var database = await sqlInstance.Build(); + await RunQuery(database, query, null, null, false, new object[] { parent, child1, child2 }); + } + + [Fact(Skip = "fix order")] + public async Task Parent_with_id_child() + { + var parent1 = new ParentEntity + { + Property = "Value1" + }; + var parent2 = new ParentEntity + { + Property = "Value2" + }; + var child1 = new ChildEntity + { + Property = "Child1", + Parent = parent1 + }; + parent1.Children.Add(child1); + var child2 = new ChildEntity + { + Property = "Child2", + Parent = parent1 + }; + parent1.Children.Add(child2); + + var query = $@" +{{ + parentEntities(id:'{parent1.Id}') + {{ + property + children + {{ + property + }} + }} +}}"; + await using var database = await sqlInstance.Build(); + await RunQuery(database, query, null, null, false, new object[] { parent1, parent2, child1, child2 }); + } + + [Fact] + public async Task Parent_with_id_child_with_id() + { + var parent1 = new ParentEntity + { + Property = "Value1" + }; + var parent2 = new ParentEntity + { + Property = "Value2" + }; + var child1 = new ChildEntity + { + Property = "Child1", + Parent = parent1 + }; + parent1.Children.Add(child1); + var child2 = new ChildEntity + { + Property = "Child2", + Parent = parent1 + }; + parent1.Children.Add(child2); + + var query = $@" +{{ + parentEntities(id:'{parent1.Id}') + {{ + property + children(id:'{child1.Id}' ) + {{ + property + }} + }} +}}"; + await using var database = await sqlInstance.Build(); + await RunQuery(database, query, null, null, false, new object[] { parent1, parent2, child1, child2 }); + } + [Fact] public async Task Many_children() { From ffb7cdb8566c3172e366b37fcedda21b6afe8b30 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 23 Sep 2022 17:17:10 +1000 Subject: [PATCH 2/4] . --- .../Where/ArgumentProcessor_List.cs | 6 --- .../Where/ArgumentProcessor_Queryable.cs | 7 --- .../Where/ArgumentReader.cs | 53 +++++++++---------- src/SampleWeb.Tests/GraphQlControllerTests.cs | 4 +- 4 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/GraphQL.EntityFramework/Where/ArgumentProcessor_List.cs b/src/GraphQL.EntityFramework/Where/ArgumentProcessor_List.cs index 1716e41ad..3aa08a403 100644 --- a/src/GraphQL.EntityFramework/Where/ArgumentProcessor_List.cs +++ b/src/GraphQL.EntityFramework/Where/ArgumentProcessor_List.cs @@ -14,12 +14,6 @@ static IEnumerable ApplyToAll(this IEnumerable items, bool var predicate = ExpressionBuilder.BuildPredicate("Id", Comparison.In, values); items = items.Where(predicate.Compile()); } - - if (ArgumentReader.TryReadId(getArguments, out var value)) - { - var predicate = ExpressionBuilder.BuildPredicate("Id", Comparison.Equal, new[] {value}); - items = items.Where(predicate.Compile()); - } } if (ArgumentReader.TryReadWhere(getArguments, out var wheres)) diff --git a/src/GraphQL.EntityFramework/Where/ArgumentProcessor_Queryable.cs b/src/GraphQL.EntityFramework/Where/ArgumentProcessor_Queryable.cs index 2ff9ca91a..a6958bfdc 100644 --- a/src/GraphQL.EntityFramework/Where/ArgumentProcessor_Queryable.cs +++ b/src/GraphQL.EntityFramework/Where/ArgumentProcessor_Queryable.cs @@ -19,13 +19,6 @@ public static IQueryable ApplyGraphQlArguments( var predicate = ExpressionBuilder.BuildPredicate(keyName, Comparison.In, values); queryable = queryable.Where(predicate); } - - if (ArgumentReader.TryReadId(GetArguments, out var value)) - { - var keyName = GetKeyName(keyNames); - var predicate = ExpressionBuilder.BuildPredicate(keyName, Comparison.Equal, new[] {value}); - queryable = queryable.Where(predicate); - } } if (ArgumentReader.TryReadWhere(GetArguments, out var wheres)) diff --git a/src/GraphQL.EntityFramework/Where/ArgumentReader.cs b/src/GraphQL.EntityFramework/Where/ArgumentReader.cs index e1928764d..e049d738d 100644 --- a/src/GraphQL.EntityFramework/Where/ArgumentReader.cs +++ b/src/GraphQL.EntityFramework/Where/ArgumentReader.cs @@ -9,48 +9,45 @@ public static bool TryReadWhere(Func getArgument, out IEn public static IEnumerable ReadOrderBy(Func getArgument) => getArgument.ReadList("orderBy"); - public static bool TryReadIds(Func getArgument, [NotNullWhen(returnValue: true)] out string[]? expression) + public static bool TryReadIds(Func getArgument, [NotNullWhen(returnValue: true)] out string[]? result) { - var argument = getArgument(typeof(object), "ids"); - if (argument is null) + string ArgumentToExpression(object argument) { - expression = null; - return false; + return argument switch + { + long l => l.ToString(CultureInfo.InvariantCulture), + int i => i.ToString(CultureInfo.InvariantCulture), + string s => s, + _ => throw new($"TryReadId got an 'id' argument of type '{argument.GetType().FullName}' which is not supported.") + }; } - if (argument is IEnumerable objCollection) + var idsArgument = getArgument(typeof(object), "ids"); + var idArgument = getArgument(typeof(object), "id"); + if (idsArgument is null && idArgument is null) { - expression = objCollection.Select(o => o.ToString()).ToArray()!; - return true; + result = null; + return false; } - throw new($"TryReadIds got an 'ids' argument of type '{argument.GetType().FullName}' which is not supported."); - } + var expressions = new List(); - public static bool TryReadId(Func getArgument, [NotNullWhen(returnValue: true)] out string? expression) - { - var argument = getArgument(typeof(object), "id"); - if (argument is null) + if (idArgument is not null) { - expression = null; - return false; + expressions.Add( ArgumentToExpression(idArgument)); } - switch (argument) + if (idsArgument is not null) { - case long l: - expression = l.ToString(CultureInfo.InvariantCulture); - break; - case int i: - expression = i.ToString(CultureInfo.InvariantCulture); - break; - case string s: - expression = s; - break; - default: - throw new($"TryReadId got an 'id' argument of type '{argument.GetType().FullName}' which is not supported."); + if (idsArgument is not IEnumerable objCollection) + { + throw new($"TryReadIds got an 'ids' argument of type '{idsArgument.GetType().FullName}' which is not supported."); + } + + expressions.AddRange(objCollection.Select(ArgumentToExpression)); } + result = expressions.ToArray(); return true; } diff --git a/src/SampleWeb.Tests/GraphQlControllerTests.cs b/src/SampleWeb.Tests/GraphQlControllerTests.cs index 358cb4bd6..61255ddc7 100644 --- a/src/SampleWeb.Tests/GraphQlControllerTests.cs +++ b/src/SampleWeb.Tests/GraphQlControllerTests.cs @@ -9,9 +9,9 @@ [UsesVerify] public class GraphQlControllerTests { - static HttpClient client = null!; + static HttpClient client; static ClientQueryExecutor clientQueryExecutor; - static WebSocketClient webSocket = null!; + static WebSocketClient webSocket; static GraphQlControllerTests() { From da64beae14573b85c99b06fd62e6cbdd2e5ccd68 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Fri, 23 Sep 2022 17:17:57 +1000 Subject: [PATCH 3/4] Update Directory.Build.props --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ec0aa5f91..e6340a09c 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ CS1591;NU5104;CS1573 - 19.2.0 + 19.2.1 1.0.0 EntityFrameworkCore, EntityFramework, GraphQL true From adeec219013b625f7e74910263b4c3ff71d00354 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Fri, 23 Sep 2022 07:18:53 +0000 Subject: [PATCH 4/4] Docs changes --- docs/configuration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 6e3b4365b..d163aea3c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -433,9 +433,9 @@ The `GraphQlController` can be tested using the [ASP.NET Integration tests](http [UsesVerify] public class GraphQlControllerTests { - static HttpClient client = null!; + static HttpClient client; static ClientQueryExecutor clientQueryExecutor; - static WebSocketClient webSocket = null!; + static WebSocketClient webSocket; static GraphQlControllerTests() {