diff --git a/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ArgumentParser.cs b/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ArgumentParser.cs index d61e7af3b56..614859525ca 100644 --- a/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ArgumentParser.cs +++ b/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ArgumentParser.cs @@ -87,8 +87,26 @@ private static bool TryGetValue( value = (T)enumType.ParseLiteral(valueNode)!; return true; + case SyntaxKind.ListValue: + { + // Support for list/array traversal: expect current path segment to be an integer index + if (int.TryParse(path[i], out int index)) + { + var list = ((ListValueNode)valueNode).Items; + if (index >= 0 && index < list.Count) + { + // ListType exposes ElementType property + var elementType = type is ListType lt ? lt.ElementType : type; + if (path.Length < ++i && elementType.IsCompositeType()) + { + break; + } + return TryGetValue(list[index], elementType, path, i, out value); + } + } + break; + } } - value = default; return false; } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/ArgumentParserTests.cs b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/ArgumentParserTests.cs new file mode 100644 index 00000000000..fb38c638f10 --- /dev/null +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/ArgumentParserTests.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using HotChocolate.Language; +using HotChocolate.Types; +using Xunit; +using HotChocolate.ApolloFederation.Resolvers; + +namespace HotChocolate.ApolloFederation.Tests.Resolvers +{ + public class ArgumentParserTests + { + private static ObjectType CreateTestObjectType() + { + return new ObjectType(d => + { + d.Name("Test"); + d.Field("foo").Type(); + d.Field("bar").Type(); + d.Field("nested").Type(new ObjectType(nd => + { + nd.Name("Nested"); + nd.Field("baz").Type(); + })); + d.Field("items").Type(new ListType(new ObjectType(ld => + { + ld.Name("Item"); + ld.Field("name").Type(); + }))); + }); + } + + [Fact] + public void GetValue_SimpleField_ReturnsValue() + { + var type = CreateTestObjectType(); + var valueNode = new ObjectValueNode( + new ObjectFieldNode("foo", new StringValueNode("abc")), + new ObjectFieldNode("bar", new IntValueNode(123)) + ); + var result = ArgumentParser.GetValue(valueNode, type, new[] { "foo" }); + Assert.Equal("abc", result); + } + + [Fact] + public void GetValue_NestedField_ReturnsValue() + { + var type = CreateTestObjectType(); + var valueNode = new ObjectValueNode( + new ObjectFieldNode("nested", new ObjectValueNode( + new ObjectFieldNode("baz", new StringValueNode("deep")) + )) + ); + var result = ArgumentParser.GetValue(valueNode, type, new[] { "nested", "baz" }); + Assert.Equal("deep", result); + } + + [Fact] + public void GetValue_ListElementField_ReturnsValue() + { + var type = CreateTestObjectType(); + var valueNode = new ObjectValueNode( + new ObjectFieldNode("items", new ListValueNode( + new ObjectValueNode(new ObjectFieldNode("name", new StringValueNode("first"))), + new ObjectValueNode(new ObjectFieldNode("name", new StringValueNode("second"))) + )) + ); + var result = ArgumentParser.GetValue(valueNode, type, new[] { "items", "1", "name" }); + Assert.Equal("second", result); + } + + [Fact] + public void GetValue_ScalarInt_ReturnsValue() + { + var type = CreateTestObjectType(); + var valueNode = new ObjectValueNode( + new ObjectFieldNode("bar", new IntValueNode(42)) + ); + var result = ArgumentParser.GetValue(valueNode, type, new[] { "bar" }); + Assert.Equal(42, result); + } + + [Fact] + public void GetValue_EnumValue_ReturnsValue() + { + var enumType = new EnumType(d => + { + d.Name("Color"); + d.Value("RED"); + d.Value("GREEN"); + }); + var valueNode = new EnumValueNode("GREEN"); + var result = ArgumentParser.GetValue(valueNode, enumType, new string[0]); + Assert.Equal("GREEN", result.ToString()); + } + } +}