Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8908d59
Added opt-in features
glen-84 Oct 28, 2024
f270649
Added OptInFeatureStability extension method to IRequestExecutorBuilder
glen-84 Oct 28, 2024
f01f415
Enforced valid GraphQL names for features and stability
glen-84 Oct 30, 2024
3d38ab7
Merge branch 'main' into gai/opt-in-features
michaelstaib Oct 31, 2024
8e5e2d1
Merge branch 'main' into gai/opt-in-features
michaelstaib Nov 4, 2024
3d79b77
Merge branch 'main' into gai/opt-in-features
glen-84 Nov 14, 2024
4a8b57c
Merge branch 'main' into gai/opt-in-features
glen-84 Nov 18, 2024
2f31b5a
Merge branch 'main' into gai/opt-in-features
glen-84 Nov 18, 2024
f7eaab4
Merge branch 'main' into gai/opt-in-features
glen-84 Dec 23, 2024
de8f95b
Merge branch 'main' into gai/opt-in-features
glen-84 Mar 29, 2025
dc2390d
Fixed error
glen-84 Mar 29, 2025
16de3ee
Merge branch 'main' into gai/opt-in-features
glen-84 May 29, 2025
631d54b
Merge branch 'main' into gai/opt-in-features
glen-84 Jul 10, 2025
4b9ec74
Merge branch 'main' into gai/opt-in-features
glen-84 Aug 30, 2025
dbb6221
Fixed analyzer errors
glen-84 Aug 30, 2025
2ce9fb9
Merge branch 'main' into gai/opt-in-features
glen-84 Oct 17, 2025
2ea18e0
Merge branch 'main' into gai/opt-in-features
michaelstaib Oct 22, 2025
47ff057
Merge 2ea18e07ce1b66d67f45ff51d14693260a62f13c into e07970c4422156975…
glen-84 Oct 22, 2025
5ec99d9
Update performance data [skip ci]
github-actions[bot] Oct 22, 2025
430b8aa
Merge branch 'main' into gai/opt-in-features
michaelstaib Oct 22, 2025
d80a6a9
Merge 430b8aad66691a3d340c903b8c8db8e6cf96aea5 into d0b6d5ee1dfbd29c3…
glen-84 Oct 22, 2025
6ea9e8b
Update performance data [skip ci]
github-actions[bot] Oct 22, 2025
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
25 changes: 25 additions & 0 deletions src/HotChocolate/Core/src/Abstractions/WellKnownDirectives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,29 @@ public static class WellKnownDirectives
/// The name of the @tag argument name.
/// </summary>
public const string Name = "name";

/// <summary>
/// The name of the @requiresOptIn directive.
/// </summary>
public const string RequiresOptIn = "requiresOptIn";

/// <summary>
/// The name of the @requiresOptIn feature argument.
/// </summary>
public const string RequiresOptInFeatureArgument = "feature";

/// <summary>
/// The name of the @optInFeatureStability directive.
/// </summary>
public const string OptInFeatureStability = "optInFeatureStability";

/// <summary>
/// The name of the @optInFeatureStability feature argument.
/// </summary>
public const string OptInFeatureStabilityFeatureArgument = "feature";

/// <summary>
/// The name of the @optInFeatureStability stability argument.
/// </summary>
public const string OptInFeatureStabilityStabilityArgument = "stability";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using HotChocolate.Types;
using HotChocolate.Types.Descriptors;
using static HotChocolate.Utilities.ErrorHelper;

namespace HotChocolate.Configuration.Validation;

internal sealed class RequiresOptInValidationRule : ISchemaValidationRule
{
public void Validate(
IDescriptorContext context,
ISchema schema,
ICollection<ISchemaError> errors)
{
if (!context.Options.EnableOptInFeatures)
{
return;
}

foreach (var type in schema.Types)
{
switch (type)
{
case IInputObjectType inputObjectType:
foreach (var field in inputObjectType.Fields)
{
if (field.Type.IsNonNullType() && field.DefaultValue is null)
{
var requiresOptInDirectives = field.Directives
.Where(d => d.Type is RequiresOptInDirectiveType);

foreach (var _ in requiresOptInDirectives)
{
errors.Add(RequiresOptInOnRequiredInputField(
inputObjectType,
field));
}
}
}

break;

case IObjectType objectType:
foreach (var field in objectType.Fields)
{
foreach (var argument in field.Arguments)
{
if (argument.Type.IsNonNullType() && argument.DefaultValue is null)
{
var requiresOptInDirectives = argument.Directives
.Where(d => d.Type is RequiresOptInDirectiveType);

foreach (var _ in requiresOptInDirectives)
{
errors.Add(RequiresOptInOnRequiredArgument(
objectType,
field,
argument));
}
}
}
}

break;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ internal static class SchemaValidator
new DirectiveValidationRule(),
new InterfaceHasAtLeastOneImplementationRule(),
new IsSelectedPatternValidation(),
new EnsureFieldResultsDeclareErrorsRule()
new EnsureFieldResultsDeclareErrorsRule(),
new RequiresOptInValidationRule()
];

public static IReadOnlyList<ISchemaError> Validate(
Expand Down
5 changes: 5 additions & 0 deletions src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ public interface IReadOnlySchemaOptions
/// </summary>
bool EnableTag { get; }

/// <summary>
/// Specifies that the opt-in features functionality will be enabled.
/// </summary>
bool EnableOptInFeatures { get; }

/// <summary>
/// Specifies the default dependency injection scope for query fields.
/// </summary>
Expand Down

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

27 changes: 27 additions & 0 deletions src/HotChocolate/Core/src/Types/Properties/TypeResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1003,4 +1003,31 @@ Type: `{0}`</value>
<data name="ObjectToDictionaryConverter_CycleInObjectGraph" xml:space="preserve">
<value>Cycle in object graph detected.</value>
</data>
<data name="RequiresOptInDirectiveType_TypeDescription" xml:space="preserve">
<value>Indicates that the given field, argument, input field, or enum value requires giving explicit consent before being used.</value>
</data>
<data name="RequiresOptInDirectiveType_FeatureDescription" xml:space="preserve">
<value>The name of the feature that requires opt in.</value>
</data>
<data name="RequiresOptInDirective_Descriptor_NotSupported" xml:space="preserve">
<value>RequiresOptIn is not supported on the specified descriptor.</value>
</data>
<data name="OptInFeatureStabilityDirectiveType_TypeDescription" xml:space="preserve">
<value>Sets the stability level of an opt-in feature.</value>
</data>
<data name="OptInFeatureStabilityDirectiveType_FeatureDescription" xml:space="preserve">
<value>The name of the feature for which to set the stability.</value>
</data>
<data name="OptInFeatureStabilityDirectiveType_StabilityDescription" xml:space="preserve">
<value>The stability level of the feature.</value>
</data>
<data name="OptInFeatureStability_Description" xml:space="preserve">
<value>An OptInFeatureStability object describes the stability level of an opt-in feature.</value>
</data>
<data name="ErrorHelper_RequiresOptInOnRequiredInputField" xml:space="preserve">
<value>The @requiresOptIn directive must not appear on required (non-null without a default) input object field definitions.</value>
</data>
<data name="ErrorHelper_RequiresOptInOnRequiredArgument" xml:space="preserve">
<value>The @requiresOptIn directive must not appear on required (non-null without a default) arguments.</value>
</data>
</root>
1 change: 1 addition & 0 deletions src/HotChocolate/Core/src/Types/SchemaBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public partial class SchemaBuilder : ISchemaBuilder
typeof(InterfaceCompletionTypeInterceptor),
typeof(MiddlewareValidationTypeInterceptor),
typeof(EnableTrueNullabilityTypeInterceptor),
typeof(OptInFeaturesTypeInterceptor)
];

private SchemaOptions _options = new();
Expand Down
5 changes: 5 additions & 0 deletions src/HotChocolate/Core/src/Types/SchemaOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ public FieldBindingFlags DefaultFieldBindingFlags
/// </summary>
public bool EnableTag { get; set; } = true;

/// <summary>
/// Specifies that the opt-in features functionality will be enabled.
/// </summary>
public bool EnableOptInFeatures { get; set; }

/// <summary>
/// Defines the default dependency injection scope for query fields.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ internal static IReadOnlyList<TypeReference> CreateReferences(
directiveTypes.Add(typeInspector.GetTypeRef(typeof(Tag)));
}

if (descriptorContext.Options.EnableOptInFeatures)
{
directiveTypes.Add(
typeInspector.GetTypeRef(typeof(OptInFeatureStabilityDirectiveType)));

directiveTypes.Add(
typeInspector.GetTypeRef(typeof(RequiresOptInDirectiveType)));
}

directiveTypes.Add(typeInspector.GetTypeRef(typeof(SkipDirectiveType)));
directiveTypes.Add(typeInspector.GetTypeRef(typeof(IncludeDirectiveType)));
directiveTypes.Add(typeInspector.GetTypeRef(typeof(DeprecatedDirectiveType)));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace HotChocolate.Types;

public sealed class OptInFeatureStabilityDirective
{
/// <summary>
/// Creates a new instance of <see cref="OptInFeatureStabilityDirective"/>.
/// </summary>
/// <param name="feature">
/// The name of the feature for which to set the stability.
/// </param>
/// <param name="stability">
/// The stability level of the feature.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="feature"/> is <c>null</c>.
/// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="stability"/> is <c>null</c>.
/// </exception>
public OptInFeatureStabilityDirective(string feature, string stability)
{
Feature = feature ?? throw new ArgumentNullException(nameof(feature));
Stability = stability ?? throw new ArgumentNullException(nameof(stability));
}

/// <summary>
/// The name of the feature for which to set the stability.
/// </summary>
[GraphQLDescription("The name of the feature for which to set the stability.")]
public string Feature { get; }

/// <summary>
/// The stability level of the feature.
/// </summary>
[GraphQLDescription("The stability level of the feature.")]
public string Stability { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace HotChocolate.Types;

public static class OptInFeatureStabilityDirectiveExtensions
{
public static ISchemaTypeDescriptor OptInFeatureStability(
this ISchemaTypeDescriptor descriptor,
string feature,
string stability)
{
return descriptor.Directive(new OptInFeatureStabilityDirective(feature, stability));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using HotChocolate.Properties;

namespace HotChocolate.Types;

/// <summary>
/// Sets the stability level of an opt-in feature.
/// </summary>
public sealed class OptInFeatureStabilityDirectiveType
: DirectiveType<OptInFeatureStabilityDirective>
{
protected override void Configure(
IDirectiveTypeDescriptor<OptInFeatureStabilityDirective> descriptor)
{
descriptor
.Name(WellKnownDirectives.OptInFeatureStability)
.Description(TypeResources.OptInFeatureStabilityDirectiveType_TypeDescription)
.Location(DirectiveLocation.Schema)
.Repeatable();

descriptor
.Argument(t => t.Feature)
.Name(WellKnownDirectives.OptInFeatureStabilityFeatureArgument)
.Description(TypeResources.OptInFeatureStabilityDirectiveType_FeatureDescription)
.Type<NonNullType<StringType>>();

descriptor
.Argument(t => t.Stability)
.Name(WellKnownDirectives.OptInFeatureStabilityStabilityArgument)
.Description(TypeResources.OptInFeatureStabilityDirectiveType_StabilityDescription)
.Type<NonNullType<StringType>>();
}
}
Loading
Loading