Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added additional unit tests for more scenarios
Fixed scenarios found through adding new unit tests
  • Loading branch information
Michannne committed May 2, 2019
commit 9d14f53fdda540b86567062148ca7adf6fdea1ee
16 changes: 16 additions & 0 deletions GraphQL-Core.Tests/Models/UserType/Author.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using GraphQL_Core.Tests.Models.Enum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GraphQL_Core.Tests.Models
Expand Down Expand Up @@ -51,4 +52,19 @@ public class Author_WithEnumTypes : Author
{
public AuthorType Type { get; set; }
}

public class Author_WithBooks : Author
{
public Book Book { get; set; }
}

public class Author_WithEnumerableBooks : Author
{
public IEnumerable<Book> Books { get; set; }
}

public class Author_WithManyManyBooks : Author
{
public IEnumerable<IQueryable<IList<Book>>> ManyBooks { get; set; }
}
}
5 changes: 5 additions & 0 deletions GraphQL-Core.Tests/Models/UserType/Book.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,9 @@ public class Book_WithEnumTypes : Book
{
public BookType Type { get; set; }
}

public class Book_WithAuthor : Book
{
public Author Author { get; set; }
}
}
9 changes: 9 additions & 0 deletions GraphQL-Core.Tests/TypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ public class TypeTests
[DataRow(typeof(Book_WithEnumTypes))]
[DataRow(typeof(Book_WithEnumerables))]
[DataRow(typeof(Book_WithAdvancedEnumerables))]
[DataRow(typeof(Book_WithAuthor))]
[DataRow(typeof(Author))]
[DataRow(typeof(Author_WithValueTypes))]
[DataRow(typeof(Author_WithEnumTypes))]
[DataRow(typeof(Author_WithEnumerableBooks))]
[DataRow(typeof(Author_WithManyManyBooks))]
public void Test_AddType_HasDynamicallyCreatedModel(Type T)
{
var testMethod = this.GetType().GetMethod("Generic_Test_AddType_HasDynamicallyCreatedModel");
Expand All @@ -40,9 +43,12 @@ public void Test_AddType_HasDynamicallyCreatedModel(Type T)
[DataRow(typeof(Book_WithEnumTypes))]
[DataRow(typeof(Book_WithEnumerables))]
[DataRow(typeof(Book_WithAdvancedEnumerables))]
[DataRow(typeof(Book_WithAuthor))]
[DataRow(typeof(Author))]
[DataRow(typeof(Author_WithValueTypes))]
[DataRow(typeof(Author_WithEnumTypes))]
[DataRow(typeof(Author_WithEnumerableBooks))]
[DataRow(typeof(Author_WithManyManyBooks))]
public void Test_AddType_HasFields(Type T)
{
var testMethod = this.GetType().GetMethod("Generic_Test_AddType_HasFields");
Expand All @@ -57,9 +63,12 @@ public void Test_AddType_HasFields(Type T)
[DataRow(typeof(Book_WithEnumTypes))]
[DataRow(typeof(Book_WithEnumerables))]
[DataRow(typeof(Book_WithAdvancedEnumerables))]
[DataRow(typeof(Book_WithAuthor))]
[DataRow(typeof(Author))]
[DataRow(typeof(Author_WithValueTypes))]
[DataRow(typeof(Author_WithEnumTypes))]
[DataRow(typeof(Author_WithEnumerableBooks))]
[DataRow(typeof(Author_WithManyManyBooks))]
public void Test_AddType_IsQueryable(Type T)
{
var testMethod = this.GetType().GetMethod("Generic_Test_AddType_IsQueryable");
Expand Down
55 changes: 41 additions & 14 deletions GraphQL-Core/Extensions/GraphQLExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ namespace GraphQLCore.Extensions
/// </summary>
public static class GraphQLExtensions
{
/// <summary>
/// Given a C# Type instance that represents a value type, enumeration, or class, returns the appropriate GraphQL.NET Type
/// </summary>
/// <param name="cSharpType">The C# Type to convert</param>
/// <returns>An appropriate GraphQL.NET Type</returns>
public static Type ConvertToGraphQLType(this Type cSharpType)
public static Type GetConvertibleBaseCSharpType(this Type cSharpType)
{
Type returnType = null;

Expand All @@ -40,28 +35,28 @@ public static Type ConvertToGraphQLType(this Type cSharpType)
|| cSharpType == typeof(short)
|| cSharpType == typeof(ushort):
{
returnType = typeof(int).GetGraphTypeFromType(numeric.IsNullable());
returnType = typeof(int);
}
break;
case Type floaty when false
|| cSharpType == typeof(float)
|| cSharpType == typeof(decimal)
|| cSharpType == typeof(double):
{
returnType = typeof(double).GetGraphTypeFromType(floaty.IsNullable());
returnType = typeof(double);
}
break;
case Type alpha when false
|| cSharpType == typeof(char)
|| cSharpType == typeof(string):
{
returnType = typeof(string).GetGraphTypeFromType(alpha.IsNullable());
returnType = typeof(string);
}
break;
case Type boolean when false
|| cSharpType == typeof(bool):
{
returnType = typeof(bool).GetGraphTypeFromType(boolean.IsNullable());
returnType = typeof(bool);
}
break;
case Type guid when false
Expand Down Expand Up @@ -94,10 +89,13 @@ public static Type ConvertToGraphQLType(this Type cSharpType)
))
{
var listType = cSharpType.GenericTypeArguments[0];

if (listType is null)
throw new InvalidCastException("Cannot create a List of objects that do not resolve to a value type");
return null;

var underClass = ConvertToGraphQLType(listType);
var underClass = GetConvertibleBaseCSharpType(listType);
if(!underClass.Implements(typeof(IGraphType)))
underClass = underClass.GetGraphTypeFromType();
returnType = typeof(ListGraphType<>).MakeGenericType(underClass);
}

Expand All @@ -108,12 +106,41 @@ public static Type ConvertToGraphQLType(this Type cSharpType)
returnType = derived;
}

if (returnType is null)
return returnType;
}

public static Type TryConvertToGraphQLType(this Type cSharpType)
{
try
{
var type = cSharpType.ConvertToGraphQLType();
return type;
}
catch(Exception e)
{
return null;
}
}

/// <summary>
/// Given a C# Type instance that represents a value type, enumeration, or class, returns the appropriate GraphQL.NET Type
/// </summary>
/// <param name="cSharpType">The C# Type to convert</param>
/// <returns>An appropriate GraphQL.NET Type</returns>
public static Type ConvertToGraphQLType(this Type cSharpType)
{
Type graphQlType = GetConvertibleBaseCSharpType(cSharpType);

if(graphQlType is null)
throw new NotSupportedException(
$"The C# type '{cSharpType.Name}' is not currently supported for auto-conversion into a GraphQL Node-type. " +
$"Consider registering it as a custom class in your GraphQL middleware");

return returnType;
if (graphQlType.Implements(typeof(IGraphType)))
return graphQlType;

else
return graphQlType.GetGraphTypeFromType(cSharpType.IsNullable());
}
}
}
3 changes: 2 additions & 1 deletion GraphQL-Core/GraphQLBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ public GraphQLBuilder(IServiceCollection services)

return service.ImplementationInstance;
});

Schema = schema;
Resolver = resolver;
Services = services;
Expand All @@ -163,7 +164,7 @@ public GraphQLBuilder(IServiceCollection services)
IGraphQLBuilder IGraphQLBuilder.Type<T>()
{
var userType = GraphQLCoreTypeWrapperGenerator.CreateGraphQLTypeWrapper<T>();
var graphQlTypeInstance = new GenericType<T>
var graphQlTypeInstance = new GenericType<T>(this)
{
Name = typeof(T).Name
};
Expand Down
28 changes: 25 additions & 3 deletions GraphQL-Core/Types/Types.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using GraphQL.Types;
using GraphQLCore.Extensions;
using GraphQLCore.GraphQL;
using System;

namespace GraphQLCore.Types
Expand All @@ -10,21 +11,42 @@ namespace GraphQLCore.Types
public interface IGraphQLGenericType { }

/// <summary>
/// Acts a container for user-defined model fields for a GraphQL Type
/// Acts as a container for user-defined model fields for a GraphQL Type
/// </summary>
/// <typeparam name="T">User-defined model type</typeparam>
public class GenericType<T> : ObjectGraphType<T>, IGraphQLGenericType
{
/// <summary>
/// Initializes the GraphQL Type, using the provided user-defined model
/// </summary>
public GenericType()
public GenericType(IGraphQLBuilder builder = null)
{
var typedClass = typeof(T);
var props = typedClass.GetProperties();
foreach (var prop in props)
{
Field(prop.PropertyType.ConvertToGraphQLType(), prop.Name);
Type propGraphQLType = null;

propGraphQLType = prop.PropertyType.TryConvertToGraphQLType();

if(propGraphQLType is null)
{
builder
.GetType()
.GetInterface("IGraphQLBuilder")
.GetMethod("Type")
.MakeGenericMethod(prop.PropertyType)
.Invoke(builder, null);

propGraphQLType = typeof(GenericType<>).MakeGenericType(prop.PropertyType);

if(propGraphQLType is null)
throw new InvalidCastException(
$"{prop.Name} was not automatically convertible into a GraphQL type. " +
$"Try explicitly adding this Type through the GraphQL-Core middleware.");
}

Field(propGraphQLType, prop.Name);
}
}

Expand Down