Skip to content

Commit 0d38466

Browse files
Fix @semanticNonNull on interface id fields (#8305)
1 parent dbe60c1 commit 0d38466

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

src/HotChocolate/Core/src/Types/SemanticNonNullTypeInterceptor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ public override void OnBeforeCompleteType(ITypeCompletionContext completionConte
104104
continue;
105105
}
106106

107+
if (field.Name == "id")
108+
{
109+
continue;
110+
}
111+
107112
var levels = GetSemanticNonNullLevels(field.Type);
108113

109114
if (levels.Count < 1)

src/HotChocolate/Core/test/Types.Tests/SemanticNonNullTests.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using HotChocolate.Execution;
44
using HotChocolate.Tests;
55
using HotChocolate.Types;
6+
using HotChocolate.Types.Descriptors;
67
using HotChocolate.Types.Relay;
78
using Microsoft.Extensions.DependencyInjection;
89

@@ -25,6 +26,20 @@ public async Task Object_With_Id_Field()
2526
.MatchSnapshotAsync();
2627
}
2728

29+
[Fact]
30+
public async Task Interface_With_Id_Field()
31+
{
32+
await new ServiceCollection()
33+
.AddGraphQL()
34+
.AddGlobalObjectIdentification()
35+
.ModifyOptions(o => o.EnableSemanticNonNull = true)
36+
.AddQueryType<QueryWithInteface>()
37+
.AddType<InterfaceImplementingNode>()
38+
.UseField(_ => _ => default)
39+
.BuildSchemaAsync()
40+
.MatchSnapshotAsync();
41+
}
42+
2843
[Fact]
2944
public async Task MutationConventions()
3045
{
@@ -137,6 +152,43 @@ type Foo {
137152
.MatchSnapshotAsync();
138153
}
139154

155+
public class QueryWithInteface
156+
{
157+
public SomeObject GetSomeObject() => new();
158+
}
159+
160+
[Node]
161+
[ImplementsInterface<InterfaceImplementingNode>]
162+
public record SomeObject
163+
{
164+
public int Id { get; set; }
165+
166+
public string Field { get; set; } = default!;
167+
168+
public static SomeObject? Get(int id) => new();
169+
}
170+
171+
public class InterfaceImplementingNode : InterfaceType
172+
{
173+
protected override void Configure(IInterfaceTypeDescriptor descriptor)
174+
{
175+
descriptor.Implements<NodeType>();
176+
descriptor
177+
.Field("field")
178+
.Type("String!");
179+
}
180+
}
181+
182+
[AttributeUsage(AttributeTargets.Class)]
183+
public sealed class ImplementsInterfaceAttribute<T> : ObjectTypeDescriptorAttribute
184+
where T : InterfaceType
185+
{
186+
protected override void OnConfigure(
187+
IDescriptorContext context,
188+
IObjectTypeDescriptor descriptor,
189+
Type type) => descriptor.Implements<T>();
190+
}
191+
140192
public class QueryType : ObjectType
141193
{
142194
protected override void Configure(IObjectTypeDescriptor descriptor)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
schema {
2+
query: QueryWithInteface
3+
}
4+
5+
interface InterfaceImplementingNode implements Node {
6+
field: String @semanticNonNull
7+
id: ID!
8+
}
9+
10+
"The node interface is implemented by entities that have a global unique identifier."
11+
interface Node {
12+
id: ID!
13+
}
14+
15+
type QueryWithInteface {
16+
"Fetches an object given its ID."
17+
node("ID of the object." id: ID!): Node
18+
"Lookup nodes by a list of IDs."
19+
nodes("The list of node IDs." ids: [ID!]!): [Node] @semanticNonNull
20+
someObject: SomeObject @semanticNonNull
21+
}
22+
23+
type SomeObject implements Node & InterfaceImplementingNode {
24+
id: ID!
25+
field: String @semanticNonNull
26+
}
27+
28+
directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION

0 commit comments

Comments
 (0)