Skip to content

Commit d3da9b0

Browse files
Correctly handle null for lists of nullable value-type IDs (#7933)
1 parent 565cc9c commit d3da9b0

File tree

6 files changed

+130
-101
lines changed

6 files changed

+130
-101
lines changed

src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ private static IInputValueFormatter CreateSerializer(
261261
return new GlobalIdInputValueFormatter(
262262
completionContext.DescriptorContext.NodeIdSerializerAccessor,
263263
resultTypeInfo.NamedType,
264-
resultType.ElementType?.Type ?? resultTypeInfo.NamedType,
264+
resultType.ElementType?.Source ?? resultTypeInfo.NamedType,
265265
typeName ?? completionContext.Type.Name,
266266
validateType);
267267
}

src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdAttributeTests.cs

Lines changed: 53 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,25 @@ query foo(
5151
nullableIntIdGivenNull: nullableIntId(id: $nullIntId)
5252
optionalIntId(id: $intId)
5353
optionalIntIdGivenNothing: optionalIntId
54-
intIdList(id: [$intId])
55-
nullableIntIdList(id: [$intId, $nullIntId])
56-
optionalIntIdList(id: [$intId])
54+
intIdList(ids: [$intId])
55+
nullableIntIdList(ids: [$intId, $nullIntId])
56+
optionalIntIdList(ids: [$intId])
5757
stringId(id: $stringId)
5858
nullableStringId(id: $stringId)
5959
nullableStringIdGivenNull: nullableStringId(id: $nullStringId)
6060
optionalStringId(id: $stringId)
6161
optionalStringIdGivenNothing: optionalStringId
62-
stringIdList(id: [$stringId])
63-
nullableStringIdList(id: [$stringId, $nullStringId])
64-
optionalStringIdList(id: [$stringId])
62+
stringIdList(ids: [$stringId])
63+
nullableStringIdList(ids: [$stringId, $nullStringId])
64+
optionalStringIdList(ids: [$stringId])
6565
guidId(id: $guidId)
6666
nullableGuidId(id: $guidId)
6767
nullableGuidIdGivenNull: nullableGuidId(id: $nullGuidId)
6868
optionalGuidId(id: $guidId)
6969
optionalGuidIdGivenNothing: optionalGuidId
70-
guidIdList(id: [$guidId $guidId])
71-
nullableGuidIdList(id: [$guidId $nullGuidId $guidId])
72-
optionalGuidIdList(id: [$guidId $guidId])
70+
guidIdList(ids: [$guidId $guidId])
71+
nullableGuidIdList(ids: [$guidId $nullGuidId $guidId])
72+
optionalGuidIdList(ids: [$guidId $guidId])
7373
customId(id: $customId)
7474
nullableCustomId(id: $customId)
7575
nullableCustomIdGivenNull: nullableCustomId(id: $nullCustomId)
@@ -106,7 +106,7 @@ public async Task InterceptedId_On_Arguments()
106106
OperationRequestBuilder.New()
107107
.SetDocument(@"query foo {
108108
interceptedId(id: 1)
109-
interceptedIds(id: [1, 2])
109+
interceptedIds(ids: [1, 2])
110110
}")
111111
.Build());
112112

@@ -464,56 +464,53 @@ public async Task EnsureIdIsOnlyAppliedOnce()
464464
[SuppressMessage("Performance", "CA1822:Mark members as static")]
465465
public class Query
466466
{
467-
public string IntId([ID] int id) => id.ToString();
468-
public string IntIdList([ID] int[] id) =>
469-
string.Join(", ", id.Select(t => t.ToString()));
467+
public int IntId([ID] int id) => id;
470468

471-
public string NullableIntId([ID] int? id) => id?.ToString() ?? "null";
472-
public string NullableIntIdList([ID] int?[] id) =>
473-
string.Join(", ", id.Select(t => t?.ToString() ?? "null"));
469+
public int[] IntIdList([ID] int[] ids) => ids;
474470

475-
public string OptionalIntId([DefaultValue("UXVlcnk6MA==")][ID] Optional<int> id) =>
476-
id.HasValue ? id.Value.ToString() : "NO VALUE";
477-
public string OptionalIntIdList([DefaultValue(new int[] {})][ID] Optional<int[]> id) =>
478-
id.HasValue ? string.Join(", ", id.Value.Select(t => t.ToString())) : "NO VALUE";
471+
public int? NullableIntId([ID] int? id) => id;
472+
473+
public int?[] NullableIntIdList([ID] int?[] ids) => ids;
474+
475+
public int? OptionalIntId([DefaultValue("UXVlcnk6MA==")][ID] Optional<int> id)
476+
=> id.HasValue ? id.Value : null;
477+
478+
public int[]? OptionalIntIdList([DefaultValue(new int[] {})][ID] Optional<int[]> ids)
479+
=> ids.HasValue ? ids.Value : null;
479480

480481
public string StringId([ID] string id) => id;
481-
public string StringIdList([ID] string[] id) =>
482-
string.Join(", ", id.Select(t => t.ToString()));
483-
484-
public string NullableStringId([ID] string? id) => id ?? "null";
485-
public string NullableStringIdList([ID] string?[] id) =>
486-
string.Join(", ", id.Select(t => t?.ToString() ?? "null"));
487-
488-
public string OptionalStringId(
489-
[DefaultValue("UXVlcnk6")][ID] Optional<string> id) =>
490-
id.HasValue ? id.Value : "NO VALUE";
491-
public string OptionalStringIdList(
492-
[DefaultValue(new string[] {})][ID] Optional<string[]> id) =>
493-
id.HasValue ? string.Join(", ", id.Value) : "NO VALUE";
494-
495-
public string GuidId([ID] Guid id) => id.ToString();
496-
public string GuidIdList([ID] IReadOnlyList<Guid> id) =>
497-
string.Join(", ", id.Select(t => t.ToString()));
498-
499-
public string NullableGuidId([ID] Guid? id) => id?.ToString() ?? "null";
500-
public string NullableGuidIdList([ID] IReadOnlyList<Guid?> id) =>
501-
string.Join(", ", id.Select(t => t?.ToString() ?? "null"));
502-
503-
public string OptionalGuidId(
504-
[DefaultValue("UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA==")][ID] Optional<Guid> id) =>
505-
id.HasValue ? id.Value.ToString() : "NO VALUE";
506-
public string OptionalGuidIdList(
507-
[DefaultValue(new object[] {})][ID] Optional<Guid[]> id) =>
508-
id.HasValue ? string.Join(", ", id.Value.Select(t => t.ToString())) : "NO VALUE";
509-
510-
public string InterceptedId([InterceptedID("Query")] [ID] int id) => id.ToString();
511-
512-
public string InterceptedIds([InterceptedID("Query")] [ID] int[] id) =>
513-
string.Join(", ", id.Select(t => t.ToString()));
514-
515-
public string CustomId([ID] StronglyTypedId id) =>
516-
id.ToString();
482+
483+
public string[] StringIdList([ID] string[] ids) => ids;
484+
485+
public string? NullableStringId([ID] string? id) => id;
486+
487+
public string?[] NullableStringIdList([ID] string?[] ids) => ids;
488+
489+
public string? OptionalStringId([DefaultValue("UXVlcnk6")][ID] Optional<string> id)
490+
=> id.HasValue ? id.Value : null;
491+
492+
public string[]? OptionalStringIdList([DefaultValue(new string[] { })] [ID] Optional<string[]> ids)
493+
=> ids.HasValue ? ids.Value : null;
494+
495+
public Guid GuidId([ID] Guid id) => id;
496+
497+
public IReadOnlyList<Guid> GuidIdList([ID] IReadOnlyList<Guid> ids) => ids;
498+
499+
public Guid? NullableGuidId([ID] Guid? id) => id;
500+
501+
public IReadOnlyList<Guid?> NullableGuidIdList([ID] IReadOnlyList<Guid?> ids) => ids;
502+
503+
public Guid? OptionalGuidId([DefaultValue("UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA==")][ID] Optional<Guid> id)
504+
=> id.HasValue ? id.Value : null;
505+
506+
public Guid[]? OptionalGuidIdList([DefaultValue(new object[] { })] [ID] Optional<Guid[]> ids)
507+
=> ids.HasValue ? ids.Value : null;
508+
509+
public int InterceptedId([InterceptedID("Query")] [ID] int id) => id;
510+
511+
public int[] InterceptedIds([InterceptedID("Query")] [ID] int[] ids) => ids;
512+
513+
public string CustomId([ID] StronglyTypedId id) => id.ToString();
517514

518515
public string NullableCustomId([ID] StronglyTypedId? id) =>
519516
id?.ToString() ?? "null";

src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Arguments.snap

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,53 @@
1-
{
1+
{
22
"data": {
3-
"intId": "1",
4-
"nullableIntId": "1",
5-
"nullableIntIdGivenNull": "null",
6-
"optionalIntId": "1",
7-
"optionalIntIdGivenNothing": "NO VALUE",
8-
"intIdList": "1",
9-
"nullableIntIdList": "1, 0",
10-
"optionalIntIdList": "1",
3+
"intId": 1,
4+
"nullableIntId": 1,
5+
"nullableIntIdGivenNull": null,
6+
"optionalIntId": 1,
7+
"optionalIntIdGivenNothing": null,
8+
"intIdList": [
9+
1
10+
],
11+
"nullableIntIdList": [
12+
1,
13+
null
14+
],
15+
"optionalIntIdList": [
16+
1
17+
],
1118
"stringId": "abc",
1219
"nullableStringId": "abc",
13-
"nullableStringIdGivenNull": "null",
20+
"nullableStringIdGivenNull": null,
1421
"optionalStringId": "abc",
15-
"optionalStringIdGivenNothing": "NO VALUE",
16-
"stringIdList": "abc",
17-
"nullableStringIdList": "abc, null",
18-
"optionalStringIdList": "abc",
22+
"optionalStringIdGivenNothing": null,
23+
"stringIdList": [
24+
"abc"
25+
],
26+
"nullableStringIdList": [
27+
"abc",
28+
null
29+
],
30+
"optionalStringIdList": [
31+
"abc"
32+
],
1933
"guidId": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
2034
"nullableGuidId": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
21-
"nullableGuidIdGivenNull": "null",
35+
"nullableGuidIdGivenNull": null,
2236
"optionalGuidId": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
23-
"optionalGuidIdGivenNothing": "NO VALUE",
24-
"guidIdList": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5, 26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
25-
"nullableGuidIdList": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5, 00000000-0000-0000-0000-000000000000, 26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
26-
"optionalGuidIdList": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5, 26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
37+
"optionalGuidIdGivenNothing": null,
38+
"guidIdList": [
39+
"26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
40+
"26a2dc8f-4dab-408c-88c6-523a0a89a2b5"
41+
],
42+
"nullableGuidIdList": [
43+
"26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
44+
null,
45+
"26a2dc8f-4dab-408c-88c6-523a0a89a2b5"
46+
],
47+
"optionalGuidIdList": [
48+
"26a2dc8f-4dab-408c-88c6-523a0a89a2b5",
49+
"26a2dc8f-4dab-408c-88c6-523a0a89a2b5"
50+
],
2751
"customId": "1-2",
2852
"nullableCustomId": "1-2",
2953
"nullableCustomIdGivenNull": "null",
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
{
2-
"result": "{\n \"data\": {\n \"foo\": {\n \"someId\": \"QmFyOjE=\",\n \"someNullableId\": null,\n \"someIds\": [\n \"QmF6OjE=\"\n ],\n \"someNullableIds\": [\n \"QmF6OjA=\",\n \"QmF6OjE=\"\n ]\n }\n }\n}",
1+
{
2+
"result": "{\n \"data\": {\n \"foo\": {\n \"someId\": \"QmFyOjE=\",\n \"someNullableId\": null,\n \"someIds\": [\n \"QmF6OjE=\"\n ],\n \"someNullableIds\": [\n null,\n \"QmF6OjE=\"\n ]\n }\n }\n}",
33
"someId": "U29tZTox",
44
"someIntId": "U29tZTox"
55
}

src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_Type_Is_Correctly_Inferred.snap

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
schema {
1+
schema {
22
query: Query
33
}
44

@@ -23,26 +23,26 @@ type FooPayload implements IFooPayload {
2323
}
2424

2525
type Query {
26-
intId(id: ID!): String!
27-
intIdList(id: [ID!]!): String!
28-
nullableIntId(id: ID): String!
29-
nullableIntIdList(id: [ID]!): String!
30-
optionalIntId(id: ID = "UXVlcnk6MA=="): String!
31-
optionalIntIdList(id: [ID!] = [ ]): String!
26+
intId(id: ID!): Int!
27+
intIdList(ids: [ID!]!): [Int!]!
28+
nullableIntId(id: ID): Int
29+
nullableIntIdList(ids: [ID]!): [Int]!
30+
optionalIntId(id: ID = "UXVlcnk6MA=="): Int
31+
optionalIntIdList(ids: [ID!] = [ ]): [Int!]
3232
stringId(id: ID!): String!
33-
stringIdList(id: [ID!]!): String!
34-
nullableStringId(id: ID): String!
35-
nullableStringIdList(id: [ID]!): String!
36-
optionalStringId(id: ID = "UXVlcnk6"): String!
37-
optionalStringIdList(id: [ID] = [ ]): String!
38-
guidId(id: ID!): String!
39-
guidIdList(id: [ID!]!): String!
40-
nullableGuidId(id: ID): String!
41-
nullableGuidIdList(id: [ID]!): String!
42-
optionalGuidId(id: ID = "UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA=="): String!
43-
optionalGuidIdList(id: [ID] = [ ]): String!
44-
interceptedId(id: ID!): String!
45-
interceptedIds(id: [ID!]!): String!
33+
stringIdList(ids: [ID!]!): [String!]!
34+
nullableStringId(id: ID): String
35+
nullableStringIdList(ids: [ID]!): [String]!
36+
optionalStringId(id: ID = "UXVlcnk6"): String
37+
optionalStringIdList(ids: [ID] = [ ]): [String!]
38+
guidId(id: ID!): UUID!
39+
guidIdList(ids: [ID!]!): [UUID!]!
40+
nullableGuidId(id: ID): UUID
41+
nullableGuidIdList(ids: [ID]!): [UUID]!
42+
optionalGuidId(id: ID = "UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA=="): UUID
43+
optionalGuidIdList(ids: [ID] = [ ]): [UUID!]
44+
interceptedId(id: ID!): Int!
45+
interceptedIds(ids: [ID!]!): [Int!]!
4646
customId(id: ID!): String!
4747
nullableCustomId(id: ID): String!
4848
customIds(ids: [ID!]!): String!
@@ -60,3 +60,8 @@ input FooInput {
6060
interceptedId: ID
6161
interceptedIds: [ID!]
6262
}
63+
64+
"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions."
65+
directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR
66+
67+
scalar UUID @specifiedBy(url: "https:\/\/tools.ietf.org\/html\/rfc4122")
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
{
1+
{
22
"data": {
3-
"interceptedId": "1",
4-
"interceptedIds": "1, 2"
3+
"interceptedId": 1,
4+
"interceptedIds": [
5+
1,
6+
2
7+
]
58
}
69
}

0 commit comments

Comments
 (0)