Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@ IReadOnlyInterfaceTypeDefinitionCollection IComplexTypeDefinition.Implements
IReadOnlyFieldDefinitionCollection<IOutputFieldDefinition> IComplexTypeDefinition.Fields
=> Fields;

/// <summary>
/// Gets a value indicating whether this type is shared across multiple source schemas.
/// </summary>
public abstract bool IsSharedType { get; }

/// <summary>
/// Gets a value indicating whether this type is an entity type.
/// An entity type is shared and has lookups that allow it to be
/// resolved independently by source schemas.
/// </summary>
public abstract bool IsEntityType { get; }

/// <summary>
/// Gets a value indicating whether this type is a value type.
/// A value type is shared across multiple source schemas but has no
/// entity lookups — it cannot be independently resolved.
/// </summary>
public bool IsValueType => IsSharedType && !IsEntityType;

/// <summary>
/// Gets metadata about this complex type in its source schemas.
/// Each entry in the collection provides information about this complex type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@ public sealed class FusionInterfaceTypeDefinition(
: FusionComplexTypeDefinition(name, description, isInaccessible, fieldsDefinition)
, IInterfaceTypeDefinition
{
private FusionTypeFlags _flags;

/// <inheritdoc />
public override TypeKind Kind => TypeKind.Interface;

/// <inheritdoc />
public override bool IsSharedType => (_flags & FusionTypeFlags.Shared) != 0;

/// <inheritdoc />
public override bool IsEntityType => (_flags & FusionTypeFlags.Entity) != 0;

/// <summary>
/// Gets metadata about this interface type in its source schemas.
/// Each entry in the collection provides information about this interface type
Expand All @@ -41,6 +49,7 @@ internal void Complete(CompositeInterfaceTypeCompletionContext context)
Implements = context.Interfaces;
base.Sources = context.Sources;
Features = context.Features;
SetFlags(context.Sources);

Complete();
}
Expand Down Expand Up @@ -76,6 +85,23 @@ public override bool IsAssignableFrom(ITypeDefinition type)
}
}

private void SetFlags(ISourceComplexTypeCollection<SourceInterfaceType> sources)
{
if (sources.Schemas.Length > 1)
{
_flags |= FusionTypeFlags.Shared;
}

foreach (var source in sources)
{
if (source.Lookups.Length > 0)
{
_flags |= FusionTypeFlags.Entity;
break;
}
}
}

/// <summary>
/// Creates a <see cref="InterfaceTypeDefinitionNode"/> from a
/// <see cref="FusionInterfaceTypeDefinition"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@ public sealed class FusionObjectTypeDefinition(
: FusionComplexTypeDefinition(name, description, isInaccessible, fieldsDefinition)
, IObjectTypeDefinition
{
private FusionTypeFlags _flags;

/// <inheritdoc />
public override TypeKind Kind => TypeKind.Object;

/// <inheritdoc />
public override bool IsSharedType => (_flags & FusionTypeFlags.Shared) != 0;

/// <inheritdoc />
public override bool IsEntityType => (_flags & FusionTypeFlags.Entity) != 0;

/// <summary>
/// Gets metadata about this object type in its source schemas.
/// Each entry in the collection provides information about this object type
Expand All @@ -43,6 +51,7 @@ internal void Complete(CompositeObjectTypeCompletionContext context)
Implements = context.Interfaces;
base.Sources = context.Sources;
Features = context.Features;
SetFlags(context.Sources);

Complete();
}
Expand Down Expand Up @@ -72,6 +81,23 @@ public override bool IsAssignableFrom(ITypeDefinition type)
return false;
}

private void SetFlags(ISourceComplexTypeCollection<SourceObjectType> sources)
{
if (sources.Schemas.Length > 1)
{
_flags |= FusionTypeFlags.Shared;
}

foreach (var source in sources)
{
if (source.Lookups.Length > 0)
{
_flags |= FusionTypeFlags.Entity;
break;
}
}
}

/// <summary>
/// Creates a <see cref="ObjectTypeDefinitionNode"/> from a
/// <see cref="FusionObjectTypeDefinition"/>.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace HotChocolate.Fusion.Types;

/// <summary>
/// Defines classification flags for Fusion type definitions.
/// </summary>
[Flags]
internal enum FusionTypeFlags : byte
{
/// <summary>
/// No flags are set.
/// </summary>
None = 0,

/// <summary>
/// The type is shared across multiple source schemas.
/// </summary>
Shared = 1 << 0,

/// <summary>
/// The type is an entity — it is shared and has lookups
/// that allow it to be resolved independently by source schemas.
/// </summary>
Entity = 1 << 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace HotChocolate.Fusion.Types;
/// </summary>
public sealed class FusionUnionTypeDefinition : IUnionTypeDefinition, IFusionTypeDefinition
{
private FusionTypeFlags _flags;
private bool _completed;

/// <summary>
Expand Down Expand Up @@ -62,6 +63,25 @@ public FusionUnionTypeDefinition(string name, string? description, bool isInacce
/// </summary>
public bool IsInaccessible { get; }

/// <summary>
/// Gets a value indicating whether this type is shared across multiple source schemas.
/// </summary>
public bool IsSharedType => (_flags & FusionTypeFlags.Shared) != 0;

/// <summary>
/// Gets a value indicating whether this type is an entity type.
/// An entity type is shared and has lookups that allow it to be
/// resolved independently by source schemas.
/// </summary>
public bool IsEntityType => (_flags & FusionTypeFlags.Entity) != 0;

/// <summary>
/// Gets a value indicating whether this type is a value type.
/// A value type is shared across multiple source schemas but has no
/// entity lookups — it cannot be independently resolved.
/// </summary>
public bool IsValueType => IsSharedType && !IsEntityType;

/// <summary>
/// Gets metadata about this union type in its source schemas.
/// Each entry in the collection provides information about this union type
Expand Down Expand Up @@ -144,6 +164,7 @@ internal void Complete(CompositeUnionTypeCompletionContext context)
Types = context.Types;
Sources = context.Sources;
Features = context.Features;
SetFlags(context.Sources);

_completed = true;
}
Expand Down Expand Up @@ -179,6 +200,23 @@ public bool IsAssignableFrom(ITypeDefinition type)
}
}

private void SetFlags(SourceUnionTypeCollection sources)
{
if (sources.Schemas.Length > 1)
{
_flags |= FusionTypeFlags.Shared;
}

foreach (var source in sources)
{
if (source.Lookups.Length > 0)
{
_flags |= FusionTypeFlags.Entity;
break;
}
}
}

/// <summary>
/// Gets the string representation of this union type definition.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using System.Text.Json;
using HotChocolate.Fusion.Text.Json;
using HotChocolate.Language;
using HotChocolate.Types;

namespace HotChocolate.Fusion.Execution.Nodes;

public sealed class IntrospectionExecutionNode : ExecutionNode
{
private readonly Selection[] _selections;
private readonly string[] _responseNames;
private readonly ResultSelectionSet _resultSelectionSet;
private readonly ExecutionNodeCondition[] _conditions;

public IntrospectionExecutionNode(
Expand All @@ -26,7 +27,8 @@ public IntrospectionExecutionNode(

Id = id;
_selections = selections;
_responseNames = selections.Select(t => t.ResponseName).ToArray();
var selectionSetNode = new SelectionSetNode(selections.Select(t => t.SyntaxNodes[0].Node).ToArray());
_resultSelectionSet = ResultSelectionSet.Create(selectionSetNode);
_conditions = conditions;
}

Expand Down Expand Up @@ -70,7 +72,7 @@ protected override ValueTask<ExecutionStatus> OnExecuteAsync(
}

ExecuteSelections(context, backlog);
context.AddPartialResults(resultBuilder.Build(), _responseNames);
context.AddPartialResults(resultBuilder.Build(), _resultSelectionSet);

return new ValueTask<ExecutionStatus>(ExecutionStatus.Success);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace HotChocolate.Fusion.Execution.Nodes;

/// <summary>
/// A selection set with 8 or more direct selections. Uses a dictionary for O(1) child lookup.
/// Handles rare wide selection sets.
/// </summary>
internal sealed class LargeResultSelectionSet : ResultSelectionSet
{
private readonly ResultSelection[] _selections;
private readonly Dictionary<string, ResultSelectionSet?> _childLookup;

internal LargeResultSelectionSet(
ResultSelection[] selections,
ResultFragment[] fragments,
string[] allResponseNames)
: base(fragments, allResponseNames)
{
_selections = selections;
_childLookup = new Dictionary<string, ResultSelectionSet?>(selections.Length, StringComparer.Ordinal);

for (var i = 0; i < selections.Length; i++)
{
_childLookup[selections[i].ResponseName] = selections[i].Child;
}
}

protected override ReadOnlySpan<ResultSelection> DirectSelections => _selections;

protected override bool TryGetDirectChild(string responseName, out ResultSelectionSet? child)
=> _childLookup.TryGetValue(responseName, out child);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public sealed class NodeFieldExecutionNode : ExecutionNode
{
private readonly Dictionary<string, ExecutionNode> _branches = [];
private ExecutionNode _fallbackQuery = null!;
private readonly ResultSelectionSet _resultSelectionSet;
private readonly string _responseName;
private readonly IValueNode _idValue;
private readonly ExecutionNodeCondition[] _conditions;
Expand All @@ -21,6 +22,8 @@ internal NodeFieldExecutionNode(
ExecutionNodeCondition[] conditions)
{
_responseName = responseName;
var resultSelectionSet = new SelectionSetNode([new FieldNode(responseName)]);
_resultSelectionSet = ResultSelectionSet.Create(resultSelectionSet);
_idValue = idValue;
Id = id;
_conditions = conditions;
Expand Down Expand Up @@ -82,14 +85,14 @@ protected override ValueTask<ExecutionStatus> OnExecuteAsync(
.SetExtension("originalValue", id)
.Build();

context.AddErrors(error, [_responseName], Path.Root);
context.AddErrors(error, _resultSelectionSet, Path.Root);

return ValueTask.FromResult(ExecutionStatus.Failed);
}

if (_branches.TryGetValue(typeName, out var operation))
{
// We have a branch and we select it for exclusive execution
// We have a branch, and we select it for exclusive execution
EnqueueDependentForExecution(context, operation);

return ValueTask.FromResult(ExecutionStatus.Success);
Expand All @@ -115,7 +118,7 @@ string GetVariableValue(VariableNode variable)
}
}

protected override IDisposable? CreateScope(OperationPlanContext context)
protected override IDisposable CreateScope(OperationPlanContext context)
=> context.DiagnosticEvents.ExecuteNodeFieldNode(context, this);

internal void AddBranch(string objectTypeName, ExecutionNode node)
Expand Down
Loading
Loading