Skip to content

Commit 166b888

Browse files
[Fusion] Support type-system directives (#7585)
1 parent 591c95f commit 166b888

34 files changed

+712
-37
lines changed

src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveLocationExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ public static class DirectiveLocationExtensions
3636
DirectiveLocation.InlineFragment,
3737
Language.DirectiveLocation.InlineFragment
3838
},
39+
{
40+
DirectiveLocation.VariableDefinition,
41+
Language.DirectiveLocation.VariableDefinition
42+
},
3943
{
4044
DirectiveLocation.Schema,
4145
Language.DirectiveLocation.Schema

src/HotChocolate/Fusion/src/Abstractions/FusionTypeBaseNames.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ internal static class FusionTypeBaseNames
4545
/// </summary>
4646
public const string FusionDirective = "fusion";
4747

48+
public const string InternalDirective = "internal";
49+
50+
public const string RenameDirective = "rename";
51+
52+
public const string RemoveDirective = "remove";
53+
54+
public const string LookupDirective = "lookup";
55+
56+
public const string RequireDirective = "require";
57+
4858
/// <summary>
4959
/// The base name of the GraphQL selection directive.
5060
/// </summary>

src/HotChocolate/Fusion/src/Abstractions/FusionTypeNames.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ private FusionTypeNames(
2222
string reEncodeIdDirective,
2323
string transportDirective,
2424
string fusionDirective,
25+
string internalDirective,
26+
string renameDirective,
27+
string removeDirective,
28+
string lookupDirective,
29+
string requireDirective,
2530
string selectionScalar,
2631
string selectionSetScalar,
2732
string typeNameScalar,
@@ -39,6 +44,11 @@ private FusionTypeNames(
3944
ReEncodeIdDirective = reEncodeIdDirective;
4045
TransportDirective = transportDirective;
4146
FusionDirective = fusionDirective;
47+
InternalDirective = internalDirective;
48+
RenameDirective = renameDirective;
49+
RemoveDirective = removeDirective;
50+
LookupDirective = lookupDirective;
51+
RequireDirective = requireDirective;
4252
SelectionScalar = selectionScalar;
4353
SelectionSetScalar = selectionSetScalar;
4454
TypeNameScalar = typeNameScalar;
@@ -56,6 +66,12 @@ private FusionTypeNames(
5666
_fusionDirectives.Add(transportDirective);
5767
_fusionDirectives.Add(fusionDirective);
5868

69+
_fusionDirectives.Add(internalDirective);
70+
_fusionDirectives.Add(renameDirective);
71+
_fusionDirectives.Add(removeDirective);
72+
_fusionDirectives.Add(lookupDirective);
73+
_fusionDirectives.Add(requireDirective);
74+
5975
_fusionTypes.Add(selectionScalar);
6076
_fusionTypes.Add(selectionSetScalar);
6177
_fusionTypes.Add(typeNameScalar);
@@ -110,6 +126,16 @@ private FusionTypeNames(
110126
/// </summary>
111127
public string FusionDirective { get; }
112128

129+
public string InternalDirective { get; }
130+
131+
public string RenameDirective { get; }
132+
133+
public string RemoveDirective { get; }
134+
135+
public string LookupDirective { get; }
136+
137+
public string RequireDirective { get; }
138+
113139
/// <summary>
114140
/// Gets the name of the GraphQL selection scalar.
115141
/// </summary>
@@ -199,6 +225,11 @@ public static FusionTypeNames Create(string? prefix = null, bool prefixSelf = fa
199225
prefixSelf
200226
? $"{prefix}_{FusionTypeBaseNames.FusionDirective}"
201227
: FusionTypeBaseNames.FusionDirective,
228+
FusionTypeBaseNames.InternalDirective,
229+
FusionTypeBaseNames.RenameDirective,
230+
FusionTypeBaseNames.RemoveDirective,
231+
FusionTypeBaseNames.LookupDirective,
232+
FusionTypeBaseNames.RequireDirective,
202233
$"{prefix}_{FusionTypeBaseNames.Selection}",
203234
$"{prefix}_{FusionTypeBaseNames.SelectionSet}",
204235
$"{prefix}_{FusionTypeBaseNames.TypeName}",
@@ -218,6 +249,11 @@ public static FusionTypeNames Create(string? prefix = null, bool prefixSelf = fa
218249
FusionTypeBaseNames.ReEncodeIdDirective,
219250
FusionTypeBaseNames.TransportDirective,
220251
FusionTypeBaseNames.FusionDirective,
252+
FusionTypeBaseNames.InternalDirective,
253+
FusionTypeBaseNames.RenameDirective,
254+
FusionTypeBaseNames.RemoveDirective,
255+
FusionTypeBaseNames.LookupDirective,
256+
FusionTypeBaseNames.RequireDirective,
221257
$"_{FusionTypeBaseNames.Selection}",
222258
$"_{FusionTypeBaseNames.SelectionSet}",
223259
$"_{FusionTypeBaseNames.TypeName}",

src/HotChocolate/Fusion/src/Composition/Extensions/ComplexTypeMergeExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public static OutputFieldDefinition CreateField(
1515
var target = new OutputFieldDefinition(source.Name);
1616
target.MergeDescriptionWith(source);
1717
target.MergeDeprecationWith(source);
18+
target.MergeDirectivesWith(source, context);
1819

1920
// Replace the type name of the field in the source with the corresponding type name
2021
// in the target schema.
@@ -33,6 +34,7 @@ public static OutputFieldDefinition CreateField(
3334
targetArgument.Type = sourceArgument.Type.ReplaceNameType(n => targetSchema.Types[n]);
3435

3536
targetArgument.MergeDeprecationWith(sourceArgument);
37+
targetArgument.MergeDirectivesWith(sourceArgument, context);
3638

3739
target.Arguments.Add(targetArgument);
3840
}
@@ -126,6 +128,8 @@ public static void MergeField(
126128
// If the target field is not deprecated and the source field is deprecated, copy over the
127129
target.MergeDeprecationWith(source);
128130

131+
target.MergeDirectivesWith(source, context);
132+
129133
foreach (var sourceArgument in source.Arguments)
130134
{
131135
var targetArgument = target.Arguments[sourceArgument.Name];
@@ -137,6 +141,8 @@ public static void MergeField(
137141
// If the target argument is not deprecated and the source argument is deprecated,
138142
targetArgument.MergeDeprecationWith(sourceArgument);
139143

144+
targetArgument.MergeDirectivesWith(sourceArgument, context);
145+
140146
// If the target argument does not have a default value and the source argument does,
141147
if (sourceArgument.DefaultValue is not null &&
142148
targetArgument.DefaultValue is null)

src/HotChocolate/Fusion/src/Composition/Extensions/InputObjectMergeExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public static InputFieldDefinition CreateField(
2020
var target = new InputFieldDefinition(source.Name, targetFieldType);
2121
target.MergeDescriptionWith(source);
2222
target.MergeDeprecationWith(source);
23+
target.MergeDirectivesWith(source, context);
2324
target.DefaultValue = source.DefaultValue;
2425
return target;
2526
}
@@ -55,5 +56,6 @@ public static void MergeField(
5556

5657
target.MergeDescriptionWith(source);
5758
target.MergeDeprecationWith(source);
59+
target.MergeDirectivesWith(source, context);
5860
}
5961
}

src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,49 @@ internal static void MergeDescriptionWith(this INamedTypeDefinition target, INam
165165
}
166166
}
167167

168+
internal static void MergeDirectivesWith(
169+
this IDirectivesProvider target,
170+
IDirectivesProvider source,
171+
CompositionContext context)
172+
{
173+
foreach (var directive in source.Directives)
174+
{
175+
if (context.FusionTypes.IsFusionDirective(directive.Name)
176+
|| BuiltIns.IsBuiltInDirective(directive.Name)
177+
// @tag is handled separately
178+
|| directive.Name == "tag")
179+
{
180+
continue;
181+
}
182+
183+
if (context.FusionGraph.DirectiveDefinitions.TryGetDirective(directive.Name, out var directiveDefinition)
184+
&& directiveDefinition.IsSpecDirective)
185+
{
186+
continue;
187+
}
188+
189+
if (!target.Directives.ContainsName(directive.Name))
190+
{
191+
target.Directives.Add(directive);
192+
}
193+
else
194+
{
195+
if (directiveDefinition is not null && directiveDefinition.IsRepeatable)
196+
{
197+
target.Directives.Add(directive);
198+
}
199+
}
200+
}
201+
}
202+
203+
internal static void MergeDescriptionWith(this DirectiveDefinition target, DirectiveDefinition source)
204+
{
205+
if (string.IsNullOrWhiteSpace(target.Description) && !string.IsNullOrWhiteSpace(source.Description))
206+
{
207+
target.Description = source.Description;
208+
}
209+
}
210+
168211
internal static void MergeDescriptionWith(this EnumValue target, EnumValue source)
169212
{
170213
if (string.IsNullOrWhiteSpace(target.Description) && !string.IsNullOrWhiteSpace(source.Description))

src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public FusionGraphComposer(
4242
[
4343
new InterfaceTypeMergeHandler(), new UnionTypeMergeHandler(),
4444
new InputObjectTypeMergeHandler(), new EnumTypeMergeHandler(),
45-
new ScalarTypeMergeHandler(),
45+
new ScalarTypeMergeHandler()
4646
],
4747
fusionTypePrefix,
4848
fusionTypeSelf,
@@ -68,7 +68,9 @@ internal FusionGraphComposer(
6868
.Use<MergeEntityMiddleware>()
6969
.Use<EntityFieldDependencyMiddleware>()
7070
.Use(() => new MergeTypeMiddleware(mergeHandlers))
71+
.Use<RemoveDirectivesWithoutLocationMiddleware>()
7172
.Use<MergeQueryAndMutationTypeMiddleware>()
73+
.Use<MergeSchemaDefinitionMiddleware>()
7274
.Use<MergeSubscriptionTypeMiddleware>()
7375
.Use<NodeMiddleware>()
7476
.Use<ApplyTagDirectiveMiddleware>()

src/HotChocolate/Fusion/src/Composition/FusionTypes.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Frozen;
12
using HotChocolate.Fusion.Composition.Properties;
23
using HotChocolate.Language;
34
using HotChocolate.Skimmed;
@@ -13,6 +14,7 @@ public sealed class FusionTypes
1314
{
1415
private readonly SchemaDefinition _fusionGraph;
1516
private readonly bool _prefixSelf;
17+
private readonly FusionTypeNames _fusionTypeNames;
1618

1719
public FusionTypes(SchemaDefinition fusionGraph, string? prefix = null, bool prefixSelf = false)
1820
{
@@ -22,6 +24,7 @@ public FusionTypes(SchemaDefinition fusionGraph, string? prefix = null, bool pre
2224
}
2325

2426
var names = FusionTypeNames.Create(prefix, prefixSelf);
27+
_fusionTypeNames = names;
2528
_fusionGraph = fusionGraph;
2629
_prefixSelf = prefixSelf;
2730

@@ -120,6 +123,8 @@ public FusionTypes(SchemaDefinition fusionGraph, string? prefix = null, bool pre
120123

121124
public DirectiveDefinition Fusion { get; }
122125

126+
public bool IsFusionDirective(string directiveName) => _fusionTypeNames.IsFusionDirective(directiveName);
127+
123128
private ScalarTypeDefinition RegisterScalarType(string name)
124129
{
125130
var scalarType = new ScalarTypeDefinition(name);

src/HotChocolate/Fusion/src/Composition/HotChocolate.Fusion.Composition.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
<ItemGroup>
1313
<InternalsVisibleTo Include="HotChocolate.Fusion.Tests" />
14+
<InternalsVisibleTo Include="HotChocolate.Fusion.Composition.Tests" />
1415
</ItemGroup>
1516

1617
<ItemGroup>

src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ public static LogEntry OutputFieldArgumentMismatch(
8686
coordinate: coordinate,
8787
member: field);
8888

89+
public static LogEntry DirectiveDefinitionArgumentMismatch(
90+
SchemaCoordinate coordinate,
91+
DirectiveDefinition directiveDefinition)
92+
=> new LogEntry(
93+
LogEntryHelper_DirectiveDefinitionArgumentMismatch,
94+
code: LogEntryCodes.DirectiveDefinitionArgumentMismatch,
95+
severity: LogSeverity.Error,
96+
coordinate: coordinate,
97+
member: directiveDefinition);
98+
8999
public static LogEntry OutputFieldArgumentSetMismatch(
90100
SchemaCoordinate coordinate,
91101
OutputFieldDefinition field,
@@ -193,5 +203,8 @@ static file class LogEntryCodes
193203
public const string FieldDependencyCannotBeResolved = "HF0008";
194204

195205
public const string TypeNotDeclared = "HF0009";
206+
196207
public const string RootNameMismatch = "HF0010";
208+
209+
public const string DirectiveDefinitionArgumentMismatch = "HF0011";
197210
}

0 commit comments

Comments
 (0)