Skip to content

Commit cbdd906

Browse files
authored
Fix argument type mapping regression (graphql-dotnet#2448)
1 parent 5415c6a commit cbdd906

File tree

3 files changed

+208
-21
lines changed

3 files changed

+208
-21
lines changed

src/GraphQL.Tests/Utilities/GetGraphTypeFromTypeTests.cs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,176 @@ public void GetGraphTypeFromType_Matrix(Type type, bool nullable, TypeMappingMod
223223
}
224224
}
225225

226+
[Theory]
227+
[InlineData(typeof(IntGraphType), typeof(IntGraphType))]
228+
[InlineData(typeof(StringGraphType), typeof(StringGraphType))]
229+
[InlineData(typeof(ListGraphType<StringGraphType>), typeof(ListGraphType<StringGraphType>))]
230+
[InlineData(typeof(NonNullGraphType<StringGraphType>), typeof(NonNullGraphType<StringGraphType>))]
231+
[InlineData(typeof(MyClassObjectType), typeof(MyClassObjectType))]
232+
[InlineData(typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassObjectType>>>>), typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassObjectType>>>>))]
233+
[InlineData(typeof(GraphQLClrOutputTypeReference<int>), typeof(IntGraphType))]
234+
[InlineData(typeof(GraphQLClrOutputTypeReference<string>), typeof(StringGraphType))]
235+
[InlineData(typeof(ListGraphType<GraphQLClrOutputTypeReference<string>>), typeof(ListGraphType<StringGraphType>))]
236+
[InlineData(typeof(NonNullGraphType<GraphQLClrOutputTypeReference<string>>), typeof(NonNullGraphType<StringGraphType>))]
237+
[InlineData(typeof(GraphQLClrOutputTypeReference<MyClass>), typeof(MyClassObjectType))]
238+
[InlineData(typeof(GraphQLClrOutputTypeReference<MyEnum>), typeof(EnumerationGraphType<MyEnum>))]
239+
[InlineData(typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<GraphQLClrOutputTypeReference<MyClass>>>>>), typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassObjectType>>>>))]
240+
public void OutputTypeIsDereferenced(Type referenceType, Type mappedType)
241+
{
242+
var query = new ObjectGraphType();
243+
query.Field(referenceType, "test");
244+
var schema = new Schema
245+
{
246+
Query = query
247+
};
248+
schema.RegisterTypeMapping(typeof(MyClass), typeof(MyClassObjectType));
249+
schema.RegisterTypeMapping(typeof(MyClass), typeof(MyClassInputType));
250+
schema.Initialize();
251+
schema.Query.Fields.Find("test").Type.ShouldBe(mappedType);
252+
}
253+
254+
[Theory]
255+
[InlineData(typeof(IntGraphType), typeof(IntGraphType))]
256+
[InlineData(typeof(StringGraphType), typeof(StringGraphType))]
257+
[InlineData(typeof(ListGraphType<StringGraphType>), typeof(ListGraphType<StringGraphType>))]
258+
[InlineData(typeof(NonNullGraphType<StringGraphType>), typeof(NonNullGraphType<StringGraphType>))]
259+
[InlineData(typeof(MyClassInputType), typeof(MyClassInputType))]
260+
[InlineData(typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>), typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>))]
261+
[InlineData(typeof(GraphQLClrInputTypeReference<int>), typeof(IntGraphType))]
262+
[InlineData(typeof(GraphQLClrInputTypeReference<string>), typeof(StringGraphType))]
263+
[InlineData(typeof(ListGraphType<GraphQLClrInputTypeReference<string>>), typeof(ListGraphType<StringGraphType>))]
264+
[InlineData(typeof(NonNullGraphType<GraphQLClrInputTypeReference<string>>), typeof(NonNullGraphType<StringGraphType>))]
265+
[InlineData(typeof(GraphQLClrInputTypeReference<MyClass>), typeof(MyClassInputType))]
266+
[InlineData(typeof(GraphQLClrInputTypeReference<MyEnum>), typeof(EnumerationGraphType<MyEnum>))]
267+
[InlineData(typeof(GraphQLClrInputTypeReference<MappedEnum>), typeof(MappedEnumGraphType))]
268+
[InlineData(typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<GraphQLClrInputTypeReference<MyClass>>>>>), typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>))]
269+
public void InputTypeIsDereferenced_Argument(Type referenceType, Type mappedType)
270+
{
271+
var query = new ObjectGraphType();
272+
query.Field(typeof(IntGraphType), "test",
273+
arguments: new QueryArguments
274+
{
275+
new QueryArgument(referenceType) { Name = "arg" }
276+
});
277+
var schema = new Schema
278+
{
279+
Query = query
280+
};
281+
schema.RegisterTypeMapping(typeof(MyClass), typeof(MyClassObjectType));
282+
schema.RegisterTypeMapping(typeof(MyClass), typeof(MyClassInputType));
283+
schema.RegisterTypeMapping(typeof(MappedEnum), typeof(MappedEnumGraphType));
284+
schema.Initialize();
285+
schema.Query.Fields.Find("test").Arguments.Find("arg").Type.ShouldBe(mappedType);
286+
}
287+
288+
[Theory]
289+
[InlineData(typeof(IntGraphType), typeof(IntGraphType))]
290+
[InlineData(typeof(StringGraphType), typeof(StringGraphType))]
291+
[InlineData(typeof(ListGraphType<StringGraphType>), typeof(ListGraphType<StringGraphType>))]
292+
[InlineData(typeof(NonNullGraphType<StringGraphType>), typeof(NonNullGraphType<StringGraphType>))]
293+
[InlineData(typeof(MyClassInputType), typeof(MyClassInputType))]
294+
[InlineData(typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>), typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>))]
295+
[InlineData(typeof(GraphQLClrInputTypeReference<int>), typeof(IntGraphType))]
296+
[InlineData(typeof(GraphQLClrInputTypeReference<string>), typeof(StringGraphType))]
297+
[InlineData(typeof(ListGraphType<GraphQLClrInputTypeReference<string>>), typeof(ListGraphType<StringGraphType>))]
298+
[InlineData(typeof(NonNullGraphType<GraphQLClrInputTypeReference<string>>), typeof(NonNullGraphType<StringGraphType>))]
299+
[InlineData(typeof(GraphQLClrInputTypeReference<MyClass>), typeof(MyClassInputType))]
300+
[InlineData(typeof(GraphQLClrInputTypeReference<MyEnum>), typeof(EnumerationGraphType<MyEnum>))]
301+
[InlineData(typeof(GraphQLClrInputTypeReference<MappedEnum>), typeof(MappedEnumGraphType))]
302+
[InlineData(typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<GraphQLClrInputTypeReference<MyClass>>>>>), typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>))]
303+
public void InputTypeIsDereferenced_DirectiveArgument(Type referenceType, Type mappedType)
304+
{
305+
var query = new ObjectGraphType();
306+
query.Field(typeof(StringGraphType), "test");
307+
var schema = new Schema
308+
{
309+
Query = query
310+
};
311+
var directive = new DirectiveGraphType("MyDirective")
312+
{
313+
Arguments = new QueryArguments
314+
{
315+
new QueryArgument(referenceType) { Name = "arg" }
316+
}
317+
};
318+
directive.Locations.Add(DirectiveLocation.Field);
319+
schema.Directives.Register(directive);
320+
schema.RegisterTypeMapping(typeof(MyClass), typeof(MyClassObjectType));
321+
schema.RegisterTypeMapping(typeof(MyClass), typeof(MyClassInputType));
322+
schema.RegisterTypeMapping(typeof(MappedEnum), typeof(MappedEnumGraphType));
323+
schema.Initialize();
324+
schema.Directives.Find("MyDirective").Arguments.Find("arg").Type.ShouldBe(mappedType);
325+
}
326+
327+
[Theory]
328+
[InlineData(typeof(IntGraphType), typeof(IntGraphType))]
329+
[InlineData(typeof(StringGraphType), typeof(StringGraphType))]
330+
[InlineData(typeof(ListGraphType<StringGraphType>), typeof(ListGraphType<StringGraphType>))]
331+
[InlineData(typeof(NonNullGraphType<StringGraphType>), typeof(NonNullGraphType<StringGraphType>))]
332+
[InlineData(typeof(MyClassInputType), typeof(MyClassInputType))]
333+
[InlineData(typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>), typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>))]
334+
[InlineData(typeof(GraphQLClrInputTypeReference<int>), typeof(IntGraphType))]
335+
[InlineData(typeof(GraphQLClrInputTypeReference<string>), typeof(StringGraphType))]
336+
[InlineData(typeof(ListGraphType<GraphQLClrInputTypeReference<string>>), typeof(ListGraphType<StringGraphType>))]
337+
[InlineData(typeof(NonNullGraphType<GraphQLClrInputTypeReference<string>>), typeof(NonNullGraphType<StringGraphType>))]
338+
[InlineData(typeof(GraphQLClrInputTypeReference<MyClass>), typeof(MyClassInputType))]
339+
[InlineData(typeof(GraphQLClrInputTypeReference<MyEnum>), typeof(EnumerationGraphType<MyEnum>))]
340+
[InlineData(typeof(GraphQLClrInputTypeReference<MappedEnum>), typeof(MappedEnumGraphType))]
341+
[InlineData(typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<GraphQLClrInputTypeReference<MyClass>>>>>), typeof(NonNullGraphType<ListGraphType<ListGraphType<NonNullGraphType<MyClassInputType>>>>))]
342+
public void InputTypeIsDereferenced_InputField(Type referenceType, Type mappedType)
343+
{
344+
var inputType = new InputObjectGraphType();
345+
inputType.Field(referenceType, "field");
346+
var query = new ObjectGraphType();
347+
query.Field(typeof(IntGraphType), "test",
348+
arguments: new QueryArguments
349+
{
350+
new QueryArgument(inputType) { Name = "arg" }
351+
});
352+
var schema = new Schema
353+
{
354+
Query = query
355+
};
356+
schema.RegisterTypeMapping(typeof(MyClass), typeof(MyClassObjectType));
357+
schema.RegisterTypeMapping(typeof(MyClass), typeof(MyClassInputType));
358+
schema.RegisterTypeMapping(typeof(MappedEnum), typeof(MappedEnumGraphType));
359+
schema.Initialize();
360+
var inputTypeActual = schema.Query.Fields.Find("test").Arguments.Find("arg").ResolvedType.ShouldBeOfType<InputObjectGraphType>();
361+
inputTypeActual.ShouldBe(inputType);
362+
inputTypeActual.Fields.Find("field").Type.ShouldBe(mappedType);
363+
}
364+
365+
private class MyClassObjectType : ObjectGraphType<MyClass>
366+
{
367+
public MyClassObjectType()
368+
{
369+
Field<IntGraphType>("field");
370+
}
371+
}
372+
373+
private class MyClassInputType : InputObjectGraphType
374+
{
375+
public MyClassInputType()
376+
{
377+
Field<IntGraphType>("field");
378+
}
379+
}
380+
226381
private class MyClass
227382
{
228383
}
229384

230385
private enum MyEnum
386+
{
387+
Value1
388+
}
389+
390+
private enum MappedEnum
391+
{
392+
Value1
393+
}
394+
395+
private class MappedEnumGraphType : EnumerationGraphType<MappedEnum>
231396
{
232397
}
233398
}

src/GraphQL/Types/Collections/SchemaTypes.cs

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -161,22 +161,7 @@ public SchemaTypes(ISchema schema, IServiceProvider serviceProvider)
161161

162162
foreach (var directive in directives)
163163
{
164-
if (directive.Arguments?.Count > 0)
165-
{
166-
foreach (var arg in directive.Arguments.List)
167-
{
168-
if (arg.ResolvedType != null)
169-
{
170-
AddTypeIfNotRegistered(arg.ResolvedType, ctx);
171-
arg.ResolvedType = ConvertTypeReference(directive, arg.ResolvedType);
172-
}
173-
else
174-
{
175-
AddTypeIfNotRegistered(arg.Type, ctx);
176-
arg.ResolvedType = BuildNamedType(arg.Type, ctx.ResolveType);
177-
}
178-
}
179-
}
164+
HandleDirective(directive, ctx);
180165
}
181166

182167
ApplyTypeReferences();
@@ -461,14 +446,51 @@ private void HandleField(IComplexGraphType parentType, FieldType field, TypeColl
461446
NameValidator.ValidateNameOnSchemaInitialize(arg.Name, NamedElement.Argument);
462447
}
463448

464-
if (arg.ResolvedType != null)
449+
if (arg.ResolvedType == null)
450+
{
451+
if (arg.Type == null)
452+
throw new InvalidOperationException($"Both ResolvedType and Type properties on argument '{parentType?.Name}.{field.Name}.{arg.Name}' are null.");
453+
454+
object typeOrError = RebuildType(arg.Type, true, context.TypeMappings);
455+
if (typeOrError is string error)
456+
throw new InvalidOperationException($"The GraphQL type for argument '{parentType.Name}.{field.Name}.{arg.Name}' could not be derived implicitly. " + error);
457+
arg.Type = (Type)typeOrError;
458+
459+
AddTypeIfNotRegistered(arg.Type, context);
460+
arg.ResolvedType = BuildNamedType(arg.Type, context.ResolveType);
461+
}
462+
else
465463
{
466464
AddTypeIfNotRegistered(arg.ResolvedType, context);
467-
continue;
468465
}
466+
}
467+
}
468+
}
469469

470-
AddTypeIfNotRegistered(arg.Type, context);
471-
arg.ResolvedType = BuildNamedType(arg.Type, context.ResolveType);
470+
private void HandleDirective(DirectiveGraphType directive, TypeCollectionContext context)
471+
{
472+
if (directive.Arguments?.Count > 0)
473+
{
474+
foreach (var arg in directive.Arguments.List)
475+
{
476+
if (arg.ResolvedType == null)
477+
{
478+
if (arg.Type == null)
479+
throw new InvalidOperationException($"Both ResolvedType and Type properties on argument '{directive.Name}.{arg.Name}' are null.");
480+
481+
object typeOrError = RebuildType(arg.Type, true, context.TypeMappings);
482+
if (typeOrError is string error)
483+
throw new InvalidOperationException($"The GraphQL type for argument '{directive.Name}.{arg.Name}' could not be derived implicitly. " + error);
484+
arg.Type = (Type)typeOrError;
485+
486+
AddTypeIfNotRegistered(arg.Type, context);
487+
arg.ResolvedType = BuildNamedType(arg.Type, context.ResolveType);
488+
}
489+
else
490+
{
491+
AddTypeIfNotRegistered(arg.ResolvedType, context);
492+
arg.ResolvedType = ConvertTypeReference(directive, arg.ResolvedType);
493+
}
472494
}
473495
}
474496
}

src/GraphQL/Types/QueryArgument.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public IGraphType ResolvedType
9292
public Type Type
9393
{
9494
get => _type;
95-
private set => _type = CheckType(value);
95+
internal set => _type = CheckType(value);
9696
}
9797

9898
private Type CheckType(Type type)

0 commit comments

Comments
 (0)