diff --git a/global.json b/global.json
index 79422f0cc1c..c685cffc7ac 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
{
"sdk": {
- "version": "3.0.100"
+ "version": "3.1.101"
}
}
diff --git a/src/Core/Abstractions/ParentAttribute.cs b/src/Core/Abstractions/ParentAttribute.cs
index f7b9a857298..19085f48c01 100644
--- a/src/Core/Abstractions/ParentAttribute.cs
+++ b/src/Core/Abstractions/ParentAttribute.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace HotChocolate
{
diff --git a/src/Core/Core.sln b/src/Core/Core.sln
index 9e90018dc7b..c0a263f2a65 100644
--- a/src/Core/Core.sln
+++ b/src/Core/Core.sln
@@ -67,6 +67,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Types.Sorting.Tests", "Type
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Types.Sorting.Mongo.Tests", "Types.Sorting.Mongo.Tests\Types.Sorting.Mongo.Tests.csproj", "{E8843255-77EB-471F-B682-528BB4B5D1D5}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Types.Selection", "Types.Selection\Types.Selection.csproj", "{0FD6B6D1-00AB-42CF-800F-4039A976A75A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Types.Selection.Abstractions.Tests", "Types.Selection.Abstractions.Tests\Types.Selection.Abstractions.Tests.csproj", "{726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Types.Selection.InMemory.Tests", "Types.Selection.InMemory.Tests\Types.Selection.InMemory.Tests.csproj", "{3DB2E3F5-0DBE-4B91-A335-D82F8794324B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Types.Selection.Mongo.Tests", "Types.Selection.Mongo.Tests\Types.Selection.Mongo.Tests.csproj", "{21AA751A-4C29-414C-B476-EE9204A48DDD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Types.Selection.SqlServer.Tests", "Types.Selection.SqlServer.Tests\Types.Selection.SqlServer.Tests.csproj", "{07674D84-4A5C-4608-BECE-03630D38275F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -449,6 +459,66 @@ Global
{E8843255-77EB-471F-B682-528BB4B5D1D5}.Release|x64.Build.0 = Release|Any CPU
{E8843255-77EB-471F-B682-528BB4B5D1D5}.Release|x86.ActiveCfg = Release|Any CPU
{E8843255-77EB-471F-B682-528BB4B5D1D5}.Release|x86.Build.0 = Release|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Debug|x64.Build.0 = Debug|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Debug|x86.Build.0 = Debug|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Release|x64.ActiveCfg = Release|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Release|x64.Build.0 = Release|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Release|x86.ActiveCfg = Release|Any CPU
+ {0FD6B6D1-00AB-42CF-800F-4039A976A75A}.Release|x86.Build.0 = Release|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Debug|x64.Build.0 = Debug|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Debug|x86.Build.0 = Debug|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Release|Any CPU.Build.0 = Release|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Release|x64.ActiveCfg = Release|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Release|x64.Build.0 = Release|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Release|x86.ActiveCfg = Release|Any CPU
+ {726D5295-3F4E-4A1A-8F3B-0EA8EC456B48}.Release|x86.Build.0 = Release|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Debug|x64.Build.0 = Debug|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Debug|x86.Build.0 = Debug|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Release|x64.ActiveCfg = Release|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Release|x64.Build.0 = Release|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Release|x86.ActiveCfg = Release|Any CPU
+ {3DB2E3F5-0DBE-4B91-A335-D82F8794324B}.Release|x86.Build.0 = Release|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Debug|x64.Build.0 = Debug|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Debug|x86.Build.0 = Debug|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Release|x64.ActiveCfg = Release|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Release|x64.Build.0 = Release|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Release|x86.ActiveCfg = Release|Any CPU
+ {21AA751A-4C29-414C-B476-EE9204A48DDD}.Release|x86.Build.0 = Release|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Debug|x64.Build.0 = Debug|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Debug|x86.Build.0 = Debug|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Release|x64.ActiveCfg = Release|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Release|x64.Build.0 = Release|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Release|x86.ActiveCfg = Release|Any CPU
+ {07674D84-4A5C-4608-BECE-03630D38275F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Core/Core/InternalsVisibleTo.cs b/src/Core/Core/InternalsVisibleTo.cs
new file mode 100644
index 00000000000..ada8fca1944
--- /dev/null
+++ b/src/Core/Core/InternalsVisibleTo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("HotChocolate.Types.Selections")]
diff --git a/src/Core/PersistedQueries.Redis.Tests/PersistedQueries.Redis.Tests.csproj b/src/Core/PersistedQueries.Redis.Tests/PersistedQueries.Redis.Tests.csproj
index 50466573a23..651a12359b7 100644
--- a/src/Core/PersistedQueries.Redis.Tests/PersistedQueries.Redis.Tests.csproj
+++ b/src/Core/PersistedQueries.Redis.Tests/PersistedQueries.Redis.Tests.csproj
@@ -21,7 +21,7 @@
-
+
diff --git a/src/Core/Subscriptions.Redis.Tests/Subscriptions.Redis.Tests.csproj b/src/Core/Subscriptions.Redis.Tests/Subscriptions.Redis.Tests.csproj
index 0350219d097..87e42e88435 100644
--- a/src/Core/Subscriptions.Redis.Tests/Subscriptions.Redis.Tests.csproj
+++ b/src/Core/Subscriptions.Redis.Tests/Subscriptions.Redis.Tests.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/src/Core/Types.Filters.Mongo.Tests/Types.Filters.Mongo.Tests.csproj b/src/Core/Types.Filters.Mongo.Tests/Types.Filters.Mongo.Tests.csproj
index c337ed835ad..e2a98914ed8 100644
--- a/src/Core/Types.Filters.Mongo.Tests/Types.Filters.Mongo.Tests.csproj
+++ b/src/Core/Types.Filters.Mongo.Tests/Types.Filters.Mongo.Tests.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/src/Core/Types.Filters/QueryableFilterMiddleware.cs b/src/Core/Types.Filters/QueryableFilterMiddleware.cs
index d0699b72828..4deffc33153 100644
--- a/src/Core/Types.Filters/QueryableFilterMiddleware.cs
+++ b/src/Core/Types.Filters/QueryableFilterMiddleware.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -26,7 +26,7 @@ public async Task InvokeAsync(IMiddlewareContext context)
{
await _next(context).ConfigureAwait(false);
- var filter = context.Argument("where");
+ IValueNode filter = context.Argument("where");
if (filter is null || filter is NullValueNode)
{
diff --git a/src/Core/Types.Filters/QueryableFilterVisitor.cs b/src/Core/Types.Filters/QueryableFilterVisitor.cs
index 155f2d9fedb..2475c42187f 100644
--- a/src/Core/Types.Filters/QueryableFilterVisitor.cs
+++ b/src/Core/Types.Filters/QueryableFilterVisitor.cs
@@ -64,6 +64,11 @@ public Expression> CreateFilter()
_parameter);
}
+ public Expression CreateFilter()
+ {
+ return Expression.Lambda(Level.Peek().Peek(), _parameter);
+ }
+
protected Stack> Level { get; } =
new Stack>();
diff --git a/src/Core/Types.Selection.Abstractions.Tests/IResolverProvider.cs b/src/Core/Types.Selection.Abstractions.Tests/IResolverProvider.cs
new file mode 100644
index 00000000000..7a6dd24c00f
--- /dev/null
+++ b/src/Core/Types.Selection.Abstractions.Tests/IResolverProvider.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using HotChocolate.Resolvers;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace HotChocolate.Types.Selections
+{
+ public interface IResolverProvider
+ {
+ public (IServiceCollection, Func>)
+ CreateResolver(params TResult[] results)
+ where TResult : class;
+ }
+}
diff --git a/src/Core/Types.Selection.Abstractions.Tests/SelectionAttributeTestsBase.cs b/src/Core/Types.Selection.Abstractions.Tests/SelectionAttributeTestsBase.cs
new file mode 100644
index 00000000000..3eb873d52b8
--- /dev/null
+++ b/src/Core/Types.Selection.Abstractions.Tests/SelectionAttributeTestsBase.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using HotChocolate.Execution;
+using HotChocolate.Resolvers;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+namespace HotChocolate.Types.Selections
+{
+ public abstract class SelectionAttributeTestsBase
+ {
+
+ private readonly IResolverProvider _provider;
+ private readonly bool _setId;
+
+ protected SelectionAttributeTestsBase(IResolverProvider provider, bool setId = false)
+ {
+ _provider = provider;
+ _setId = setId;
+ }
+
+ [Fact]
+ public void Execute_Selection_MultipleScalar()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(
+ Foo.Create("aa", 1, _setId),
+ Foo.Create("bb", 2, _setId));
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(d =>
+ d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ }))
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { bar baz } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Equal("aa", x.Bar);
+ Assert.Equal(1, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.NestedCollection);
+ },
+ x =>
+ {
+ Assert.Equal("bb", x.Bar);
+ Assert.Equal(2, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.NestedCollection);
+ });
+ }
+
+ public class Query
+ {
+ [UseSelection]
+ public IQueryable Foos { get; }
+ }
+
+ public class Foo
+ {
+ private static int idCounter = 1;
+
+ [Key]
+ public int Id { get; set; }
+
+ public string Bar { get; set; }
+
+ public int Baz { get; set; }
+
+ public NestedFoo Nested { get; set; }
+
+ public List NestedCollection { get; set; }
+
+ public static Foo Create(string bar, int baz, bool setId)
+ {
+ var value = new Foo
+ {
+ Bar = bar,
+ Baz = baz,
+ Nested = new NestedFoo()
+ {
+ Bar = "nested" + bar,
+ Baz = baz * 2
+ },
+ NestedCollection = new List()
+ {
+ new NestedFoo()
+ {
+ Bar = "nestedcollection" + bar,
+ Baz = baz * 3
+ },
+ }
+ };
+ if (setId)
+ {
+ value.Id = ++idCounter;
+ }
+ return value;
+ }
+ }
+
+ public class NestedFoo
+ {
+ [Key]
+ public int Id { get; set; }
+
+ public string Bar { get; set; }
+
+ public int Baz { get; set; }
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.Abstractions.Tests/SelectionTestsBase.cs b/src/Core/Types.Selection.Abstractions.Tests/SelectionTestsBase.cs
new file mode 100644
index 00000000000..ceae93cd69c
--- /dev/null
+++ b/src/Core/Types.Selection.Abstractions.Tests/SelectionTestsBase.cs
@@ -0,0 +1,1151 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+using HotChocolate.Execution;
+using HotChocolate.Resolvers;
+using HotChocolate.Types.Relay;
+using Xunit;
+
+namespace HotChocolate.Types.Selections
+{
+ public abstract class SelectionTestsBase
+ {
+ private readonly static Foo[] SAMPLE =
+ new[] {
+ Foo.Create("aa", 1),
+ Foo.Create("bb", 2) };
+
+ private readonly IResolverProvider _provider;
+
+ protected SelectionTestsBase(IResolverProvider provider)
+ {
+ _provider = provider;
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Fragment()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { ...test } } fragment test on Foo { bar baz}");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Equal("aa", x.Bar);
+ Assert.Equal(1, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("bb", x.Bar);
+ Assert.Equal(2, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Scalar()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { bar } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Equal("aa", x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("bb", x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_MultipleScalar()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { bar baz } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Equal("aa", x.Bar);
+ Assert.Equal(1, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("bb", x.Bar);
+ Assert.Equal(2, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Object()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { nested { bar } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.NotNull(x.Nested);
+ Assert.Equal("nestedaa", x.Nested.Bar);
+ Assert.Equal(0, x.Nested.Baz);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.NotNull(x.Nested);
+ Assert.Equal("nestedbb", x.Nested.Bar);
+ Assert.Equal(0, x.Nested.Baz);
+ Assert.Null(x.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Array()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { objectArray { bar } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.ObjectArray);
+ Assert.Equal("objectArrayaa", x.ObjectArray[0].Bar);
+ Assert.Equal(0, x.ObjectArray[0].Baz);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.ObjectArray);
+ Assert.Equal("objectArraybb", x.ObjectArray[0].Bar);
+ Assert.Equal(0, x.ObjectArray[0].Baz);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_List()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { objectList { bar } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.ObjectList);
+ Assert.Equal("objectListaa", x.ObjectList[0].Bar);
+ Assert.Equal(0, x.ObjectList[0].Baz);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.ObjectList);
+ Assert.Equal("objectListbb", x.ObjectList[0].Bar);
+ Assert.Equal(0, x.ObjectList[0].Baz);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_List_Interface()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { iObjectList { bar } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.IObjectList);
+ Assert.Equal("iListaa", x.IObjectList[0].Bar);
+ Assert.Equal(0, x.IObjectList[0].Baz);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.IObjectList);
+ Assert.Equal("iListbb", x.IObjectList[0].Bar);
+ Assert.Equal(0, x.IObjectList[0].Baz);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Set_Interface()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { iSet { bar } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.ISet);
+ Assert.Equal("iSetaa", x.ISet.First().Bar);
+ Assert.Equal(0, x.ISet.First().Baz);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.ISet);
+ Assert.Equal("iSetbb", x.ISet.First().Bar);
+ Assert.Equal(0, x.ISet.First().Baz);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Set()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { hashSet { bar } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.HashSet);
+ Assert.Equal("hashSetaa", x.HashSet.First().Bar);
+ Assert.Equal(0, x.HashSet.First().Baz);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.HashSet);
+ Assert.Equal("hashSetbb", x.HashSet.First().Bar);
+ Assert.Equal(0, x.HashSet.First().Baz);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_SortedSet()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { sortedSet { bar } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.SortedSet);
+ Assert.Equal("sortedSetaa", x.SortedSet.First().Bar);
+ Assert.Equal(0, x.SortedSet.First().Baz);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.SortedSet);
+ Assert.Equal("sortedSetbb", x.SortedSet.First().Bar);
+ Assert.Equal(0, x.SortedSet.First().Baz);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_ObjectDeep()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { nested { nested { nested { nested { bar } } } } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.NotNull(x.Nested);
+ Assert.Equal(0, x.Nested.Baz);
+ Assert.Null(x.ObjectArray);
+ Assert.Null(x.Nested.ObjectArray);
+ Assert.Null(x.Nested.Nested.Bar);
+ Assert.Null(x.Nested.Nested.Nested.Bar);
+ Assert.Equal("recursiveaa", x.Nested.Nested.Nested.Nested.Bar);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.NotNull(x.Nested);
+ Assert.Equal(0, x.Nested.Baz);
+ Assert.Null(x.ObjectArray);
+ Assert.Null(x.Nested.ObjectArray);
+ Assert.Null(x.Nested.Nested.Bar);
+ Assert.Null(x.Nested.Nested.Nested.Bar);
+ Assert.Equal("recursivebb", x.Nested.Nested.Nested.Nested.Bar);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_ArrayDeep()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ executor.Execute(
+ "{ foos { objectArray { objectArray { objectArray { bar } } } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.ObjectArray);
+ Assert.Null(x.ObjectArray[0].Bar);
+ Assert.Null(x.ObjectArray[0].ObjectArray[0].Bar);
+ Assert.Equal(
+ "recursiveaa",
+ x.ObjectArray[0].ObjectArray[0].ObjectArray[0].Bar);
+ Assert.Equal(0, x.ObjectArray[0].Baz);
+ },
+ x =>
+ {
+ Assert.Null(x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Single(x.ObjectArray);
+ Assert.Null(x.ObjectArray[0].Bar);
+ Assert.Null(x.ObjectArray[0].ObjectArray[0].Bar);
+ Assert.Equal(
+ "recursivebb",
+ x.ObjectArray[0].ObjectArray[0].ObjectArray[0].Bar);
+ Assert.Equal(0, x.ObjectArray[0].Baz);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Object_Paging_Nodes()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ Connection resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as Connection;
+ })
+ .UsePaging>()
+ .UseFiltering()
+ .UseSorting()
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ IExecutionResult result = executor.Execute(
+ "{ foos { nodes { nested { bar } } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.Edges.ToArray(),
+ x =>
+ {
+ Assert.Null(x.Node.Bar);
+ Assert.Equal(0, x.Node.Baz);
+ Assert.NotNull(x.Node.Nested);
+ Assert.Equal("nestedaa", x.Node.Nested.Bar);
+ Assert.Equal(0, x.Node.Nested.Baz);
+ Assert.Null(x.Node.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Null(x.Node.Bar);
+ Assert.Equal(0, x.Node.Baz);
+ Assert.NotNull(x.Node.Nested);
+ Assert.Equal("nestedbb", x.Node.Nested.Bar);
+ Assert.Equal(0, x.Node.Nested.Baz);
+ Assert.Null(x.Node.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Object_Paging_Edges()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ Connection resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as Connection;
+ })
+ .UsePaging>()
+ .UseFiltering()
+ .UseSorting()
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ IExecutionResult result = executor.Execute(
+ "{ foos { edges { node { bar }} } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.Edges.ToArray(),
+ x =>
+ {
+ Assert.Equal("aa", x.Node.Bar);
+ Assert.Equal(0, x.Node.Baz);
+ Assert.Null(x.Node.Nested);
+ Assert.Null(x.Node.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("bb", x.Node.Bar);
+ Assert.Equal(0, x.Node.Baz);
+ Assert.Null(x.Node.Nested);
+ Assert.Null(x.Node.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Object_Paging_Combined()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ Connection resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as Connection;
+ })
+ .UsePaging>()
+ .UseFiltering()
+ .UseSorting()
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ IExecutionResult result = executor.Execute(
+ "{ foos { nodes { nested { bar } } edges { node { bar }} } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.Edges.ToArray(),
+ x =>
+ {
+ Assert.Equal("aa", x.Node.Bar);
+ Assert.Equal(0, x.Node.Baz);
+ Assert.NotNull(x.Node.Nested);
+ Assert.Equal("nestedaa", x.Node.Nested.Bar);
+ Assert.Equal(0, x.Node.Nested.Baz);
+ Assert.Null(x.Node.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("bb", x.Node.Bar);
+ Assert.Equal(0, x.Node.Baz);
+ Assert.NotNull(x.Node.Nested);
+ Assert.Equal("nestedbb", x.Node.Nested.Bar);
+ Assert.Equal(0, x.Node.Nested.Baz);
+ Assert.Null(x.Node.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_DeepPaging()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseFiltering()
+ .UseSorting()
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ IExecutionResult result = executor.Execute(
+ "{ foos { middlewareList { nodes { bar } } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray()[0].MiddlewareList,
+ x =>
+ {
+ Assert.Equal("aa", x.Bar.Substring(2));
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("aa", x.Bar.Substring(2));
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("aa", x.Bar.Substring(2));
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Nested_Filtering()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseFiltering()
+ .UseSorting()
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ IExecutionResult result = executor.Execute(
+ "{ foos { middlewareList(where:{bar: \"ccaa\"}) { nodes { bar } } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Single(resultCtx.ToArray()[0].MiddlewareList);
+ Assert.Collection(resultCtx.ToArray()[0].MiddlewareList,
+ x =>
+ {
+ Assert.Equal("ccaa", x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Nested_Sorting()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseFiltering()
+ .UseSorting()
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ IExecutionResult result = executor.Execute(
+ "{ foos { middlewareList(order_by:{baz: ASC, bar: ASC}) { nodes { bar } } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray()[0].MiddlewareList,
+ x =>
+ {
+ Assert.Equal("aaaa", x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("ccaa", x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("bbaa", x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ });
+ }
+
+ [Fact]
+ public virtual void Execute_Selection_Nested_FilteringAndSorting()
+ {
+ // arrange
+ IServiceCollection services;
+ Func> resolver;
+ (services, resolver) = _provider.CreateResolver(SAMPLE);
+
+ IQueryable resultCtx = null;
+ ISchema schema = SchemaBuilder.New()
+ .AddServices(services.BuildServiceProvider())
+ .AddQueryType(
+ d => d.Field(t => t.Foos)
+ .Resolver(resolver)
+ .Use(next => async ctx =>
+ {
+ await next(ctx).ConfigureAwait(false);
+ resultCtx = ctx.Result as IQueryable;
+ })
+ .UseFiltering()
+ .UseSorting()
+ .UseSelection())
+ .Create();
+ IQueryExecutor executor = schema.MakeExecutable();
+
+ // act
+ IExecutionResult result = executor.Execute(
+ "{ foos { middlewareList(where: {bar_in:[\"aaaa\",\"bbaa\"]}, " +
+ "order_by:{baz: ASC, bar: ASC}) { nodes { bar } } } }");
+
+ // assert
+ Assert.NotNull(resultCtx);
+ Assert.Collection(resultCtx.ToArray()[0].MiddlewareList,
+ x =>
+ {
+ Assert.Equal("aaaa", x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ },
+ x =>
+ {
+ Assert.Equal("bbaa", x.Bar);
+ Assert.Equal(0, x.Baz);
+ Assert.Null(x.Nested);
+ Assert.Null(x.ObjectArray);
+ });
+ }
+
+ public class Query
+ {
+ public IQueryable Foos { get; }
+ }
+
+ public class Foo
+ {
+ [Key]
+ public Guid Id { get; set; }
+
+ public string Bar { get; set; }
+
+ public int Baz { get; set; }
+
+ public NestedFoo Nested { get; set; }
+
+ public NestedFoo[] ObjectArray { get; set; }
+
+ public List ObjectList { get; set; }
+
+ public IList IObjectList { get; set; }
+
+ public HashSet HashSet { get; set; }
+
+ public SortedSet SortedSet { get; set; }
+
+ public ISet ISet { get; set; }
+
+ [UsePaging]
+ [UseFiltering]
+ [UseSorting]
+ public List MiddlewareList { get; set; }
+
+ public static Foo Create(string bar, int baz)
+ {
+ var recursive = new NestedFoo()
+ {
+ Bar = "recursive" + bar,
+ Baz = 10
+ };
+ var recursive2 = new NestedFoo()
+ {
+ Bar = "recursive" + bar,
+ Baz = 10
+ };
+ for (var level = 0; level < 10; level++)
+ {
+ recursive = new NestedFoo()
+ {
+ Bar = "recursive" + bar,
+ Baz = 10,
+ Nested = recursive.Clone(),
+ ObjectArray = new List { recursive2.Clone() }
+ };
+
+ recursive2 = new NestedFoo()
+ {
+ Bar = "recursive" + bar,
+ Baz = 10,
+ Nested = recursive.Clone(),
+ ObjectArray = new List { recursive2.Clone() }
+ };
+ }
+ return new Foo
+ {
+ Bar = bar,
+ Baz = baz,
+ Nested = new NestedFoo()
+ {
+ Bar = "nested" + bar,
+ Baz = baz * 2,
+ Nested = recursive.Clone(),
+ ObjectArray = new List { recursive2.Clone() }
+ },
+ ObjectArray = new NestedFoo[]
+ {
+ new NestedFoo()
+ {
+ Bar = "objectArray" + bar,
+ Baz = baz * 3,
+ Nested = recursive.Clone(),
+ ObjectArray = new List { recursive2.Clone() }
+ },
+ },
+ ObjectList = new List
+ {
+ new NestedFoo()
+ {
+ Bar = "objectList" + bar,
+ Baz = baz * 3
+ },
+ },
+ IObjectList = new List
+ {
+ new NestedFoo()
+ {
+ Bar = "iList" + bar,
+ Baz = baz * 3
+ },
+ },
+ HashSet = new HashSet
+ {
+ new NestedFoo()
+ {
+ Bar = "hashSet" + bar,
+ Baz = baz * 3
+ },
+ },
+ SortedSet = new SortedSet
+ {
+ new NestedFoo()
+ {
+ Bar = "sortedSet" + bar,
+ Baz = baz * 3
+ },
+ },
+ ISet = new HashSet
+ {
+ new NestedFoo()
+ {
+ Bar = "iSet" + bar,
+ Baz = baz * 3
+ },
+ },
+ MiddlewareList = new List
+ {
+ new NestedFoo()
+ {
+ Bar = "cc" + bar,
+ Baz = baz * 1
+ },
+ new NestedFoo()
+ {
+ Bar = "aa" + bar,
+ Baz = baz * 1
+ },
+ new NestedFoo()
+ {
+ Bar = "bb" + bar,
+ Baz = baz * 2
+ },
+ }
+ };
+ }
+ }
+
+ public class NestedFoo
+ {
+ [Key]
+ public int Id { get; set; }
+
+ public string Bar { get; set; }
+
+ public int Baz { get; set; }
+
+ public NestedFoo Nested { get; set; }
+
+ public List ObjectArray { get; set; }
+
+ public NestedFoo Clone()
+ {
+ NestedFoo clone = (NestedFoo)base.MemberwiseClone();
+ return clone;
+ }
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.Abstractions.Tests/Types.Selection.Abstractions.Tests.csproj b/src/Core/Types.Selection.Abstractions.Tests/Types.Selection.Abstractions.Tests.csproj
new file mode 100644
index 00000000000..0813588003d
--- /dev/null
+++ b/src/Core/Types.Selection.Abstractions.Tests/Types.Selection.Abstractions.Tests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ netcoreapp2.0
+ $(MSBuildThisFileDirectory.TrimEnd('\').TrimEnd('/'))
+ $([System.IO.Path]::Combine($(ChilliCurrentDirectory), '..', '..', '..', 'tools'))
+ $([System.IO.Path]::Combine($(ChilliImport), 'CoreTestFramework.props'))
+ $([System.IO.Path]::Combine($(ChilliImport), 'TestSettings.props'))
+
+
+
+
+
+
+ HotChocolate.Types.Selections.Abstractions.Tests
+ HotChocolate.Types.Selections
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Core/Types.Selection.InMemory.Tests/InMemoryProvider.cs b/src/Core/Types.Selection.InMemory.Tests/InMemoryProvider.cs
new file mode 100644
index 00000000000..20044680de3
--- /dev/null
+++ b/src/Core/Types.Selection.InMemory.Tests/InMemoryProvider.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.DependencyInjection;
+using HotChocolate.Resolvers;
+
+namespace HotChocolate.Types.Selections
+{
+ public class InMemoryProvider : IResolverProvider
+ {
+ public (IServiceCollection, Func>)
+ CreateResolver(params TResult[] results) where TResult : class
+ {
+ var services = new ServiceCollection();
+ return (services, ctx => results);
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.InMemory.Tests/SelectionAttributeTests.cs b/src/Core/Types.Selection.InMemory.Tests/SelectionAttributeTests.cs
new file mode 100644
index 00000000000..5ba6d2704f5
--- /dev/null
+++ b/src/Core/Types.Selection.InMemory.Tests/SelectionAttributeTests.cs
@@ -0,0 +1,13 @@
+using Xunit;
+
+namespace HotChocolate.Types.Selections
+{
+ public class SelectionAttributeTests
+ : SelectionAttributeTestsBase
+ , IClassFixture
+ {
+ public SelectionAttributeTests(InMemoryProvider provider) : base(provider)
+ {
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.InMemory.Tests/SelectionTests.cs b/src/Core/Types.Selection.InMemory.Tests/SelectionTests.cs
new file mode 100644
index 00000000000..896be6ef8d5
--- /dev/null
+++ b/src/Core/Types.Selection.InMemory.Tests/SelectionTests.cs
@@ -0,0 +1,13 @@
+using Xunit;
+
+namespace HotChocolate.Types.Selections
+{
+ public class SelectionTests
+ : SelectionTestsBase
+ , IClassFixture
+ {
+ public SelectionTests(InMemoryProvider provider) : base(provider)
+ {
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.InMemory.Tests/Types.Selection.InMemory.Tests.csproj b/src/Core/Types.Selection.InMemory.Tests/Types.Selection.InMemory.Tests.csproj
new file mode 100644
index 00000000000..4ec00b13785
--- /dev/null
+++ b/src/Core/Types.Selection.InMemory.Tests/Types.Selection.InMemory.Tests.csproj
@@ -0,0 +1,23 @@
+
+
+
+ netcoreapp2.0
+ $(MSBuildThisFileDirectory.TrimEnd('\').TrimEnd('/'))
+ $([System.IO.Path]::Combine($(ChilliCurrentDirectory), '..', '..', '..', 'tools'))
+ $([System.IO.Path]::Combine($(ChilliImport), 'CoreTestFramework.props'))
+ $([System.IO.Path]::Combine($(ChilliImport), 'TestSettings.props'))
+
+
+
+
+
+
+ HotChocolate.Types.Selections.InMemory.Tests
+ HotChocolate.Types.Selections
+
+
+
+
+
+
+
diff --git a/src/Core/Types.Selection.Mongo.Tests/MongoProvider.cs b/src/Core/Types.Selection.Mongo.Tests/MongoProvider.cs
new file mode 100644
index 00000000000..ce0380e6ca4
--- /dev/null
+++ b/src/Core/Types.Selection.Mongo.Tests/MongoProvider.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using HotChocolate.Resolvers;
+using Microsoft.Extensions.DependencyInjection;
+using MongoDB.Driver;
+using Squadron;
+
+namespace HotChocolate.Types.Selections
+{
+ public class MongoProvider : IResolverProvider
+ {
+ private readonly MongoResource _resource;
+
+ public MongoProvider(MongoResource resource)
+ {
+ _resource = resource;
+ }
+
+ public (IServiceCollection, Func>)
+ CreateResolver(params TResult[] results)
+ where TResult : class
+ {
+ var services = new ServiceCollection();
+ IMongoDatabase database = _resource.CreateDatabase();
+ IMongoCollection collection = database.GetCollection("col");
+ collection.InsertMany(results);
+
+ services.AddSingleton>(collection);
+
+ return (services, ctx => ctx.Service>().AsQueryable());
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.Mongo.Tests/SelectionAttributeTests.cs b/src/Core/Types.Selection.Mongo.Tests/SelectionAttributeTests.cs
new file mode 100644
index 00000000000..e8d725d22fb
--- /dev/null
+++ b/src/Core/Types.Selection.Mongo.Tests/SelectionAttributeTests.cs
@@ -0,0 +1,14 @@
+using Squadron;
+using Xunit;
+
+namespace HotChocolate.Types.Selections
+{
+ public class SelectionAttributeTests
+ : SelectionAttributeTestsBase, IClassFixture
+ {
+ public SelectionAttributeTests(MongoResource provider)
+ : base(new MongoProvider(provider), true)
+ {
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.Mongo.Tests/SelectionTests.cs b/src/Core/Types.Selection.Mongo.Tests/SelectionTests.cs
new file mode 100644
index 00000000000..0e4a651e8ab
--- /dev/null
+++ b/src/Core/Types.Selection.Mongo.Tests/SelectionTests.cs
@@ -0,0 +1,69 @@
+using Squadron;
+using Xunit;
+
+namespace HotChocolate.Types.Selections
+{
+ public class SelectionTests
+ : SelectionTestsBase
+ , IClassFixture
+ {
+ public SelectionTests(MongoResource provider)
+ : base(new MongoProvider(provider))
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_Array()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_ArrayDeep()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_DeepPaging()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_List()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_List_Interface()
+ {
+ }
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_Set()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_Set_Interface()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_Nested_Filtering()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_Nested_FilteringAndSorting()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_Nested_Sorting()
+ {
+ }
+
+ [Fact(Skip = "Not yet supported!")]
+ public override void Execute_Selection_SortedSet()
+ {
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.Mongo.Tests/Types.Selection.Mongo.Tests.csproj b/src/Core/Types.Selection.Mongo.Tests/Types.Selection.Mongo.Tests.csproj
new file mode 100644
index 00000000000..4dd1882cb6e
--- /dev/null
+++ b/src/Core/Types.Selection.Mongo.Tests/Types.Selection.Mongo.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ netcoreapp2.0
+ $(MSBuildThisFileDirectory.TrimEnd('\').TrimEnd('/'))
+ $([System.IO.Path]::Combine($(ChilliCurrentDirectory), '..', '..', '..', 'tools'))
+ $([System.IO.Path]::Combine($(ChilliImport), 'CoreTestFramework.props'))
+ $([System.IO.Path]::Combine($(ChilliImport), 'TestSettings.props'))
+
+
+
+
+
+
+ HotChocolate.Types.Selections.Mongo.Tests
+ HotChocolate.Types.Selections
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Core/Types.Selection.SqlServer.Tests/SelectionAttributeTests.txt b/src/Core/Types.Selection.SqlServer.Tests/SelectionAttributeTests.txt
new file mode 100644
index 00000000000..8a873eea834
--- /dev/null
+++ b/src/Core/Types.Selection.SqlServer.Tests/SelectionAttributeTests.txt
@@ -0,0 +1,15 @@
+using Squadron;
+using Xunit;
+
+namespace HotChocolate.Types.Selections
+{
+ public class SelectionAttributeTests
+ : SelectionAttributeTestsBase
+ , IClassFixture
+ {
+ public SelectionAttributeTests(SqlServerResource provider)
+ : base(new SqlServerProvider(provider))
+ {
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.SqlServer.Tests/SelectionTests.txt b/src/Core/Types.Selection.SqlServer.Tests/SelectionTests.txt
new file mode 100644
index 00000000000..1ab73b1d7cf
--- /dev/null
+++ b/src/Core/Types.Selection.SqlServer.Tests/SelectionTests.txt
@@ -0,0 +1,27 @@
+using Squadron;
+using Xunit;
+
+namespace HotChocolate.Types.Selections
+{
+ public class SelectionTests
+ : SelectionTestsBase
+ , IClassFixture
+ {
+ public SelectionTests(SqlServerResource provider)
+ : base(new SqlServerProvider(provider))
+ {
+ }
+
+ public override void Execute_Selection_Array()
+ {
+ // EF does not support array
+ Assert.True(true);
+ }
+
+ public override void Execute_Selection_ArrayDeep()
+ {
+ // EF does not support array
+ Assert.True(true);
+ }
+ }
+}
diff --git a/src/Core/Types.Selection.SqlServer.Tests/SqlServerProvider.txt b/src/Core/Types.Selection.SqlServer.Tests/SqlServerProvider.txt
new file mode 100644
index 00000000000..f1892b7a5be
--- /dev/null
+++ b/src/Core/Types.Selection.SqlServer.Tests/SqlServerProvider.txt
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using HotChocolate.Resolvers;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Squadron;
+
+namespace HotChocolate.Types.Selections
+{
+ public class SqlServerProvider : IResolverProvider
+ {
+ private static readonly ILoggerFactory ConsoleLogger =
+ LoggerFactory.Create(x => x.AddConsole());
+
+ private static readonly ConcurrentDictionary