diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Extensions/MutableObjectTypeDefinitionExtensions.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Extensions/MutableObjectTypeDefinitionExtensions.cs index c9a9a3d41bc..d3c5c321c9b 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Extensions/MutableObjectTypeDefinitionExtensions.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Composition/Extensions/MutableObjectTypeDefinitionExtensions.cs @@ -34,6 +34,8 @@ public static IEnumerable GetFusionLookupDirectives( .ToList(); // To use an abstract lookup, the type must exist in the source schema. + // TODO: For abstract lookups we also need to validate that the type + // is part of the abstract type's possible types in the specific source schema. if (type.ExistsInSchema(schemaName)) { // Interface lookups. diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceInterfaceTypeCollection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceInterfaceTypeCollection.cs index 01fc23818e7..34289061a99 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceInterfaceTypeCollection.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceInterfaceTypeCollection.cs @@ -3,16 +3,11 @@ namespace HotChocolate.Fusion.Types.Collections; -public class SourceInterfaceTypeCollection - : SourceMemberCollection +public class SourceInterfaceTypeCollection(IEnumerable members) + : SourceMemberCollection(members) , ISourceComplexTypeCollection , ISourceComplexTypeCollection { - public SourceInterfaceTypeCollection(IEnumerable members) - : base(members) - { - } - ISourceComplexType ISourceMemberCollection.this[string schemaName] => this[schemaName]; @@ -22,12 +17,9 @@ public ImmutableArray Types ImmutableArray ISourceComplexTypeCollection.Types => [.. Members]; - public bool TryGetType(string schemaName, [NotNullWhen(true)] out SourceInterfaceType? type) - => TryGetMember(schemaName, out type); - - public bool TryGetType(string schemaName, [NotNullWhen(true)] out ISourceComplexType? type) + public bool TryGetMember(string schemaName, [NotNullWhen(true)] out ISourceComplexType? type) { - if (TryGetMember(schemaName, out var member)) + if (base.TryGetMember(schemaName, out var member)) { type = member; return true; diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceMemberCollection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceMemberCollection.cs index e4b647c1fdf..9012162981a 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceMemberCollection.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceMemberCollection.cs @@ -11,7 +11,7 @@ public class SourceMemberCollection { private readonly FrozenDictionary _members; - protected SourceMemberCollection(IEnumerable members) + public SourceMemberCollection(IEnumerable members) { _members = members.ToFrozenDictionary(t => t.SchemaName); } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceObjectTypeCollection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceObjectTypeCollection.cs index 964b507c5ab..e39a1a3e087 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceObjectTypeCollection.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceObjectTypeCollection.cs @@ -17,12 +17,9 @@ public ImmutableArray Types ImmutableArray ISourceComplexTypeCollection.Types => [.. Members]; - public bool TryGetType(string schemaName, [NotNullWhen(true)] out SourceObjectType? type) - => TryGetMember(schemaName, out type); - - public bool TryGetType(string schemaName, [NotNullWhen(true)] out ISourceComplexType? type) + public bool TryGetMember(string schemaName, [NotNullWhen(true)] out ISourceComplexType? type) { - if (TryGetMember(schemaName, out var member)) + if (base.TryGetMember(schemaName, out var member)) { type = member; return true; diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceUnionTypeCollection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceUnionTypeCollection.cs new file mode 100644 index 00000000000..ce0d4924c4e --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Collections/SourceUnionTypeCollection.cs @@ -0,0 +1,6 @@ +namespace HotChocolate.Fusion.Types.Collections; + +public class SourceUnionTypeCollection(IEnumerable members) + : SourceMemberCollection(members) +{ +} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompletionTools.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompletionTools.cs index 96c418abce8..43bd5483625 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompletionTools.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompletionTools.cs @@ -137,6 +137,29 @@ public static SourceInterfaceTypeCollection CreateSourceInterfaceTypeCollection( return new SourceInterfaceTypeCollection(temp); } + public static SourceUnionTypeCollection CreateSourceUnionTypeCollection( + UnionTypeDefinitionNode typeDef, + CompositeSchemaBuilderContext context) + { + var types = TypeDirectiveParser.Parse(typeDef.Directives); + var lookupDirectives = LookupDirectiveParser.Parse(typeDef.Directives); + var temp = new SourceUnionType[types.Length]; + + for (var i = 0; i < types.Length; i++) + { + var type = types[i]; + var lookups = GetLookupBySchema(lookupDirectives, type.SchemaName, typeDef.Name.Value); + context.RegisterForCompletionRange(lookups); + + temp[i] = new SourceUnionType( + typeDef.Name.Value, + type.SchemaName, + lookups); + } + + return new SourceUnionTypeCollection(temp); + } + private static ImmutableArray GetLookupBySchema( ImmutableArray allLookups, string schemaName, diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompositeSchemaBuilder.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompositeSchemaBuilder.cs index b31c5c2d362..4bba603eba7 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompositeSchemaBuilder.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompositeSchemaBuilder.cs @@ -532,7 +532,8 @@ private static void CompleteUnionType( { var directives = CompletionTools.CreateDirectiveCollection(typeDef.Directives, context); var types = CompletionTools.CreateObjectTypeCollection(typeDef.Types, context); - type.Complete(new CompositeUnionTypeCompletionContext(types, directives, FeatureCollection.Empty)); + var sources = CompletionTools.CreateSourceUnionTypeCollection(typeDef, context); + type.Complete(new CompositeUnionTypeCompletionContext(types, directives, sources, FeatureCollection.Empty)); } private static void CompleteOutputField( diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompositeUnionTypeCompletionContext.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompositeUnionTypeCompletionContext.cs index e028aa92d5d..99e9600a028 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompositeUnionTypeCompletionContext.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Completion/CompositeUnionTypeCompletionContext.cs @@ -6,11 +6,14 @@ namespace HotChocolate.Fusion.Types.Completion; internal readonly ref struct CompositeUnionTypeCompletionContext( FusionObjectTypeDefinitionCollection types, FusionDirectiveCollection directives, + SourceUnionTypeCollection sources, IFeatureCollection features) { public FusionDirectiveCollection Directives { get; } = directives; public FusionObjectTypeDefinitionCollection Types { get; } = types; + public SourceUnionTypeCollection Sources { get; } = sources; + public IFeatureCollection Features { get; } = features; } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Contracts/ISourceComplexTypeCollection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Contracts/ISourceComplexTypeCollection.cs index 58df65ac957..ca0f884f47a 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Contracts/ISourceComplexTypeCollection.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Contracts/ISourceComplexTypeCollection.cs @@ -7,7 +7,5 @@ public interface ISourceComplexTypeCollection : ISourceMemberCollection where TType : ISourceComplexType { - bool TryGetType(string schemaName, [NotNullWhen(true)] out TType? type); - ImmutableArray Types { get; } } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Contracts/ISourceMemberCollection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Contracts/ISourceMemberCollection.cs index c35792c1ee6..edffc2f372f 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Contracts/ISourceMemberCollection.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Contracts/ISourceMemberCollection.cs @@ -1,8 +1,9 @@ using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; namespace HotChocolate.Fusion.Types; -public interface ISourceMemberCollection +public interface ISourceMemberCollection : IEnumerable where TMember : ISourceMember { @@ -13,4 +14,6 @@ public interface ISourceMemberCollection bool ContainsSchema(string schemaName); ImmutableArray Schemas { get; } + + bool TryGetMember(string schemaName, [NotNullWhen(true)] out TMember? type); } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/FusionUnionTypeDefinition.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/FusionUnionTypeDefinition.cs index 290723d3e96..05023c6d38a 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/FusionUnionTypeDefinition.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/FusionUnionTypeDefinition.cs @@ -32,6 +32,23 @@ public FusionUnionTypeDefinition(string name, string? description) public SchemaCoordinate Coordinate => new(Name, ofDirective: false); + /// + /// Gets the source type definition of this type. + /// + /// + /// The source type definition of this type. + /// + public SourceUnionTypeCollection Sources + { + get; + private set + { + EnsureNotSealed(_completed); + + field = value; + } + } = null!; + public FusionObjectTypeDefinitionCollection Types { get; @@ -73,6 +90,7 @@ internal void Complete(CompositeUnionTypeCompletionContext context) Directives = context.Directives; Types = context.Types; + Sources = context.Sources; Features = context.Features; _completed = true; diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Metadata/Lookup.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Metadata/Lookup.cs index eee393dd04f..0acb0261f47 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Metadata/Lookup.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Metadata/Lookup.cs @@ -17,7 +17,7 @@ public sealed class Lookup : INeedsCompletion /// /// The name of the source schema. /// The name of the type that declares the field. - /// The name of the lookup field. + /// The name of the lookup field. /// The arguments that represent field requirements. /// The paths to the field that are required. /// @@ -30,13 +30,13 @@ public sealed class Lookup : INeedsCompletion public Lookup( string schemaName, string declaringTypeName, - string name, + string fieldName, ImmutableArray arguments, ImmutableArray fields) { ArgumentException.ThrowIfNullOrEmpty(schemaName); ArgumentException.ThrowIfNullOrEmpty(declaringTypeName); - ArgumentException.ThrowIfNullOrEmpty(name); + ArgumentException.ThrowIfNullOrEmpty(fieldName); if (arguments.Length == 0) { @@ -50,7 +50,7 @@ public Lookup( _declaringTypeName = declaringTypeName; SchemaName = schemaName; - Name = name; + FieldName = fieldName; Arguments = arguments; Fields = fields; } @@ -63,7 +63,12 @@ public Lookup( /// /// Gets the name of the lookup field. /// - public string Name { get; } + public string FieldName { get; } + + /// + /// Get the name of lookup field type. + /// + public string FieldType => _declaringTypeName; /// /// Gets the arguments that represent field requirements. diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Metadata/SourceUnionType.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Metadata/SourceUnionType.cs new file mode 100644 index 00000000000..9bc20835f72 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution.Types/Metadata/SourceUnionType.cs @@ -0,0 +1,15 @@ +using System.Collections.Immutable; + +namespace HotChocolate.Fusion.Types; + +public sealed class SourceUnionType( + string name, + string schemaName, + ImmutableArray lookups) : ISourceMember +{ + public string Name { get; } = name; + + public string SchemaName { get; } = schemaName; + + public ImmutableArray Lookups { get; } = lookups; +} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationDefinitionBuilder.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationDefinitionBuilder.cs index 6c3f35f7d28..8222d050ef4 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationDefinitionBuilder.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationDefinitionBuilder.cs @@ -12,6 +12,7 @@ internal sealed class OperationDefinitionBuilder private string? _name; private string? _description; private Lookup? _lookup; + private string? _lookupTypeRefinement; private string? _requirementKey; private SelectionSetNode? _selectionSet; @@ -40,9 +41,10 @@ public OperationDefinitionBuilder SetDescription(string? description) return this; } - public OperationDefinitionBuilder SetLookup(Lookup? lookup, string? requirementKey) + public OperationDefinitionBuilder SetLookup(Lookup? lookup, string? lookupTypeRefinement, string? requirementKey) { _lookup = lookup; + _lookupTypeRefinement = lookupTypeRefinement; _requirementKey = requirementKey; return this; } @@ -76,7 +78,7 @@ public OperationDefinitionBuilder SetSelectionSet(SelectionSetNode selectionSet) } var lookupField = new FieldNode( - new NameNode(_lookup.Name), + new NameNode(_lookup.FieldName), null, [], arguments, @@ -87,7 +89,13 @@ public OperationDefinitionBuilder SetSelectionSet(SelectionSetNode selectionSet) var indexBuilder = index.ToBuilder(); indexBuilder.Register(selectionSet); index = indexBuilder; - selectionPath = selectionPath.AppendField(_lookup.Name); + + if (!string.IsNullOrEmpty(_lookupTypeRefinement)) + { + selectionPath = selectionPath.AppendFragment(_lookupTypeRefinement); + } + + selectionPath = selectionPath.AppendField(_lookup.FieldName); } var definition = new OperationDefinitionNode( diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationPlanner.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationPlanner.cs index fd81280d548..1b08659b3b2 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationPlanner.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationPlanner.cs @@ -293,7 +293,32 @@ lookup is null requirements = requirements.Add(argumentRequirementKey, operationRequirement); } - operationBuilder.SetLookup(lookup, requirementKey); + string? lookupTypeRefinement = null; + if (_schema.Types.TryGetType(lookup.FieldType, out var lookupFieldType) + && lookupFieldType != workItem.SelectionSet.Type + && !resolvable.Selections.All(s => s is InlineFragmentNode inlineFragment + && inlineFragment.TypeCondition?.Name.Value == workItem.SelectionSet.Type.Name)) + { + var typeRefinement = + new InlineFragmentNode( + null, + new NamedTypeNode(workItem.SelectionSet.Type.Name), + [], + resolvable); + var selectionSetWithTypeRefinement = new SelectionSetNode(null, [typeRefinement]); + + var indexBuilder = index.ToBuilder(); + + indexBuilder.Register(resolvable, selectionSetWithTypeRefinement); + + index = indexBuilder; + + operationBuilder.SetSelectionSet(selectionSetWithTypeRefinement); + + lookupTypeRefinement = workItem.SelectionSet.Type.Name; + } + + operationBuilder.SetLookup(lookup, lookupTypeRefinement, requirementKey); } (var definition, index, var source) = operationBuilder.Build(index); @@ -664,7 +689,32 @@ private void PlanFieldWithRequirement( requirements = requirements.Add(argumentRequirementKey, operationRequirement); } - operationBuilder.SetLookup(workItem.Lookup, requirementKey); + string? lookupTypeRefinement = null; + if (_schema.Types.TryGetType(lookup.FieldType, out var lookupFieldType) + && lookupFieldType != selectionSetStub.Type + && !selectionSetNode.Selections.All(s => s is InlineFragmentNode inlineFragment + && inlineFragment.TypeCondition?.Name.Value == selectionSetStub.Type.Name)) + { + var typeRefinement = + new InlineFragmentNode( + null, + new NamedTypeNode(selectionSetStub.Type.Name), + [], + selectionSetNode); + var selectionSetWithTypeRefinement = new SelectionSetNode(null, [typeRefinement]); + + var indexBuilder = index.ToBuilder(); + + indexBuilder.Register(selectionSetNode, selectionSetWithTypeRefinement); + + index = indexBuilder; + + operationBuilder.SetSelectionSet(selectionSetWithTypeRefinement); + + lookupTypeRefinement = selectionSetStub.Type.Name; + } + + operationBuilder.SetLookup(workItem.Lookup, lookupTypeRefinement, requirementKey); var (definition, _, source) = operationBuilder.Build(index); @@ -1046,7 +1096,7 @@ public static void Enqueue( break; case FieldRequirementWorkItem wi: - possiblePlans.EnqueueRequirePlanNodes(planNodeTemplate, wi); + possiblePlans.EnqueueRequirePlanNodes(planNodeTemplate, wi, compositeSchema); break; default: @@ -1083,7 +1133,7 @@ private static void EnqueueLookupPlanNodes( foreach (var (schemaName, resolutionCost) in compositeSchema.GetPossibleSchemas(workItem.SelectionSet)) { - foreach (var lookup in workItem.SelectionSet.Type.GetPossibleLookups(schemaName)) + foreach (var lookup in workItem.SelectionSet.Type.GetPossibleLookups(schemaName, compositeSchema)) { possiblePlans.Enqueue( planNodeTemplate with @@ -1100,7 +1150,8 @@ planNodeTemplate with private static void EnqueueRequirePlanNodes( this PriorityQueue possiblePlans, PlanNode planNodeTemplate, - FieldRequirementWorkItem workItem) + FieldRequirementWorkItem workItem, + FusionSchemaDefinition compositeSchema) { var backlog = planNodeTemplate.Backlog.Pop(); @@ -1116,7 +1167,7 @@ planNodeTemplate with Backlog = backlog.Push(workItem) }); - foreach (var lookup in workItem.Selection.Field.DeclaringType.GetPossibleLookups(schemaName)) + foreach (var lookup in workItem.Selection.Field.DeclaringType.GetPossibleLookups(schemaName, compositeSchema)) { possiblePlans.Enqueue( planNodeTemplate with @@ -1130,7 +1181,7 @@ planNodeTemplate with } else { - foreach (var lookup in workItem.Selection.Field.DeclaringType.GetPossibleLookups(schemaName)) + foreach (var lookup in workItem.Selection.Field.DeclaringType.GetPossibleLookups(schemaName, compositeSchema)) { possiblePlans.Enqueue( planNodeTemplate with @@ -1215,12 +1266,42 @@ static void TrackSchema(Dictionary possibleSchemas, string schemaNa } } - private static IEnumerable GetPossibleLookups(this ITypeDefinition type, string schemaName) + private static IEnumerable GetPossibleLookups( + this ITypeDefinition type, + string schemaName, + FusionSchemaDefinition compositeSchema) { + // TODO: Currently we just check that the type exists in the given source schema + // and that there are lookups for itself and / or the abstract types + // it's a part of. However, we don't check that the type is part of the + // abstract type in the given source schema. if (type is FusionComplexTypeDefinition complexType - && complexType.Sources.TryGetType(schemaName, out var source)) + && complexType.Sources.TryGetMember(schemaName, out var source)) { - return source.Lookups; + var lookups = new List(source.Lookups); + + foreach (var interfaceType in complexType.Implements) + { + if (interfaceType.Sources.TryGetMember(schemaName, out var interfaceSource)) + { + lookups.AddRange(interfaceSource.Lookups); + } + } + + var unionTypes = compositeSchema.Types + .OfType() + .Where(u => u.Types.Contains(type)) + .ToArray(); + + foreach (var unionType in unionTypes) + { + if (unionType.Sources.TryGetMember(schemaName, out var unionSource)) + { + lookups.AddRange(unionSource.Lookups); + } + } + + return lookups; } return []; diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/OperationPlannerTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/OperationPlannerTests.cs index 85fb44d2f74..b2c5899a61a 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/OperationPlannerTests.cs +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/OperationPlannerTests.cs @@ -148,6 +148,116 @@ query GetTopProducts { MatchSnapshot(plan); } + [Fact] + public void Plan_Simple_Interface_Lookup() + { + // arrange + var schema = ComposeSchema( + """ + schema @schemaName(value: "A") { + query: Query + } + + type Query { + topProduct: Product + } + + type Product { + id: ID! + name: String! + } + """, + """ + schema @schemaName(value: "B") { + query: Query + } + + type Query { + node(id: ID!): Node @lookup @internal + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + price: Float! + } + """); + + // act + var plan = PlanOperation( + schema, + """ + query GetTopProducts { + topProduct { + id + name + price + } + } + """); + + // assert + MatchSnapshot(plan); + } + + [Fact] + public void Plan_Simple_Union_Lookup() + { + // arrange + var schema = ComposeSchema( + """ + schema @schemaName(value: "A") { + query: Query + } + + type Query { + topProduct: Product + # Just here to satisfy satisfiability as I can't make the union lookup internal... + productById(id: ID!): Product @lookup @internal + } + + type Product { + id: ID! + name: String! + } + """, + """ + schema @schemaName(value: "B") { + query: Query + } + + type Query { + lookupUnionById(id: ID!): SomeUnion @lookup + } + + union SomeUnion = Product + + type Product { + id: ID! + price: Float! + } + """); + + // act + var plan = PlanOperation( + schema, + """ + query GetTopProducts { + topProduct { + id + name + price + } + } + """); + + // assert + MatchSnapshot(plan); + } + [Fact] public void Plan_Simple_Requirement() { diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/OperationPlannerTests.Plan_Simple_Interface_Lookup.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/OperationPlannerTests.Plan_Simple_Interface_Lookup.yaml new file mode 100644 index 00000000000..a6080f2fdcc --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/OperationPlannerTests.Plan_Simple_Interface_Lookup.yaml @@ -0,0 +1,41 @@ +operation: + - document: >- + query GetTopProducts { + topProduct { + id + name + price + id @fusion__requirement + } + } + name: GetTopProducts + hash: 123 +nodes: + - id: 1 + schema: A + operation: >- + query GetTopProducts_123_1 { + topProduct { + id + name + } + } + - id: 2 + schema: B + operation: >- + query GetTopProducts_123_2( + $__fusion_1_id: ID! + ) { + node(id: $__fusion_1_id) { + ... on Product { + price + } + } + } + source: $.node. + target: $.topProduct + requirements: + - name: __fusion_1_id + selectionMap: id + dependencies: + - id: 1 diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/OperationPlannerTests.Plan_Simple_Union_Lookup.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/OperationPlannerTests.Plan_Simple_Union_Lookup.yaml new file mode 100644 index 00000000000..052aeaaaf0e --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Planning/__snapshots__/OperationPlannerTests.Plan_Simple_Union_Lookup.yaml @@ -0,0 +1,41 @@ +operation: + - document: >- + query GetTopProducts { + topProduct { + id + name + price + id @fusion__requirement + } + } + name: GetTopProducts + hash: 123 +nodes: + - id: 1 + schema: A + operation: >- + query GetTopProducts_123_1 { + topProduct { + id + name + } + } + - id: 2 + schema: B + operation: >- + query GetTopProducts_123_2( + $__fusion_1_id: ID! + ) { + lookupUnionById(id: $__fusion_1_id) { + ... on Product { + price + } + } + } + source: $.lookupUnionById. + target: $.topProduct + requirements: + - name: __fusion_1_id + selectionMap: id + dependencies: + - id: 1