Skip to content

Commit ce7a74a

Browse files
authored
[Fusion] Added source schema enricher (#8719)
1 parent 56833bd commit ce7a74a

File tree

5 files changed

+172
-0
lines changed

5 files changed

+172
-0
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Immutable;
2+
using HotChocolate.Fusion.Features;
23
using HotChocolate.Types;
34
using static HotChocolate.Fusion.WellKnownArgumentNames;
45
using static HotChocolate.Fusion.WellKnownDirectiveNames;
@@ -24,4 +25,9 @@ public static ImmutableArray<string> GetSchemaNames(
2425

2526
return [.. schemaNames];
2627
}
28+
29+
public static SourceFieldMetadata? GetSourceFieldMetadata(this IOutputFieldDefinition field)
30+
{
31+
return field.Features.Get<SourceFieldMetadata>();
32+
}
2733
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace HotChocolate.Fusion.Features;
2+
3+
internal sealed class SourceFieldMetadata
4+
{
5+
public bool IsKeyField { get; set; }
6+
7+
public bool IsShareable { get; set; }
8+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ public CompositionResult<MutableSchemaDefinition> Compose()
5151
return preprocessResult;
5252
}
5353

54+
// Enrich Source Schemas
55+
var enrichmentResult = schemas.Select(schema => new SourceSchemaEnricher(schema).Enrich()).Combine();
56+
57+
if (enrichmentResult.IsFailure)
58+
{
59+
return enrichmentResult;
60+
}
61+
5462
// Validate Source Schemas
5563
var validationResult =
5664
new SourceSchemaValidator(schemas, s_sourceSchemaRules, _log).Validate();
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using HotChocolate.Features;
2+
using HotChocolate.Fusion.Extensions;
3+
using HotChocolate.Fusion.Features;
4+
using HotChocolate.Fusion.Results;
5+
using HotChocolate.Fusion.SyntaxWalkers;
6+
using HotChocolate.Language;
7+
using HotChocolate.Types.Mutable;
8+
using ArgumentNames = HotChocolate.Fusion.WellKnownArgumentNames;
9+
using DirectiveNames = HotChocolate.Fusion.WellKnownDirectiveNames;
10+
11+
namespace HotChocolate.Fusion;
12+
13+
internal sealed class SourceSchemaEnricher(MutableSchemaDefinition schema)
14+
{
15+
public CompositionResult Enrich()
16+
{
17+
foreach (var type in schema.Types)
18+
{
19+
if (type is MutableComplexTypeDefinition complexType)
20+
{
21+
// Key fields.
22+
foreach (var keyField in GetKeyFields(complexType, schema))
23+
{
24+
var sourceFieldMetadata = keyField.Features.GetOrSet<SourceFieldMetadata>();
25+
sourceFieldMetadata.IsKeyField = true;
26+
}
27+
28+
foreach (var field in complexType.Fields)
29+
{
30+
EnrichField(field, complexType);
31+
}
32+
}
33+
}
34+
35+
return CompositionResult.Success();
36+
}
37+
38+
private static void EnrichField(MutableOutputFieldDefinition field, MutableComplexTypeDefinition complexType)
39+
{
40+
var sourceFieldMetadata = field.Features.GetOrSet<SourceFieldMetadata>();
41+
42+
// Shareable fields.
43+
if (!field.HasExternalDirective()
44+
&& (
45+
sourceFieldMetadata.IsKeyField
46+
|| field.HasShareableDirective()
47+
|| (complexType is MutableObjectTypeDefinition objectType && objectType.HasShareableDirective())))
48+
{
49+
sourceFieldMetadata.IsShareable = true;
50+
}
51+
}
52+
53+
private static List<MutableOutputFieldDefinition> GetKeyFields(
54+
MutableComplexTypeDefinition complexType,
55+
MutableSchemaDefinition schema)
56+
{
57+
var keyDirectives = complexType.Directives.AsEnumerable().Where(d => d.Name == DirectiveNames.Key);
58+
var keyFields = new List<MutableOutputFieldDefinition>();
59+
60+
foreach (var keyDirective in keyDirectives)
61+
{
62+
var fieldsArgument = ((StringValueNode)keyDirective.Arguments[ArgumentNames.Fields]).Value;
63+
var selectionSet = Utf8GraphQLParser.Syntax.ParseSelectionSet($"{{ {fieldsArgument} }}");
64+
var fieldsExtractor = new SelectionSetFieldsExtractor(schema);
65+
var fieldGroup = fieldsExtractor.ExtractFields(selectionSet, complexType);
66+
keyFields.AddRange(fieldGroup.Select(f => f.Field));
67+
}
68+
69+
return keyFields;
70+
}
71+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using HotChocolate.Fusion.Extensions;
2+
using HotChocolate.Fusion.Logging;
3+
using HotChocolate.Types.Mutable;
4+
5+
namespace HotChocolate.Fusion;
6+
7+
public sealed class SourceSchemaEnricherTests
8+
{
9+
[Fact]
10+
public void Enrich_SourceSchema_SetsIsKeyFieldAndIsShareableMetadata()
11+
{
12+
// arrange
13+
var sourceSchemaText =
14+
new SourceSchemaText(
15+
"A",
16+
"""
17+
type Product
18+
@key(fields: "id, category { id }")
19+
@key(fields: "sku")
20+
@key(fields: "alreadyShareable")
21+
@key(fields: "externalField") {
22+
id: ID!
23+
category: Category!
24+
sku: String!
25+
alreadyShareable: String! @shareable
26+
externalField: String! @external
27+
nonKeyField: String
28+
}
29+
30+
interface Category {
31+
id: ID!
32+
}
33+
34+
type BookCategory implements Category @shareable {
35+
id: ID!
36+
count: Int!
37+
}
38+
""");
39+
var sourceSchemaParser = new SourceSchemaParser([sourceSchemaText], new CompositionLog());
40+
var schema = sourceSchemaParser.Parse().Value.Single();
41+
var enricher = new SourceSchemaEnricher(schema);
42+
43+
// act
44+
enricher.Enrich();
45+
46+
var productType = (MutableObjectTypeDefinition)schema.Types["Product"];
47+
var productIdFieldMetadata = productType.Fields["id"].GetSourceFieldMetadata();
48+
var productCategoryFieldMetadata = productType.Fields["category"].GetSourceFieldMetadata();
49+
var productSkuFieldMetadata = productType.Fields["sku"].GetSourceFieldMetadata();
50+
var productAlreadyShareableFieldMetadata = productType.Fields["alreadyShareable"].GetSourceFieldMetadata();
51+
var productExternalFieldMetadata = productType.Fields["externalField"].GetSourceFieldMetadata();
52+
var productNonKeyFieldMetadata = productType.Fields["nonKeyField"].GetSourceFieldMetadata();
53+
var categoryType = (MutableInterfaceTypeDefinition)schema.Types["Category"];
54+
var categoryIdFieldMetadata = categoryType.Fields["id"].GetSourceFieldMetadata();
55+
var bookCategoryType = (MutableObjectTypeDefinition)schema.Types["BookCategory"];
56+
var bookCategoryIdFieldMetadata = bookCategoryType.Fields["id"].GetSourceFieldMetadata();
57+
var bookCategoryCountFieldMetadata = bookCategoryType.Fields["count"].GetSourceFieldMetadata();
58+
59+
// assert
60+
Assert.True(productIdFieldMetadata?.IsKeyField);
61+
Assert.True(productIdFieldMetadata?.IsShareable);
62+
Assert.True(productCategoryFieldMetadata?.IsKeyField);
63+
Assert.True(productCategoryFieldMetadata?.IsShareable);
64+
Assert.True(productSkuFieldMetadata?.IsKeyField);
65+
Assert.True(productSkuFieldMetadata?.IsShareable);
66+
Assert.True(productAlreadyShareableFieldMetadata?.IsKeyField);
67+
Assert.True(productAlreadyShareableFieldMetadata?.IsShareable);
68+
Assert.True(productExternalFieldMetadata?.IsKeyField);
69+
Assert.False(productExternalFieldMetadata?.IsShareable);
70+
Assert.False(productNonKeyFieldMetadata?.IsKeyField);
71+
Assert.False(productNonKeyFieldMetadata?.IsShareable);
72+
Assert.True(categoryIdFieldMetadata?.IsKeyField);
73+
Assert.True(categoryIdFieldMetadata?.IsShareable);
74+
Assert.False(bookCategoryIdFieldMetadata?.IsKeyField);
75+
Assert.True(bookCategoryIdFieldMetadata?.IsShareable);
76+
Assert.False(bookCategoryCountFieldMetadata?.IsKeyField);
77+
Assert.True(bookCategoryCountFieldMetadata?.IsShareable);
78+
}
79+
}

0 commit comments

Comments
 (0)