Skip to content

Add support for list traversal in ArgumentParser #8503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,26 @@ private static bool TryGetValue<T>(

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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<StringType>();
d.Field("bar").Type<IntType>();
d.Field("nested").Type(new ObjectType(nd =>
{
nd.Name("Nested");
nd.Field("baz").Type<StringType>();
}));
d.Field("items").Type(new ListType(new ObjectType(ld =>
{
ld.Name("Item");
ld.Field("name").Type<StringType>();
})));
});
}

[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<string>(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<string>(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<string>(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<int>(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<string>(valueNode, enumType, new string[0]);
Assert.Equal("GREEN", result.ToString());
}
}
}