diff --git a/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ArgumentParser.cs b/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ArgumentParser.cs index 0262125cd79..561f17e516e 100644 --- a/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ArgumentParser.cs +++ b/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ArgumentParser.cs @@ -1,3 +1,5 @@ +using System.Collections; +using System.Reflection; using System.Runtime.CompilerServices; using HotChocolate.Language; using HotChocolate.Utilities; @@ -62,6 +64,45 @@ private static bool TryGetValue( value = default; return true; } + case SyntaxKind.ListValue: + { + if (type is not ListType listType) + { + break; + } + + var list = CreateList(listType); + var items = ((ListValueNode)valueNode).Items; + var flatList = !listType.ElementType.IsListType(); + var elementType = type.ElementType(); + var elementClrType = typeof(T).GetGenericArguments().Single()!; + + var innerTryGetValueMethod = typeof(ArgumentParser) + .GetMethod(nameof(TryGetValue), BindingFlags.NonPublic | BindingFlags.Static)! + .MakeGenericMethod(elementClrType); + + if (flatList) + { + for (var j = 0; j < items.Count; j++) + { + var parameters = new object?[] { items[j], elementType, path, i + 1, null }; + var succeeded = (bool) innerTryGetValueMethod.Invoke(null, parameters)!; + if (!succeeded) + { + throw new NotImplementedException(); + } + var innerValue = parameters[4]!; + list.Add(innerValue); + } + } + else + { + throw new NotImplementedException(); + } + + value = (T)list; + return true; + } case SyntaxKind.StringValue: case SyntaxKind.IntValue: case SyntaxKind.FloatValue: @@ -99,6 +140,9 @@ private static bool TryGetValue( return false; } + private static IList CreateList(ListType type) + => (IList)Activator.CreateInstance(type.ToRuntimeType())!; + public static bool Matches(IValueNode valueNode, IReadOnlyList required) { if (required.Count == 1) diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/EntitiesResolverTests.cs b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/EntitiesResolverTests.cs index 9ac09ff4d52..d8f59c9c810 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/EntitiesResolverTests.cs +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/EntitiesResolverTests.cs @@ -3,6 +3,7 @@ using HotChocolate.ApolloFederation.Types; using HotChocolate.Execution; using HotChocolate.Language; +using HotChocolate.Resolvers; using Microsoft.Extensions.DependencyInjection; using Moq; using static HotChocolate.ApolloFederation.TestHelper; @@ -71,6 +72,36 @@ public async Task TestResolveViaForeignServiceType_MixedTypes() Assert.Equal("InternalValue", obj.InternalField); } + [Fact] + public async Task TestResolveViaForeignServiceType_ListType() + { + // arrange + var schema = await new ServiceCollection() + .AddGraphQL() + .AddApolloFederation() + .AddQueryType() + .BuildSchemaAsync(); + + var context = CreateResolverContext(schema); + + // act + var representations = new List + { + new("ListFieldType", + new ObjectValueNode( + new ObjectFieldNode("id", "1"), + new ObjectFieldNode("listField", new ListValueNode(new List { new(1), new(2), new(3) })))), + }; + + // assert + var result = + await EntitiesResolver.ResolveAsync(schema, representations, context); + var obj = Assert.IsType(result[0]); + Assert.Equal("1", obj.Id); + Assert.Equal([1, 2, 3], obj.ListField); + Assert.Equal("InternalValue", obj.InternalField); + } + [Fact] public async Task TestResolveViaEntityResolver() { @@ -249,6 +280,7 @@ public class Query public TypeWithoutRefResolver TypeWithoutRefResolver { get; set; } = default!; public MixedFieldTypes MixedFieldTypes { get; set; } = default!; public FederatedType TypeWithReferenceResolverMany { get; set; } = default!; + public ListFieldType ListFieldType { get; set; } = default!; } public class TypeWithoutRefResolver @@ -335,6 +367,31 @@ public class FederatedType } } + [ExtendServiceType] + public class ListFieldType + { + public ListFieldType(string id, List listField) + { + Id = id; + ListField = listField; + } + + [Key] + [External] + public string Id { get; } + + [External] + public List ListField { get; } + + public string InternalField { get; set; } = "InternalValue"; + + [ReferenceResolver] + public static ListFieldType GetByExternal(string id, List listField, IResolverContext context) + { + return new(id, listField); + } + } + public class FederatedTypeDataLoader : BatchDataLoader { public int TimesCalled { get; private set; }