Skip to content

Commit

Permalink
[Fusion] Changed @require(fields:) to @require(field:) (#8077)
Browse files Browse the repository at this point in the history
  • Loading branch information
glen-84 authored Feb 27, 2025
1 parent 33f2330 commit 5c69f8a
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,23 @@ internal record ProvidesFieldsInvalidTypeEvent(
MutableComplexTypeDefinition Type,
MutableSchemaDefinition Schema) : IEvent;

internal record RequireFieldNodeEvent(
FieldNode FieldNode,
ImmutableArray<string> FieldNamePath,
internal record RequireFieldInvalidSyntaxEvent(
Directive RequireDirective,
MutableInputFieldDefinition Argument,
MutableOutputFieldDefinition Field,
MutableComplexTypeDefinition Type,
MutableSchemaDefinition Schema) : IEvent;

internal record RequireFieldsInvalidSyntaxEvent(
internal record RequireFieldInvalidTypeEvent(
Directive RequireDirective,
MutableInputFieldDefinition Argument,
MutableOutputFieldDefinition Field,
MutableComplexTypeDefinition Type,
MutableSchemaDefinition Schema) : IEvent;

internal record RequireFieldsInvalidTypeEvent(
internal record RequireFieldNodeEvent(
FieldNode FieldNode,
ImmutableArray<string> FieldNamePath,
Directive RequireDirective,
MutableInputFieldDefinition Argument,
MutableOutputFieldDefinition Field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public static class LogEntryCodes
public const string ProvidesInvalidSyntax = "PROVIDES_INVALID_SYNTAX";
public const string ProvidesOnNonCompositeField = "PROVIDES_ON_NON_COMPOSITE_FIELD";
public const string QueryRootTypeInaccessible = "QUERY_ROOT_TYPE_INACCESSIBLE";
public const string RequireDirectiveInFieldsArg = "REQUIRE_DIRECTIVE_IN_FIELDS_ARG";
public const string RequireInvalidFieldsType = "REQUIRE_INVALID_FIELDS_TYPE";
public const string RequireDirectiveInFieldArg = "REQUIRE_DIRECTIVE_IN_FIELD_ARG";
public const string RequireInvalidFieldType = "REQUIRE_INVALID_FIELD_TYPE";
public const string RequireInvalidSyntax = "REQUIRE_INVALID_SYNTAX";
public const string RootMutationUsed = "ROOT_MUTATION_USED";
public const string RootQueryUsed = "ROOT_QUERY_USED";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ public static LogEntry QueryRootTypeInaccessible(
schema);
}

public static LogEntry RequireDirectiveInFieldsArgument(
public static LogEntry RequireDirectiveInFieldArgument(
ImmutableArray<string> fieldNamePath,
Directive requireDirective,
string argumentName,
Expand All @@ -736,18 +736,18 @@ public static LogEntry RequireDirectiveInFieldsArgument(

return new LogEntry(
string.Format(
LogEntryHelper_RequireDirectiveInFieldsArgument,
LogEntryHelper_RequireDirectiveInFieldArgument,
coordinate,
schema.Name,
string.Join(".", fieldNamePath)),
LogEntryCodes.RequireDirectiveInFieldsArg,
LogEntryCodes.RequireDirectiveInFieldArg,
LogSeverity.Error,
coordinate,
requireDirective,
schema);
}

public static LogEntry RequireInvalidFieldsType(
public static LogEntry RequireInvalidFieldType(
Directive requireDirective,
string argumentName,
string fieldName,
Expand All @@ -757,8 +757,8 @@ public static LogEntry RequireInvalidFieldsType(
var coordinate = new SchemaCoordinate(typeName, fieldName, argumentName);

return new LogEntry(
string.Format(LogEntryHelper_RequireInvalidFieldsType, coordinate, schema.Name),
LogEntryCodes.RequireInvalidFieldsType,
string.Format(LogEntryHelper_RequireInvalidFieldType, coordinate, schema.Name),
LogEntryCodes.RequireInvalidFieldType,
LogSeverity.Error,
coordinate,
requireDirective,
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,14 @@
<data name="LogEntryHelper_QueryRootTypeInaccessible" xml:space="preserve">
<value>The root query type in schema '{0}' must be accessible.</value>
</data>
<data name="LogEntryHelper_RequireDirectiveInFieldsArgument" xml:space="preserve">
<data name="LogEntryHelper_RequireDirectiveInFieldArgument" xml:space="preserve">
<value>The @require directive on argument '{0}' in schema '{1}' references field '{2}', which must not include directive applications.</value>
</data>
<data name="LogEntryHelper_RequireInvalidFieldsType" xml:space="preserve">
<value>The @require directive on argument '{0}' in schema '{1}' must specify a string value for the 'fields' argument.</value>
<data name="LogEntryHelper_RequireInvalidFieldType" xml:space="preserve">
<value>The @require directive on argument '{0}' in schema '{1}' must specify a string value for the 'field' argument.</value>
</data>
<data name="LogEntryHelper_RequireInvalidSyntax" xml:space="preserve">
<value>The @require directive on argument '{0}' in schema '{1}' contains invalid syntax in the 'fields' argument.</value>
<value>The @require directive on argument '{0}' in schema '{1}' contains invalid syntax in the 'field' argument.</value>
</data>
<data name="LogEntryHelper_RootMutationUsed" xml:space="preserve">
<value>The root mutation type in schema '{0}' must be named 'Mutation'.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ public CompositionResult<MutableSchemaDefinition> Compose()
new ProvidesInvalidSyntaxRule(),
new ProvidesOnNonCompositeFieldRule(),
new QueryRootTypeInaccessibleRule(),
new RequireDirectiveInFieldsArgumentRule(),
new RequireInvalidFieldsTypeRule(),
new RequireDirectiveInFieldArgumentRule(),
new RequireInvalidFieldTypeRule(),
new RequireInvalidSyntaxRule(),
new RootMutationUsedRule(),
new RootQueryUsedRule(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ namespace HotChocolate.Fusion.SourceSchemaValidationRules;

/// <summary>
/// The <c>@require</c> directive is used to specify fields on the same type that an argument
/// depends on in order to resolve the annotated field. When using <c>@require(fields: "…")</c>, the
/// <c>fields</c> argument must be a valid selection set string <b>without</b> any additional
/// depends on in order to resolve the annotated field. When using <c>@require(field: "…")</c>, the
/// <c>field</c> argument must be a valid selection set string <b>without</b> any additional
/// directive applications. Applying a directive (e.g., <c>@lowercase</c>) inside this selection set
/// is not supported and triggers the <c>REQUIRE_DIRECTIVE_IN_FIELDS_ARG</c> error.
/// is not supported and triggers the <c>REQUIRE_DIRECTIVE_IN_FIELD_ARG</c> error.
/// </summary>
/// <seealso href="https://graphql.github.io/composite-schemas-spec/draft/#sec-Require-Directive-in-Fields-Argument">
/// <seealso href="https://graphql.github.io/composite-schemas-spec/draft/#sec-Require-Directive-in-Field-Argument">
/// Specification
/// </seealso>
internal sealed class RequireDirectiveInFieldsArgumentRule : IEventHandler<RequireFieldNodeEvent>
internal sealed class RequireDirectiveInFieldArgumentRule : IEventHandler<RequireFieldNodeEvent>
{
public void Handle(RequireFieldNodeEvent @event, CompositionContext context)
{
Expand All @@ -23,7 +23,7 @@ public void Handle(RequireFieldNodeEvent @event, CompositionContext context)
if (fieldNode.Directives.Count != 0)
{
context.Log.Write(
RequireDirectiveInFieldsArgument(
RequireDirectiveInFieldArgument(
fieldNamePath,
requireDirective,
argument.Name,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using HotChocolate.Fusion.Events;
using HotChocolate.Fusion.Events.Contracts;
using static HotChocolate.Fusion.Logging.LogEntryHelper;

namespace HotChocolate.Fusion.SourceSchemaValidationRules;

/// <summary>
/// When using the <c>@require</c> directive, the <c>field</c> argument must always be a string that
/// defines a (potentially nested) selection set of fields from the same type. If the <c>field</c>
/// argument is provided as a type other than a string (such as an integer, boolean, or enum), the
/// directive usage is invalid and will cause schema composition to fail.
/// </summary>
/// <seealso href="https://graphql.github.io/composite-schemas-spec/draft/#sec-Require-Invalid-Field-Type">
/// Specification
/// </seealso>
internal sealed class RequireInvalidFieldTypeRule : IEventHandler<RequireFieldInvalidTypeEvent>
{
public void Handle(RequireFieldInvalidTypeEvent @event, CompositionContext context)
{
var (requireDirective, argument, field, type, schema) = @event;

context.Log.Write(
RequireInvalidFieldType(
requireDirective,
argument.Name,
field.Name,
type.Name,
schema));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
namespace HotChocolate.Fusion.SourceSchemaValidationRules;

/// <summary>
/// The <c>@require</c> directive’s <c>fields</c> argument must be syntactically valid GraphQL. If
/// The <c>@require</c> directive’s <c>field</c> argument must be syntactically valid GraphQL. If
/// the selection map string is malformed (e.g., missing closing braces, unbalanced quotes, invalid
/// tokens), then the schema cannot be composed correctly. In such cases, the error
/// <c>REQUIRE_INVALID_SYNTAX</c> is raised.
/// </summary>
/// <seealso href="https://graphql.github.io/composite-schemas-spec/draft/#sec-Require-Invalid-Syntax">
/// Specification
/// </seealso>
internal sealed class RequireInvalidSyntaxRule : IEventHandler<RequireFieldsInvalidSyntaxEvent>
internal sealed class RequireInvalidSyntaxRule : IEventHandler<RequireFieldInvalidSyntaxEvent>
{
public void Handle(RequireFieldsInvalidSyntaxEvent @event, CompositionContext context)
public void Handle(RequireFieldInvalidSyntaxEvent @event, CompositionContext context)
{
var (requireDirective, argument, field, type, schema) = @event;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private void PublishEntityEvents(
foreach (var keyDirective in keyDirectives)
{
if (!keyDirective.Arguments.TryGetValue(ArgumentNames.Fields, out var f)
|| f is not StringValueNode fields)
|| f is not StringValueNode fieldsArgument)
{
PublishEvent(
new KeyFieldsInvalidTypeEvent(keyDirective, entityType, schema),
Expand All @@ -117,7 +117,7 @@ private void PublishEntityEvents(

try
{
var selectionSet = Syntax.ParseSelectionSet($"{{{fields.Value}}}");
var selectionSet = Syntax.ParseSelectionSet($"{{{fieldsArgument.Value}}}");

PublishKeyFieldEvents(
selectionSet,
Expand Down Expand Up @@ -222,7 +222,7 @@ private void PublishProvidesEvents(
var providesDirective = field.Directives.AsEnumerable().First(d => d.Name == DirectiveNames.Provides);

if (!providesDirective.Arguments.TryGetValue(ArgumentNames.Fields, out var f)
|| f is not StringValueNode fields)
|| f is not StringValueNode fieldsArgument)
{
PublishEvent(
new ProvidesFieldsInvalidTypeEvent(providesDirective, field, type, schema),
Expand All @@ -233,7 +233,7 @@ private void PublishProvidesEvents(

try
{
var selectionSet = Syntax.ParseSelectionSet($"{{{fields.Value}}}");
var selectionSet = Syntax.ParseSelectionSet($"{{{fieldsArgument.Value}}}");

PublishProvidesFieldEvents(
selectionSet,
Expand Down Expand Up @@ -335,19 +335,19 @@ private void PublishRequireEvents(
{
var requireDirective = argument.Directives.AsEnumerable().First(d => d.Name == DirectiveNames.Require);

if (!requireDirective.Arguments.TryGetValue(ArgumentNames.Fields, out var f)
|| f is not StringValueNode fields)
if (!requireDirective.Arguments.TryGetValue(ArgumentNames.Field, out var f)
|| f is not StringValueNode fieldArgument)
{
PublishEvent(
new RequireFieldsInvalidTypeEvent(requireDirective, argument, field, type, schema),
new RequireFieldInvalidTypeEvent(requireDirective, argument, field, type, schema),
context);

return;
}

try
{
var selectionSet = Syntax.ParseSelectionSet($"{{{fields.Value}}}");
var selectionSet = Syntax.ParseSelectionSet($"{{{fieldArgument.Value}}}");

PublishRequireFieldEvents(
selectionSet,
Expand All @@ -362,7 +362,7 @@ private void PublishRequireEvents(
catch (SyntaxException)
{
PublishEvent(
new RequireFieldsInvalidSyntaxEvent(
new RequireFieldInvalidSyntaxEvent(
requireDirective,
argument,
field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

namespace HotChocolate.Fusion.SourceSchemaValidationRules;

public sealed class RequireDirectiveInFieldsArgumentRuleTests : CompositionTestBase
public sealed class RequireDirectiveInFieldArgumentRuleTests : CompositionTestBase
{
private static readonly object s_rule = new RequireDirectiveInFieldsArgumentRule();
private static readonly object s_rule = new RequireDirectiveInFieldArgumentRule();
private static readonly ImmutableArray<object> s_rules = [s_rule];
private readonly CompositionLog _log = new();

Expand Down Expand Up @@ -39,22 +39,22 @@ public void Examples_Invalid(string[] sdl, string[] errorMessages)
// assert
Assert.True(result.IsFailure);
Assert.Equal(errorMessages, _log.Select(e => e.Message).ToArray());
Assert.True(_log.All(e => e.Code == "REQUIRE_DIRECTIVE_IN_FIELDS_ARG"));
Assert.True(_log.All(e => e.Code == "REQUIRE_DIRECTIVE_IN_FIELD_ARG"));
Assert.True(_log.All(e => e.Severity == LogSeverity.Error));
}

public static TheoryData<string[]> ValidExamplesData()
{
return new TheoryData<string[]>
{
// In this valid usage, the @require directive’s "fields" argument references "name"
// In this valid usage, the @require directive’s "field" argument references "name"
// without any directive applications, avoiding the error.
{
[
"""
type User @key(fields: "id name") {
id: ID!
profile(name: String! @require(fields: "name")): Profile
profile(name: String! @require(field: "name")): Profile
}
type Profile {
Expand All @@ -72,15 +72,15 @@ public static TheoryData<string[], string[]> InvalidExamplesData()
return new TheoryData<string[], string[]>
{
// Because the @require selection ("name @lowercase") includes a directive application
// (@lowercase), this violates the rule and triggers a REQUIRE_DIRECTIVE_IN_FIELDS_ARG
// (@lowercase), this violates the rule and triggers a REQUIRE_DIRECTIVE_IN_FIELD_ARG
// error.
{
[
"""
type User @key(fields: "id name") {
id: ID!
name: String
profile(name: String! @require(fields: "name @lowercase")): Profile
profile(name: String! @require(field: "name @lowercase")): Profile
}
type Profile {
Expand All @@ -101,7 +101,7 @@ type Profile {
type User @key(fields: "id name") {
id: ID!
name: String
profile(name: String! @require(fields: "info { name @lowercase }")): Profile
profile(name: String! @require(field: "info { name @lowercase }")): Profile
}
type Profile {
Expand Down Expand Up @@ -129,7 +129,7 @@ type User @key(fields: "id name") {
id: ID!
name: String
profile(
name: String! @require(fields: "id @example name @example")
name: String! @require(field: "id @example name @example")
): Profile
}
"""
Expand Down
Loading

0 comments on commit 5c69f8a

Please sign in to comment.