Skip to content

[Fusion] Support abstract lookups #8485

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

Merged
merged 8 commits into from
Aug 13, 2025
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 @@ -34,6 +34,8 @@ public static IEnumerable<IDirective> GetFusionLookupDirectives(
.ToList();

// To use an abstract lookup, the type must exist in the source schema.
// TODO: For abstract lookups we also need to validate that the type
// is part of the abstract type's possible types in the specific source schema.
if (type.ExistsInSchema(schemaName))
{
// Interface lookups.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@

namespace HotChocolate.Fusion.Types.Collections;

public class SourceInterfaceTypeCollection
: SourceMemberCollection<SourceInterfaceType>
public class SourceInterfaceTypeCollection(IEnumerable<SourceInterfaceType> members)
: SourceMemberCollection<SourceInterfaceType>(members)
, ISourceComplexTypeCollection<SourceInterfaceType>
, ISourceComplexTypeCollection<ISourceComplexType>
{
public SourceInterfaceTypeCollection(IEnumerable<SourceInterfaceType> members)
: base(members)
{
}

ISourceComplexType ISourceMemberCollection<ISourceComplexType>.this[string schemaName]
=> this[schemaName];

Expand All @@ -22,12 +17,9 @@ public ImmutableArray<SourceInterfaceType> Types
ImmutableArray<ISourceComplexType> ISourceComplexTypeCollection<ISourceComplexType>.Types
=> [.. Members];

public bool TryGetType(string schemaName, [NotNullWhen(true)] out SourceInterfaceType? type)
=> TryGetMember(schemaName, out type);

public bool TryGetType(string schemaName, [NotNullWhen(true)] out ISourceComplexType? type)
public bool TryGetMember(string schemaName, [NotNullWhen(true)] out ISourceComplexType? type)
{
if (TryGetMember(schemaName, out var member))
if (base.TryGetMember(schemaName, out var member))
{
type = member;
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class SourceMemberCollection<TMember>
{
private readonly FrozenDictionary<string, TMember> _members;

protected SourceMemberCollection(IEnumerable<TMember> members)
public SourceMemberCollection(IEnumerable<TMember> members)
{
_members = members.ToFrozenDictionary(t => t.SchemaName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ public ImmutableArray<SourceObjectType> Types
ImmutableArray<ISourceComplexType> ISourceComplexTypeCollection<ISourceComplexType>.Types
=> [.. Members];

public bool TryGetType(string schemaName, [NotNullWhen(true)] out SourceObjectType? type)
=> TryGetMember(schemaName, out type);

public bool TryGetType(string schemaName, [NotNullWhen(true)] out ISourceComplexType? type)
public bool TryGetMember(string schemaName, [NotNullWhen(true)] out ISourceComplexType? type)
{
if (TryGetMember(schemaName, out var member))
if (base.TryGetMember(schemaName, out var member))
{
type = member;
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace HotChocolate.Fusion.Types.Collections;

public class SourceUnionTypeCollection(IEnumerable<SourceUnionType> members)
: SourceMemberCollection<SourceUnionType>(members)
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,29 @@ public static SourceInterfaceTypeCollection CreateSourceInterfaceTypeCollection(
return new SourceInterfaceTypeCollection(temp);
}

public static SourceUnionTypeCollection CreateSourceUnionTypeCollection(
UnionTypeDefinitionNode typeDef,
CompositeSchemaBuilderContext context)
{
var types = TypeDirectiveParser.Parse(typeDef.Directives);
var lookupDirectives = LookupDirectiveParser.Parse(typeDef.Directives);
var temp = new SourceUnionType[types.Length];

for (var i = 0; i < types.Length; i++)
{
var type = types[i];
var lookups = GetLookupBySchema(lookupDirectives, type.SchemaName, typeDef.Name.Value);
context.RegisterForCompletionRange(lookups);

temp[i] = new SourceUnionType(
typeDef.Name.Value,
type.SchemaName,
lookups);
}

return new SourceUnionTypeCollection(temp);
}

private static ImmutableArray<Lookup> GetLookupBySchema(
ImmutableArray<LookupDirective> allLookups,
string schemaName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,8 @@ private static void CompleteUnionType(
{
var directives = CompletionTools.CreateDirectiveCollection(typeDef.Directives, context);
var types = CompletionTools.CreateObjectTypeCollection(typeDef.Types, context);
type.Complete(new CompositeUnionTypeCompletionContext(types, directives, FeatureCollection.Empty));
var sources = CompletionTools.CreateSourceUnionTypeCollection(typeDef, context);
type.Complete(new CompositeUnionTypeCompletionContext(types, directives, sources, FeatureCollection.Empty));
}

private static void CompleteOutputField(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ namespace HotChocolate.Fusion.Types.Completion;
internal readonly ref struct CompositeUnionTypeCompletionContext(
FusionObjectTypeDefinitionCollection types,
FusionDirectiveCollection directives,
SourceUnionTypeCollection sources,
IFeatureCollection features)
{
public FusionDirectiveCollection Directives { get; } = directives;

public FusionObjectTypeDefinitionCollection Types { get; } = types;

public SourceUnionTypeCollection Sources { get; } = sources;

public IFeatureCollection Features { get; } = features;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,5 @@ public interface ISourceComplexTypeCollection<TType>
: ISourceMemberCollection<TType>
where TType : ISourceComplexType
{
bool TryGetType(string schemaName, [NotNullWhen(true)] out TType? type);

ImmutableArray<TType> Types { get; }
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;

namespace HotChocolate.Fusion.Types;

public interface ISourceMemberCollection<out TMember>
public interface ISourceMemberCollection<TMember>
: IEnumerable<TMember>
where TMember : ISourceMember
{
Expand All @@ -13,4 +14,6 @@ public interface ISourceMemberCollection<out TMember>
bool ContainsSchema(string schemaName);

ImmutableArray<string> Schemas { get; }

bool TryGetMember(string schemaName, [NotNullWhen(true)] out TMember? type);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ public FusionUnionTypeDefinition(string name, string? description)

public SchemaCoordinate Coordinate => new(Name, ofDirective: false);

/// <summary>
/// Gets the source type definition of this type.
/// </summary>
/// <value>
/// The source type definition of this type.
/// </value>
public SourceUnionTypeCollection Sources
{
get;
private set
{
EnsureNotSealed(_completed);

field = value;
}
} = null!;

public FusionObjectTypeDefinitionCollection Types
{
get;
Expand Down Expand Up @@ -73,6 +90,7 @@ internal void Complete(CompositeUnionTypeCompletionContext context)

Directives = context.Directives;
Types = context.Types;
Sources = context.Sources;
Features = context.Features;

_completed = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class Lookup : INeedsCompletion
/// </summary>
/// <param name="schemaName">The name of the source schema.</param>
/// <param name="declaringTypeName">The name of the type that declares the field.</param>
/// <param name="name">The name of the lookup field.</param>
/// <param name="fieldName">The name of the lookup field.</param>
/// <param name="arguments">The arguments that represent field requirements.</param>
/// <param name="fields">The paths to the field that are required.</param>
/// <exception cref="ArgumentException">
Expand All @@ -30,13 +30,13 @@ public sealed class Lookup : INeedsCompletion
public Lookup(
string schemaName,
string declaringTypeName,
string name,
string fieldName,
ImmutableArray<LookupArgument> arguments,
ImmutableArray<IValueSelectionNode> fields)
{
ArgumentException.ThrowIfNullOrEmpty(schemaName);
ArgumentException.ThrowIfNullOrEmpty(declaringTypeName);
ArgumentException.ThrowIfNullOrEmpty(name);
ArgumentException.ThrowIfNullOrEmpty(fieldName);

if (arguments.Length == 0)
{
Expand All @@ -50,7 +50,7 @@ public Lookup(

_declaringTypeName = declaringTypeName;
SchemaName = schemaName;
Name = name;
FieldName = fieldName;
Arguments = arguments;
Fields = fields;
}
Expand All @@ -63,7 +63,12 @@ public Lookup(
/// <summary>
/// Gets the name of the lookup field.
/// </summary>
public string Name { get; }
public string FieldName { get; }

/// <summary>
/// Get the name of lookup field type.
/// </summary>
public string FieldType => _declaringTypeName;

/// <summary>
/// Gets the arguments that represent field requirements.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Immutable;

namespace HotChocolate.Fusion.Types;

public sealed class SourceUnionType(
string name,
string schemaName,
ImmutableArray<Lookup> lookups) : ISourceMember
{
public string Name { get; } = name;

public string SchemaName { get; } = schemaName;

public ImmutableArray<Lookup> Lookups { get; } = lookups;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal sealed class OperationDefinitionBuilder
private string? _name;
private string? _description;
private Lookup? _lookup;
private string? _lookupTypeRefinement;
private string? _requirementKey;
private SelectionSetNode? _selectionSet;

Expand Down Expand Up @@ -40,9 +41,10 @@ public OperationDefinitionBuilder SetDescription(string? description)
return this;
}

public OperationDefinitionBuilder SetLookup(Lookup? lookup, string? requirementKey)
public OperationDefinitionBuilder SetLookup(Lookup? lookup, string? lookupTypeRefinement, string? requirementKey)
{
_lookup = lookup;
_lookupTypeRefinement = lookupTypeRefinement;
_requirementKey = requirementKey;
return this;
}
Expand Down Expand Up @@ -76,7 +78,7 @@ public OperationDefinitionBuilder SetSelectionSet(SelectionSetNode selectionSet)
}

var lookupField = new FieldNode(
new NameNode(_lookup.Name),
new NameNode(_lookup.FieldName),
null,
[],
arguments,
Expand All @@ -87,7 +89,13 @@ public OperationDefinitionBuilder SetSelectionSet(SelectionSetNode selectionSet)
var indexBuilder = index.ToBuilder();
indexBuilder.Register(selectionSet);
index = indexBuilder;
selectionPath = selectionPath.AppendField(_lookup.Name);

if (!string.IsNullOrEmpty(_lookupTypeRefinement))
{
selectionPath = selectionPath.AppendFragment(_lookupTypeRefinement);
}

selectionPath = selectionPath.AppendField(_lookup.FieldName);
}

var definition = new OperationDefinitionNode(
Expand Down
Loading
Loading