Skip to content

Commit

Permalink
Fixed issue with abstract types in operation compiler. (#7322)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Aug 1, 2024
1 parent 9ca20f0 commit 15071ee
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public IOperation Compile(

// collect root fields
var rootPath = SelectionPath.Root;
var id = GetOrCreateSelectionSetRefId(operationDefinition.SelectionSet, rootPath);
var id = GetOrCreateSelectionSetRefId(operationDefinition.SelectionSet, operationType.Name, rootPath);
var variants = GetOrCreateSelectionVariants(id);
SelectionSetInfo[] infos = [new(operationDefinition.SelectionSet, 0),];

Expand Down Expand Up @@ -356,7 +356,7 @@ private void CompleteSelectionSet(CompilerContext context)
}

var selectionPath = context.Path.Append(selection.ResponseName);
selectionSetId = GetOrCreateSelectionSetRefId(selection.SelectionSet, selectionPath);
selectionSetId = GetOrCreateSelectionSetRefId(selection.SelectionSet, fieldType.Name, selectionPath);
var possibleTypes = context.Schema.GetPossibleTypes(fieldType);

if (_enqueuedSelectionSets.Add(selectionSetId))
Expand Down Expand Up @@ -598,7 +598,8 @@ private void ResolveFragment(
ifConditionFlags = GetSelectionIncludeCondition(ifCondition, includeCondition);
}
var id = GetOrCreateSelectionSetRefId(selectionSet, context.Path);
var typeName = typeCondition?.Name.Value ?? context.Type.Name;
var id = GetOrCreateSelectionSetRefId(selectionSet, typeName, context.Path);
var variants = GetOrCreateSelectionVariants(id);
var infos = new SelectionSetInfo[] { new(selectionSet, includeCondition), };
Expand Down Expand Up @@ -683,9 +684,12 @@ private FragmentDefinitionNode GetFragmentDefinition(

private int GetNextFragmentId() => _nextFragmentId++;

private int GetOrCreateSelectionSetRefId(SelectionSetNode selectionSet, SelectionPath path)
private int GetOrCreateSelectionSetRefId(
SelectionSetNode selectionSet,
string selectionSetType,
SelectionPath path)
{
var selectionSetRef = new SelectionSetRef(selectionSet, path);
var selectionSetRef = new SelectionSetRef(selectionSet, selectionSetType, path);

if (!_selectionSetIdLookup.TryGetValue(selectionSetRef, out var selectionSetId))
{
Expand Down Expand Up @@ -849,23 +853,28 @@ internal void RegisterNewSelection(Selection newSelection)

private readonly struct SelectionSetRef(
SelectionSetNode selectionSet,
string selectionSetTypeName,
SelectionPath path)
: IEquatable<SelectionSetRef>
{
public SelectionSetNode SelectionSet { get; } = selectionSet;

public SelectionPath Path { get; } = path;

public string SelectionSetTypeName { get; } = selectionSetTypeName;

public bool Equals(SelectionSetRef other)
=> SyntaxComparer.BySyntax.Equals(SelectionSet, other.SelectionSet)
&& Path.Equals(other.Path);
&& Path.Equals(other.Path)
&& Ordinal.Equals(SelectionSetTypeName, other.SelectionSetTypeName);

public override bool Equals(object? obj)
=> obj is SelectionSetRef other && Equals(other);

public override int GetHashCode()
=> HashCode.Combine(
SyntaxComparer.BySyntax.GetHashCode(SelectionSet),
Path.GetHashCode());
Path.GetHashCode(),
Ordinal.GetHashCode(SelectionSetTypeName));
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System.Text;
using ChilliCream.Testing;
using HotChocolate.Language;
using HotChocolate.StarWars;
using HotChocolate.Types;
using HotChocolate.Utilities;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Snapshooter.Xunit;
using CookieCrumble;

namespace HotChocolate.Execution.Processing;

Expand Down Expand Up @@ -1149,6 +1148,89 @@ public async Task Crypto_List_Test()
MatchSnapshot(document, operation);
}

[Fact]
public async Task Resolve_Concrete_Types_From_Unions()
{
// arrange
var schema =
await new ServiceCollection()
.AddGraphQLServer()
.AddQueryType<UnionQuery>()
.AddType<TypeOne>()
.AddType<TypeTwo>()
.UseField(next => next)
.BuildSchemaAsync();

var document = Utf8GraphQLParser.Parse(
"""
query QueryName {
oneOrTwo {
...TypeOneParts
...TypeTwoParts
}
}

fragment TypeOneParts on TypeOne {
field1 { name }
}

fragment TypeTwoParts on TypeTwo {
field1 { name }
}
""");

var operationDefinition = document.Definitions.OfType<OperationDefinitionNode>().Single();

// act
var compiler = new OperationCompiler(new InputParser());
var operation = compiler.Compile(
"opid",
operationDefinition,
schema.QueryType,
document,
schema);

// assert
MatchSnapshot(document, operation);
}

[Fact]
public async Task Resolve_Concrete_Types_From_Unions_Execute()
{
// arrange
var executor =
await new ServiceCollection()
.AddGraphQLServer()
.AddQueryType<UnionQuery>()
.AddType<TypeOne>()
.AddType<TypeTwo>()
.BuildRequestExecutorAsync();

var document = Utf8GraphQLParser.Parse(
"""
query QueryName {
oneOrTwo {
...TypeOneParts
...TypeTwoParts
}
}

fragment TypeOneParts on TypeOne {
field1 { name }
}

fragment TypeTwoParts on TypeTwo {
field1 { name }
}
""");

// act
var result = await executor.ExecuteAsync(builder => builder.SetQuery(document));

// assert
result.MatchSnapshot();
}

private static void MatchSnapshot(DocumentNode original, IOperation compiled)
{
var sb = new StringBuilder();
Expand Down Expand Up @@ -1201,4 +1283,32 @@ public void OptimizeSelectionSet(SelectionSetOptimizerContext context)
}
}
}

public class UnionQuery
{
public IOneOrTwo OneOrTwo() => new TypeOne();
}

public class TypeOne : IOneOrTwo
{
public FieldOne1 Field1 => new();
}

public class TypeTwo : IOneOrTwo
{
public FieldTwo1 Field1 => new();
}

[UnionType]
public interface IOneOrTwo;

public class FieldOne1
{
public string Name => "Name";
}

public class FieldTwo1
{
public string Name => "Name";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
query QueryName {
oneOrTwo {
... TypeOneParts
... TypeTwoParts
}
}

fragment TypeOneParts on TypeOne {
field1 {
name
}
}

fragment TypeTwoParts on TypeTwo {
field1 {
name
}
}

---------------------------------------------------------

query QueryName {
... on UnionQuery {
oneOrTwo @__execute(id: 0, kind: DEFAULT, type: COMPOSITE) {
... on TypeOne {
field1 @__execute(id: 1, kind: DEFAULT, type: COMPOSITE) {
... on FieldOne1 {
name @__execute(id: 2, kind: DEFAULT, type: LEAF)
}
}
}
... on TypeTwo {
field1 @__execute(id: 3, kind: DEFAULT, type: COMPOSITE) {
... on FieldTwo1 {
name @__execute(id: 4, kind: DEFAULT, type: LEAF)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"data": {
"oneOrTwo": {
"field1": {
"name": "Name"
}
}
}
}

0 comments on commit 15071ee

Please sign in to comment.