Skip to content

Commit 54adba1

Browse files
[Fusion] Add Fusion v1 compatibility mode (#8961)
1 parent 255e6ad commit 54adba1

File tree

13 files changed

+710
-129
lines changed

13 files changed

+710
-129
lines changed

src/HotChocolate/Fusion-vnext/src/Fusion.Aspire/CompositionHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ await archive.GetSourceSchemaNamesAsync(cancellationToken),
115115

116116
var metadata = new ArchiveMetadata
117117
{
118-
SupportedGatewayFormats = [new Version(2, 0, 0)],
118+
SupportedGatewayFormats = [WellKnownVersions.LatestGatewayFormatVersion],
119119
SourceSchemas = [.. newSourceSchemaNames]
120120
};
121121

@@ -133,7 +133,7 @@ await archive.SetSourceSchemaConfigurationAsync(
133133
await archive.SetGatewayConfigurationAsync(
134134
result.Value.ToString(),
135135
JsonDocument.Parse(buffer.WrittenMemory),
136-
new Version(2, 0, 0),
136+
WellKnownVersions.LatestGatewayFormatVersion,
137137
cancellationToken);
138138

139139
await archive.CommitAsync(cancellationToken);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using HotChocolate.Types;
2+
using HotChocolate.Types.Mutable;
3+
using ArgumentNames = HotChocolate.Fusion.WellKnownArgumentNames;
4+
using DirectiveNames = HotChocolate.Fusion.WellKnownDirectiveNames;
5+
6+
namespace HotChocolate.Fusion.Extensions;
7+
8+
internal static class MutableInputFieldDefinitionExtensions
9+
{
10+
extension(MutableInputFieldDefinition field)
11+
{
12+
public void ApplyIsDirective(string fieldName)
13+
{
14+
var isDirectiveExists =
15+
field.Directives.AsEnumerable().Any(d => d.Name == DirectiveNames.Is);
16+
17+
if (!isDirectiveExists)
18+
{
19+
field.Directives.Add(
20+
new Directive(
21+
FusionBuiltIns.SourceSchemaDirectives[DirectiveNames.Is],
22+
new ArgumentAssignment(ArgumentNames.Field, fieldName)));
23+
}
24+
}
25+
}
26+
}

src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Extensions/MutableOutputFieldDefinitionExtensions.cs

Lines changed: 129 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -12,152 +12,171 @@ namespace HotChocolate.Fusion.Extensions;
1212

1313
internal static class MutableOutputFieldDefinitionExtensions
1414
{
15-
public static bool ExistsInSchema(this MutableOutputFieldDefinition field, string schemaName)
15+
extension(MutableOutputFieldDefinition field)
1616
{
17-
return field.Directives.AsEnumerable().Any(
18-
d =>
19-
d.Name == DirectiveNames.FusionField
20-
&& (string)d.Arguments[ArgumentNames.Schema].Value! == schemaName);
21-
}
17+
public void ApplyLookupDirective()
18+
{
19+
var lookupDirectiveExists =
20+
field.Directives.AsEnumerable().Any(d => d.Name == DirectiveNames.Lookup);
2221

23-
public static string? GetFusionFieldProvides(
24-
this MutableOutputFieldDefinition field,
25-
string schemaName)
26-
{
27-
var fusionFieldDirective =
28-
field.Directives.AsEnumerable().FirstOrDefault(
22+
if (!lookupDirectiveExists)
23+
{
24+
field.Directives.Add(new Directive(FusionBuiltIns.SourceSchemaDirectives[DirectiveNames.Lookup]));
25+
}
26+
}
27+
28+
public void ApplyShareableDirective()
29+
{
30+
var shareableDirectiveExists =
31+
field.Directives.AsEnumerable().Any(d => d.Name == DirectiveNames.Shareable);
32+
33+
if (!shareableDirectiveExists)
34+
{
35+
field.Directives.Add(new Directive(FusionBuiltIns.SourceSchemaDirectives[DirectiveNames.Shareable]));
36+
}
37+
}
38+
39+
public bool ExistsInSchema(string schemaName)
40+
{
41+
return field.Directives.AsEnumerable().Any(
2942
d =>
3043
d.Name == DirectiveNames.FusionField
3144
&& (string)d.Arguments[ArgumentNames.Schema].Value! == schemaName);
45+
}
3246

33-
if (fusionFieldDirective?.Arguments.TryGetValue(ArgumentNames.Provides, out var provides) == true)
47+
public string? GetFusionFieldProvides(string schemaName)
3448
{
35-
return (string?)provides.Value;
36-
}
49+
var fusionFieldDirective =
50+
field.Directives.AsEnumerable().FirstOrDefault(
51+
d =>
52+
d.Name == DirectiveNames.FusionField
53+
&& (string)d.Arguments[ArgumentNames.Schema].Value! == schemaName);
3754

38-
return null;
39-
}
55+
if (fusionFieldDirective?.Arguments.TryGetValue(ArgumentNames.Provides, out var provides) == true)
56+
{
57+
return (string?)provides.Value;
58+
}
4059

41-
// productById(id: ID!) -> ["id"].
42-
// productByIdAndCategoryId(id: ID!, categoryId: Int) -> ["id", "categoryId"].
43-
// personByAddressId(id: ID! @is(field: "address.id")) -> ["address.id"].
44-
public static List<string> GetFusionLookupMap(this MutableOutputFieldDefinition field)
45-
{
46-
var items = new List<string>();
60+
return null;
61+
}
4762

48-
foreach (var argument in field.Arguments)
63+
public List<string> GetFusionLookupMap()
4964
{
50-
var @is = argument.GetIsFieldSelectionMap();
65+
var items = new List<string>();
5166

52-
items.Add(@is ?? argument.Name);
67+
foreach (var argument in field.Arguments)
68+
{
69+
var @is = argument.GetIsFieldSelectionMap();
70+
71+
items.Add(@is ?? argument.Name);
72+
}
73+
74+
return items;
5375
}
5476

55-
return items;
56-
}
77+
public SelectionSetNode? GetFusionRequiresRequirements(string schemaName)
78+
{
79+
var fusionRequiresDirective =
80+
field.Directives
81+
.AsEnumerable()
82+
.FirstOrDefault(
83+
d =>
84+
d.Name == DirectiveNames.FusionRequires
85+
&& (string)d.Arguments[ArgumentNames.Schema].Value! == schemaName);
86+
87+
if (fusionRequiresDirective is null)
88+
{
89+
return null;
90+
}
5791

58-
public static SelectionSetNode? GetFusionRequiresRequirements(
59-
this MutableOutputFieldDefinition field,
60-
string schemaName)
61-
{
62-
var fusionRequiresDirective =
63-
field.Directives
64-
.AsEnumerable()
65-
.FirstOrDefault(
66-
d =>
67-
d.Name == DirectiveNames.FusionRequires
68-
&& (string)d.Arguments[ArgumentNames.Schema].Value! == schemaName);
92+
var requirements = (string)fusionRequiresDirective.Arguments[ArgumentNames.Requirements].Value!;
6993

70-
if (fusionRequiresDirective is null)
94+
return ParseSelectionSet($"{{ {requirements} }}");
95+
}
96+
97+
/// <summary>
98+
/// Rewrites the provided value selections into a selection set string that can be used in a key
99+
/// directive.
100+
/// <example>["a", "b.c"] -> "a b { c }"</example>
101+
/// </summary>
102+
public string GetKeyFields(List<IValueSelectionNode> valueSelections,
103+
MutableSchemaDefinition schema)
71104
{
72-
return null;
105+
var valueSelectionToSelectionSetRewriter =
106+
new ValueSelectionToSelectionSetRewriter(schema, ignoreMissingTypeSystemMembers: true);
107+
var fieldType = field.Type.AsTypeDefinition();
108+
var selectionSets = valueSelections
109+
.Select(s => valueSelectionToSelectionSetRewriter.Rewrite(s, fieldType))
110+
.ToImmutableArray();
111+
var mergedSelectionSet = selectionSets.Length == 1
112+
? selectionSets[0]
113+
: new MergeSelectionSetRewriter(schema).Merge(selectionSets, fieldType);
114+
115+
return mergedSelectionSet.ToString(indented: false).AsSpan()[2..^2].ToString();
73116
}
74117

75-
var requirements = (string)fusionRequiresDirective.Arguments[ArgumentNames.Requirements].Value!;
118+
public string? GetOverrideFrom()
119+
{
120+
var overrideDirective =
121+
field.Directives.AsEnumerable().SingleOrDefault(d => d.Name == DirectiveNames.Override);
76122

77-
return ParseSelectionSet($"{{ {requirements} }}");
78-
}
123+
return (string?)overrideDirective?.Arguments[ArgumentNames.From].Value;
124+
}
79125

80-
/// <summary>
81-
/// Rewrites the provided value selections into a selection set string that can be used in a key
82-
/// directive.
83-
/// <example>["a", "b.c"] -> "a b { c }"</example>
84-
/// </summary>
85-
public static string GetKeyFields(
86-
this MutableOutputFieldDefinition field,
87-
List<IValueSelectionNode> valueSelections,
88-
MutableSchemaDefinition schema)
89-
{
90-
var valueSelectionToSelectionSetRewriter =
91-
new ValueSelectionToSelectionSetRewriter(schema, ignoreMissingTypeSystemMembers: true);
92-
var fieldType = field.Type.AsTypeDefinition();
93-
var selectionSets = valueSelections
94-
.Select(s => valueSelectionToSelectionSetRewriter.Rewrite(s, fieldType))
95-
.ToImmutableArray();
96-
var mergedSelectionSet = selectionSets.Length == 1
97-
? selectionSets[0]
98-
: new MergeSelectionSetRewriter(schema).Merge(selectionSets, fieldType);
99-
100-
return mergedSelectionSet.ToString(indented: false).AsSpan()[2..^2].ToString();
101-
}
126+
/// <summary>
127+
/// Gets all combinations of value selections after splitting choice nodes.
128+
/// <example>["a", "{ b } | { c }"] -> [["a", "b"], ["a", "c"]]</example>
129+
/// </summary>
130+
public List<List<IValueSelectionNode>> GetValueSelectionGroups()
131+
{
132+
var lookupMap = field.GetFusionLookupMap();
133+
var valueSelections = lookupMap.Select(a => new FieldSelectionMapParser(a).Parse());
102134

103-
public static string? GetOverrideFrom(this MutableOutputFieldDefinition field)
104-
{
105-
var overrideDirective =
106-
field.Directives.AsEnumerable().SingleOrDefault(d => d.Name == DirectiveNames.Override);
135+
var valueSelectionGroups = valueSelections.Select(v =>
136+
{
137+
List<IValueSelectionNode> items = [];
107138

108-
return (string?)overrideDirective?.Arguments[ArgumentNames.From].Value;
109-
}
139+
if (v is ChoiceValueSelectionNode choiceValueSelectionNode)
140+
{
141+
items.AddRange(choiceValueSelectionNode.Branches.ToArray());
142+
}
143+
else
144+
{
145+
items.Add(v);
146+
}
110147

111-
/// <summary>
112-
/// Gets all combinations of value selections after splitting choice nodes.
113-
/// <example>["a", "{ b } | { c }"] -> [["a", "b"], ["a", "c"]]</example>
114-
/// </summary>
115-
public static List<List<IValueSelectionNode>> GetValueSelectionGroups(
116-
this MutableOutputFieldDefinition field)
117-
{
118-
var lookupMap = field.GetFusionLookupMap();
119-
var valueSelections = lookupMap.Select(a => new FieldSelectionMapParser(a).Parse());
148+
return items.ToArray();
149+
});
150+
151+
return GetAllCombinations(valueSelectionGroups.ToArray());
152+
}
120153

121-
var valueSelectionGroups = valueSelections.Select(v =>
154+
public bool IsPartial(string schemaName)
122155
{
123-
List<IValueSelectionNode> items = [];
156+
var fusionFieldDirective =
157+
field.Directives.AsEnumerable().FirstOrDefault(
158+
d =>
159+
d.Name == DirectiveNames.FusionField
160+
&& (string)d.Arguments[ArgumentNames.Schema].Value! == schemaName);
124161

125-
if (v is ChoiceValueSelectionNode choiceValueSelectionNode)
162+
if (fusionFieldDirective is null)
126163
{
127-
items.AddRange(choiceValueSelectionNode.Branches.ToArray());
164+
return false;
128165
}
129-
else
166+
167+
if (fusionFieldDirective.Arguments.TryGetValue(ArgumentNames.Partial, out var partial))
130168
{
131-
items.Add(v);
169+
return (bool)partial.Value!;
132170
}
133171

134-
return items.ToArray();
135-
});
136-
137-
return GetAllCombinations(valueSelectionGroups.ToArray());
138-
}
139-
140-
public static bool IsPartial(this MutableOutputFieldDefinition field, string schemaName)
141-
{
142-
var fusionFieldDirective =
143-
field.Directives.AsEnumerable().FirstOrDefault(
144-
d =>
145-
d.Name == DirectiveNames.FusionField
146-
&& (string)d.Arguments[ArgumentNames.Schema].Value! == schemaName);
147-
148-
if (fusionFieldDirective is null)
149-
{
150172
return false;
151173
}
152-
153-
if (fusionFieldDirective.Arguments.TryGetValue(ArgumentNames.Partial, out var partial))
154-
{
155-
return (bool)partial.Value!;
156-
}
157-
158-
return false;
159174
}
160175

176+
// productById(id: ID!) -> ["id"].
177+
// productByIdAndCategoryId(id: ID!, categoryId: Int) -> ["id", "categoryId"].
178+
// personByAddressId(id: ID! @is(field: "address.id")) -> ["address.id"].
179+
161180
private static List<List<T>> GetAllCombinations<T>(params T[][] arrays)
162181
{
163182
if (arrays.Length == 0)

src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Options/SourceSchemaPreprocessorOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,9 @@ public sealed class SourceSchemaPreprocessorOptions
1515
/// implement.
1616
/// </summary>
1717
public bool InheritInterfaceKeys { get; set; } = true;
18+
19+
/// <summary>
20+
/// The source schema version.
21+
/// </summary>
22+
public Version Version { get; set; } = WellKnownVersions.LatestSourceSchemaVersion;
1823
}

src/HotChocolate/Fusion-vnext/src/Fusion.Composition/SchemaComposer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public CompositionResult<MutableSchemaDefinition> Compose()
5656
preprocessorOptions = new SourceSchemaPreprocessorOptions();
5757
}
5858

59-
return new SourceSchemaPreprocessor(schema, preprocessorOptions).Process();
59+
return new SourceSchemaPreprocessor(schema, schemas, preprocessorOptions).Process();
6060
}).Combine();
6161

6262
if (preprocessResult.IsFailure)

0 commit comments

Comments
 (0)