Skip to content

Commit 1529d93

Browse files
authored
[Fusion] Applied inferred key directives (#8717)
1 parent 7708d98 commit 1529d93

16 files changed

+286
-415
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Collections.Immutable;
2+
using HotChocolate.Fusion.Results;
3+
4+
namespace HotChocolate.Fusion.Extensions;
5+
6+
internal static class EnumerableCompositionResultExtensions
7+
{
8+
public static CompositionResult Combine(this IEnumerable<CompositionResult> results)
9+
{
10+
var failedResults = results.Where(r => r.IsFailure).ToImmutableArray();
11+
12+
if (failedResults.Length == 0)
13+
{
14+
return CompositionResult.Success();
15+
}
16+
17+
return failedResults.SelectMany(r => r.Errors).ToImmutableArray();
18+
}
19+
}
Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1+
using HotChocolate.Language;
12
using HotChocolate.Types;
23
using HotChocolate.Types.Mutable;
34
using ArgumentNames = HotChocolate.Fusion.WellKnownArgumentNames;
5+
using DirectiveNames = HotChocolate.Fusion.WellKnownDirectiveNames;
46

57
namespace HotChocolate.Fusion.Extensions;
68

79
internal static class MutableComplexTypeDefinitionExtensions
810
{
9-
public static void ApplyKeyDirective(this MutableComplexTypeDefinition type, string[] fields)
11+
public static void ApplyKeyDirective(this MutableComplexTypeDefinition type, string keyFields)
1012
{
11-
var fieldsArgument = new ArgumentAssignment(ArgumentNames.Fields, string.Join(" ", fields));
12-
var keyDirective =
13-
new Directive(new MutableDirectiveDefinition(WellKnownDirectiveNames.Key), fieldsArgument);
13+
var keyDirectiveExists =
14+
type.Directives.AsEnumerable().Any(
15+
d =>
16+
d.Name == DirectiveNames.Key
17+
&& ((StringValueNode)d.Arguments[ArgumentNames.Fields]).Value.Equals(keyFields));
1418

15-
type.Directives.Add(keyDirective);
19+
if (!keyDirectiveExists)
20+
{
21+
type.Directives.Add(
22+
new Directive(
23+
FusionBuiltIns.SourceSchemaDirectives[DirectiveNames.Key],
24+
new ArgumentAssignment(ArgumentNames.Fields, keyFields)));
25+
}
1626
}
1727
}

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ namespace HotChocolate.Fusion.Extensions;
55

66
internal static class MutableObjectTypeDefinitionExtensions
77
{
8-
public static void ApplyShareableDirective(this MutableObjectTypeDefinition type)
9-
{
10-
type.Directives.Add(new Directive(new MutableDirectiveDefinition(DirectiveNames.Shareable)));
11-
}
12-
138
public static bool HasInternalDirective(this MutableObjectTypeDefinition type)
149
{
1510
return type.Directives.ContainsName(DirectiveNames.Internal);

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

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
using System.Collections.Immutable;
2+
using HotChocolate.Fusion.Definitions;
3+
using HotChocolate.Fusion.Language;
4+
using HotChocolate.Fusion.Rewriters;
15
using HotChocolate.Language;
6+
using HotChocolate.Types;
27
using HotChocolate.Types.Mutable;
38
using static HotChocolate.Language.Utf8GraphQLParser.Syntax;
49
using ArgumentNames = HotChocolate.Fusion.WellKnownArgumentNames;
@@ -8,9 +13,14 @@ namespace HotChocolate.Fusion.Extensions;
813

914
internal static class MutableOutputFieldDefinitionExtensions
1015
{
11-
public static void ApplyLookupDirective(this MutableOutputFieldDefinition field)
16+
public static void ApplyShareableDirective(this MutableOutputFieldDefinition field)
1217
{
13-
field.Directives.Add(new Directive(new MutableDirectiveDefinition(DirectiveNames.Lookup)));
18+
if (field.Directives.ContainsName(DirectiveNames.Shareable))
19+
{
20+
return;
21+
}
22+
23+
field.Directives.Add(new Directive(new ShareableMutableDirectiveDefinition()));
1424
}
1525

1626
public static bool ExistsInSchema(this MutableOutputFieldDefinition field, string schemaName)
@@ -39,6 +49,23 @@ public static bool ExistsInSchema(this MutableOutputFieldDefinition field, strin
3949
return null;
4050
}
4151

52+
// productById(id: ID!) -> ["id"].
53+
// productByIdAndCategoryId(id: ID!, categoryId: Int) -> ["id", "categoryId"].
54+
// personByAddressId(id: ID! @is(field: "address.id")) -> ["address.id"].
55+
public static List<string> GetFusionLookupMap(this MutableOutputFieldDefinition field)
56+
{
57+
var items = new List<string>();
58+
59+
foreach (var argument in field.Arguments)
60+
{
61+
var @is = argument.GetIsFieldSelectionMap();
62+
63+
items.Add(@is ?? argument.Name);
64+
}
65+
66+
return items;
67+
}
68+
4269
public static SelectionSetNode? GetFusionRequiresRequirements(
4370
this MutableOutputFieldDefinition field,
4471
string schemaName)
@@ -61,6 +88,24 @@ public static bool ExistsInSchema(this MutableOutputFieldDefinition field, strin
6188
return ParseSelectionSet($"{{ {requirements} }}");
6289
}
6390

91+
public static string GetKeyFields(
92+
this MutableOutputFieldDefinition field,
93+
List<string> lookupMap,
94+
MutableSchemaDefinition schema)
95+
{
96+
var selectedValues = lookupMap.Select(a => new FieldSelectionMapParser(a).Parse());
97+
var valueSelectionToSelectionSetRewriter = new ValueSelectionToSelectionSetRewriter(schema);
98+
var fieldType = field.Type.AsTypeDefinition();
99+
var selectionSets = selectedValues
100+
.Select(s => valueSelectionToSelectionSetRewriter.Rewrite(s, fieldType))
101+
.ToImmutableArray();
102+
var mergedSelectionSet = selectionSets.Length == 1
103+
? selectionSets[0]
104+
: new MergeSelectionSetRewriter(schema).Merge(selectionSets, fieldType);
105+
106+
return mergedSelectionSet.ToString(indented: false).AsSpan()[2..^2].ToString();
107+
}
108+
64109
public static bool HasInternalDirective(this MutableOutputFieldDefinition type)
65110
{
66111
return type.Directives.ContainsName(DirectiveNames.Internal);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ internal static class MutableSchemaDefinitionExtensions
99
{
1010
public static void AddBuiltInFusionTypes(this MutableSchemaDefinition schema)
1111
{
12-
foreach (var builtInScalar in FusionBuiltIns.SourceSchemaScalars)
12+
foreach (var builtInScalar in FusionBuiltIns.SourceSchemaScalars.Values)
1313
{
1414
schema.Types.Add(builtInScalar);
1515
}
1616
}
1717

1818
public static void AddBuiltInFusionDirectives(this MutableSchemaDefinition schema)
1919
{
20-
foreach (var builtInDirective in FusionBuiltIns.SourceSchemaDirectives)
20+
foreach (var builtInDirective in FusionBuiltIns.SourceSchemaDirectives.Values)
2121
{
2222
schema.DirectiveDefinitions.Add(builtInDirective);
2323
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal static class FusionBuiltIns
1515

1616
private static readonly MutableScalarTypeDefinition s_stringType = BuiltIns.String.Create();
1717

18-
public static FrozenSet<MutableDirectiveDefinition> SourceSchemaDirectives { get; } =
18+
public static FrozenDictionary<string, MutableDirectiveDefinition> SourceSchemaDirectives { get; } =
1919
new HashSet<MutableDirectiveDefinition>(
2020
[
2121
new ExternalMutableDirectiveDefinition(),
@@ -28,11 +28,11 @@ internal static class FusionBuiltIns
2828
new ProvidesMutableDirectiveDefinition(s_fieldSelectionSetType),
2929
new RequireMutableDirectiveDefinition(s_fieldSelectionMapType),
3030
new ShareableMutableDirectiveDefinition()
31-
]).ToFrozenSet();
31+
]).ToFrozenDictionary(d => d.Name);
3232

33-
public static FrozenSet<MutableScalarTypeDefinition> SourceSchemaScalars { get; } =
33+
public static FrozenDictionary<string, MutableScalarTypeDefinition> SourceSchemaScalars { get; } =
3434
new HashSet<MutableScalarTypeDefinition>([s_fieldSelectionMapType, s_fieldSelectionSetType])
35-
.ToFrozenSet();
35+
.ToFrozenDictionary(s => s.Name);
3636

3737
public static bool IsBuiltInSourceSchemaScalar(string typeName)
3838
{

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ namespace HotChocolate.Fusion.Options;
33
public sealed class SchemaComposerOptions
44
{
55
public required bool EnableGlobalObjectIdentification { get; init; }
6+
7+
public SourceSchemaPreprocessorOptions Preprocessor { get; init; } = new();
68
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
namespace HotChocolate.Fusion.Options;
22

3-
internal sealed class SourceSchemaPreprocessorOptions
3+
public sealed class SourceSchemaPreprocessorOptions
44
{
5-
public bool ApplyShareableToAllTypes { get; set; } = true;
5+
/// <summary>
6+
/// Applies inferred key directives to types that are returned by lookup fields.
7+
/// </summary>
8+
public bool ApplyInferredKeyDirectives { get; set; } = true;
69
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Immutable;
2+
using HotChocolate.Fusion.Extensions;
23
using HotChocolate.Fusion.Logging.Contracts;
34
using HotChocolate.Fusion.Options;
45
using HotChocolate.Fusion.PostMergeValidationRules;
@@ -40,6 +41,16 @@ public CompositionResult<MutableSchemaDefinition> Compose()
4041
return parseErrors;
4142
}
4243

44+
// Preprocess Source Schemas
45+
var preprocessorOptions = _schemaComposerOptions.Preprocessor;
46+
var preprocessResult =
47+
schemas.Select(schema => new SourceSchemaPreprocessor(schema, preprocessorOptions).Process()).Combine();
48+
49+
if (preprocessResult.IsFailure)
50+
{
51+
return preprocessResult;
52+
}
53+
4354
// Validate Source Schemas
4455
var validationResult =
4556
new SourceSchemaValidator(schemas, s_sourceSchemaRules, _log).Validate();

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

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,19 +1011,8 @@ private void AddFusionLookupDirectives(
10111011
foreach (var (sourceField, sourcePath, sourceSchema) in lookupFieldGroup)
10121012
{
10131013
var schemaArgument = new EnumValueNode(_schemaConstantNames[sourceSchema]);
1014-
var lookupMap = GetFusionLookupMap(sourceField);
1015-
var selectedValues = lookupMap.Select(a => new FieldSelectionMapParser(a).Parse());
1016-
var selectedValueToSelectionSetRewriter =
1017-
GetSelectedValueToSelectionSetRewriter(sourceSchema);
1018-
var selectionSets = selectedValues
1019-
.Select(
1020-
s => selectedValueToSelectionSetRewriter.Rewrite(s, type))
1021-
.ToImmutableArray();
1022-
var mergedSelectionSet = selectionSets.Length == 1
1023-
? selectionSets[0]
1024-
: GetMergeSelectionSetRewriter(sourceSchema).Merge(selectionSets, type);
1025-
var keyArgument =
1026-
mergedSelectionSet.ToString(indented: false).AsSpan()[2..^2].ToString();
1014+
var lookupMap = sourceField.GetFusionLookupMap();
1015+
var keyArgument = sourceField.GetKeyFields(lookupMap, sourceSchema);
10271016

10281017
var fieldArgument =
10291018
s_fieldDefinitionRewriter
@@ -1050,23 +1039,6 @@ private void AddFusionLookupDirectives(
10501039
}
10511040
}
10521041

1053-
// productById(id: ID!) -> ["id"].
1054-
// productByIdAndCategoryId(id: ID!, categoryId: Int) -> ["id", "categoryId"].
1055-
// personByAddressId(id: ID! @is(field: "address.id")) -> ["address.id"].
1056-
private static List<string> GetFusionLookupMap(MutableOutputFieldDefinition field)
1057-
{
1058-
var items = new List<string>();
1059-
1060-
foreach (var argument in field.Arguments)
1061-
{
1062-
var @is = argument.GetIsFieldSelectionMap();
1063-
1064-
items.Add(@is ?? argument.Name);
1065-
}
1066-
1067-
return items;
1068-
}
1069-
10701042
private void AddFusionRequiresDirectives(
10711043
MutableOutputFieldDefinition field,
10721044
MutableComplexTypeDefinition complexType,

0 commit comments

Comments
 (0)