Skip to content

Commit 06075c6

Browse files
BUG: TypeExpression type names on TypeExtensions (#139)
* Fixed a bug with type expression naming on extension fields
1 parent 027adde commit 06075c6

14 files changed

+527
-23
lines changed

src/graphql-aspnet/Schemas/GraphTypeExpression_Statics.cs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -156,19 +156,39 @@ public static GraphTypeExpression FromDeclaration(ReadOnlySpan<char> typeExpress
156156
/// <param name="typeWrappers">An optional set of wrappers to use as a set of overrides on the type provided.</param>
157157
/// <returns>GraphFieldOptions.</returns>
158158
public static GraphTypeExpression FromType(Type typeToCheck, MetaGraphTypes[] typeWrappers = null)
159+
{
160+
return FromType(typeToCheck, TypeKind.OBJECT, typeWrappers);
161+
}
162+
163+
/// <summary>
164+
/// Inspects the provided type and generates a type expression to represent it in the object grpah.
165+
/// </summary>
166+
/// <param name="typeToCheck">The complete type specification to check.</param>
167+
/// <param name="typeKind">An explicit typekind to use when determining the name of the <paramref name="typeToCheck"/>
168+
/// that will be included in the expression.</param>
169+
/// <param name="typeWrappers">An optional set of wrappers to use as a set of overrides on the type provided.</param>
170+
/// <returns>GraphFieldOptions.</returns>
171+
public static GraphTypeExpression FromType(Type typeToCheck, TypeKind typeKind, MetaGraphTypes[] typeWrappers = null)
172+
{
173+
var name = GraphTypeNames.ParseName(typeToCheck, typeKind);
174+
return FromType(typeToCheck, name, typeWrappers);
175+
}
176+
177+
/// <summary>
178+
/// Inspects the provided type and generates a type expression to represent it in the object grpah.
179+
/// </summary>
180+
/// <param name="typeToCheck">The complete type specification to check.</param>
181+
/// <param name="typeName">An explicit typename to use in the type expression. This value
182+
/// will override any name gleaned from <paramref name="typeToCheck"/>. </param>
183+
/// <param name="typeWrappers">An optional set of wrappers to use as a set of overrides on the type provided.</param>
184+
/// <returns>GraphFieldOptions.</returns>
185+
public static GraphTypeExpression FromType(Type typeToCheck, string typeName, MetaGraphTypes[] typeWrappers = null)
159186
{
160187
Validation.ThrowIfNull(typeToCheck, nameof(typeToCheck));
188+
typeName = Validation.ThrowIfNullWhiteSpaceOrReturn(typeName, nameof(typeName));
161189

162190
if (typeWrappers != null)
163-
{
164-
typeToCheck = GraphValidation.EliminateWrappersFromCoreType(
165-
typeToCheck,
166-
eliminateEnumerables: true,
167-
eliminateTask: true,
168-
eliminateNullableT: true);
169-
170-
return new GraphTypeExpression(typeToCheck.FriendlyGraphTypeName(), typeWrappers);
171-
}
191+
return new GraphTypeExpression(typeName, typeWrappers);
172192

173193
// strip out Task{T} before doin any type inspections
174194
typeToCheck = GraphValidation.EliminateWrappersFromCoreType(
@@ -207,13 +227,7 @@ public static GraphTypeExpression FromType(Type typeToCheck, MetaGraphTypes[] ty
207227
wrappers.Add(MetaGraphTypes.IsNotNull);
208228
}
209229

210-
typeToCheck = GraphValidation.EliminateWrappersFromCoreType(
211-
typeToCheck,
212-
eliminateEnumerables: false,
213-
eliminateTask: false,
214-
eliminateNullableT: true);
215-
216-
return new GraphTypeExpression(typeToCheck.FriendlyGraphTypeName(), wrappers);
230+
return new GraphTypeExpression(typeName, wrappers);
217231
}
218232

219233
/// <summary>

src/unit-tests/graphql-aspnet-tests/Execution/IntrospectionTests.cs

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,5 +2013,191 @@ public async Task Descriptions_OnInheritedInterfaces_AreRetrievedViaIntrospectio
20132013

20142014
CommonAssertions.AreEqualJsonStrings(expectedResponse, response);
20152015
}
2016+
2017+
[Test]
2018+
public async Task TypeExtension_ReturnsChildObject_WithCustomName_RendersWithCorrectCustomName()
2019+
{
2020+
var server = new TestServerBuilder()
2021+
.AddController<TypeWithNoCustomNameTypeExtensionController>()
2022+
.Build();
2023+
2024+
var context = server.CreateQueryContextBuilder()
2025+
.AddQueryText(@"
2026+
{
2027+
__type(name: ""TypeWithNoCustomName"")
2028+
{
2029+
kind
2030+
name
2031+
fields(includeDeprecated: true) {
2032+
name
2033+
}
2034+
}
2035+
}");
2036+
2037+
var response = await server.RenderResult(context);
2038+
2039+
var expectedResult = @"
2040+
{
2041+
""data"": {
2042+
""__type"": {
2043+
""kind"": ""OBJECT"",
2044+
""name"": ""TypeWithNoCustomName"",
2045+
""fields"": [
2046+
{
2047+
""name"": ""field1""
2048+
},
2049+
{
2050+
""name"": ""fieldTwo""
2051+
}
2052+
]
2053+
}
2054+
}
2055+
}";
2056+
2057+
CommonAssertions.AreEqualJsonStrings(expectedResult, response);
2058+
2059+
Assert.IsNotNull(server.Schema.KnownTypes.FindGraphType("TypeWithNoCustomName"));
2060+
Assert.IsNotNull(server.Schema.KnownTypes.FindGraphType("Type_With_Custom_Name"));
2061+
}
2062+
2063+
[Test]
2064+
public async Task BatchTypeExntesion_ReturnsChildObject_WithCustomName_RendersWithCorrectCustomName()
2065+
{
2066+
var server = new TestServerBuilder()
2067+
.AddController<TypeWithNoCustomNameBatchExtensionController>()
2068+
.Build();
2069+
2070+
var context = server.CreateQueryContextBuilder()
2071+
.AddQueryText(@"
2072+
{
2073+
__type(name: ""TypeWithNoCustomName"")
2074+
{
2075+
kind
2076+
name
2077+
fields(includeDeprecated: true) {
2078+
name
2079+
}
2080+
}
2081+
}");
2082+
2083+
var response = await server.RenderResult(context);
2084+
2085+
var expectedResult = @"
2086+
{
2087+
""data"": {
2088+
""__type"": {
2089+
""kind"": ""OBJECT"",
2090+
""name"": ""TypeWithNoCustomName"",
2091+
""fields"": [
2092+
{
2093+
""name"": ""field1""
2094+
},
2095+
{
2096+
""name"": ""fieldTwo""
2097+
}
2098+
]
2099+
}
2100+
}
2101+
}";
2102+
2103+
CommonAssertions.AreEqualJsonStrings(expectedResult, response);
2104+
2105+
Assert.IsNotNull(server.Schema.KnownTypes.FindGraphType("TypeWithNoCustomName"));
2106+
Assert.IsNotNull(server.Schema.KnownTypes.FindGraphType("Type_With_Custom_Name"));
2107+
}
2108+
2109+
[Test]
2110+
public async Task TypeExtension_Type_ThatHasCustomName_AndChildOfSameType_RendersProperly()
2111+
{
2112+
var server = new TestServerBuilder()
2113+
.AddController<TypeWithCustomNameTypeExtensionController>()
2114+
.Build();
2115+
2116+
var context = server.CreateQueryContextBuilder()
2117+
.AddQueryText(@"
2118+
{
2119+
#custom named
2120+
__type(name: ""Type_With_Custom_Name"")
2121+
{
2122+
kind
2123+
name
2124+
fields(includeDeprecated: true) {
2125+
name
2126+
}
2127+
}
2128+
}");
2129+
2130+
var response = await server.RenderResult(context);
2131+
2132+
var expectedResult = @"
2133+
{
2134+
""data"": {
2135+
""__type"": {
2136+
""kind"": ""OBJECT"",
2137+
""name"": ""Type_With_Custom_Name"",
2138+
""fields"": [
2139+
{
2140+
""name"": ""field1""
2141+
},
2142+
{
2143+
""name"": ""fieldTwo""
2144+
},
2145+
{
2146+
""name"": ""fieldThree""
2147+
}
2148+
]
2149+
}
2150+
}
2151+
}";
2152+
2153+
CommonAssertions.AreEqualJsonStrings(expectedResult, response);
2154+
}
2155+
2156+
[Test]
2157+
public async Task BatchTypeExtension_Type_ThatHasCustomName_AndChildrenOfSameType_RendersProperly()
2158+
{
2159+
var server = new TestServerBuilder()
2160+
.AddController<TypeWithCustomNameBatchExtensionController>()
2161+
.Build();
2162+
2163+
var context = server.CreateQueryContextBuilder()
2164+
.AddQueryText(@"
2165+
{
2166+
#custom named
2167+
__type(name: ""Type_With_Custom_Name"")
2168+
{
2169+
kind
2170+
name
2171+
fields(includeDeprecated: true) {
2172+
name
2173+
}
2174+
}
2175+
}");
2176+
2177+
var response = await server.RenderResult(context);
2178+
2179+
var expectedResult = @"
2180+
{
2181+
""data"": {
2182+
""__type"": {
2183+
""kind"": ""OBJECT"",
2184+
""name"": ""Type_With_Custom_Name"",
2185+
""fields"": [
2186+
{
2187+
""name"": ""field1""
2188+
},
2189+
{
2190+
""name"": ""fieldTwo""
2191+
},
2192+
{
2193+
""name"": ""fieldThree""
2194+
}
2195+
]
2196+
}
2197+
}
2198+
}";
2199+
2200+
CommonAssertions.AreEqualJsonStrings(expectedResult, response);
2201+
}
20162202
}
20172203
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// *************************************************************
2+
// project: graphql-aspnet
3+
// --
4+
// repo: https://github.com/graphql-aspnet
5+
// docs: https://graphql-aspnet.github.io
6+
// --
7+
// License: MIT
8+
// *************************************************************
9+
10+
namespace GraphQL.AspNet.Tests.Execution.TestData.IntrospectionTestData
11+
{
12+
using GraphQL.AspNet.Attributes;
13+
14+
[GraphType("Type_With_Custom_Name")]
15+
public class TypeWithCustomName
16+
{
17+
public int Field1 { get; set; }
18+
19+
[GraphField("FieldTwo")]
20+
public string Field2 { get; set; }
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// *************************************************************
2+
// project: graphql-aspnet
3+
// --
4+
// repo: https://github.com/graphql-aspnet
5+
// docs: https://graphql-aspnet.github.io
6+
// --
7+
// License: MIT
8+
// *************************************************************
9+
10+
namespace GraphQL.AspNet.Tests.Execution.TestData.IntrospectionTestData
11+
{
12+
using System.Collections;
13+
using System.Collections.Generic;
14+
using GraphQL.AspNet.Attributes;
15+
using GraphQL.AspNet.Controllers;
16+
17+
public class TypeWithCustomNameBatchExtensionController : GraphController
18+
{
19+
[BatchTypeExtension(typeof(TypeWithCustomName), "fieldThree")]
20+
public IDictionary<TypeWithCustomName, IEnumerable<TypeWithCustomName>> TypeWithCustomNameField3(
21+
IEnumerable<TypeWithCustomName> parents)
22+
{
23+
var dic = new Dictionary<TypeWithCustomName, IEnumerable<TypeWithCustomName>>();
24+
var i = 0;
25+
foreach (var item in parents)
26+
{
27+
dic.Add(item, new List<TypeWithCustomName>()
28+
{
29+
new TypeWithCustomName()
30+
{
31+
Field1 = i++,
32+
Field2 = $"Child_Of_{item.Field2}",
33+
},
34+
new TypeWithCustomName()
35+
{
36+
Field1 = i++,
37+
Field2 = $"Child_Of_{item.Field2}",
38+
},
39+
});
40+
}
41+
42+
return dic;
43+
}
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// *************************************************************
2+
// project: graphql-aspnet
3+
// --
4+
// repo: https://github.com/graphql-aspnet
5+
// docs: https://graphql-aspnet.github.io
6+
// --
7+
// License: MIT
8+
// *************************************************************
9+
10+
namespace GraphQL.AspNet.Tests.Execution.TestData.IntrospectionTestData
11+
{
12+
using GraphQL.AspNet.Attributes;
13+
using GraphQL.AspNet.Controllers;
14+
15+
public class TypeWithCustomNameTypeExtensionController : GraphController
16+
{
17+
[TypeExtension(typeof(TypeWithCustomName), "fieldThree")]
18+
public TypeWithCustomName TypeWithCustomNameField3(
19+
TypeWithCustomName parent)
20+
{
21+
return new TypeWithCustomName()
22+
{
23+
Field1 = 0,
24+
Field2 = $"Child_Of_{parent.Field2}",
25+
};
26+
}
27+
}
28+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// *************************************************************
2+
// project: graphql-aspnet
3+
// --
4+
// repo: https://github.com/graphql-aspnet
5+
// docs: https://graphql-aspnet.github.io
6+
// --
7+
// License: MIT
8+
// *************************************************************
9+
10+
namespace GraphQL.AspNet.Tests.Execution.TestData.IntrospectionTestData
11+
{
12+
public class TypeWithNoCustomName
13+
{
14+
public string Field1 { get; set; }
15+
}
16+
}

0 commit comments

Comments
 (0)