Skip to content

[Fusion] Adds Introspection Support #8439

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 13 commits into from
Jul 22, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public interface IScalarTypeDefinition
, IInputTypeDefinition
, ISyntaxNodeProvider<ScalarTypeDefinitionNode>
{
Uri? SpecifiedBy { get; }

/// <summary>
/// Checks if the value is an instance of this type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

namespace HotChocolate.Types.Introspection;

[Introspection]
// ReSharper disable once InconsistentNaming
[Introspection]
internal sealed class __Field : ObjectType<IOutputFieldDefinition>
{
protected override ObjectTypeConfiguration CreateConfiguration(ITypeDiscoveryContext context)
Expand Down
12 changes: 12 additions & 0 deletions src/HotChocolate/Core/src/Validation/Rules/VariableVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ protected override ISyntaxVisitorAction Enter(
{
if (IntrospectionFieldNames.TypeName.Equals(node.Name.Value, StringComparison.Ordinal))
{
if (node.Directives.Count > 0)
{
foreach (var directive in node.Directives)
{
var result = Visit(directive, context);
if (result.IsBreak())
{
return result;
}
}
}

return Skip;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public sealed class FusionDirectiveCollection

public FusionDirectiveCollection(FusionDirective[] directives)
{
ArgumentNullException.ThrowIfNull(directives);
_directives = directives;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Collections;
using System.Collections.Frozen;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using HotChocolate.Types;

namespace HotChocolate.Fusion.Types.Collections;

public sealed class FusionEnumValueCollection
: IReadOnlyList<FusionEnumValue>
, IReadOnlyEnumValueCollection
{
private readonly FusionEnumValue[] _values;
private readonly FrozenDictionary<string, FusionEnumValue> _map;

public FusionEnumValueCollection(FusionEnumValue[] values)
{
ArgumentNullException.ThrowIfNull(values);
_map = values.ToFrozenDictionary(t => t.Name);
_values = values;
}

public int Count => _values.Length;

/// <summary>
/// Gets the enum value with the specified name.
/// </summary>
public FusionEnumValue this[string name] => _map[name];

IEnumValue IReadOnlyEnumValueCollection.this[string name] => _map[name];

public FusionEnumValue this[int index] => _values[index];

IEnumValue IReadOnlyList<IEnumValue>.this[int index] => this[index];

/// <summary>
/// Tries to get the <paramref name="value"/> for
/// the specified <paramref name="name"/>.
/// </summary>
/// <param name="name">
/// The GraphQL enum value name.
/// </param>
/// <param name="value">
/// The GraphQL enum value.
/// </param>
/// <returns>
/// <c>true</c> if the <paramref name="name"/> represents a value of this enum type;
/// otherwise, <c>false</c>.
/// </returns>
public bool TryGetValue(string name, [NotNullWhen(true)] out FusionEnumValue? value)
=> _map.TryGetValue(name, out value);

bool IReadOnlyEnumValueCollection.TryGetValue(string name, [NotNullWhen(true)] out IEnumValue? value)
{
if(_map.TryGetValue(name, out var enumValue))
{
value = enumValue;
return true;
}

value = null;
return false;
}

/// <summary>
/// Determines whether the collection contains an enum value with the specified name.
/// </summary>
/// <param name="name">
/// The GraphQL enum value name.
/// </param>
/// <returns>
/// <c>true</c> if the collection contains an enum value with the specified name;
/// otherwise, <c>false</c>.
/// </returns>
public bool ContainsName(string name)
=> _map.ContainsKey(name);

public IEnumerable<FusionEnumValue> AsEnumerable()
=> _values;

public IEnumerator<FusionEnumValue> GetEnumerator()
=> Unsafe.As<IEnumerable<FusionEnumValue>>(_values).GetEnumerator();

IEnumerator<IEnumValue> IEnumerable<IEnumValue>.GetEnumerator()
=> GetEnumerator();

IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();

public static FusionEnumValueCollection Empty { get; } = new([]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using HotChocolate.Features;
using HotChocolate.Language;
using HotChocolate.Types;

namespace HotChocolate.Fusion.Types.Completion;

internal sealed class AggregateCompositeTypeInterceptor : CompositeTypeInterceptor
{
private readonly CompositeTypeInterceptor[] _interceptors;

public AggregateCompositeTypeInterceptor(CompositeTypeInterceptor[] interceptors)
{
ArgumentNullException.ThrowIfNull(interceptors);
_interceptors = interceptors;
}

public override void OnCompleteSchema(
ICompositeSchemaBuilderContext context,
ref IFeatureCollection features)
{
foreach (var interceptor in _interceptors)
{
interceptor.OnCompleteSchema(context, ref features);
}
}

public override void OnCompleteOutputField(
ICompositeSchemaBuilderContext context,
IComplexTypeDefinition type,
IOutputFieldDefinition field,
OperationType? operationType,
ref IFeatureCollection features)
{
foreach (var interceptor in _interceptors)
{
interceptor.OnCompleteOutputField(context, type, field, operationType, ref features);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal static class CompletionTools
{
public static FusionDirectiveCollection CreateDirectiveCollection(
IReadOnlyList<DirectiveNode> directives,
CompositeSchemaContext context)
CompositeSchemaBuilderContext context)
{
directives = DirectiveTools.GetUserDirectives(directives);

Expand Down Expand Up @@ -60,7 +60,7 @@ private static ArgumentAssignment CreateArgumentAssignment(

public static FusionInterfaceTypeDefinitionCollection CreateInterfaceTypeCollection(
IReadOnlyList<NamedTypeNode> interfaceTypes,
CompositeSchemaContext context)
CompositeSchemaBuilderContext context)
{
if (interfaceTypes.Count == 0)
{
Expand All @@ -79,7 +79,7 @@ public static FusionInterfaceTypeDefinitionCollection CreateInterfaceTypeCollect

public static FusionObjectTypeDefinitionCollection CreateObjectTypeCollection(
IReadOnlyList<NamedTypeNode> types,
CompositeSchemaContext context)
CompositeSchemaBuilderContext context)
{
var temp = new FusionObjectTypeDefinition[types.Count];

Expand All @@ -93,7 +93,7 @@ public static FusionObjectTypeDefinitionCollection CreateObjectTypeCollection(

public static SourceObjectTypeCollection CreateSourceObjectTypeCollection(
ObjectTypeDefinitionNode typeDef,
CompositeSchemaContext context)
CompositeSchemaBuilderContext context)
{
var types = TypeDirectiveParser.Parse(typeDef.Directives);
var lookups = LookupDirectiveParser.Parse(typeDef.Directives);
Expand All @@ -113,7 +113,7 @@ public static SourceObjectTypeCollection CreateSourceObjectTypeCollection(

public static SourceInterfaceTypeCollection CreateSourceInterfaceTypeCollection(
InterfaceTypeDefinitionNode typeDef,
CompositeSchemaContext context)
CompositeSchemaBuilderContext context)
{
var types = TypeDirectiveParser.Parse(typeDef.Directives);
var lookups = LookupDirectiveParser.Parse(typeDef.Directives);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using HotChocolate.Features;
using HotChocolate.Fusion.Types.Collections;

namespace HotChocolate.Fusion.Types.Completion;

internal readonly ref struct CompositeEnumTypeCompletionContext(
FusionDirectiveCollection directives,
IFeatureCollection features)
{
public FusionDirectiveCollection Directives { get; } = directives;

public IFeatureCollection Features { get; } = features;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using HotChocolate.Features;
using HotChocolate.Fusion.Types.Collections;
using HotChocolate.Types;

namespace HotChocolate.Fusion.Types.Completion;

internal readonly ref struct CompositeEnumValueCompletionContext(
IEnumTypeDefinition declaringType,
FusionDirectiveCollection directives,
IFeatureCollection features)
{
public IEnumTypeDefinition DeclaringType { get; } = declaringType;

public FusionDirectiveCollection Directives { get; } = directives;

public IFeatureCollection Features { get; } = features;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ namespace HotChocolate.Fusion.Types.Completion;

internal readonly ref struct CompositeScalarTypeCompletionContext(
ScalarValueKind valueKind,
FusionDirectiveCollection directives)
FusionDirectiveCollection directives,
Uri? specifiedBy)
{
public ScalarValueKind ValueKind { get; } = valueKind;

public FusionDirectiveCollection Directives { get; } = directives;

public Uri? SpecifiedBy { get; } = specifiedBy;
}
Loading
Loading