Skip to content

Commit 531ca73

Browse files
tobias-tenglermichaelstaib
authored andcommitted
Correctly handle null for lists of nullable value-type IDs (#7933)
1 parent 2c86080 commit 531ca73

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
@@ -52,25 +52,25 @@ query foo(
5252
nullableIntIdGivenNull: nullableIntId(id: $nullIntId)
5353
optionalIntId(id: $intId)
5454
optionalIntIdGivenNothing: optionalIntId
55-
intIdList(id: [$intId])
56-
nullableIntIdList(id: [$intId, $nullIntId])
57-
optionalIntIdList(id: [$intId])
55+
intIdList(ids: [$intId])
56+
nullableIntIdList(ids: [$intId, $nullIntId])
57+
optionalIntIdList(ids: [$intId])
5858
stringId(id: $stringId)
5959
nullableStringId(id: $stringId)
6060
nullableStringIdGivenNull: nullableStringId(id: $nullStringId)
6161
optionalStringId(id: $stringId)
6262
optionalStringIdGivenNothing: optionalStringId
63-
stringIdList(id: [$stringId])
64-
nullableStringIdList(id: [$stringId, $nullStringId])
65-
optionalStringIdList(id: [$stringId])
63+
stringIdList(ids: [$stringId])
64+
nullableStringIdList(ids: [$stringId, $nullStringId])
65+
optionalStringIdList(ids: [$stringId])
6666
guidId(id: $guidId)
6767
nullableGuidId(id: $guidId)
6868
nullableGuidIdGivenNull: nullableGuidId(id: $nullGuidId)
6969
optionalGuidId(id: $guidId)
7070
optionalGuidIdGivenNothing: optionalGuidId
71-
guidIdList(id: [$guidId $guidId])
72-
nullableGuidIdList(id: [$guidId $nullGuidId $guidId])
73-
optionalGuidIdList(id: [$guidId $guidId])
71+
guidIdList(ids: [$guidId $guidId])
72+
nullableGuidIdList(ids: [$guidId $nullGuidId $guidId])
73+
optionalGuidIdList(ids: [$guidId $guidId])
7474
customId(id: $customId)
7575
nullableCustomId(id: $customId)
7676
nullableCustomIdGivenNull: nullableCustomId(id: $nullCustomId)
@@ -107,7 +107,7 @@ public async Task InterceptedId_On_Arguments()
107107
OperationRequestBuilder.New()
108108
.SetDocument(@"query foo {
109109
interceptedId(id: 1)
110-
interceptedIds(id: [1, 2])
110+
interceptedIds(ids: [1, 2])
111111
}")
112112
.Build());
113113

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

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

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

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

519516
public string NullableCustomId([ID] StronglyTypedId? id) =>
520517
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)