Skip to content

Commit

Permalink
Reworked resolver task to handle stream results better (#5234)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Jul 12, 2022
1 parent b01e494 commit 431238f
Show file tree
Hide file tree
Showing 22 changed files with 342 additions and 33 deletions.
Expand Up @@ -159,6 +159,12 @@
<Compile Update="Processing\Result\ResultBuilder.Pooling.cs">
<DependentUpon>ResultBuilder.cs</DependentUpon>
</Compile>
<Compile Update="Processing\Tasks\ResolverTask.CompleteValue.cs">
<DependentUpon>ResolverTask.cs</DependentUpon>
</Compile>
<Compile Update="Processing\Tasks\ResolverTask.Pooling.cs">
<DependentUpon>ResolverTask.cs</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
17 changes: 15 additions & 2 deletions src/HotChocolate/Core/src/Execution/Processing/Selection.cs
Expand Up @@ -55,6 +55,11 @@ public class Selection : ISelection
{
_flags |= Flags.List;
}

if (Field.HasStreamResult)
{
_flags |= Flags.StreamResult;
}
}

protected Selection(Selection selection)
Expand Down Expand Up @@ -123,10 +128,17 @@ protected Selection(Selection selection)
/// <inheritdoc />
public IArgumentMap Arguments { get; }

public bool IsStream(long includeFlags)
/// <inheritdoc />
public bool HasStreamResult => (_flags & Flags.StreamResult) == Flags.StreamResult;

/// <inheritdoc />
public bool HasStreamDirective(long includeFlags)
=> (_flags & Flags.Stream) == Flags.Stream &&
(_streamIfCondition is 0 || (includeFlags & _streamIfCondition) != _streamIfCondition);

/// <summary>
/// Specifies if the current selection is immutable.
/// </summary>
public bool IsReadOnly => (_flags & Flags.Sealed) == Flags.Sealed;

/// <inheritdoc />
Expand Down Expand Up @@ -324,6 +336,7 @@ private enum Flags
Internal = 1,
Sealed = 2,
List = 4,
Stream = 8
Stream = 8,
StreamResult = 16
}
}
Expand Up @@ -130,12 +130,18 @@ private async ValueTask ExecuteResolverPipelineAsync(CancellationToken cancellat
return;
}

if (_selection.IsStream(_operationContext.IncludeFlags))
if (_selection.HasStreamDirective(_operationContext.IncludeFlags))
{
_resolverContext.Result = await CreateStreamResultAsync(result).ConfigureAwait(false);
return;
}

if (_selection.HasStreamResult)
{
_resolverContext.Result = await CreateListFromStreamAsync(result).ConfigureAwait(false);
return;
}

switch (_resolverContext.Result)
{
case IExecutable executable:
Expand Down
Expand Up @@ -3,7 +3,6 @@
using System.Threading;
using System.Threading.Tasks;
using HotChocolate.Execution.Instrumentation;
using HotChocolate.Types;
using Microsoft.Extensions.ObjectPool;

namespace HotChocolate.Execution.Processing.Tasks;
Expand Down
Expand Up @@ -17,5 +17,17 @@ public interface IOptionalSelection
/// </summary>
bool IsConditional { get; }

/// <summary>
/// Defines if this selection will be included into the request execution.
/// </summary>
/// <param name="includeFlags">
/// The execution include flags.
/// </param>
/// <param name="allowInternals">
/// Allow internal selections to be included.
/// </param>
/// <returns>
/// True, if this selection shall be included into the request execution.
/// </returns>
bool IsIncluded(long includeFlags, bool allowInternals = false);
}
22 changes: 19 additions & 3 deletions src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs
@@ -1,6 +1,6 @@
#nullable enable

using System.Diagnostics.SymbolStore;
using System.Collections.Generic;
using HotChocolate.Language;
using HotChocolate.Resolvers;
using HotChocolate.Types;
Expand Down Expand Up @@ -38,7 +38,7 @@ public interface ISelection : IOptionalSelection
TypeKind TypeKind { get; }

/// <summary>
/// Specifies if the return type fo this selection is a list.
/// Specifies if the return type of this selection is a list type.
/// </summary>
bool IsList { get; }

Expand Down Expand Up @@ -82,5 +82,21 @@ public interface ISelection : IOptionalSelection
/// </summary>
IArgumentMap Arguments { get; }

bool IsStream(long includeFlags);
/// <summary>
/// Defines that the resolver pipeline returns an
/// <see cref="IAsyncEnumerable{T}"/> as its result.
/// </summary>
bool HasStreamResult { get; }

/// <summary>
/// Defines if this selection is annotated with the stream directive.
/// </summary>
/// <param name="includeFlags">
/// The execution include flags that determine if the stream directive is applied for the
/// current execution run.
/// </param>
/// <returns>
/// Returns if this selection is annotated with the stream directive.
/// </returns>
bool HasStreamDirective(long includeFlags);
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/HotChocolate/Core/src/Types/Properties/TypeResources.resx
Expand Up @@ -873,4 +873,7 @@ Type: `{0}`</value>
<data name="InterfaceTypeDescriptor_MustBePropertyOrMethod" xml:space="preserve">
<value>A field of an interface can only be inferred from a property or a method.</value>
</data>
<data name="ThrowHelper_FieldBase_Sealed" xml:space="preserve">
<value>The field is already sealed and cannot be mutated.</value>
</data>
</root>
@@ -0,0 +1,22 @@
#nullable enable
using System;
using System.Reflection;
using HotChocolate.Types.Descriptors;

namespace HotChocolate.Types;

/// <summary>
/// Marks a resolver as returning a stream result
/// which will allow the execution engine to compile a result handler for the resolver.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
public sealed class StreamResultAttribute : ObjectFieldDescriptorAttribute
{
public override void OnConfigure(
IDescriptorContext context,
IObjectFieldDescriptor descriptor,
MemberInfo member)
{
descriptor.StreamResult();
}
}
Expand Up @@ -21,6 +21,12 @@ public interface IObjectField : IOutputField
/// </summary>
bool IsParallelExecutable { get; }

/// <summary>
/// Defines that the resolver pipeline returns an
/// <see cref="IAsyncEnumerable{T}"/> as its result.
/// </summary>
bool HasStreamResult { get; }

/// <summary>
/// Gets the field resolver middleware.
/// </summary>
Expand Down
@@ -1,5 +1,7 @@
#nullable enable

using System.Collections.Generic;

namespace HotChocolate.Types;

/// <summary>
Expand Down
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using HotChocolate.Language;
Expand Down Expand Up @@ -91,6 +92,12 @@ IObjectFieldDescriptor Type<TOutputType>(TOutputType outputType)
/// </summary>
IObjectFieldDescriptor Type(Type type);

/// <summary>
/// Defines weather the resolver pipeline will return <see cref="IAsyncEnumerable{T}"/>
/// as its result.
/// </summary>
IObjectFieldDescriptor StreamResult(bool hasStreamResult = true);

/// <summary>
/// Defines a field argument.
/// </summary>
Expand Down
Expand Up @@ -14,16 +14,38 @@ public abstract class FieldDefinitionBase
, IHasIgnore
{
private List<DirectiveDefinition>? _directives;
private string? _deprecationReason;

/// <summary>
/// Gets the internal field flags from this field.
/// </summary>
internal FieldFlags Flags { get; set; } = FieldFlags.None;

/// <summary>
/// Describes why this syntax node is deprecated.
/// </summary>
public string? DeprecationReason { get; set; }
public string? DeprecationReason
{
get => _deprecationReason;
set
{
if (string.IsNullOrEmpty(value))
{
Flags &= ~FieldFlags.Deprecated;
}
else
{
Flags |= FieldFlags.Deprecated;
}

_deprecationReason = value;
}
}

/// <summary>
/// If true, the field is deprecated
/// </summary>
public bool IsDeprecated => !string.IsNullOrEmpty(DeprecationReason);
public bool IsDeprecated => (Flags & FieldFlags.Deprecated) == FieldFlags.Deprecated;

/// <summary>
/// Gets the field type.
Expand All @@ -34,18 +56,32 @@ public abstract class FieldDefinitionBase
/// Defines if this field is ignored and will
/// not be included into the schema.
/// </summary>
public bool Ignore { get; set; }
public bool Ignore
{
get => (Flags & FieldFlags.Ignored) == FieldFlags.Ignored;
set
{
if (value)
{
Flags |= FieldFlags.Ignored;
}
else
{
Flags &= ~FieldFlags.Ignored;
}
}
}

/// <summary>
/// Gets the list of directives that are annotated to this field.
/// </summary>
public IList<DirectiveDefinition> Directives =>
_directives ??= new List<DirectiveDefinition>();
public IList<DirectiveDefinition> Directives
=> _directives ??= new List<DirectiveDefinition>();

/// <summary>
/// Specifies if this field has any directives.
/// </summary>
public bool HasDirectives => _directives is { Count: > 0 };
public bool HasDirectives => _directives?.Count > 0;

/// <summary>
/// Gets the list of directives that are annotated to this field.
Expand Down

0 comments on commit 431238f

Please sign in to comment.