Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ public static IFilterOperationFieldDescriptor ID(
.Extend()
.OnBeforeCompletion((c, d) =>
{
if (c.Features.Get<NodeSchemaFeature>()?.IsEnabled == true)
if (c.Features.Get<NodeSchemaFeature>() is { IsEnabled: true } nodeFeature)
{
var returnType = d.Member is null ? typeof(string) : d.Member.GetReturnType();
var returnTypeInfo = c.DescriptorContext.TypeInspector.CreateTypeInfo(returnType);
d.Formatters.Push(CreateSerializer(c, returnTypeInfo.NamedType));
d.Formatters.Push(CreateSerializer(c, returnTypeInfo.NamedType, nodeFeature.NodeIdTypes));
}
});

Expand All @@ -45,8 +45,10 @@ public static IFilterOperationFieldDescriptor ID(

private static IInputValueFormatter CreateSerializer(
ITypeCompletionContext completionContext,
Type namedType)
Type namedType,
IDictionary<string, Type>? nodeIdTypes)
=> new FilterGlobalIdInputValueFormatter(
completionContext.DescriptorContext.NodeIdSerializerAccessor,
namedType);
namedType,
nodeIdTypes);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ namespace HotChocolate.Data.Filters;

internal class FilterGlobalIdInputValueFormatter(
INodeIdSerializerAccessor serializerAccessor,
Type namedType)
Type namedType,
IDictionary<string, Type>? nodeIdTypes = null)
: IInputValueFormatter
, INodeIdRuntimeTypeLookup
{
private INodeIdSerializer? _serializer;

Expand All @@ -28,7 +30,7 @@ internal class FilterGlobalIdInputValueFormatter(
{
try
{
return _serializer.Parse(s, namedType).InternalId;
return _serializer.Parse(s, this).InternalId;
}
catch (Exception ex) when (ex is not GraphQLException)
{
Expand Down Expand Up @@ -83,7 +85,7 @@ internal class FilterGlobalIdInputValueFormatter(
continue;
}

id = _serializer.Parse(sv, namedType);
id = _serializer.Parse(sv, this);
list.Add(id.InternalId);
}

Expand All @@ -97,4 +99,20 @@ internal class FilterGlobalIdInputValueFormatter(

throw ThrowHelper.GlobalIdInputValueFormatter_SpecifiedValueIsNotAValidId();
}

Type? INodeIdRuntimeTypeLookup.GetNodeIdRuntimeType(string typeName)
{
if (namedType != typeof(string))
{
return namedType;
}

if (nodeIdTypes is not null
&& nodeIdTypes.TryGetValue(typeName, out var runtimeType))
{
return runtimeType;
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Text.Json;
using HotChocolate.Execution;
using HotChocolate.Types.Relay;

namespace HotChocolate.Data.Filters;

public class Issue7110ReproTests : IClassFixture<SchemaCache>
{
private readonly SchemaCache _cache;

public Issue7110ReproTests(SchemaCache cache)
{
_cache = cache;
}

[Fact]
public async Task GuidRelayId_Filter_Equals_Does_Not_Throw_ConversionError()
{
EntityWithGuidId[] values =
[
new EntityWithGuidId { Id = Guid.Parse("84e9a610-aeb4-4e20-aeda-cf85d0af8d61") },
new EntityWithGuidId { Id = Guid.Parse("94e9a610-aeb4-4e20-aeda-cf85d0af8d62") }
];

var executor = _cache.CreateSchema<EntityWithGuidId, EntityWithGuidIdFilterInput>(
values,
configure: sb => sb.AddGlobalObjectIdentification(false));

var idsResult = await executor.ExecuteAsync("{ root { id } }");
using var idsDocument = JsonDocument.Parse(idsResult.ToJson());
var relayId = idsDocument
.RootElement
.GetProperty("data")
.GetProperty("root")[0]
.GetProperty("id")
.GetString();

Assert.NotNull(relayId);

var filteredResult =
await executor.ExecuteAsync(
OperationRequestBuilder.New()
.SetDocument(
"""
query($id: ID!) {
root(where: { id: { eq: $id } }) {
id
}
}
""")
.SetVariableValues(new Dictionary<string, object?> { ["id"] = relayId! })
.Build());

var operationResult = filteredResult.ExpectOperationResult();
Assert.Empty(operationResult.Errors ?? []);
}

public class EntityWithGuidId
{
[ID]
public Guid Id { get; set; }
}

public class EntityWithGuidIdFilterInput : FilterInputType<EntityWithGuidId>
{
protected override void Configure(IFilterInputTypeDescriptor<EntityWithGuidId> descriptor)
{
descriptor.Field(t => t.Id);
}
}
}
Loading