Skip to content

Commit

Permalink
Fixed naming issues with generic types (#6461)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Staib <[email protected]>
  • Loading branch information
N-Olbert and michaelstaib authored Aug 21, 2023
1 parent 1e78ece commit a13fa04
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 15 deletions.
51 changes: 36 additions & 15 deletions src/HotChocolate/Core/src/Abstractions/NameFormattingHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using HotChocolate.Properties;
using HotChocolate.Utilities;
Expand All @@ -17,7 +18,7 @@ internal static class NameFormattingHelpers
{
private const string _get = "Get";
private const string _async = "Async";
private const string _typePostfix = "`1";
private const char _genericTypeDelimiter = '`';

public static string GetGraphQLName(this Type type)
{
Expand All @@ -26,10 +27,7 @@ public static string GetGraphQLName(this Type type)
throw new ArgumentNullException(nameof(type));
}

var typeInfo = type.GetTypeInfo();
var name = typeInfo.IsDefined(typeof(GraphQLNameAttribute), false)
? typeInfo.GetCustomAttribute<GraphQLNameAttribute>()!.Name
: GetFromType(typeInfo);
var name = GetFromType(type);

return NameUtils.MakeValidGraphQLName(name)!;
}
Expand Down Expand Up @@ -184,21 +182,44 @@ public static bool IsDeprecated(

private static string GetFromType(Type type)
{
if (type.GetTypeInfo().IsGenericType)
var typeName = type.IsDefined(typeof(GraphQLNameAttribute), false)
? type.GetCustomAttribute<GraphQLNameAttribute>()!.Name
: null;

if (type.IsGenericType)
{
var name = type.GetTypeInfo()
.GetGenericTypeDefinition()
.Name;
if (typeName == null)
{
typeName = type.GetGenericTypeDefinition().Name;

var nameSpan = typeName.AsSpan();
var index = nameSpan.LastIndexOf(_genericTypeDelimiter);

if (index >= 0)
{
nameSpan = nameSpan.Slice(0, index);
}

typeName = nameSpan.ToString();
}

name = name.Substring(0, name.Length - _typePostfix.Length);
var arguments = type.GetGenericArguments();
var stringBuilder = new StringBuilder(typeName).Append("Of");

var arguments = type
.GetTypeInfo().GenericTypeArguments
.Select(GetFromType);
for (var i = 0; i < arguments.Length; i++)
{
if (i > 0)
{
stringBuilder.Append("And");
}

return $"{name}Of{string.Join("And", arguments)}";
stringBuilder.Append(GetFromType(arguments[i]));
}

return stringBuilder.ToString();
}
return type.Name;

return typeName ?? type.Name;
}

public static unsafe string FormatFieldName(string fieldName)
Expand Down
84 changes: 84 additions & 0 deletions src/HotChocolate/Core/test/Types.Tests/GenericTypeNamingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Threading.Tasks;
using HotChocolate.Execution;
using HotChocolate.Tests;
using Microsoft.Extensions.DependencyInjection;

#nullable enable

namespace HotChocolate;

public class GenericTypesNamingTests
{
[Fact]
public async Task NamingResolution()
{
await new ServiceCollection()
.AddGraphQL()
.AddQueryType<Query>()
.BuildSchemaAsync()
.MatchSnapshotAsync();
}

public class Query
{
public Tuple<int> OneGenericType => default!;
public Tuple<int, int> TwoGenericsType => default!;
public Tuple<int, int, int> ThreeGenericsType => default!;
public Tuple<int, int, int, int> FourGenericsType => default!;
public Tuple<int, int, int, int, int> FiveGenericTypes => default!;
public Tuple<int, int, int, int, int, int> SixGenericTypes => default!;
public Tuple<int, int, int, int, int, int, int> SevenGenericTypes => default!;
public EightElementsTuple<int, int, int, int, int, int, int, int> EightGenericTypes => default!;
public NineElementsTuple<int, int, int, int, int, int, int, int, int> NineGenericTypes => default!;
public TenElementsTuple<int, int, int, int, int, int, int, int, int, int> TenGenericTypes => default!;
public Foo<int> IntBar => default!;
public Foo<string> StringBar => default!;
public Foo<Bar> CustomNameBar => default!;
}

public class EightElementsTuple<T1, T2, T3, T4, T5, T6, T7, T8> : Tuple<T1, T2, T3, T4, T5, T6, T7>
{
public T8 Item8 { get; set; } = default!;

public EightElementsTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
: base(item1, item2, item3, item4, item5, item6, item7)
{
Item8 = item8;
}
}

public class NineElementsTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9> : EightElementsTuple<T1, T2, T3, T4, T5, T6, T7, T8>
{
public T9 Item9 { get; set; } = default!;

public NineElementsTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8, T9 item9)
: base(item1, item2, item3, item4, item5, item6, item7, item8)
{
Item9 = item9;
}
}

public class TenElementsTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : NineElementsTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>
{
public T10 Item10 { get; set; } = default!;

public TenElementsTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8, T9 item9, T10 item10)
: base(item1, item2, item3, item4, item5, item6, item7, item8, item9)
{
Item10 = item10;
}
}

[GraphQLName("Bar")]
public class Foo<T>
{
public T Test { get; init; }
}

[GraphQLName("MyType")]
public class Bar
{
public int Test { get; init; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
schema {
query: Query
}

type BarOfInt32 {
test: Int!
}

type BarOfMyType {
test: MyType!
}

type BarOfString {
test: String!
}

type EightElementsTupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32 {
item8: Int!
item1: Int!
item2: Int!
item3: Int!
item4: Int!
item5: Int!
item6: Int!
item7: Int!
}

type MyType {
test: Int!
}

type NineElementsTupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32 {
item9: Int!
item8: Int!
item1: Int!
item2: Int!
item3: Int!
item4: Int!
item5: Int!
item6: Int!
item7: Int!
}

type Query {
oneGenericType: TupleOfInt32!
twoGenericsType: TupleOfInt32AndInt32!
threeGenericsType: TupleOfInt32AndInt32AndInt32!
fourGenericsType: TupleOfInt32AndInt32AndInt32AndInt32!
fiveGenericTypes: TupleOfInt32AndInt32AndInt32AndInt32AndInt32!
sixGenericTypes: TupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32!
sevenGenericTypes: TupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32!
eightGenericTypes: EightElementsTupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32!
nineGenericTypes: NineElementsTupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32!
tenGenericTypes: TenElementsTupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32!
intBar: BarOfInt32!
stringBar: BarOfString!
customNameBar: BarOfMyType!
}

type TenElementsTupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32 {
item10: Int!
item9: Int!
item8: Int!
item1: Int!
item2: Int!
item3: Int!
item4: Int!
item5: Int!
item6: Int!
item7: Int!
}

type TupleOfInt32 {
item1: Int!
}

type TupleOfInt32AndInt32 {
item1: Int!
item2: Int!
}

type TupleOfInt32AndInt32AndInt32 {
item1: Int!
item2: Int!
item3: Int!
}

type TupleOfInt32AndInt32AndInt32AndInt32 {
item1: Int!
item2: Int!
item3: Int!
item4: Int!
}

type TupleOfInt32AndInt32AndInt32AndInt32AndInt32 {
item1: Int!
item2: Int!
item3: Int!
item4: Int!
item5: Int!
}

type TupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32 {
item1: Int!
item2: Int!
item3: Int!
item4: Int!
item5: Int!
item6: Int!
}

type TupleOfInt32AndInt32AndInt32AndInt32AndInt32AndInt32AndInt32 {
item1: Int!
item2: Int!
item3: Int!
item4: Int!
item5: Int!
item6: Int!
item7: Int!
}

0 comments on commit a13fa04

Please sign in to comment.