diff --git a/src/HotChocolate/Fusion/src/Composition/Directives/DirectivesHelper.cs b/src/HotChocolate/Fusion/src/Composition/Directives/DirectivesHelper.cs
index 250e0c8f040..93e7e73f3e8 100644
--- a/src/HotChocolate/Fusion/src/Composition/Directives/DirectivesHelper.cs
+++ b/src/HotChocolate/Fusion/src/Composition/Directives/DirectivesHelper.cs
@@ -8,6 +8,7 @@ namespace HotChocolate.Fusion.Composition;
internal static class DirectivesHelper
{
public const string IsDirectiveName = "is";
+ public const string RequireDirectiveName = "require";
public const string RemoveDirectiveName = "remove";
public const string RenameDirectiveName = "rename";
public const string CoordinateArg = "coordinate";
@@ -38,4 +39,21 @@ public static IsDirective GetIsDirective(this IHasDirectives member)
throw new InvalidOperationException(
DirectivesHelper_GetIsDirective_NoFieldAndNoCoordinate);
}
+
+ public static bool ContainsRequireDirective(this IHasDirectives member)
+ => member.Directives.ContainsName(RequireDirectiveName);
+
+ public static RequireDirective GetRequireDirective(this IHasDirectives member)
+ {
+ var directive = member.Directives[RequireDirectiveName].First();
+ var arg = directive.Arguments.FirstOrDefault(t => t.Name.EqualsOrdinal(FieldArg));
+
+ if (arg is { Value: StringValueNode field })
+ {
+ return new RequireDirective(Utf8GraphQLParser.Syntax.ParseField(field.Value));
+ }
+
+ throw new InvalidOperationException(
+ DirectivesHelper_GetRequireDirective_NoFieldArg);
+ }
}
diff --git a/src/HotChocolate/Fusion/src/Composition/Directives/RequireDirective.cs b/src/HotChocolate/Fusion/src/Composition/Directives/RequireDirective.cs
new file mode 100644
index 00000000000..b2aa7288a4a
--- /dev/null
+++ b/src/HotChocolate/Fusion/src/Composition/Directives/RequireDirective.cs
@@ -0,0 +1,13 @@
+using HotChocolate.Language;
+
+namespace HotChocolate.Fusion.Composition;
+
+internal sealed class RequireDirective
+{
+ public RequireDirective(FieldNode field)
+ {
+ Field = field;
+ }
+
+ public FieldNode Field { get; }
+}
diff --git a/src/HotChocolate/Fusion/src/Composition/Entities/FieldDependency.cs b/src/HotChocolate/Fusion/src/Composition/Entities/FieldDependency.cs
index f0afb7cd4ea..71ce189d68a 100644
--- a/src/HotChocolate/Fusion/src/Composition/Entities/FieldDependency.cs
+++ b/src/HotChocolate/Fusion/src/Composition/Entities/FieldDependency.cs
@@ -1,7 +1,8 @@
-using HotChocolate.Skimmed;
-
namespace HotChocolate.Fusion.Composition;
+///
+/// Describes the dependency a field has to another subgraph.
+///
internal sealed class FieldDependency
{
public FieldDependency(int id, string subgraphName)
@@ -10,15 +11,19 @@ public FieldDependency(int id, string subgraphName)
SubgraphName = subgraphName;
}
+ ///
+ /// Gets the internal ID of this dependency,
+ ///
public int Id { get; }
+ ///
+ /// Gets the name of the subgraph in which it depends on other subgraph data.
+ /// There might be multiple resolver overloads that have different dependencies.
+ ///
public string SubgraphName { get; }
+ ///
+ /// The arguments that represent dependencies.
+ ///
public Dictionary Arguments { get; } = new();
}
-
-internal sealed record MemberReference(IsDirective Reference, InputField Argument)
-{
- public bool IsRequired => Argument.Type.Kind is TypeKind.NonNull;
-}
-
diff --git a/src/HotChocolate/Fusion/src/Composition/Entities/MemberReference.cs b/src/HotChocolate/Fusion/src/Composition/Entities/MemberReference.cs
new file mode 100644
index 00000000000..b2ed407cf2e
--- /dev/null
+++ b/src/HotChocolate/Fusion/src/Composition/Entities/MemberReference.cs
@@ -0,0 +1,6 @@
+using HotChocolate.Language;
+using HotChocolate.Skimmed;
+
+namespace HotChocolate.Fusion.Composition;
+
+internal sealed record MemberReference(InputField Argument, FieldNode Requirement);
diff --git a/src/HotChocolate/Fusion/src/Composition/Extensions/SchemaExtensions.cs b/src/HotChocolate/Fusion/src/Composition/Extensions/SchemaExtensions.cs
index 523dfa9c2ea..61c3adc1ca9 100644
--- a/src/HotChocolate/Fusion/src/Composition/Extensions/SchemaExtensions.cs
+++ b/src/HotChocolate/Fusion/src/Composition/Extensions/SchemaExtensions.cs
@@ -24,8 +24,8 @@ public static string CreateVariableName(
SchemaCoordinate coordinate)
=> $"{type.Name}_{coordinate.MemberName}";
- private static string CreateVariableName(
- ObjectType type,
+ public static string CreateVariableName(
+ this ObjectType type,
FieldNode field)
{
var context = new FieldVariableNameContext();
diff --git a/src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs b/src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs
index acfe7e117f9..4d2e2d593d6 100644
--- a/src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs
+++ b/src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs
@@ -102,16 +102,6 @@ public static LogEntry OutputFieldArgumentSetMismatch(
coordinate: coordinate,
member: field);
- public static LogEntry CoordinateNotAllowedForRequirements(
- SchemaCoordinate coordinate,
- Schema schema)
- => new LogEntry(
- LogEntryHelper_CoordinateNotAllowedForRequirements,
- severity: LogSeverity.Error,
- code: LogEntryCodes.CoordinateNotAllowedForRequirements,
- coordinate: coordinate,
- schema: schema);
-
public static LogEntry FieldDependencyCannotBeResolved(
SchemaCoordinate coordinate,
FieldNode dependency,
diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/Enrichers/RequireEnricher.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/Enrichers/RequireEnricher.cs
index 33d7ae57ad5..ee4b33544e1 100644
--- a/src/HotChocolate/Fusion/src/Composition/Pipeline/Enrichers/RequireEnricher.cs
+++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/Enrichers/RequireEnricher.cs
@@ -17,9 +17,11 @@ public ValueTask EnrichAsync(
foreach (var argument in field.Arguments)
{
- if (argument.ContainsIsDirective())
+ if (argument.ContainsRequireDirective())
{
- var memberRef = new MemberReference(argument.GetIsDirective(), argument);
+ var memberRef = new MemberReference(
+ argument,
+ argument.GetRequireDirective().Field);
dependency ??= new FieldDependency(++nextId, schema.Name);
dependency.Arguments.Add(argument.Name, memberRef);
}
diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/EntityFieldDependencyMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/EntityFieldDependencyMiddleware.cs
index f58f78456c8..cda1ce714fa 100644
--- a/src/HotChocolate/Fusion/src/Composition/Pipeline/EntityFieldDependencyMiddleware.cs
+++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/EntityFieldDependencyMiddleware.cs
@@ -99,22 +99,10 @@ private static void ResolveDependencies(
supportedBy.Add(subgraph.Name);
}
- if (memberRef.Reference.IsCoordinate)
- {
- context.Log.Write(
- CoordinateNotAllowedForRequirements(
- new SchemaCoordinate(
- entityType.Name,
- entityField.Name,
- argumentName),
- context.GetSubgraphSchema(dependency.SubgraphName)));
- continue;
- }
-
if (!CanResolve(
context,
entityType,
- memberRef.Reference.Field,
+ memberRef.Requirement,
supportedBy))
{
context.Log.Write(
@@ -123,12 +111,12 @@ private static void ResolveDependencies(
entityType.Name,
entityField.Name,
argumentName),
- memberRef.Reference.Field,
+ memberRef.Requirement,
context.GetSubgraphSchema(dependency.SubgraphName)));
continue;
}
- var argumentRef = entityType.CreateVariableName(memberRef.Reference);
+ var argumentRef = entityType.CreateVariableName(memberRef.Requirement);
argumentRefLookup.Add(argumentName, argumentRef);
arguments.Add(argumentRef, memberRef.Argument.Type.ToTypeNode());
@@ -138,7 +126,7 @@ private static void ResolveDependencies(
context.FusionTypes.CreateVariableDirective(
subgraph,
argumentRef,
- memberRef.Reference.Field));
+ memberRef.Requirement));
}
}
}
diff --git a/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.Designer.cs b/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.Designer.cs
index d3794f4cc2e..6a164b33b03 100644
--- a/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.Designer.cs
+++ b/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.Designer.cs
@@ -122,5 +122,11 @@ internal static string CannotFindCorrelatingSubgraphField {
return ResourceManager.GetString("CannotFindCorrelatingSubgraphField", resourceCulture);
}
}
+
+ internal static string DirectivesHelper_GetRequireDirective_NoFieldArg {
+ get {
+ return ResourceManager.GetString("DirectivesHelper_GetRequireDirective_NoFieldArg", resourceCulture);
+ }
+ }
}
}
diff --git a/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.resx b/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.resx
index d57d6cdc1ae..7032ac4f7fc 100644
--- a/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.resx
+++ b/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.resx
@@ -43,7 +43,7 @@
The number of arguments in an output field does not match the number of arguments in the same field in of a subgraph schema.
- The is argument must have a value for coordinate or field.
+ The is directive must have a value for coordinate or field.
An output field (`{0}`) that is being merged into another has a different set of arguments.
@@ -60,4 +60,7 @@ Source Arguments: {2}
Cannot find correlating subgraph field.
+
+ The require directive must have a value for the argument field.
+
diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs b/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs
index 8b8549a4161..85a0534595e 100644
--- a/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs
+++ b/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs
@@ -120,48 +120,4 @@ public async Task Accounts_And_Reviews_Products_AutoCompose_With_Node()
.FormatAsString(fusionConfig)
.MatchSnapshot(extension: ".graphql");
}
-
- [Fact]
- public async Task Accounts_And_Reviews_Products_Shipping_With_Is_Directive()
- {
- // arrange
- using var demoProject = await DemoProject.CreateAsync();
-
- var composer = new FusionGraphComposer(logFactory: _logFactory);
-
- var fusionConfig = await composer.ComposeAsync(
- new[]
- {
- demoProject.Accounts.ToConfiguration(),
- demoProject.Reviews.ToConfiguration(),
- demoProject.Products.ToConfiguration(ProductsExtensionSdl),
- demoProject.Shipping.ToConfiguration(ShippingExtensionSdl),
- });
-
- SchemaFormatter
- .FormatAsString(fusionConfig)
- .MatchSnapshot(extension: ".graphql");
- }
-
- [Fact]
- public async Task Accounts_And_Reviews_Products_Shipping_With_Map_Directive()
- {
- // arrange
- using var demoProject = await DemoProject.CreateAsync();
-
- var composer = new FusionGraphComposer(logFactory: _logFactory);
-
- var fusionConfig = await composer.ComposeAsync(
- new[]
- {
- demoProject.Accounts.ToConfiguration(),
- demoProject.Reviews.ToConfiguration(),
- demoProject.Products.ToConfiguration(ProductsExtensionSdl),
- demoProject.Shipping.ToConfiguration(ShippingExtensionSdl2),
- });
-
- SchemaFormatter
- .FormatAsString(fusionConfig)
- .MatchSnapshot(extension: ".graphql");
- }
}
diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/RequireTests.cs b/src/HotChocolate/Fusion/test/Composition.Tests/RequireTests.cs
new file mode 100644
index 00000000000..2cce2a41ab3
--- /dev/null
+++ b/src/HotChocolate/Fusion/test/Composition.Tests/RequireTests.cs
@@ -0,0 +1,56 @@
+using CookieCrumble;
+using HotChocolate.Fusion.Shared;
+using HotChocolate.Skimmed.Serialization;
+using Xunit.Abstractions;
+using static HotChocolate.Fusion.Shared.DemoProjectSchemaExtensions;
+
+namespace HotChocolate.Fusion.Composition;
+
+public class RequireTests
+{
+ private readonly Func _logFactory;
+
+ public RequireTests(ITestOutputHelper output)
+ {
+ _logFactory = () => new TestCompositionLog(output);
+ }
+
+ [Fact]
+ public async Task Require_Scalar_Arguments_No_Overloads()
+ {
+ // arrange
+ using var demoProject = await DemoProject.CreateAsync();
+
+ var composer = new FusionGraphComposer(logFactory: _logFactory);
+
+ var fusionConfig = await composer.ComposeAsync(
+ new[]
+ {
+ demoProject.Accounts.ToConfiguration(),
+ demoProject.Reviews.ToConfiguration(),
+ demoProject.Products.ToConfiguration(
+ """
+ extend type Query {
+ productById(id: ID! @is(field: "id")): Product
+ }
+ """),
+ demoProject.Shipping.ToConfiguration(
+ """
+ extend type Query {
+ productById(id: ID! @is(field: "id")): Product
+ }
+
+ extend type Product {
+ deliveryEstimate(
+ size: Int! @require(field: "dimension { size }"),
+ weight: Int! @require(field: "dimension { weight }"),
+ zip: String!): DeliveryEstimate!
+ }
+ """),
+ });
+
+ SchemaFormatter
+ .FormatAsString(fusionConfig)
+ .MatchSnapshot(extension: ".graphql");
+ }
+}
diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews_Products_Shipping_With_Is_Directive.graphql b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RequireTests.Require_Scalar_Arguments_No_Overloads.graphql
similarity index 100%
rename from src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews_Products_Shipping_With_Is_Directive.graphql
rename to src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RequireTests.Require_Scalar_Arguments_No_Overloads.graphql
diff --git a/src/HotChocolate/Fusion/test/Shared/DemoProjectSchemaExtensions.cs b/src/HotChocolate/Fusion/test/Shared/DemoProjectSchemaExtensions.cs
index 6baa77401b2..5ce00627ab6 100644
--- a/src/HotChocolate/Fusion/test/Shared/DemoProjectSchemaExtensions.cs
+++ b/src/HotChocolate/Fusion/test/Shared/DemoProjectSchemaExtensions.cs
@@ -50,8 +50,8 @@ extend type Query {
extend type Product {
deliveryEstimate(
- size: Int! @is(field: "dimension { size }"),
- weight: Int! @is(field: "dimension { weight }"),
+ size: Int! @require(field: "dimension { size }"),
+ weight: Int! @require(field: "dimension { weight }"),
zip: String!): DeliveryEstimate!
}
""";
diff --git a/src/HotChocolate/Fusion/test/Shared/Shipping/ProductDimension.cs b/src/HotChocolate/Fusion/test/Shared/Shipping/DeliveryEstimate.cs
similarity index 100%
rename from src/HotChocolate/Fusion/test/Shared/Shipping/ProductDimension.cs
rename to src/HotChocolate/Fusion/test/Shared/Shipping/DeliveryEstimate.cs