Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
41 changes: 41 additions & 0 deletions GraphQL-Core.Tests/CustomTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using GraphQLCore;
using GraphQL_Core.Tests.Models;
using GraphQLCore.Types;
using System;
using GraphQLCore.Resolvers;
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
{
/// <summary>
/// To be used for customized tests
/// Any imported models should be removed upon a PR
/// This class should not be added to source control
///
/// Recommended to remove any additional using statements and NuGet packages downloaded for testing
/// </summary>
[TestClass]
public class CustomTests
{
public Initializer initializer { get; set; } = new Initializer();

[TestMethod]
public void Configure()
{
initializer.Init();
initializer.services.AddGraphQL()
.Build();

initializer.Ask($@"

");
}
}
}
2 changes: 1 addition & 1 deletion GraphQL-Core.Tests/GraphQL-Core.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
Expand Down
34 changes: 34 additions & 0 deletions GraphQL-Core.Tests/Initialize.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using GraphQL;
using GraphQL.Types;
using GraphQLCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;

Expand All @@ -12,12 +15,14 @@ public class Initializer
public IServiceCollection services { get; set; }
public ServiceProvider provider { get; set; }
public IDependencyResolver resolver { get; set; }
public IDocumentExecuter executer { get; set; }

[TestInitialize()]
public void Init()
{
services = new ServiceCollection();
provider = services.BuildServiceProvider();
executer = new DocumentExecuter();
resolver = new FuncDependencyResolver(type =>
{
var service = services.Where(svc => svc.ServiceType == type).FirstOrDefault();
Expand All @@ -28,6 +33,35 @@ public void Init()

return service.ImplementationInstance;
});
GraphQLCoreTypeWrapperGenerator.Clear();
}

public (bool error, ExecutionResult) Ask(string query, string operation = null, JObject variables = null)
{
try
{
ISchema schema = resolver.Resolve<ISchema>();
var inputs = variables?.ToInputs();

var executionOptions = new ExecutionOptions()
{
Schema = schema,
Query = query,
Inputs = inputs,
ExposeExceptions = true,
EnableMetrics = true
};

var resultTask = executer.ExecuteAsync(executionOptions);
resultTask.Wait();
var result = resultTask.Result;

return (result.Errors != null && result.Errors.Any(), result);
}
catch(Exception e)
{
return (false, null);
}
}
}
}
62 changes: 62 additions & 0 deletions GraphQL-Core.Tests/TestExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using GraphQL;
using GraphQL_Core.Tests.Models.Enum;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GraphQL_Core.Tests
{
public static class TestExtensions
{
public static dynamic DefaultUnknownProperty(this Type t)
{
dynamic defaultValue = null;

switch (t)
{
case Type u when t == typeof(string):
defaultValue = "";
break;
case Type u when t.IsEnum:
defaultValue = 11;
break;
case Type u when t.IsInterface:
break;
default:
defaultValue = Activator.CreateInstance(t);
break;
}

return defaultValue;
}

public static 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;
}
}
}
22 changes: 19 additions & 3 deletions GraphQL-Core.Tests/TypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
using System;
using GraphQLCore.Resolvers;
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 @@ -121,8 +127,6 @@ public void Test_AddType_HasFields(Type T)
[DataRow(typeof(Author))]
[DataRow(typeof(Author_WithValueTypes))]
[DataRow(typeof(Author_WithEnumTypes))]
[DataRow(typeof(Author_WithEnumerableBooks))]
[DataRow(typeof(Author_WithManyManyBooks))]
[DataRow(typeof(Book_WithDuplicateBaseTypes))]
[DataRow(typeof(Book_WithTwoAuthors))]
[DataRow(typeof(Book_WithVirtualAuthorAndId))]
Expand Down Expand Up @@ -207,7 +211,8 @@ public void Generic_Test_AddType_IsQueryable<T>()
GraphQLQuery defaultQuery =
() => new Query() {
Expression = $"get_{field.Name}",
Resolver = (context) => Activator.CreateInstance(field.PropertyType)
Resolver = (context) =>
field.PropertyType.DefaultUnknownProperty()
};

var methodInfo = builder
Expand All @@ -233,6 +238,17 @@ public void Generic_Test_AddType_IsQueryable<T>()
foreach (var field in typeof(T).GetProperties())
{
Assert.IsTrue(userModelInstance.HasField(field.Name));

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

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

Assert.IsFalse(hasError);
}
}
}
Expand Down
101 changes: 73 additions & 28 deletions GraphQL-Core/CSharpClassBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using GraphQLCore.Types;
using GraphQLCore.Exceptions;
using GraphQLCore.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
Expand All @@ -21,17 +23,30 @@ public static class GraphQLCoreTypeWrapperGenerator

public static Type CreateGraphQLTypeWrapper<T>()
{
if (genericTypeParentClasses.ContainsKey(typeof(GenericType<T>)))
return genericTypeParentClasses[typeof(GenericType<T>)];
TypeBuilder tb = GetTypeBuilder(typeof(T).Name + typeNameAppend);
_ = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
tb.SetParent(typeof(GenericType<T>));
TypeInfo objectTypeInfo = tb.CreateTypeInfo();
Type type = objectTypeInfo.AsType();
try
{
if (genericTypeParentClasses.ContainsKey(typeof(GenericType<T>)))
return genericTypeParentClasses[typeof(GenericType<T>)];

genericTypeParentClasses[typeof(GenericType<T>)] = type;
TypeBuilder tb = GetTypeBuilder(typeof(T).Name + typeNameAppend, typeof(GenericType<T>));
tb.DefineDefaultConstructor(
MethodAttributes.Public
| MethodAttributes.SpecialName
| MethodAttributes.RTSpecialName);

return type;
tb.SetParent(typeof(GenericType<T>));

TypeInfo objectTypeInfo = tb.CreateTypeInfo();
Type type = objectTypeInfo.AsType();

genericTypeParentClasses[typeof(GenericType<T>)] = type;

return type;
}
catch(Exception e)
{
throw new GraphQLCoreMSILException("Attempt to create new C# type failed. Refer to inner exception for details", e);
}
}

public static Type GetDerivedGenericUserType<T>()
Expand All @@ -50,29 +65,59 @@ public static Type GetDerivedGenericUserType(this Type T)
return null;
}

public static Type GetBaseGenericUserType(this Type T)
{
if (genericTypeParentClasses.ContainsValue(T))
return genericTypeParentClasses.Where(p => p.Value == T).FirstOrDefault().Key;

return null;
}

private static AssemblyName CreateDynamicAssemblyName()
{
var currentAssemblyName = Assembly.GetEntryAssembly().GetName().Clone() as AssemblyName;
currentAssemblyName.Name += asmNameAppend;
return currentAssemblyName;
try
{
var currentAssemblyName = Assembly.GetEntryAssembly().GetName().Clone() as AssemblyName;
var asmName = new AssemblyName(Guid.NewGuid().ToString());
asmName.Name = currentAssemblyName.Name + asmNameAppend;
return currentAssemblyName;
}
catch(Exception e)
{
throw new GraphQLCoreAssemblyException("Attempt to create new dynamic assembly failed. Refer to inner exception for details", e);
}
}

private static TypeBuilder GetTypeBuilder(string typeName, Type baseClass)
{
try
{
var typeSignature = typeName;

if (mdBuilder is null)
mdBuilder = asmBuilder.DefineDynamicModule(mdName);

TypeBuilder tb = mdBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
baseClass);
return tb;
}
catch(Exception e)
{
throw new GraphQLCoreClassDefinitionException("Attempt to define a new C# type failed. Refer to inner exception for details", e);
}
}

private static TypeBuilder GetTypeBuilder(string typeName)
public static void Clear()
{
var typeSignature = typeName;

if(mdBuilder is null)
mdBuilder = asmBuilder.DefineDynamicModule(mdName);

TypeBuilder tb = mdBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
return tb;
genericTypeParentClasses.Clear();
mdBuilder = null;
asmBuilder = AssemblyBuilder.DefineDynamicAssembly(CreateDynamicAssemblyName(), AssemblyBuilderAccess.Run);
}
}
}
20 changes: 20 additions & 0 deletions GraphQL-Core/Exceptions/BuilderExceptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace GraphQLCore.Exceptions
{
public class GraphQLCoreStitchException : Exception
{
public GraphQLCoreStitchException() { }
public GraphQLCoreStitchException(string message) : base(message) { }
public GraphQLCoreStitchException(string message, Exception inner) : base(message, inner) { }
}

public class GraphQLCoreTypeException : Exception
{
public GraphQLCoreTypeException() { }
public GraphQLCoreTypeException(string message) : base(message) { }
public GraphQLCoreTypeException(string message, Exception inner) : base(message, inner) { }
}
}
13 changes: 13 additions & 0 deletions GraphQL-Core/Exceptions/ExtensionExceptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace GraphQLCore.Exceptions
{
public class GraphQLCoreConversionException : Exception
{
public GraphQLCoreConversionException() { }
public GraphQLCoreConversionException(string message) : base(message) { }
public GraphQLCoreConversionException(string message, Exception inner) : base(message, inner) { }
}
}
Loading