Skip to content

Commit

Permalink
[Fusion] Added pre-merge validation rule "ProvidesFieldsHasArgumentsR…
Browse files Browse the repository at this point in the history
…ule" (#7884)
  • Loading branch information
glen-84 authored Jan 1, 2025
1 parent 1a6811b commit 6dcaa60
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static class LogEntryCodes
public const string KeyInvalidSyntax = "KEY_INVALID_SYNTAX";
public const string OutputFieldTypesNotMergeable = "OUTPUT_FIELD_TYPES_NOT_MERGEABLE";
public const string ProvidesDirectiveInFieldsArg = "PROVIDES_DIRECTIVE_IN_FIELDS_ARG";
public const string ProvidesFieldsHasArgs = "PROVIDES_FIELDS_HAS_ARGS";
public const string RootMutationUsed = "ROOT_MUTATION_USED";
public const string RootQueryUsed = "ROOT_QUERY_USED";
public const string RootSubscriptionUsed = "ROOT_SUBSCRIPTION_USED";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,29 @@ public static LogEntry ProvidesDirectiveInFieldsArgument(
schema);
}

public static LogEntry ProvidesFieldsHasArguments(
string providedFieldName,
string providedTypeName,
Directive providesDirective,
string fieldName,
string typeName,
SchemaDefinition schema)
{
var coordinate = new SchemaCoordinate(typeName, fieldName);

return new LogEntry(
string.Format(
LogEntryHelper_ProvidesFieldsHasArguments,
coordinate,
schema.Name,
new SchemaCoordinate(providedTypeName, providedFieldName)),
LogEntryCodes.ProvidesFieldsHasArgs,
LogSeverity.Error,
coordinate,
providesDirective,
schema);
}

public static LogEntry RootMutationUsed(SchemaDefinition schema)
{
return new LogEntry(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ internal record OutputFieldGroupEvent(
ImmutableArray<OutputFieldInfo> FieldGroup,
string TypeName) : IEvent;

internal record ProvidesFieldEvent(
OutputFieldDefinition ProvidedField,
ComplexTypeDefinition ProvidedType,
Directive ProvidesDirective,
OutputFieldDefinition Field,
ComplexTypeDefinition Type,
SchemaDefinition Schema) : IEvent;

internal record ProvidesFieldNodeEvent(
FieldNode FieldNode,
ImmutableArray<string> FieldNamePath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ private void PublishProvidesEvents(
type,
providesDirective,
[],
field.Type,
schema,
context);
}
Expand All @@ -278,9 +279,12 @@ private void PublishProvidesFieldEvents(
ComplexTypeDefinition type,
Directive providesDirective,
List<string> fieldNamePath,
ITypeDefinition? parentType,
SchemaDefinition schema,
CompositionContext context)
{
ComplexTypeDefinition? nextParentType = null;

foreach (var selection in selectionSet.Selections)
{
if (selection is FieldNode fieldNode)
Expand All @@ -297,6 +301,34 @@ private void PublishProvidesFieldEvents(
schema),
context);

if (parentType?.NullableType() is ComplexTypeDefinition providedType)
{
if (
providedType.Fields.TryGetField(
fieldNode.Name.Value,
out var providedField))
{
PublishEvent(
new ProvidesFieldEvent(
providedField,
providedType,
providesDirective,
field,
type,
schema),
context);

if (providedField.Type.NullableType() is ComplexTypeDefinition fieldType)
{
nextParentType = fieldType;
}
}
else
{
nextParentType = null;
}
}

if (fieldNode.SelectionSet is not null)
{
PublishProvidesFieldEvents(
Expand All @@ -305,6 +337,7 @@ private void PublishProvidesFieldEvents(
type,
providesDirective,
fieldNamePath,
nextParentType,
schema,
context);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using HotChocolate.Fusion.Events;
using static HotChocolate.Fusion.Logging.LogEntryHelper;

namespace HotChocolate.Fusion.PreMergeValidation.Rules;

/// <summary>
/// The <c>@provides</c> directive specifies fields that a resolver provides for the parent type.
/// The <c>fields</c> argument must reference fields that do not have arguments, as fields with
/// arguments introduce variability that is incompatible with the consistent behavior expected of
/// <c>@provides</c>.
/// </summary>
/// <seealso href="https://graphql.github.io/composite-schemas-spec/draft/#sec-Provides-Fields-Has-Arguments">
/// Specification
/// </seealso>
internal sealed class ProvidesFieldsHasArgumentsRule : IEventHandler<ProvidesFieldEvent>
{
public void Handle(ProvidesFieldEvent @event, CompositionContext context)
{
var (providedField, providedType, providesDirective, field, type, schema) = @event;

if (providedField.Arguments.Count != 0)
{
context.Log.Write(
ProvidesFieldsHasArguments(
providedField.Name,
providedType.Name,
providesDirective,
field.Name,
type.Name,
schema));
}
}
}
Loading

0 comments on commit 6dcaa60

Please sign in to comment.