Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IExecutable to Data #2527

Merged
merged 23 commits into from
Nov 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 42 additions & 1 deletion src/HotChocolate/Core/src/Abstractions/IExecutable.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,53 @@
using System.Collections;
using System.Threading;
using System.Threading.Tasks;

#nullable enable

namespace HotChocolate
{
/// <summary>
/// Represents a abstract executable that is well known in the framework. If the execution
/// engine encounters a <see cref="IExecutable"/>, it will call execute it
/// </summary>
public interface IExecutable
{
ValueTask<object> ExecuteAsync(CancellationToken cancellationToken);
/// <summary>
/// The current state of the executable
/// </summary>
object Source { get; }
michaelstaib marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Executes the executable and returns a list
/// </summary>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the execution.
/// </param>
/// <returns>Returns a arbitrary list</returns>
ValueTask<IList> ToListAsync(CancellationToken cancellationToken);

/// <summary>
/// Returns the first element of a sequence, or a default value if the sequence contains no
/// elements.
/// </summary>
/// <param name="cancellationToken">
/// A cancellation token that can be used to cancel the execution.
/// </param>
/// <returns>Returns the result</returns>
ValueTask<object?> FirstOrDefaultAsync(CancellationToken cancellationToken);

/// <summary>
/// Returns the only element of a default value if no such element exists. This method
/// throws an exception if more than one element satisfies the condition.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
ValueTask<object?> SingleOrDefaultAsync(CancellationToken cancellationToken);

/// <summary>
/// Prints the executable in its current state
/// </summary>
/// <returns>A string that represents the executables state</returns>
string Print();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Text;
using System.Threading.Tasks;
using HotChocolate.Execution;
using HotChocolate.Execution.Batching;
using HotChocolate.Execution.Serialization;

namespace HotChocolate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat
switch (_context.Result)
{
case IExecutable executable:
_context.Result = await executable.ExecuteAsync(cancellationToken);
_context.Result = await executable.ToListAsync(cancellationToken);
break;

case IQueryable queryable:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@ public QueryableCursorPagingHandler(PagingOptions options)
object source,
CursorPagingArguments arguments)
{
if (source is IQueryable<TEntity> queryable)
CancellationToken ct = context.RequestAborted;
return source switch
{
return ResolveAsync(queryable, arguments, context.RequestAborted);
}

if (source is IEnumerable<TEntity> enumerable)
{
return ResolveAsync(enumerable.AsQueryable(), arguments, context.RequestAborted);
}

throw new GraphQLException("Cannot handle the specified data source.");
IQueryable<TEntity> q => ResolveAsync(q, arguments, ct),
IEnumerable<TEntity> e => ResolveAsync(e.AsQueryable(), arguments, ct),
IExecutable<TEntity> ex => SliceAsync(context, ex.Source, arguments),
_ => throw new GraphQLException("Cannot handle the specified data source.")
};
}

private async ValueTask<Connection> ResolveAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,25 @@ public QueryableOffsetPagingHandler(PagingOptions options)
{
}

protected override async ValueTask<CollectionSegment> SliceAsync(
protected override ValueTask<CollectionSegment> SliceAsync(
IResolverContext context,
object source,
OffsetPagingArguments arguments)
{
IQueryable<TItemType> queryable = source switch
return source switch
{
IQueryable<TItemType> q => q,
IEnumerable<TItemType> e => e.AsQueryable(),
IQueryable<TItemType> q => ResolveAsync(context, q, arguments),
IEnumerable<TItemType> e => ResolveAsync(context,e.AsQueryable(),arguments),
IExecutable<TItemType> ex => SliceAsync(context, ex.Source, arguments),
_ => throw new GraphQLException("Cannot handle the specified data source.")
};
}

private async ValueTask<CollectionSegment> ResolveAsync(
IResolverContext context,
IQueryable<TItemType> queryable,
OffsetPagingArguments arguments = default)
{
IQueryable<TItemType> original = queryable;

if (arguments.Skip.HasValue)
Expand Down
32 changes: 19 additions & 13 deletions src/HotChocolate/Core/test/Execution.Tests/CodeFirstTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -285,7 +286,7 @@ public string GetTest()

public IExecutable<string> GetQuery()
{
return MockQuery<string>.From("foo", "bar");
return new MockExecutable<string>(new []{ "foo", "bar" }.AsQueryable());
}

public string TestProp => "Hello World!";
Expand Down Expand Up @@ -461,31 +462,36 @@ public class DogType
}
}

public class MockQuery<T> : IExecutable<T>
public class MockExecutable<T> : IExecutable<T>
{
private readonly IReadOnlyList<T> _list;
private readonly IQueryable<T> _source;

private MockQuery(IEnumerable<T> list)
public MockExecutable(IQueryable<T> source)
{
_list = list.ToArray();
_source = source;
}

async ValueTask<object> IExecutable.ExecuteAsync(CancellationToken cancellationToken)
public object Source =>_source;

public ValueTask<IList> ToListAsync(CancellationToken cancellationToken)
{
return await ExecuteAsync(cancellationToken);
return new ValueTask<IList>(_source.ToList());
}

public ValueTask<IReadOnlyList<T>> ExecuteAsync(CancellationToken cancellationToken)
public ValueTask<object?> FirstOrDefaultAsync(CancellationToken cancellationToken)
{
return new ValueTask<IReadOnlyList<T>>(_list);
return new ValueTask<object?>(_source.FirstOrDefault());
}

public string Print()
public ValueTask<object?> SingleOrDefaultAsync(CancellationToken cancellationToken)
{
return _list.ToString();
return new ValueTask<object?>(_source.SingleOrDefault());
}

public static MockQuery<T> From(params T[] items) => new MockQuery<T>(items);
public string Print()
{
return _source.ToString();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="Current">
<PropertyGroup>
<AssemblyName>HotChocolate.Types.Tests</AssemblyName>
<RootNamespace>HotChocolate</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Core\HotChocolate.Core.csproj" />
<ProjectReference Include="..\Types.Tests.Documentation\HotChocolate.Types.Tests.Documentation.csproj" />
<ProjectReference Include="..\Utilities\HotChocolate.Tests.Utilities.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="$(MSBuildProjectDirectory)\__resources__\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="$(MSBuildProjectDirectory)\xunit.runner.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<!--For Visual Studio for Mac Test Explorer we need this reference here-->
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="Current">

<PropertyGroup>
<AssemblyName>HotChocolate.Types.Tests</AssemblyName>
<RootNamespace>HotChocolate</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Core\HotChocolate.Core.csproj"/>
<ProjectReference Include="..\Types.Tests.Documentation\HotChocolate.Types.Tests.Documentation.csproj"/>
<ProjectReference Include="..\Utilities\HotChocolate.Tests.Utilities.csproj"/>
</ItemGroup>

<ItemGroup>
<None Update="$(MSBuildProjectDirectory)\__resources__\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="$(MSBuildProjectDirectory)\xunit.runner.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

<!--For Visual Studio for Mac Test Explorer we need this reference here-->
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using HotChocolate.Execution;
Expand Down Expand Up @@ -410,6 +413,44 @@ await executor
.MatchSnapshotAsync();
}

[Fact]
public async Task Executable_With_Field_Settings()
{
Snapshot.FullName();

IRequestExecutor executor =
await new ServiceCollection()
.AddGraphQL()
.AddQueryType<ExecutableQueryType>()
.Services
.BuildServiceProvider()
.GetRequestExecutorAsync();

await executor
.ExecuteAsync(@"
{
fooExecutable {
edges {
node {
bar
}
cursor
}
nodes {
bar
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}")
.MatchSnapshotAsync();
}

[Fact]
public async Task Attribute_Nested_List_With_Field_Settings()
{
Expand Down Expand Up @@ -595,6 +636,22 @@ protected override void Configure(IObjectTypeDescriptor<Query> descriptor)
}
}

public class ExecutableQueryType : ObjectType<ExecutableQuery>
{
protected override void Configure(IObjectTypeDescriptor<ExecutableQuery> descriptor)
{
descriptor
.Field(t => t.FoosExecutable())
.Name("fooExecutable")
.UsePaging(
options: new PagingOptions
{
MaxPageSize = 2,
IncludeTotalCount = true
});
}
}

public class Query
{
public string[] Letters => new[]
Expand Down Expand Up @@ -623,6 +680,19 @@ public class Query
};
}

public class ExecutableQuery
{
public IExecutable<Foo> FoosExecutable() => new MockExecutable<Foo>(new List<Foo>
{
new Foo { Bar = "a" },
new Foo { Bar = "b" },
new Foo { Bar = "c" } ,
new Foo { Bar = "d" },
new Foo { Bar = "e" },
new Foo { Bar = "f" }
}.AsQueryable());
}

public class Foo
{
public string Bar { get; set; } = default!;
Expand Down Expand Up @@ -675,4 +745,36 @@ public interface ISome2
public string[] ExplicitType();
}
}

public class MockExecutable<T> : IExecutable<T>
{
private readonly IQueryable<T> _source;

public MockExecutable(IQueryable<T> source)
{
_source = source;
}

public object Source =>_source;

public ValueTask<IList> ToListAsync(CancellationToken cancellationToken)
{
return new ValueTask<IList>(_source.ToList());
}

public ValueTask<object?> FirstOrDefaultAsync(CancellationToken cancellationToken)
{
return new ValueTask<object?>(_source.FirstOrDefault());
}

public ValueTask<object?> SingleOrDefaultAsync(CancellationToken cancellationToken)
{
return new ValueTask<object?>(_source.SingleOrDefault());
}

public string Print()
{
return _source.ToString();
}
}
}