Skip to content

Commit 002da3a

Browse files
authoredApr 20, 2021
Fixed issue where types were merged incorrectly (#3554)
1 parent cb60fbd commit 002da3a

File tree

9 files changed

+155
-57
lines changed

9 files changed

+155
-57
lines changed
 

‎src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs

+6-9
Original file line numberDiff line numberDiff line change
@@ -525,15 +525,12 @@ private void CompileResolvers()
525525
RegisteredResolver registered = item.Value;
526526
if (registered.Field is FieldMember member)
527527
{
528-
ResolverDescriptor descriptor =
529-
registered.IsSourceResolver
530-
? new ResolverDescriptor(
531-
registered.SourceType,
532-
member)
533-
: new ResolverDescriptor(
534-
registered.ResolverType,
535-
registered.SourceType,
536-
member);
528+
ResolverDescriptor descriptor = new(
529+
registered.SourceType,
530+
member,
531+
resolverType: registered.IsSourceResolver
532+
? null
533+
: registered.ResolverType);
537534
_resolvers[item.Key] = registered.WithField(
538535
ResolverCompiler.Resolve.Compile(descriptor));
539536
}
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,44 @@
11
using System;
22

3+
#nullable enable
4+
35
namespace HotChocolate.Resolvers.Expressions
46
{
57
/// <summary>
68
/// Describes a resolver that is based on a resolver type.
79
/// </summary>
810
internal class ResolverDescriptor
911
{
12+
/// <summary>
13+
/// Creates a new instance of <see cref="ResolverType"/>
14+
/// </summary>
1015
public ResolverDescriptor(
11-
Type resolverType,
1216
Type sourceType,
13-
FieldMember field)
17+
FieldMember field,
18+
Type? resolverType = null)
1419
{
15-
ResolverType = resolverType
16-
?? throw new ArgumentNullException(nameof(resolverType));
1720
SourceType = sourceType
1821
?? throw new ArgumentNullException(nameof(sourceType));
1922
Field = field
2023
?? throw new ArgumentNullException(nameof(field));
21-
}
2224

23-
public ResolverDescriptor(
24-
Type sourceType,
25-
FieldMember field)
26-
{
27-
SourceType = sourceType
28-
?? throw new ArgumentNullException(nameof(sourceType));
29-
Field = field
30-
?? throw new ArgumentNullException(nameof(field));
25+
ResolverType = resolverType == typeof(object) ? null : resolverType;
3126
}
3227

33-
public Type ResolverType { get; }
28+
/// <summary>
29+
/// Gets the resolver type.
30+
/// If a resolver type is the <see cref="Field"/> belongs to this type.
31+
/// </summary>
32+
public Type? ResolverType { get; }
3433

34+
/// <summary>
35+
/// Gets the source type aka runtime type of a GraphQL type.
36+
/// </summary>
3537
public Type SourceType { get; }
3638

39+
/// <summary>
40+
/// Gets the member that shall be compiled to a resolver.
41+
/// </summary>
3742
public FieldMember Field { get; }
3843
}
3944
}

‎src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectTypeDefinition.cs

+20-8
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public override Type RuntimeType
3939
public bool IsExtension { get; set; }
4040

4141
public IList<ITypeReference> Interfaces =>
42-
_interfaces ??=new List<ITypeReference>();
42+
_interfaces ??= new List<ITypeReference>();
4343

4444
public IBindableList<ObjectFieldDefinition> Fields { get; } =
4545
new BindableList<ObjectFieldDefinition>();
@@ -168,14 +168,14 @@ protected internal void MergeInto(ObjectTypeDefinition target)
168168

169169
if (removeField)
170170
{
171-
if(targetField is not null)
171+
if (targetField is not null)
172172
{
173173
target.Fields.Remove(targetField);
174174
}
175175
}
176176
else if (targetField is null || replaceField)
177177
{
178-
if(targetField is not null)
178+
if (targetField is not null)
179179
{
180180
target.Fields.Remove(targetField);
181181
}
@@ -184,21 +184,33 @@ protected internal void MergeInto(ObjectTypeDefinition target)
184184
field.CopyTo(newField);
185185
newField.SourceType = target.RuntimeType;
186186

187-
if (newField.Member is not null && newField.ResolverMember is null)
188-
{
189-
newField.ResolverMember = newField.Member;
190-
newField.Member = targetField?.Member;
191-
}
187+
SetResolverMember(newField, targetField);
192188

193189
target.Fields.Add(newField);
194190
}
195191
else
196192
{
193+
SetResolverMember(field, targetField);
197194
field.MergeInto(targetField);
198195
}
199196
}
200197

201198
target.IsOfType ??= IsOfType;
202199
}
200+
201+
private static void SetResolverMember(
202+
ObjectFieldDefinition sourceField,
203+
ObjectFieldDefinition? targetField)
204+
{
205+
// we prepare the field that is merged in to use the resolver member instead of member.
206+
// this will ensure that the original source type member is preserved after we have
207+
// merged the type extensions.
208+
209+
if (sourceField.Member is not null && sourceField.ResolverMember is null)
210+
{
211+
sourceField.ResolverMember = sourceField.Member;
212+
sourceField.Member = targetField?.Member;
213+
}
214+
}
203215
}
204216
}

‎src/HotChocolate/Core/src/Types/Types/Relay/Descriptors/NodeDescriptorBase.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ public IObjectFieldDescriptor ResolveNodeWith<TResolver>(
6969
FieldResolver resolver =
7070
ResolverCompiler.Resolve.Compile(
7171
new ResolverDescriptor(
72-
typeof(TResolver),
7372
typeof(object),
74-
new FieldMember("_", "_", m)));
73+
new FieldMember("_", "_", m),
74+
resolverType: typeof(TResolver)));
7575
return ResolveNode(resolver.Resolver);
7676
}
7777

@@ -90,9 +90,9 @@ public IObjectFieldDescriptor ResolveNodeWith(MethodInfo method)
9090
FieldResolver resolver =
9191
ResolverCompiler.Resolve.Compile(
9292
new ResolverDescriptor(
93-
method.DeclaringType ?? typeof(object),
9493
typeof(object),
95-
new FieldMember("_", "_", method)));
94+
new FieldMember("_", "_", method),
95+
resolverType: method.DeclaringType ?? typeof(object)));
9696

9797
return ResolveNode(resolver.Resolver);
9898
}

‎src/HotChocolate/Core/src/Types/Types/Relay/Descriptors/NodeDescriptor~2.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ public IObjectFieldDescriptor ResolveNodeWith<TResolver>(
7171
FieldResolver resolver =
7272
ResolverCompiler.Resolve.Compile(
7373
new ResolverDescriptor(
74-
typeof(TResolver),
7574
typeof(object),
76-
new FieldMember("_", "_", m)));
75+
new FieldMember("_", "_", m),
76+
resolverType: typeof(TResolver)));
7777
return ResolveNode(resolver.Resolver);
7878
}
7979

@@ -93,9 +93,9 @@ public IObjectFieldDescriptor ResolveNodeWith(
9393
FieldResolver resolver =
9494
ResolverCompiler.Resolve.Compile(
9595
new ResolverDescriptor(
96-
method.DeclaringType ?? typeof(object),
9796
typeof(object),
98-
new FieldMember("_", "_", method)));
97+
new FieldMember("_", "_", method),
98+
resolverType: method.DeclaringType ?? typeof(object)));
9999
return ResolveNode(resolver.Resolver);
100100
}
101101

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Linq;
2+
using HotChocolate.Resolvers.Expressions;
3+
using Xunit;
4+
5+
namespace HotChocolate.Resolvers
6+
{
7+
public class ResolverDescriptorTests
8+
{
9+
[Fact]
10+
public void Create_With_ResolverType_Object()
11+
{
12+
var descriptor = new ResolverDescriptor(
13+
typeof(string),
14+
new FieldMember("a", "b", typeof(object).GetMembers().First()),
15+
resolverType: typeof(object));
16+
17+
Assert.Equal(typeof(string), descriptor.SourceType);
18+
Assert.Null(descriptor.ResolverType);
19+
Assert.NotNull(descriptor.Field.Member);
20+
Assert.Equal("a", descriptor.Field.TypeName.Value);
21+
Assert.Equal("b", descriptor.Field.FieldName.Value);
22+
}
23+
24+
[Fact]
25+
public void Create_With_ResolverType_Null()
26+
{
27+
var descriptor = new ResolverDescriptor(
28+
typeof(string),
29+
new FieldMember("a", "b", typeof(object).GetMembers().First()));
30+
31+
Assert.Equal(typeof(string), descriptor.SourceType);
32+
Assert.Null(descriptor.ResolverType);
33+
Assert.NotNull(descriptor.Field.Member);
34+
Assert.Equal("a", descriptor.Field.TypeName.Value);
35+
Assert.Equal("b", descriptor.Field.FieldName.Value);
36+
}
37+
38+
[Fact]
39+
public void Create_With_ResolverType_Int()
40+
{
41+
var descriptor = new ResolverDescriptor(
42+
typeof(string),
43+
new FieldMember("a", "b", typeof(object).GetMembers().First()),
44+
resolverType: typeof(int));
45+
46+
Assert.Equal(typeof(string), descriptor.SourceType);
47+
Assert.Equal(typeof(int), descriptor.ResolverType);
48+
Assert.NotNull(descriptor.Field.Member);
49+
Assert.Equal("a", descriptor.Field.TypeName.Value);
50+
Assert.Equal("b", descriptor.Field.FieldName.Value);
51+
}
52+
}
53+
}

‎src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverPropertyGeneratorTests.cs

+18-18
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,9 @@ public async Task Compile_TaskObjMethod_NoParams_Resolver()
265265
Type type = typeof(Resolvers);
266266
MemberInfo resolverMember = type.GetMethod(nameof(Resolvers.ObjectTaskResolver));
267267
var resolverDescriptor = new ResolverDescriptor(
268-
type,
269268
typeof(Entity),
270-
new FieldMember("A", "b", resolverMember!));
269+
new FieldMember("A", "b", resolverMember!),
270+
resolverType: type);
271271

272272
// act
273273
var compiler = new ResolveCompiler();
@@ -287,9 +287,9 @@ public async Task Compile_TaskStringMethod_NoParams_Resolver()
287287
Type type = typeof(Resolvers);
288288
MemberInfo resolverMember = type.GetMethod(nameof(Resolvers.StringTaskResolver));
289289
var resolverDescriptor = new ResolverDescriptor(
290-
type,
291290
typeof(Entity),
292-
new FieldMember("A", "b", resolverMember!));
291+
new FieldMember("A", "b", resolverMember!),
292+
resolverType: type);
293293

294294
// act
295295
var compiler = new ResolveCompiler();
@@ -310,9 +310,9 @@ public async Task Compile_TaskStringMethod_WithParams_Resolver()
310310
MemberInfo resolverMember =
311311
type.GetMethod(nameof(Resolvers.StringTaskResolverWithArg));
312312
var resolverDescriptor = new ResolverDescriptor(
313-
type,
314313
typeof(Entity),
315-
new FieldMember("A", "b", resolverMember!));
314+
new FieldMember("A", "b", resolverMember!),
315+
resolverType: type);
316316

317317
// act
318318
var compiler = new ResolveCompiler();
@@ -333,9 +333,9 @@ public async Task Compile_ObjMethod_NoParams_Resolver()
333333
Type type = typeof(Resolvers);
334334
MemberInfo resolverMember = type.GetMethod(nameof(Resolvers.ObjectResolver));
335335
var resolverDescriptor = new ResolverDescriptor(
336-
type,
337336
typeof(Entity),
338-
new FieldMember("A", "b", resolverMember!));
337+
new FieldMember("A", "b", resolverMember!),
338+
resolverType: type);
339339

340340
// act
341341
var compiler = new ResolveCompiler();
@@ -355,9 +355,9 @@ public async Task Compile_StringMethod_NoParams_Resolver()
355355
Type type = typeof(Resolvers);
356356
MemberInfo resolverMember = type.GetMethod(nameof(Resolvers.StringResolver));
357357
var resolverDescriptor = new ResolverDescriptor(
358-
type,
359358
typeof(Entity),
360-
new FieldMember("A", "b", resolverMember!));
359+
new FieldMember("A", "b", resolverMember!),
360+
type);
361361

362362
// act
363363
var compiler = new ResolveCompiler();
@@ -378,9 +378,9 @@ public async Task Compile_StringMethod_WithParams_Resolver()
378378
MemberInfo resolverMember =
379379
type.GetMethod(nameof(Resolvers.StringResolverWithArg));
380380
var resolverDescriptor = new ResolverDescriptor(
381-
type,
382381
typeof(Entity),
383-
new FieldMember("A", "b", resolverMember!));
382+
new FieldMember("A", "b", resolverMember!),
383+
type);
384384

385385
// act
386386
var compiler = new ResolveCompiler();
@@ -402,9 +402,9 @@ public async Task Compile_ObjTaskProperty_Resolver()
402402
MemberInfo resolverMember =
403403
type.GetProperty("ObjectTaskStringProp");
404404
var resolverDescriptor = new ResolverDescriptor(
405-
type,
406405
typeof(Entity),
407-
new FieldMember("A", "b", resolverMember!));
406+
new FieldMember("A", "b", resolverMember!),
407+
resolverType: type);
408408

409409
// act
410410
var compiler = new ResolveCompiler();
@@ -425,9 +425,9 @@ public async Task Compile_StringTaskProperty_Resolver()
425425
MemberInfo resolverMember =
426426
type.GetProperty("StringTaskResolverProp");
427427
var resolverDescriptor = new ResolverDescriptor(
428-
type,
429428
typeof(Entity),
430-
new FieldMember("A", "b", resolverMember!));
429+
new FieldMember("A", "b", resolverMember!),
430+
resolverType: type);
431431

432432
// act
433433
var compiler = new ResolveCompiler();
@@ -448,9 +448,9 @@ public async Task Compile_StringProperty_Resolver()
448448
MemberInfo resolverMember =
449449
type.GetProperty("StringProp");
450450
var resolverDescriptor = new ResolverDescriptor(
451-
type,
452451
typeof(Entity),
453-
new FieldMember("A", "b", resolverMember!));
452+
new FieldMember("A", "b", resolverMember!),
453+
resolverType: type);
454454

455455
// act
456456
var compiler = new ResolveCompiler();

‎src/HotChocolate/Core/test/Types.Tests/Types/ObjectTypeExtensionTests.cs

+26
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,20 @@ public async Task Replace_Field_With_The_Same_Name_Execute()
588588
.MatchSnapshotAsync();
589589
}
590590

591+
[Fact]
592+
public async Task Extended_Field_Overwrites_Extended_Field()
593+
{
594+
Snapshot.FullName();
595+
596+
await new ServiceCollection()
597+
.AddGraphQL()
598+
.AddQueryType()
599+
.AddTypeExtension<ExtensionA>()
600+
.AddTypeExtension<ExtensionB>()
601+
.ExecuteRequestAsync("{ foo }")
602+
.MatchSnapshotAsync();
603+
}
604+
591605
public class FooType
592606
: ObjectType<Foo>
593607
{
@@ -843,5 +857,17 @@ public class Replace_Field_PersonResolvers_2
843857
public string SomeId([Parent] IPersonDto dto, string arg = "abc") =>
844858
dto.SomeId() + arg;
845859
}
860+
861+
[ExtendObjectType(OperationTypeNames.Query)]
862+
public class ExtensionA
863+
{
864+
public string Foo() => "abc";
865+
}
866+
867+
[ExtendObjectType(OperationTypeNames.Query)]
868+
public class ExtensionB
869+
{
870+
public string Foo() => "def";
871+
}
846872
}
847873
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"data": {
3+
"foo": "def"
4+
}
5+
}

0 commit comments

Comments
 (0)
Please sign in to comment.