Skip to content

Added opt-in features #7652

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

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
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,35 @@
using HotChocolate;
using HotChocolate.Execution.Configuration;
using HotChocolate.Types;

namespace Microsoft.Extensions.DependencyInjection;

public static partial class RequestExecutorBuilderExtensions
{
public static IRequestExecutorBuilder OptInFeatureStability(
this IRequestExecutorBuilder builder,
string feature,
string stability)
{
if (builder is null)
{
throw new ArgumentNullException(nameof(builder));
}

if (feature is null)
{
throw new ArgumentNullException(nameof(feature));
}

if (stability is null)
{
throw new ArgumentNullException(nameof(stability));
}

return Configure(
builder,
options => options.OnConfigureSchemaServicesHooks.Add(
(ctx, _) => ctx.SchemaBuilder.AddSchemaConfiguration(
d => d.Directive(new OptInFeatureStabilityDirective(feature, 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,
ISchemaDefinition schema,
ICollection<ISchemaError> errors)
{
if (!context.Options.EnableOptInFeatures)
{
return;
}

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

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

break;

case IObjectTypeDefinition 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.Definition 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 @@ -187,6 +187,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
108 changes: 108 additions & 0 deletions src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs

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

36 changes: 36 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,40 @@ Type: `{0}`</value>
<data name="TypeExtensions_KindIsNotSupported" xml:space="preserve">
<value>The specified type kind is not supported.</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>
<data name="RequiresOptInDirective_FeatureName_NotValid" xml:space="preserve">
<value>The feature name must follow the GraphQL type name rules.</value>
</data>
<data name="OptInFeatureStabilityDirective_FeatureName_NotValid" xml:space="preserve">
<value>The feature name must follow the GraphQL type name rules.</value>
</data>
<data name="OptInFeatureStabilityDirective_Stability_NotValid" xml:space="preserve">
<value>The stability must follow the GraphQL type name rules.</value>
</data>
</root>
3 changes: 2 additions & 1 deletion src/HotChocolate/Core/src/Types/SchemaBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public partial class SchemaBuilder : ISchemaBuilder
typeof(InterfaceCompletionTypeInterceptor),
typeof(MiddlewareValidationTypeInterceptor),
typeof(SemanticNonNullTypeInterceptor),
typeof(StoreGlobalPagingOptionsTypeInterceptor)
typeof(StoreGlobalPagingOptionsTypeInterceptor),
typeof(OptInFeaturesTypeInterceptor)
];

private readonly SchemaOptions _options = new();
Expand Down
3 changes: 3 additions & 0 deletions src/HotChocolate/Core/src/Types/SchemaOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ public FieldBindingFlags DefaultFieldBindingFlags
/// <inheritdoc cref="IReadOnlySchemaOptions.EnableTag"/>
public bool EnableTag { get; set; } = true;

/// <inheritdoc cref="IReadOnlySchemaOptions.EnableOptInFeatures"/>
public bool EnableOptInFeatures { get; set; }

/// <inheritdoc cref="IReadOnlySchemaOptions.DefaultQueryDependencyInjectionScope"/>
public DependencyInjectionScope DefaultQueryDependencyInjectionScope { get; set; } =
DependencyInjectionScope.Resolver;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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,54 @@
using HotChocolate.Properties;
using HotChocolate.Utilities;

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="ArgumentException">
/// <paramref name="feature"/> is not a valid name.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="stability"/> is not a valid name.
/// </exception>
public OptInFeatureStabilityDirective(string feature, string stability)
{
if (!feature.IsValidGraphQLName())
{
throw new ArgumentException(
TypeResources.OptInFeatureStabilityDirective_FeatureName_NotValid,
nameof(feature));
}

if (!stability.IsValidGraphQLName())
{
throw new ArgumentException(
TypeResources.OptInFeatureStabilityDirective_Stability_NotValid,
nameof(stability));
}

Feature = feature;
Stability = 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));
}
}
Loading
Loading