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
@@ -0,0 +1,36 @@
namespace HotChocolate.Types.Introspection;

internal static class OptInIntrospectionHelper
{
/// <summary>
/// Determines whether a type system member is included for the given set of opted-in features.
/// A member without any <c>@requiresOptIn</c> directives is always included. A member that
/// requires one or more features is only included when <paramref name="includeOptIn"/> lists at
/// least one of those features.
/// </summary>
/// <param name="directives">
/// The directives of the type system member.
/// </param>
/// <param name="includeOptIn">
/// The features the client opted into.
/// </param>
public static bool IsIncluded(IReadOnlyDirectiveCollection directives, string[] includeOptIn)
{
var requiresOptIn = false;

foreach (var directive in directives)
{
if (directive.Definition is RequiresOptInDirectiveType)
{
requiresOptIn = true;

if (includeOptIn.Contains(directive.ToValue<RequiresOptIn>().Feature))
{
return true;
}
}
}

return !requiresOptIn;
}
}
Comment thread
glen-84 marked this conversation as resolved.
17 changes: 2 additions & 15 deletions src/HotChocolate/Core/src/Types/Types/Introspection/__Directive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,25 +116,12 @@ public static object Locations(IResolverContext context)
return DirectiveLocationUtils.AsEnumerable(locations);
}

public static object ArgumentsWithOptIn(IResolverContext context)
public static IEnumerable<IInputValueDefinition> ArgumentsWithOptIn(IResolverContext context)
{
var includeOptIn = context.ArgumentValue<string[]?>(Names.IncludeOptIn) ?? [];

// If an argument has no @requiresOptIn directives, it is always included.
// If an argument requires opting into features "f1" and "f2", then `includeOptIn`
// must list at least one of the features in order for the argument to be included.
return Arguments(context).Where(
a =>
{
var requiredFeatures = a
.Directives
.Where(d => d.Definition is RequiresOptInDirectiveType)
.Select(d => d.ToValue<RequiresOptIn>().Feature)
.ToList();

return requiredFeatures.Count == 0
|| requiredFeatures.Any(feature => includeOptIn.Contains(feature));
});
a => OptInIntrospectionHelper.IsIncluded(a.Directives, includeOptIn));
}

public static IEnumerable<IInputValueDefinition> Arguments(IResolverContext context)
Expand Down
17 changes: 2 additions & 15 deletions src/HotChocolate/Core/src/Types/Types/Introspection/__Field.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,12 @@ public static string Name(IResolverContext context)
public static string? Description(IResolverContext context)
=> context.Parent<IOutputFieldDefinition>().Description;

public static object ArgumentsWithOptIn(IResolverContext context)
public static IEnumerable<IInputValueDefinition> ArgumentsWithOptIn(IResolverContext context)
{
var includeOptIn = context.ArgumentValue<string[]?>(Names.IncludeOptIn) ?? [];

// If an argument has no @requiresOptIn directives, it is always included.
// If an argument requires opting into features "f1" and "f2", then `includeOptIn`
// must list at least one of the features in order for the argument to be included.
return Arguments(context).Where(
a =>
{
var requiredFeatures = a
.Directives
.Where(d => d.Definition is RequiresOptInDirectiveType)
.Select(d => d.ToValue<RequiresOptIn>().Feature)
.ToList();

return requiredFeatures.Count == 0
|| requiredFeatures.Any(feature => includeOptIn.Contains(feature));
});
a => OptInIntrospectionHelper.IsIncluded(a.Directives, includeOptIn));
}

public static IEnumerable<IInputValueDefinition> Arguments(IResolverContext context)
Expand Down
17 changes: 2 additions & 15 deletions src/HotChocolate/Core/src/Types/Types/Introspection/__Schema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,25 +122,12 @@ public static IEnumerable<IDirectiveDefinition> Directives(IResolverContext cont
: directiveDefinitions.Where(t => !t.IsDeprecated);
}

public static object DirectivesWithOptIn(IResolverContext context)
public static IEnumerable<IDirectiveDefinition> DirectivesWithOptIn(IResolverContext context)
{
var includeOptIn = context.ArgumentValue<string[]?>(Names.IncludeOptIn) ?? [];

// If a directive has no @requiresOptIn directives, it is always included.
// If a directive requires opting into features "f1" and "f2", then `includeOptIn`
// must list at least one of the features in order for the directive to be included.
return Directives(context).Where(
t =>
{
var requiredFeatures = ((IDirectivesProvider)t)
.Directives
.Where(d => d.Definition is RequiresOptInDirectiveType)
.Select(d => d.ToValue<RequiresOptIn>().Feature)
.ToList();

return requiredFeatures.Count == 0
|| requiredFeatures.Any(feature => includeOptIn.Contains(feature));
});
t => OptInIntrospectionHelper.IsIncluded(t.Directives, includeOptIn));
}

public static object AppliedDirectives(IResolverContext context)
Expand Down
52 changes: 6 additions & 46 deletions src/HotChocolate/Core/src/Types/Types/Introspection/__Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public static object Kind(IResolverContext context)
public static object? Description(IResolverContext context)
=> context.Parent<IType>() is ITypeDefinition n ? n.Description : null;

public static object? FieldsWithOptIn(IResolverContext context)
public static IEnumerable<IOutputFieldDefinition>? FieldsWithOptIn(IResolverContext context)
{
var type = context.Parent<IType>();

Expand All @@ -149,21 +149,8 @@ public static object Kind(IResolverContext context)

var includeOptIn = context.ArgumentValue<string[]?>(Names.IncludeOptIn) ?? [];

// If a field has no @requiresOptIn directives, it is always included.
// If a field requires opting into features "f1" and "f2", then `includeOptIn`
// must list at least one of the features in order for the field to be included.
return fields.Where(
f =>
{
var requiredFeatures = f
.Directives
.Where(d => d.Definition is RequiresOptInDirectiveType)
.Select(d => d.ToValue<RequiresOptIn>().Feature)
.ToList();

return requiredFeatures.Count == 0
|| requiredFeatures.Any(feature => includeOptIn.Contains(feature));
});
f => OptInIntrospectionHelper.IsIncluded(f.Directives, includeOptIn));
}

return default;
Expand Down Expand Up @@ -196,7 +183,7 @@ public static object Kind(IResolverContext context)
: null
: null;

public static object? EnumValuesWithOptIn(IResolverContext context)
public static IEnumerable<IEnumValue>? EnumValuesWithOptIn(IResolverContext context)
{
var type = context.Parent<IType>();

Expand All @@ -211,21 +198,8 @@ public static object Kind(IResolverContext context)

var includeOptIn = context.ArgumentValue<string[]?>(Names.IncludeOptIn) ?? [];

// If an enum value has no @requiresOptIn directives, it is always included.
// If an enum value requires opting into features "f1" and "f2", then `includeOptIn`
// must list at least one of the features in order for the value to be included.
return enumValues.Where(
v =>
{
var requiredFeatures = v
.Directives
.Where(d => d.Definition is RequiresOptInDirectiveType)
.Select(d => d.ToValue<RequiresOptIn>().Feature)
.ToList();

return requiredFeatures.Count == 0
|| requiredFeatures.Any(feature => includeOptIn.Contains(feature));
});
v => OptInIntrospectionHelper.IsIncluded(v.Directives, includeOptIn));
}

return default;
Expand All @@ -238,7 +212,7 @@ public static object Kind(IResolverContext context)
: et.Values.Where(t => !t.IsDeprecated)
: null;

public static object? InputFieldsWithOptIn(IResolverContext context)
public static IEnumerable<IInputValueDefinition>? InputFieldsWithOptIn(IResolverContext context)
{
var type = context.Parent<IType>();

Expand All @@ -253,22 +227,8 @@ public static object Kind(IResolverContext context)

var includeOptIn = context.ArgumentValue<string[]?>(Names.IncludeOptIn) ?? [];

// If an input field has no @requiresOptIn directives, it is always included.
// If an input field requires opting into features "f1" and "f2", then
// `includeOptIn` must list at least one of the features in order for the field to
// be included.
return inputFields.Where(
f =>
{
var requiredFeatures = f
.Directives
.Where(d => d.Definition is RequiresOptInDirectiveType)
.Select(d => d.ToValue<RequiresOptIn>().Feature)
.ToList();

return requiredFeatures.Count == 0
|| requiredFeatures.Any(feature => includeOptIn.Contains(feature));
});
f => OptInIntrospectionHelper.IsIncluded(f.Directives, includeOptIn));
}

return default;
Expand Down
Loading