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
Extended test checking to check if Query resolves
Used new test to fix issues with newly added types
Added method to resolve unsupported value type conversions in GraphQL.NET
  • Loading branch information
Michannne committed May 5, 2019
commit 6e14ee7dcdf183c3a214085133903c63d687e736
38 changes: 35 additions & 3 deletions GraphQL-Core.Tests/TypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
using GraphQLCore.GraphQL;
using GraphQL_Core.Tests.Models.Enum;
using GraphQL.Types;
using GraphQL;
using System.Collections.Generic;
using System.Collections;
using System.Linq;

namespace GraphQL_Core.Tests
{
Expand Down Expand Up @@ -114,7 +118,7 @@ public void Test_AddType_HasFields(Type T)
[TestMethod]
[TestCategory("Types")]
[DataTestMethod()]
[DataRow(typeof(Book))]
//[DataRow(typeof(Book))]
[DataRow(typeof(Book_WithValueTypes))]
[DataRow(typeof(Book_WithEnumTypes))]
[DataRow(typeof(Book_WithEnumerables))]
Expand Down Expand Up @@ -202,7 +206,6 @@ public void Generic_Test_AddType_IsQueryable<T>()
{
initializer.Init();
var builder = initializer.services.AddGraphQL()
.Type<EnumerationGraphType<BookType>>()
.Type<T>();

foreach (var field in typeof(T).GetProperties())
Expand Down Expand Up @@ -238,15 +241,44 @@ public void Generic_Test_AddType_IsQueryable<T>()
{
Assert.IsTrue(userModelInstance.HasField(field.Name));

var subFields = ConstructSubFieldSelector(field.PropertyType, "");

//add sub-selection is field is class, use all props
(var hasError, var result) = initializer.Ask($@"
{{
get_{field.Name}
get_{field.Name} {subFields}
}}
", null, null);

Assert.IsFalse(hasError);
}
}

public string ConstructSubFieldSelector(Type T, string subFieldSelector = "")
{
var implementedInterfaces = T.GetInterfaces().OfType<Type>().ToList();

if (T.IsClass
&& !implementedInterfaces.Contains(typeof(IList))
&& !implementedInterfaces.Contains(typeof(IEnumerable))
&& !implementedInterfaces.Contains(typeof(IQueryable))
&& T != typeof(string)
&& T.GetProperties().Length > 0)
{
subFieldSelector += "{";

foreach (var subfield in T.GetProperties())
{
if (subfield.PropertyType.IsClass && subfield.PropertyType != typeof(string))
subFieldSelector += ConstructSubFieldSelector(subfield.PropertyType, subFieldSelector);
else
subFieldSelector += "\n" + subfield.Name.ToCamelCase();
}

subFieldSelector += "\n}";
}

return subFieldSelector;
}
}
}
35 changes: 35 additions & 0 deletions GraphQL-Core/Extensions/GraphQLExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public static (Type, bool) GetConvertibleBaseCSharpType(this Type cSharpType)
|| cSharpType == typeof(string):
{
returnType = typeof(string);
nullable = true;
}
break;
case Type boolean when false
Expand All @@ -74,24 +75,28 @@ public static (Type, bool) GetConvertibleBaseCSharpType(this Type cSharpType)
|| cSharpType == typeof(DateTime):
{
returnType = typeof(DateTimeGraphType);
nullable = true;
}
break;
case Type time when false
|| cSharpType == typeof(TimeSpan):
{
returnType = typeof(TimeSpanSecondsGraphType);
nullable = true;
}
break;
case Type timeoffset when false
|| cSharpType == typeof(DateTimeOffset):
{
returnType = typeof(DateTimeOffsetGraphType);
nullable = true;
}
break;
case Type guid when false
|| cSharpType == typeof(Guid):
{
returnType = typeof(GuidGraphType);
nullable = true;
}
break;
case Type str when false
Expand All @@ -100,6 +105,7 @@ public static (Type, bool) GetConvertibleBaseCSharpType(this Type cSharpType)
var genericType = typeof(GenericType<>).MakeGenericType(str);
var derived = genericType.GetDerivedGenericUserType();
returnType = derived;
nullable = true;
}
break;
default:
Expand All @@ -126,13 +132,15 @@ public static (Type, bool) GetConvertibleBaseCSharpType(this Type cSharpType)
if(!underClass.Implements(typeof(IGraphType)))
underClass = underClass.GetGraphTypeFromType(true);
returnType = typeof(ListGraphType<>).MakeGenericType(underClass);
nullable = true;
}

else
{
var genericType = typeof(GenericType<>).MakeGenericType(cSharpType);
var derived = genericType.GetDerivedGenericUserType();
returnType = derived;
nullable = true;
}

return (returnType, nullable);
Expand Down Expand Up @@ -171,5 +179,32 @@ public static Type ConvertToGraphQLType(this Type cSharpType)
else
return graphQlType.GetGraphTypeFromType(nullable);
}

/// <summary>
/// GraphQL.NET does not support a number of conversions
/// </summary>
public static void AddUnsupportedGraphQLConversions()
{
ValueConverter.Register(typeof(decimal), typeof(double), GraphQLNETValueConversions.DecimalToDouble);
}

public static bool IsExtendedGraphQLType(Type T)
{
return T.BaseType == typeof(EnumerationGraphType)
|| T == typeof(GuidGraphType)
|| T == typeof(DateTimeGraphType)
|| T == typeof(DateTimeOffsetGraphType)
|| T == typeof(TimeSpanSecondsGraphType)
|| T == typeof(TimeSpanMillisecondsGraphType);
}

public static string ToCamelCase(this string str)
{
if (!string.IsNullOrEmpty(str) && str.Length > 1)
{
return Char.ToLowerInvariant(str[0]) + str.Substring(1);
}
return str;
}
}
}
14 changes: 14 additions & 0 deletions GraphQL-Core/Extensions/GraphQLNETValueConversions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace GraphQLCore.Extensions
{
public static class GraphQLNETValueConversions
{
public static object DecimalToDouble(object d)
{
return Convert.ToDouble(d);
}
}
}
7 changes: 5 additions & 2 deletions GraphQL-Core/GraphQLBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Linq.Expressions;
using System.Reflection;
using GraphQLCore.Extensions;
using GraphQLExtensions = GraphQLCore.Extensions.GraphQLExtensions;

namespace GraphQLCore.GraphQL
{
Expand Down Expand Up @@ -159,13 +160,15 @@ public GraphQLBuilder(IServiceCollection services)
Services = services;
services.AddSingleton(schema);
services.AddSingleton<IDocumentExecuter, DocumentExecuter>();

GraphQLExtensions.AddUnsupportedGraphQLConversions();
}

IGraphQLBuilder IGraphQLBuilder.Type<T>()
{
if(typeof(T).IsGenericType && typeof(T).GenericTypeArguments[0].IsEnum)
if(GraphQLExtensions.IsExtendedGraphQLType(typeof(T)))
{
var enumType = typeof(T).GenericTypeArguments[0];
var type = typeof(T).IsGenericType ? typeof(T).GenericTypeArguments[0] : typeof(T);

if (((IGraphQLBuilder)this).GraphQLTypes.ContainsKey(typeof(T)))
return this;
Expand Down
8 changes: 5 additions & 3 deletions GraphQL-Core/Types/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,18 @@ public GenericType(IGraphQLBuilder builder = null)

propGraphQLType = prop.PropertyType.TryConvertToGraphQLType();

if(propGraphQLType is null || propGraphQLType.IsEnum)
if(propGraphQLType is null || GraphQLExtensions.IsExtendedGraphQLType(propGraphQLType))
{
var resolvedType = propGraphQLType ?? prop.PropertyType;

builder
.GetType()
.GetInterface("IGraphQLBuilder")
.GetMethod("Type")
.MakeGenericMethod(prop.PropertyType)
.MakeGenericMethod(resolvedType)
.Invoke(builder, null);

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

if(propGraphQLType is null)
throw new InvalidCastException(
Expand Down