Skip to content

Commit

Permalink
[Fusion] Added pre-merge validation rule "KeyInvalidSyntaxRule"
Browse files Browse the repository at this point in the history
  • Loading branch information
glen-84 committed Dec 30, 2024
1 parent 80c45d1 commit 9e692d2
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public static class LogEntryCodes
public const string KeyFieldsHasArgs = "KEY_FIELDS_HAS_ARGS";
public const string KeyFieldsSelectInvalidType = "KEY_FIELDS_SELECT_INVALID_TYPE";
public const string KeyInvalidFields = "KEY_INVALID_FIELDS";
public const string KeyInvalidSyntax = "KEY_INVALID_SYNTAX";
public const string OutputFieldTypesNotMergeable = "OUTPUT_FIELD_TYPES_NOT_MERGEABLE";
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 @@ -224,6 +224,23 @@ public static LogEntry KeyInvalidFields(
schema);
}

public static LogEntry KeyInvalidSyntax(
string entityTypeName,
Directive keyDirective,
SchemaDefinition schema)
{
return new LogEntry(
string.Format(
LogEntryHelper_KeyInvalidSyntax,
entityTypeName,
schema.Name),
LogEntryCodes.KeyInvalidSyntax,
LogSeverity.Error,
new SchemaCoordinate(entityTypeName),
keyDirective,
schema);
}

public static LogEntry OutputFieldTypesNotMergeable(
OutputFieldDefinition field,
string typeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ internal record KeyFieldsInvalidReferenceEvent(
ComplexTypeDefinition Type,
SchemaDefinition Schema) : IEvent;

internal record KeyFieldsInvalidSyntaxEvent(
ComplexTypeDefinition EntityType,
Directive KeyDirective,
SchemaDefinition Schema) : IEvent;

internal record OutputFieldEvent(
OutputFieldDefinition Field,
INamedTypeDefinition Type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ private void PublishEntityEvents(
}
catch (SyntaxException)
{
// Ignore.
PublishEvent(
new KeyFieldsInvalidSyntaxEvent(entityType, keyDirective, schema),
context);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using HotChocolate.Fusion.Events;
using static HotChocolate.Fusion.Logging.LogEntryHelper;

namespace HotChocolate.Fusion.PreMergeValidation.Rules;

/// <summary>
/// Each <c>@key</c> directive must specify the fields that uniquely identify an entity using a
/// valid GraphQL selection set in its <c>fields</c> argument. If the <c>fields</c> argument string
/// is syntactically incorrect—missing closing braces, containing invalid tokens, or otherwise
/// malformed—it cannot be composed into a valid schema and triggers the <c>KEY_INVALID_SYNTAX</c>
/// error.
/// </summary>
/// <seealso href="https://graphql.github.io/composite-schemas-spec/draft/#sec-Key-Invalid-Syntax">
/// Specification
/// </seealso>
internal sealed class KeyInvalidSyntaxRule : IEventHandler<KeyFieldsInvalidSyntaxEvent>
{
public void Handle(KeyFieldsInvalidSyntaxEvent @event, CompositionContext context)
{
var (entityType, keyDirective, schema) = @event;

context.Log.Write(KeyInvalidSyntax(entityType.Name, keyDirective, schema));
}
}

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 @@ -57,6 +57,9 @@
<data name="LogEntryHelper_KeyInvalidFields" xml:space="preserve">
<value>A @key directive on type '{0}' in schema '{1}' references field '{2}', which does not exist.</value>
</data>
<data name="LogEntryHelper_KeyInvalidSyntax" xml:space="preserve">
<value>A @key directive on type '{0}' in schema '{1}' contains invalid syntax in the 'fields' argument.</value>
</data>
<data name="LogEntryHelper_OutputFieldTypesNotMergeable" xml:space="preserve">
<value>Field '{0}' has a different type shape in schema '{1}' than it does in schema '{2}'.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ private CompositionResult<SchemaDefinition> MergeSchemaDefinitions(CompositionCo
new KeyFieldsHasArgumentsRule(),
new KeyFieldsSelectInvalidTypeRule(),
new KeyInvalidFieldsRule(),
new KeyInvalidSyntaxRule(),
new OutputFieldTypesMergeableRule(),
new RootMutationUsedRule(),
new RootQueryUsedRule(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using HotChocolate.Fusion.Logging;
using HotChocolate.Fusion.PreMergeValidation;
using HotChocolate.Fusion.PreMergeValidation.Rules;

namespace HotChocolate.Composition.PreMergeValidation.Rules;

public sealed class KeyInvalidSyntaxRuleTests : CompositionTestBase
{
private readonly PreMergeValidator _preMergeValidator = new([new KeyInvalidSyntaxRule()]);

[Theory]
[MemberData(nameof(ValidExamplesData))]
public void Examples_Valid(string[] sdl)
{
// arrange
var context = CreateCompositionContext(sdl);

// act
var result = _preMergeValidator.Validate(context);

// assert
Assert.True(result.IsSuccess);
Assert.True(context.Log.IsEmpty);
}

[Theory]
[MemberData(nameof(InvalidExamplesData))]
public void Examples_Invalid(string[] sdl, string[] errorMessages)
{
// arrange
var context = CreateCompositionContext(sdl);

// act
var result = _preMergeValidator.Validate(context);

// assert
Assert.True(result.IsFailure);
Assert.Equal(errorMessages, context.Log.Select(e => e.Message).ToArray());
Assert.True(context.Log.All(e => e.Code == "KEY_INVALID_SYNTAX"));
Assert.True(context.Log.All(e => e.Severity == LogSeverity.Error));
}

public static TheoryData<string[]> ValidExamplesData()
{
return new TheoryData<string[]>
{
// In this example, the "fields" argument is a correctly formed selection set:
// "sku featuredItem { id }" is properly balanced and contains no syntax errors.
{
[
"""
type Product @key(fields: "sku featuredItem { id }") {
sku: String!
featuredItem: Node!
}
interface Node {
id: ID!
}
"""
]
}
};
}

public static TheoryData<string[], string[]> InvalidExamplesData()
{
return new TheoryData<string[], string[]>
{
// Here, the selection set "featuredItem { id" is missing the closing brace "}". It is
// thus invalid syntax, causing a "KEY_INVALID_SYNTAX" error.
{
[
"""
type Product @key(fields: "featuredItem { id") {
featuredItem: Node!
sku: String!
}
interface Node {
id: ID!
}
"""
],
[
"A @key directive on type 'Product' in schema 'A' contains invalid syntax in " +
"the 'fields' argument."
]
}
};
}
}

0 comments on commit 9e692d2

Please sign in to comment.