From a6d1bb04a31bc24b39daffc1e6d2052c1fe91294 Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Sat, 5 Sep 2020 02:49:50 -0400
Subject: [PATCH 01/11] custom scalar type to graph types pool

---
 .../GraphQl/GraphQlTypePool.cs                |  7 +++-
 .../GraphqlControllerServiceBuilder.cs        |  9 ++++-
 .../Services/ICustomScalarTypesResolver.cs    | 34 +++++++++++++++++++
 3 files changed, 48 insertions(+), 2 deletions(-)
 create mode 100644 GraphqlController/GraphqlController/Services/ICustomScalarTypesResolver.cs

diff --git a/GraphqlController/GraphqlController/GraphQl/GraphQlTypePool.cs b/GraphqlController/GraphqlController/GraphQl/GraphQlTypePool.cs
index aec9870..1615d6c 100644
--- a/GraphqlController/GraphqlController/GraphQl/GraphQlTypePool.cs
+++ b/GraphqlController/GraphqlController/GraphQl/GraphQlTypePool.cs
@@ -40,9 +40,14 @@ public IGraphType GetSubscriptionType(IEnumerable<Type> subscriptionTypes)
         public IGraphType GetMutationType(IEnumerable<Type> mutationTypes)
            => new DynamicMutationType(this, mutationTypes);
 
-        public GraphQlTypePool(IAssemblyResolver assemblyResolver)
+        public GraphQlTypePool(IAssemblyResolver assemblyResolver, ICustomScalarTypesResolver customScalarTypesResolver)
         {
             _assemblyResolver = assemblyResolver;
+
+            foreach(var keyValue in customScalarTypesResolver.GetCustomScalars())
+            {
+                ScalarTypeMap.Add(keyValue.Key, keyValue.Value);
+            }
         }
 
         public IGraphType GetGraphType(Type type)
diff --git a/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs b/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
index f739959..dad9397 100644
--- a/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
+++ b/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
@@ -13,12 +13,15 @@ public class GraphqlControllerServiceBuilder
         public IServiceCollection Services { get; }
 
         AssemblyResolver _assemblyResolver;
+        CustomScalarTypesResolver _customScalarTypesResolver;
 
         internal GraphqlControllerServiceBuilder(IServiceCollection services)
         {
             Services = services;
             _assemblyResolver = new AssemblyResolver();
-            services.AddSingleton<IAssemblyResolver>(_assemblyResolver);            
+            _customScalarTypesResolver = new CustomScalarTypesResolver();
+            services.AddSingleton<IAssemblyResolver>(_assemblyResolver);
+            services.AddSingleton<ICustomScalarTypesResolver>(_customScalarTypesResolver);
         }
 
         /// <summary>
@@ -47,5 +50,9 @@ public void AddAssembly(Assembly assembly)
             }
         }
 
+        public void AddCustomScalarType(Type type, IGraphType graphType)
+        {
+            _customScalarTypesResolver.AddScalarType(type, graphType);
+        }
     }
 }
diff --git a/GraphqlController/GraphqlController/Services/ICustomScalarTypesResolver.cs b/GraphqlController/GraphqlController/Services/ICustomScalarTypesResolver.cs
new file mode 100644
index 0000000..df3dc03
--- /dev/null
+++ b/GraphqlController/GraphqlController/Services/ICustomScalarTypesResolver.cs
@@ -0,0 +1,34 @@
+﻿using GraphQL.Types;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace GraphqlController.Services
+{
+    public interface ICustomScalarTypesResolver
+    {
+        public IEnumerable<KeyValuePair<Type, IGraphType>> GetCustomScalars();
+    }
+
+    public class CustomScalarTypesResolver : ICustomScalarTypesResolver
+    {
+        List<KeyValuePair<Type, IGraphType>> _types = new List<KeyValuePair<Type, IGraphType>>(); 
+
+        public CustomScalarTypesResolver()
+        {
+
+        }
+
+        public void AddScalarType(Type type, IGraphType graphType)
+        {
+            _types.Add(new KeyValuePair<Type, IGraphType>(type, graphType));
+        }
+
+        public IEnumerable<KeyValuePair<Type, IGraphType>> GetCustomScalars()
+        {
+            return _types.Select(x => x);
+        }
+    }
+
+}

From eb24f4b3dc04e5644a5fa879495e8a0cc86ed655 Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Mon, 7 Sep 2020 01:18:59 -0400
Subject: [PATCH 02/11] s

---
 azure-pipelines.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index e7e6ef5..7ef98df 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -60,7 +60,7 @@ jobs:
         displayName: 'Create Nuget for GraphqlController'
 
       - script: dotnet pack $(GraphControllerAspNetCoreProjectDirectory) -c $(buildConfiguration) -p:Version=$(GitVersion.NugetVersion) -o . --no-build
-        displayName: 'Create Nuget for GraphqlController.AspNetCore'
+        displayName: 'Create Nuget for GraphqlController.AspNetCore' 
 
       - task: Bash@3
         inputs:

From 2bd0db05d326d138e6f5aa6616ef418776487e54 Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Mon, 7 Sep 2020 01:26:07 -0400
Subject: [PATCH 03/11] Update azure-pipelines.yml for Azure Pipelines

---
 azure-pipelines.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 7ef98df..5c742bb 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -65,14 +65,14 @@ jobs:
       - task: Bash@3
         inputs:
           targetType: 'inline'
-          script: 'dotnet nuget push ./GraphqlController.$(GitVersion.NugetVersion).nupkg -k oy2gje7rzqplk2kmmkoqoahcevj7eicexjdilbvyr55uy4 -s https://api.nuget.org/v3/index.json'
+          script: 'dotnet nuget push ./GraphqlController.$(GitVersion.NugetVersion).nupkg -k oy2gavcitljlhkcru7kaw42yvwny267e3lhx7m73dtm4jy -s https://api.nuget.org/v3/index.json'
         env: 
           NUGET_KEY: $(nugetorgKey)
 
       - task: Bash@3
         inputs:
           targetType: 'inline'
-          script: 'dotnet nuget push ./GraphqlController.AspNetCore.$(GitVersion.NugetVersion).nupkg -k oy2gje7rzqplk2kmmkoqoahcevj7eicexjdilbvyr55uy4 -s https://api.nuget.org/v3/index.json'
+          script: 'dotnet nuget push ./GraphqlController.AspNetCore.$(GitVersion.NugetVersion).nupkg -k oy2gavcitljlhkcru7kaw42yvwny267e3lhx7m73dtm4jy -s https://api.nuget.org/v3/index.json'
         env: 
           NUGET_KEY: $(nugetorgKey)
 

From fe3abb28e8e4fd6e62a0f7f21e18e56d70fb85a8 Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Mon, 7 Sep 2020 02:14:06 -0400
Subject: [PATCH 04/11] methods in base types and better naming for unions

---
 .../Types/Root.cs                             |  8 ++++-
 .../GraphQl/DynamicGraphType.cs               |  5 +--
 .../GraphQl/DynamicMutationType.cs            |  3 +-
 .../GraphQl/DynamicSubscriptionType.cs        |  3 +-
 .../GraphQl/DynamicUnionGraphType.cs          | 31 +++++++++++--------
 5 files changed, 32 insertions(+), 18 deletions(-)

diff --git a/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs b/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
index 96c36c6..c025c08 100644
--- a/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
+++ b/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
@@ -13,7 +13,7 @@ namespace GraphqlController.WebAppTest.Types
     /// The root type
     /// </summary>
     [RootType]
-    public class Root : GraphNodeType
+    public class Root : BaseTest
     {
 
         /// <summary>
@@ -35,4 +35,10 @@ public IEnumerable<Teacher> AllTeacher(int skip, int take)
 
     }
 
+    public class BaseTest : GraphNodeType
+    {
+        public string TestProperty => "Test";
+        public string TestMethod(int a) => "Test";
+    }
+
 }
diff --git a/GraphqlController/GraphqlController/GraphQl/DynamicGraphType.cs b/GraphqlController/GraphqlController/GraphQl/DynamicGraphType.cs
index 1a63bad..71e24ed 100644
--- a/GraphqlController/GraphqlController/GraphQl/DynamicGraphType.cs
+++ b/GraphqlController/GraphqlController/GraphQl/DynamicGraphType.cs
@@ -91,12 +91,13 @@ public DynamicGraphType(IGraphQlTypePool graphTypePool, Type type, bool isRoot =
             }
 
             // work with the methods
-            var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
+            var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+                              .Where(x => x.DeclaringType != typeof(object))
                               .Where(x => x.GetAttribute<IgnoreAttribute>() == null);
 
             // for each method public
             foreach (var method in methods)
-            {
+            {                
                 if (method.Name == nameof(GraphNodeType<object>.OnCreateAsync) ||
                    method.IsSpecialName)
                 {
diff --git a/GraphqlController/GraphqlController/GraphQl/DynamicMutationType.cs b/GraphqlController/GraphqlController/GraphQl/DynamicMutationType.cs
index 3648147..53c8db9 100644
--- a/GraphqlController/GraphqlController/GraphQl/DynamicMutationType.cs
+++ b/GraphqlController/GraphqlController/GraphQl/DynamicMutationType.cs
@@ -26,7 +26,8 @@ public DynamicMutationType(IGraphQlTypePool graphTypePool, IEnumerable<Type> typ
                 }
 
                 // work with the methods
-                var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
+                var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+                                  .Where(x => x.DeclaringType != typeof(object))
                                   .Where(x => x.GetAttribute<IgnoreAttribute>() == null);
 
                 foreach (var method in methods)
diff --git a/GraphqlController/GraphqlController/GraphQl/DynamicSubscriptionType.cs b/GraphqlController/GraphqlController/GraphQl/DynamicSubscriptionType.cs
index 0efcdf0..c87ec3b 100644
--- a/GraphqlController/GraphqlController/GraphQl/DynamicSubscriptionType.cs
+++ b/GraphqlController/GraphqlController/GraphQl/DynamicSubscriptionType.cs
@@ -71,7 +71,8 @@ public DynamicSubscriptionType(IGraphQlTypePool graphTypePool, IEnumerable<Type>
                 }
 
                 // work with the methods
-                var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
+                var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+                                  .Where(x => x.DeclaringType != typeof(object))
                                   .Where(x => x.GetAttribute<IgnoreAttribute>() == null);
 
                 foreach (var method in methods)
diff --git a/GraphqlController/GraphqlController/GraphQl/DynamicUnionGraphType.cs b/GraphqlController/GraphqlController/GraphQl/DynamicUnionGraphType.cs
index 9d2199c..f63464e 100644
--- a/GraphqlController/GraphqlController/GraphQl/DynamicUnionGraphType.cs
+++ b/GraphqlController/GraphqlController/GraphQl/DynamicUnionGraphType.cs
@@ -30,15 +30,28 @@ public DynamicUnionGraphType(IGraphQlTypePool pool, Type type)
             if (!typeof(IUnionGraphType).IsAssignableFrom(type))
                 throw new InvalidOperationException("Invalid Union type");
 
-            if( IsUnionType(type) )
+            string name = "";            
+
+            {
+                var genericArguments = type.GetGenericArguments();
+
+                foreach (var genericArgument in genericArguments)
+                {
+                    var gType = pool.GetGraphType(genericArgument) as IObjectGraphType;
+                    name += gType.Name;
+                    AddPossibleType(gType);
+                }
+            }
+
+            if (IsUnionType(type))
             {
                 // Generate name
                 var genericArguments = type.GetGenericArguments();
-                Name = string.Join('_', genericArguments.Select(x => x.Name)); 
-            } 
+                Name = name + "Union";
+            }
             else
             {
-                if( !IsUnionType(type.BaseType) )
+                if (!IsUnionType(type.BaseType))
                     throw new InvalidOperationException("Invalid Union type");
 
                 var nameAttr = type.GetAttribute<NameAttribute>();
@@ -46,18 +59,10 @@ public DynamicUnionGraphType(IGraphQlTypePool pool, Type type)
 
                 Name = nameAttr?.Name ?? type.Name;
                 Description = descAttr?.Description ?? DocXmlHelper.DocReader.GetTypeComments(type).Summary;
-                
+
                 type = type.BaseType;
             }
 
-            {
-                var genericArguments = type.GetGenericArguments();
-
-                foreach (var genericArgument in genericArguments)
-                {
-                    AddPossibleType(pool.GetGraphType(genericArgument) as IObjectGraphType);
-                }
-            }
         }
 
         private static bool IsUnionType(Type type)

From df9af85fd32ec9ef185c227ceebf9d924c9b39b1 Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Mon, 7 Sep 2020 11:31:06 -0400
Subject: [PATCH 05/11] name generator

---
 .../GraphqlController.WebAppTest/Types/Root.cs         |  5 +++++
 .../Attributes/DescriptionAttribute.cs                 |  2 +-
 .../GraphqlController/Attributes/NameAttribute.cs      |  2 +-
 .../GraphqlController/GraphQl/DynamicGraphType.cs      | 10 ++++++++--
 .../GraphqlController/GraphQl/DynamicMutationType.cs   |  2 ++
 .../GraphQl/DynamicSubscriptionType.cs                 |  2 ++
 6 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs b/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
index c025c08..2dcbf31 100644
--- a/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
+++ b/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
@@ -32,6 +32,11 @@ public IEnumerable<Teacher> AllTeacher(int skip, int take)
             return list.Skip(skip).Take(take);
         }
 
+        public static string GenerateName(Type type)
+        {
+            return "ElRoot";
+        }
+
 
     }
 
diff --git a/GraphqlController/GraphqlController/Attributes/DescriptionAttribute.cs b/GraphqlController/GraphqlController/Attributes/DescriptionAttribute.cs
index 3e6cdde..ae5b92a 100644
--- a/GraphqlController/GraphqlController/Attributes/DescriptionAttribute.cs
+++ b/GraphqlController/GraphqlController/Attributes/DescriptionAttribute.cs
@@ -4,7 +4,7 @@
 
 namespace GraphqlController.Attributes
 {
-    [AttributeUsage(AttributeTargets.Parameter)]
+    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class | AttributeTargets.Method)]
     public class DescriptionAttribute : Attribute
     {
         public string Description { get; set; }
diff --git a/GraphqlController/GraphqlController/Attributes/NameAttribute.cs b/GraphqlController/GraphqlController/Attributes/NameAttribute.cs
index 95d6205..ba59b8c 100644
--- a/GraphqlController/GraphqlController/Attributes/NameAttribute.cs
+++ b/GraphqlController/GraphqlController/Attributes/NameAttribute.cs
@@ -4,7 +4,7 @@
 
 namespace GraphqlController.Attributes
 {
-    [AttributeUsage(AttributeTargets.Parameter)]
+    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class | AttributeTargets.Method)]
     public class NameAttribute : Attribute
     {
         public string Name { get; set; }
diff --git a/GraphqlController/GraphqlController/GraphQl/DynamicGraphType.cs b/GraphqlController/GraphqlController/GraphQl/DynamicGraphType.cs
index 71e24ed..d9477b6 100644
--- a/GraphqlController/GraphqlController/GraphQl/DynamicGraphType.cs
+++ b/GraphqlController/GraphqlController/GraphQl/DynamicGraphType.cs
@@ -22,13 +22,17 @@ public DynamicGraphType(IGraphQlTypePool graphTypePool, Type type, bool isRoot =
                 throw new ArgumentException("Invalid object type");
             }
 
+            // check if have the generate name and description functions
+            var nameFunction = type.GetMethod("GenerateName", BindingFlags.Static | BindingFlags.Public, null, new Type[]{ typeof(Type) }, null);
+            var descriptionFunction = type.GetMethod("GenerateDescription", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(Type) }, null);
+
             // get the name
             var nameAttr = type.GetAttribute<NameAttribute>();
             var descAttr = type.GetAttribute<DescriptionAttribute>();
 
             // set type name and description
-            Name = nameAttr?.Name ?? type.Name;
-            Description = descAttr?.Description ?? DocXmlHelper.DocReader.GetTypeComments(type).Summary;
+            Name = nameFunction?.Invoke(null, new object[] { type }) as string ?? (nameAttr?.Name ?? type.Name);
+            Description = descriptionFunction?.Invoke(null, new object[] { type }) as string ?? (descAttr?.Description ?? DocXmlHelper.DocReader.GetTypeComments(type).Summary);
 
             // set the type metadata
             Metadata["type"] = type;
@@ -92,6 +96,8 @@ public DynamicGraphType(IGraphQlTypePool graphTypePool, Type type, bool isRoot =
 
             // work with the methods
             var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+                              .Where(x => !(x.DeclaringType.IsGenericType && x.DeclaringType.GetGenericTypeDefinition() == typeof(GraphNodeType<>)))
+                              .Where(x => x.DeclaringType != typeof(GraphNodeType))
                               .Where(x => x.DeclaringType != typeof(object))
                               .Where(x => x.GetAttribute<IgnoreAttribute>() == null);
 
diff --git a/GraphqlController/GraphqlController/GraphQl/DynamicMutationType.cs b/GraphqlController/GraphqlController/GraphQl/DynamicMutationType.cs
index 53c8db9..6b72812 100644
--- a/GraphqlController/GraphqlController/GraphQl/DynamicMutationType.cs
+++ b/GraphqlController/GraphqlController/GraphQl/DynamicMutationType.cs
@@ -27,6 +27,8 @@ public DynamicMutationType(IGraphQlTypePool graphTypePool, IEnumerable<Type> typ
 
                 // work with the methods
                 var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+                                  .Where(x => !(x.DeclaringType.IsGenericType && x.DeclaringType.GetGenericTypeDefinition() == typeof(GraphNodeType<>)))
+                                  .Where(x => x.DeclaringType != typeof(GraphNodeType))
                                   .Where(x => x.DeclaringType != typeof(object))
                                   .Where(x => x.GetAttribute<IgnoreAttribute>() == null);
 
diff --git a/GraphqlController/GraphqlController/GraphQl/DynamicSubscriptionType.cs b/GraphqlController/GraphqlController/GraphQl/DynamicSubscriptionType.cs
index c87ec3b..56a707c 100644
--- a/GraphqlController/GraphqlController/GraphQl/DynamicSubscriptionType.cs
+++ b/GraphqlController/GraphqlController/GraphQl/DynamicSubscriptionType.cs
@@ -72,6 +72,8 @@ public DynamicSubscriptionType(IGraphQlTypePool graphTypePool, IEnumerable<Type>
 
                 // work with the methods
                 var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+                                  .Where(x => !(x.DeclaringType.IsGenericType && x.DeclaringType.GetGenericTypeDefinition() == typeof(GraphNodeType<>)))
+                                  .Where(x => x.DeclaringType != typeof(GraphNodeType))
                                   .Where(x => x.DeclaringType != typeof(object))
                                   .Where(x => x.GetAttribute<IgnoreAttribute>() == null);
 

From eaad6910d2e4be4f72cf1110cefe872cd0e4fba7 Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Mon, 7 Sep 2020 13:10:02 -0400
Subject: [PATCH 06/11] public assembly resolver

---
 .../Services/GraphqlControllerServiceBuilder.cs             | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs b/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
index dad9397..fbad004 100644
--- a/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
+++ b/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
@@ -12,15 +12,15 @@ public class GraphqlControllerServiceBuilder
     {
         public IServiceCollection Services { get; }
 
-        AssemblyResolver _assemblyResolver;
+        public AssemblyResolver AssemblyResolver;
         CustomScalarTypesResolver _customScalarTypesResolver;
 
         internal GraphqlControllerServiceBuilder(IServiceCollection services)
         {
             Services = services;
-            _assemblyResolver = new AssemblyResolver();
+            AssemblyResolver = new AssemblyResolver();
             _customScalarTypesResolver = new CustomScalarTypesResolver();
-            services.AddSingleton<IAssemblyResolver>(_assemblyResolver);
+            services.AddSingleton<IAssemblyResolver>(AssemblyResolver);
             services.AddSingleton<ICustomScalarTypesResolver>(_customScalarTypesResolver);
         }
 

From c69c6106ffea5a704d90ada711f8f7df0d3a6ec1 Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Mon, 7 Sep 2020 13:14:00 -0400
Subject: [PATCH 07/11] p

---
 .../Services/GraphqlControllerServiceBuilder.cs                 | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs b/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
index fbad004..757580f 100644
--- a/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
+++ b/GraphqlController/GraphqlController/Services/GraphqlControllerServiceBuilder.cs
@@ -39,7 +39,7 @@ public void AddCurrentAssembly()
         public void AddAssembly(Assembly assembly)
         {
             // Add the assembly to the resolver
-            _assemblyResolver.AddAssembly(assembly);
+            AssemblyResolver.AddAssembly(assembly);
 
             // Add all type nodes to the container as transient
             var nodeTypes = assembly.GetTypes().Where(x => typeof(IGraphNodeType).IsAssignableFrom(x));

From dd71641f8e70c78f2c939bab3d83051e50ebc65f Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Thu, 17 Sep 2020 08:36:23 -0400
Subject: [PATCH 08/11] authorization

---
 .../AppBuilderExtensions.cs                   |   8 +-
 .../AuthorizationExecutionMiddleware.cs       |  18 +++
 .../AuthorizationFieldMiddleware.cs           | 131 ++++++++++++++++++
 .../Authorization/AuthorizeAttribute.cs       |  14 ++
 .../Cache/ServiceCollectionExtensions.cs      |   2 +
 .../Controllers/GraphQLController.cs          |   2 +-
 .../GraphqlController.AspNetCore.csproj       |   1 +
 .../Services/ServiceCollectionExtensions.cs   |   4 +
 .../GraphqlController.WebAppTest/Startup.cs   |   3 +-
 .../Types/Root.cs                             |   6 +-
 10 files changed, 184 insertions(+), 5 deletions(-)
 create mode 100644 GraphqlController/GraphqlController.AspNet/Authorization/AuthorizationExecutionMiddleware.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Authorization/AuthorizationFieldMiddleware.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Authorization/AuthorizeAttribute.cs

diff --git a/GraphqlController/GraphqlController.AspNet/AppBuilderExtensions.cs b/GraphqlController/GraphqlController.AspNet/AppBuilderExtensions.cs
index f08dc6f..87b8afd 100644
--- a/GraphqlController/GraphqlController.AspNet/AppBuilderExtensions.cs
+++ b/GraphqlController/GraphqlController.AspNet/AppBuilderExtensions.cs
@@ -1,4 +1,5 @@
 ﻿using GraphQL.Types;
+using GraphqlController.AspNetCore.Authorization;
 using GraphqlController.AspNetCore.Services;
 using GraphqlController.Execution;
 using GraphqlController.Services;
@@ -30,7 +31,12 @@ public static IApplicationBuilder UseGraphQLController(this IApplicationBuilder
         public static IGraphQLExecutionBuilder UseGraphQlExecutionFor(this IApplicationBuilder app, Type root)
         {
             var executionBuilderResolver = (IExecutionBuilderResolver)app.ApplicationServices.GetService(typeof(IExecutionBuilderResolver));
-            return executionBuilderResolver.GetGraphqlExecutionBuilder(root);
+            var builder = executionBuilderResolver.GetGraphqlExecutionBuilder(root);
+            
+            // add authorization as a built in feature
+            builder.Use<AuthorizationExecutionMiddleware>();
+
+            return builder;
         }
 
         public static IGraphQLExecutionBuilder UseGraphQlExecutionFor<T>(this IApplicationBuilder app)
diff --git a/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizationExecutionMiddleware.cs b/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizationExecutionMiddleware.cs
new file mode 100644
index 0000000..6794a45
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizationExecutionMiddleware.cs
@@ -0,0 +1,18 @@
+﻿using GraphqlController.Execution;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using GraphQL.Instrumentation;
+
+namespace GraphqlController.AspNetCore.Authorization
+{
+    public class AuthorizationExecutionMiddleware : IExecutionMiddleware
+    {
+        public async Task ExecuteAsync(ExecutionContext executionContext, Func<Task> next)
+        {
+            executionContext.FieldMiddleware.Use<AuthorizationFieldMiddleware>();
+            await next();
+        }
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizationFieldMiddleware.cs b/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizationFieldMiddleware.cs
new file mode 100644
index 0000000..5c1c756
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizationFieldMiddleware.cs
@@ -0,0 +1,131 @@
+﻿using GraphQL;
+using GraphQL.Instrumentation;
+using GraphQL.Types;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using WebApiContrib.Core.Results;
+
+namespace GraphqlController.AspNetCore.Authorization
+{
+    public class AuthorizationFieldMiddleware
+    {        
+        public async Task<object> Resolve(
+          ResolveFieldContext context,
+          FieldMiddlewareDelegate next)
+        {
+            var serviceProvider = (IServiceProvider)context.UserContext["serviceProvider"];
+
+            var authorizationService = serviceProvider.GetService(typeof(IAuthorizationService)) as IAuthorizationService;
+            var httpContext = (serviceProvider.GetService(typeof(IHttpContextAccessor)) as IHttpContextAccessor)?.HttpContext;
+
+            if(httpContext == null)
+            {
+                return await next(context);
+            }
+
+            object type;
+            
+            if(context.FieldDefinition.Metadata.TryGetValue("type", out type))
+            {
+
+                AuthorizeAttribute fieldAuthorizeAttribute = null;
+                AuthorizeAttribute resolveTypeAuthorizeAttribute = null;
+
+                if (type is PropertyInfo propertyInfo)
+                {
+                    fieldAuthorizeAttribute = propertyInfo.GetCustomAttribute<AuthorizeAttribute>();
+                    resolveTypeAuthorizeAttribute = propertyInfo.PropertyType.GetCustomAttribute<AuthorizeAttribute>();
+                }
+                else if (type is MethodInfo methodInfo)
+                {
+                    fieldAuthorizeAttribute = methodInfo.GetCustomAttribute<AuthorizeAttribute>();
+                    resolveTypeAuthorizeAttribute = methodInfo.ReturnType.GetCustomAttribute<AuthorizeAttribute>();
+                }
+
+                if(fieldAuthorizeAttribute != null)
+                {
+                    if(!await AuthorizeAsync(authorizationService, httpContext, fieldAuthorizeAttribute))
+                    {
+                        var error = new ExecutionError($"You are not authorize to access field: {context.FieldName}")
+                        {
+                            Path = context.Path,
+                            Code = "401",
+                        };
+                        error.AddLocation(context.FieldAst.SourceLocation.Line, context.FieldAst.SourceLocation.Column);
+                        context.Errors.Add(error);
+                        return null;
+                    }
+                }
+
+                if (resolveTypeAuthorizeAttribute != null)
+                {
+                    if(!await AuthorizeAsync(authorizationService, httpContext, resolveTypeAuthorizeAttribute))
+                    {
+                        var error = new ExecutionError($"You are not authorize to access entities of type ${context.ReturnType.Name}")
+                        {
+                            Path = context.Path,
+                            Code = "401",
+                        };
+                        error.AddLocation(context.FieldAst.SourceLocation.Line, context.FieldAst.SourceLocation.Column);
+                        context.Errors.Add(error);
+                        return null;
+                    }
+                }
+            }
+
+
+            return await next(context);
+        }
+
+        static async Task<bool> AuthorizeAsync(IAuthorizationService authorizationService, HttpContext httpContext, IAuthorizeData authorizeData)
+        {
+            var user = httpContext.User;
+
+            AuthorizationResult authorizationResult = null;
+            if(authorizeData.Roles != null)
+            {
+                if(!user.IsInRole(authorizeData.Roles))
+                {
+                    authorizationResult = AuthorizationResult.Failed();
+                }
+            }
+
+            if(authorizationResult == null && authorizeData.Policy != null)
+            {
+                authorizationResult = await authorizationService.AuthorizeAsync(user, authorizeData.Policy);
+            }
+
+            if(authorizationResult == null)
+            {
+                if(user.Identity.IsAuthenticated)
+                {                    
+                    return true;
+                }
+                else
+                {
+                    httpContext.Response.StatusCode = 401;
+                    return false;
+                }
+            } 
+            else
+            {
+                if(authorizationResult.Succeeded)
+                {                    
+                    return true;
+                }
+                else
+                {
+                    httpContext.Response.StatusCode = 401;
+                    return false;
+                }                
+            }
+        }
+
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizeAttribute.cs b/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizeAttribute.cs
new file mode 100644
index 0000000..846f3b5
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Authorization/AuthorizeAttribute.cs
@@ -0,0 +1,14 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GraphqlController.AspNetCore.Authorization
+{
+    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
+    public class AuthorizeAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute
+    {
+        public AuthorizeAttribute() { }
+
+        public AuthorizeAttribute(string policy) : base(policy) { }        
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Cache/ServiceCollectionExtensions.cs b/GraphqlController/GraphqlController.AspNet/Cache/ServiceCollectionExtensions.cs
index 6943843..d4354dc 100644
--- a/GraphqlController/GraphqlController.AspNet/Cache/ServiceCollectionExtensions.cs
+++ b/GraphqlController/GraphqlController.AspNet/Cache/ServiceCollectionExtensions.cs
@@ -23,6 +23,8 @@ public static IServiceCollection AddGraphqlCache(this IServiceCollection service
 
             services.AddSingleton(cacheConfig);
 
+            services.AddHttpContextAccessor();
+
             services.AddScoped<IGraphqlInAppCacheService, GraphqlInAppCacheService>();
             services.AddScoped<ICachePolicy, DefaultCachePolicy>();
             services.AddScoped<GraphQlExecutionMiddleware>();
diff --git a/GraphqlController/GraphqlController.AspNet/Controllers/GraphQLController.cs b/GraphqlController/GraphqlController.AspNet/Controllers/GraphQLController.cs
index c3696b2..ecb79e2 100644
--- a/GraphqlController/GraphqlController.AspNet/Controllers/GraphQLController.cs
+++ b/GraphqlController/GraphqlController.AspNet/Controllers/GraphQLController.cs
@@ -44,7 +44,7 @@ public async Task<ActionResult<Dictionary<string, object>>> ExecuteQuery(Cancell
 
             if (HttpContext.Response.StatusCode != 200)
             {
-                return StatusCode(HttpContext.Response.StatusCode);
+                return StatusCode(HttpContext.Response.StatusCode, result.result);
             }
 
             return new ActionResult<Dictionary<string, object>>(result.result);
diff --git a/GraphqlController/GraphqlController.AspNet/GraphqlController.AspNetCore.csproj b/GraphqlController/GraphqlController.AspNet/GraphqlController.AspNetCore.csproj
index 25a94cd..4d26367 100644
--- a/GraphqlController/GraphqlController.AspNet/GraphqlController.AspNetCore.csproj
+++ b/GraphqlController/GraphqlController.AspNet/GraphqlController.AspNetCore.csproj
@@ -13,6 +13,7 @@
     <PackageReference Include="GraphQL" Version="3.0.0-preview-1352" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.2" />
     <PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.2" />
+    <PackageReference Include="WebApiContrib.Core" Version="2.2.1" />
     <FrameworkReference Include="Microsoft.AspNetCore.App" />
   </ItemGroup>
 
diff --git a/GraphqlController/GraphqlController.AspNet/Services/ServiceCollectionExtensions.cs b/GraphqlController/GraphqlController.AspNet/Services/ServiceCollectionExtensions.cs
index 9827290..35f34b6 100644
--- a/GraphqlController/GraphqlController.AspNet/Services/ServiceCollectionExtensions.cs
+++ b/GraphqlController/GraphqlController.AspNet/Services/ServiceCollectionExtensions.cs
@@ -1,4 +1,5 @@
 ﻿using GraphQL.Instrumentation;
+using GraphqlController.AspNetCore.Authorization;
 using GraphqlController.AspNetCore.Services;
 using GraphqlController.Execution;
 using Microsoft.Extensions.DependencyInjection;
@@ -19,6 +20,9 @@ public static IServiceCollection AddGraphQlEndpoint(this IServiceCollection serv
 
             services.AddSingleton<IExecutionBuilderResolver, ExecutionBuilderResolver>();
 
+            // add built in authorization
+            services.AddSingleton<AuthorizationExecutionMiddleware>();
+
             services.AddControllers().AddApplicationPart(Assembly.GetExecutingAssembly())
                                      .AddControllersAsServices();
 
diff --git a/GraphqlController/GraphqlController.WebAppTest/Startup.cs b/GraphqlController/GraphqlController.WebAppTest/Startup.cs
index 127b0be..6d8d2b7 100644
--- a/GraphqlController/GraphqlController.WebAppTest/Startup.cs
+++ b/GraphqlController/GraphqlController.WebAppTest/Startup.cs
@@ -64,7 +64,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
             app.UseHttpsRedirection();
 
             app.UseRouting();
-                        
+
+            app.UseAuthentication();
             app.UseAuthorization();
 
             app.UseGraphQLController()
diff --git a/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs b/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
index 2dcbf31..2ce222e 100644
--- a/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
+++ b/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
@@ -1,4 +1,5 @@
-﻿using GraphqlController.AspNetCore.Cache;
+﻿using GraphqlController.AspNetCore.Authorization;
+using GraphqlController.AspNetCore.Cache;
 using GraphqlController.Attributes;
 using GraphqlController.Services;
 using GraphqlController.WebAppTest.Repositories;
@@ -12,13 +13,14 @@ namespace GraphqlController.WebAppTest.Types
     /// <summary>
     /// The root type
     /// </summary>
-    [RootType]
+    [RootType]    
     public class Root : BaseTest
     {
 
         /// <summary>
         /// All people in the school
         /// </summary>
+        [Authorize(Roles = "Admin")]
         public IEnumerable<Teacher> AllTeacher(int skip, int take)
         {
             var list = new Teacher[]

From 436c5b978827f5fe4c3713b661709a9c561cb517 Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Fri, 18 Sep 2020 12:06:32 -0400
Subject: [PATCH 09/11] Update azure-pipelines.yml for Azure Pipelines

---
 azure-pipelines.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 5c742bb..a16c7e4 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -65,14 +65,14 @@ jobs:
       - task: Bash@3
         inputs:
           targetType: 'inline'
-          script: 'dotnet nuget push ./GraphqlController.$(GitVersion.NugetVersion).nupkg -k oy2gavcitljlhkcru7kaw42yvwny267e3lhx7m73dtm4jy -s https://api.nuget.org/v3/index.json'
+          script: 'dotnet nuget push ./GraphqlController.$(GitVersion.NugetVersion).nupkg -k oy2h6kk7ubuiqk4y2e6ailprvg7cxuyerh3rqjqcbxbjcq -s https://api.nuget.org/v3/index.json'
         env: 
           NUGET_KEY: $(nugetorgKey)
 
       - task: Bash@3
         inputs:
           targetType: 'inline'
-          script: 'dotnet nuget push ./GraphqlController.AspNetCore.$(GitVersion.NugetVersion).nupkg -k oy2gavcitljlhkcru7kaw42yvwny267e3lhx7m73dtm4jy -s https://api.nuget.org/v3/index.json'
+          script: 'dotnet nuget push ./GraphqlController.AspNetCore.$(GitVersion.NugetVersion).nupkg -k oy2h6kk7ubuiqk4y2e6ailprvg7cxuyerh3rqjqcbxbjcq -s https://api.nuget.org/v3/index.json'
         env: 
           NUGET_KEY: $(nugetorgKey)
 

From cf41786545ae83f78fcc3d08db39d665e147b0cb Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Sun, 29 Nov 2020 18:01:56 -0500
Subject: [PATCH 10/11] connection and node

---
 .../Extensions/Base64Extensions.cs            | 22 ++++++
 .../Relay/AppBuilderExtensions.cs             | 19 +++++
 .../Relay/Connection.cs                       | 51 +++++++++++++
 .../Relay/GlobalIdType.cs                     | 72 +++++++++++++++++++
 .../GraphqlController.AspNet/Relay/Node.cs    | 57 +++++++++++++++
 .../Relay/NodeResolver.cs                     | 24 +++++++
 .../Relay/ObjectResolverService.cs            | 38 ++++++++++
 .../Relay/ObjectResolversContianer.cs         | 40 +++++++++++
 .../Relay/ServiceCollectionExtensions.cs      | 43 +++++++++++
 .../Relay/Types/GlobalId.cs                   | 68 ++++++++++++++++++
 .../Relay/Types/INode.cs                      | 13 ++++
 .../Types/Root.cs                             |  2 -
 12 files changed, 447 insertions(+), 2 deletions(-)
 create mode 100644 GraphqlController/GraphqlController.AspNet/Extensions/Base64Extensions.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/AppBuilderExtensions.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/Connection.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/GlobalIdType.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/Node.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/NodeResolver.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/ObjectResolverService.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/ObjectResolversContianer.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/ServiceCollectionExtensions.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/Types/GlobalId.cs
 create mode 100644 GraphqlController/GraphqlController.AspNet/Relay/Types/INode.cs

diff --git a/GraphqlController/GraphqlController.AspNet/Extensions/Base64Extensions.cs b/GraphqlController/GraphqlController.AspNet/Extensions/Base64Extensions.cs
new file mode 100644
index 0000000..9a3fe91
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Extensions/Base64Extensions.cs
@@ -0,0 +1,22 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GraphqlController.AspNetCore.Extensions
+{
+    public static class Base64Extensions
+    {
+        public static string Base64Encode(this string plainText)
+        {
+            var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
+            return Convert.ToBase64String(plainTextBytes);
+        }
+
+        public static string Base64Decode(this string base64EncodedData)
+        {
+            var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
+            return Encoding.UTF8.GetString(base64EncodedBytes);
+        }
+
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/AppBuilderExtensions.cs b/GraphqlController/GraphqlController.AspNet/Relay/AppBuilderExtensions.cs
new file mode 100644
index 0000000..1170970
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/AppBuilderExtensions.cs
@@ -0,0 +1,19 @@
+﻿using Microsoft.AspNetCore.Builder;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using GraphqlController.AspNetCore;
+
+namespace GraphqlController.AspNetCore.Relay
+{
+    public static class AppBuilderExtensions
+    {
+        public static void AddGraphqlRelay(this IApplicationBuilder app)
+        {
+            app.AddSchemaInitializer(schema =>
+            {
+                schema.RegisterValueConverter(new GlobalIdAstValueConverter());
+            });
+        }
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/Connection.cs b/GraphqlController/GraphqlController.AspNet/Relay/Connection.cs
new file mode 100644
index 0000000..20d103e
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/Connection.cs
@@ -0,0 +1,51 @@
+﻿using GraphqlController.Attributes;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GraphqlController.AspNetCore.Relay
+{    
+    public class Connection<T>
+    {
+        public Connection(IEnumerable<Edge<T>> edges, PageInfo pageInfo)
+        {
+            Edges = edges;
+            PageInfo = pageInfo;
+        }
+
+        public static string GenerateName(Type type)
+        {
+            var genericType = type.GetGenericArguments()[0];
+            return $"{genericType.Name}Connection";            
+        }
+
+        public IEnumerable<Edge<T>> Edges { get; }
+        public PageInfo PageInfo { get; }
+    }
+
+    public class PageInfo
+    {
+        public bool HasNextPage { get; set; }
+        public bool HasPreviousPage { get; set; }
+        public string StartCursor { get; set; }
+        public string EndCursor { get; set; }
+    }
+
+    public class Edge<T>
+    {
+        public Edge(string cursor, T node)
+        {
+            Cursor = cursor;
+            Node = node;
+        }
+
+        public static string GenerateName(Type type)
+        {
+            var genericType = type.GetGenericArguments()[0];
+            return $"{genericType.Name}Edge";
+        }
+
+        public string Cursor { get; }
+        public T Node { get; }
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/GlobalIdType.cs b/GraphqlController/GraphqlController.AspNet/Relay/GlobalIdType.cs
new file mode 100644
index 0000000..bf0755f
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/GlobalIdType.cs
@@ -0,0 +1,72 @@
+﻿using GraphQL;
+using GraphQL.Language.AST;
+using GraphQL.Types;
+using Gski.Relay.Types;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace GraphqlController.AspNetCore.Relay
+{
+    public class GlobalIdType : ScalarGraphType
+    {
+        public GlobalIdType()
+        {
+            Name = "GlobalId";
+            Description = "Represent an id of an entity globaly";
+        }
+
+        public override object ParseLiteral(IValue value)
+        {
+            if (value is GlobalIdValueNode globalIdValue)
+                return ParseValue(globalIdValue.Value);
+
+            return value is StringValue stringValue
+                         ? ParseValue(stringValue.Value)
+                         : null;
+        }
+
+        public override object ParseValue(object value)
+        {
+            return ValueConverter.ConvertTo(value, typeof(GlobalId));
+        }
+
+        public override object Serialize(object value)
+        {
+            return value switch
+            {
+                string s => ValueConverter.ConvertTo(value, typeof(GlobalId)),
+                GlobalId v => v.Deserialize(),
+                _ => throw new NotImplementedException()
+            };            
+        }
+    }
+
+    public class GlobalIdValueNode : ValueNode<GlobalId>
+    {
+
+        public GlobalIdValueNode(GlobalId value)
+        {
+            Value = value;
+        }
+
+        protected override bool Equals(ValueNode<GlobalId> node)
+        {
+            return Value.Equals(node);
+        }
+    }
+
+    public class GlobalIdAstValueConverter : IAstFromValueConverter
+    {
+        public IValue Convert(object value, IGraphType type)
+        {
+            return new GlobalIdValueNode(value as GlobalId);
+        }
+
+        public bool Matches(object value, IGraphType type)
+        {
+            return value is GlobalId;
+        }
+    }
+
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/Node.cs b/GraphqlController/GraphqlController.AspNet/Relay/Node.cs
new file mode 100644
index 0000000..763a863
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/Node.cs
@@ -0,0 +1,57 @@
+﻿using GraphqlController;
+using GraphqlController.Attributes;
+using GraphqlController.Helpers;
+using Gski.Relay.Types;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GraphqlController.AspNetCore.Relay
+{
+    public class InjectableNode<T> : GraphNodeType<T>, INode
+    {
+        public GlobalId Id { get; private set; }
+
+        protected void SetId(string id)
+        {
+            var name = GetEntityName();
+            Id = new GlobalId(id, name);
+        }
+
+        protected string GetEntityName()
+        {
+            var type = this.GetType();
+            var nameAttr = type.GetAttribute<NameAttribute>();
+            var name = nameAttr?.Name ?? type.Name;
+            return name;
+        }
+    }
+
+
+    public class Node : INode
+    {
+        public GlobalId Id { get; private set; }
+
+        protected void SetId(string id)
+        {
+            var name = GetEntityName();
+            Id = new GlobalId(id, name);
+        }
+
+        protected string GetEntityName()
+        {
+            var type = this.GetType();
+            var nameAttr = type.GetAttribute<NameAttribute>();
+            var name = nameAttr?.Name ?? type.Name;
+            return name;
+        }
+    }
+
+
+    public class InjectableNode : InjectableNode<object>
+    {
+
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/NodeResolver.cs b/GraphqlController/GraphqlController.AspNet/Relay/NodeResolver.cs
new file mode 100644
index 0000000..fbe24b7
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/NodeResolver.cs
@@ -0,0 +1,24 @@
+﻿using Gski.Relay.Types;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GraphqlController.AspNetCore.Relay
+{
+    public interface INodeResolver
+    {
+        public Task<INode> GetByIdAsync(GlobalId id, CancellationToken cancellationToken);
+    }
+
+    public abstract class NodeResolver<T> : INodeResolver where T : INode
+    {
+        public abstract Task<T> GetByIdAsync(string id, CancellationToken cancellationToken);
+
+        public async Task<INode> GetByIdAsync(GlobalId id, CancellationToken cancellationToken)
+        {
+            return await GetByIdAsync(id.Id, cancellationToken);
+        }
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolverService.cs b/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolverService.cs
new file mode 100644
index 0000000..4ed590d
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolverService.cs
@@ -0,0 +1,38 @@
+﻿using GraphqlController.Attributes;
+using GraphqlController.Helpers;
+using GraphqlController.Services;
+using Gski.Relay.Types;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GraphqlController.AspNetCore.Relay
+{
+    public interface IObjectResolverService
+    {
+        Task<INode> GetByIdAsync(GlobalId id, CancellationToken cancellationToken);
+    }
+
+    public class ObjectResolverService : IObjectResolverService
+    {
+        ObjectResolversContianer _objectResolversContianer;
+        IServiceProvider _serviceProvider;
+
+        public ObjectResolverService(ObjectResolversContianer objectResolversContianer, IScopedServiceProviderResolver serviceProvider)
+        {
+            _objectResolversContianer = objectResolversContianer;
+            _serviceProvider = serviceProvider.GetProvider();
+        }
+
+        public Task<INode> GetByIdAsync(GlobalId id, CancellationToken cancellationToken)
+        {
+            var resolverType = _objectResolversContianer.GetResolver(id.ObjectType);
+            var resolver = _serviceProvider.GetService(resolverType) as INodeResolver;
+            return resolver.GetByIdAsync(id, cancellationToken);
+        }
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolversContianer.cs b/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolversContianer.cs
new file mode 100644
index 0000000..6e2d99e
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolversContianer.cs
@@ -0,0 +1,40 @@
+﻿using GraphqlController.Attributes;
+using GraphqlController.Helpers;
+using GraphqlController.Services;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace GraphqlController.AspNetCore.Relay
+{
+    public class ObjectResolversContianer
+    {
+        ConcurrentDictionary<string, Type> _resolvers;
+
+        public ObjectResolversContianer(IAssemblyResolver assemblyResolver)
+        {
+            var resolvers = assemblyResolver
+               .GetAssemblies()
+               .SelectMany(x => x.GetTypes())
+               .Where(x => typeof(INodeResolver).IsAssignableFrom(x));
+
+            _resolvers = new ConcurrentDictionary<string, Type>();
+
+            foreach (var resolver in resolvers)
+            {
+                var genericParameter = resolver.GetGenericArguments()[0];
+                var nameAttr = genericParameter.GetAttribute<NameAttribute>();
+                var name = nameAttr?.Name ?? genericParameter.Name;
+                _resolvers.TryAdd(name, resolver);
+            }
+        }
+
+        public Type GetResolver(string typeName)
+        {
+            return _resolvers[typeName];
+        }
+
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/ServiceCollectionExtensions.cs b/GraphqlController/GraphqlController.AspNet/Relay/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..525da0f
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/ServiceCollectionExtensions.cs
@@ -0,0 +1,43 @@
+﻿using GraphQL;
+using GraphqlController.Services;
+using Gski.Relay.Types;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace GraphqlController.AspNetCore.Relay
+{
+    public static class ServiceCollectionExtensions
+    {
+        public static void AddRelay(this GraphqlControllerServiceBuilder builder)
+        {
+            builder.Services.AddSingleton<GlobalIdType>();
+            builder.AddCustomScalarType(typeof(GlobalId), new GlobalIdType());
+            ValueConverter.Register(typeof(string), typeof(GlobalId), ConvertStringToGlobalId);
+
+            var resolvers = builder.AssemblyResolver
+                .GetAssemblies()
+                .SelectMany(x => x.GetTypes())
+                .Where(x => typeof(INodeResolver).IsAssignableFrom(x));
+
+            foreach(var resolver in resolvers)
+            {
+                builder.Services.AddTransient(resolver);
+            }
+
+            builder.AddAssembly(typeof(INode).Assembly);
+
+            builder.Services.AddSingleton<ObjectResolversContianer>();
+            builder.Services.AddTransient<IObjectResolverService, ObjectResolverService>();
+        }        
+
+        private static object ConvertStringToGlobalId(object arg)
+        {
+            var str = arg as string;
+            return new GlobalId(str);
+        }
+    }
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/Types/GlobalId.cs b/GraphqlController/GraphqlController.AspNet/Relay/Types/GlobalId.cs
new file mode 100644
index 0000000..a0ddd82
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/Types/GlobalId.cs
@@ -0,0 +1,68 @@
+﻿using GraphqlController.AspNetCore.Extensions;
+
+namespace GraphqlController.AspNetCore.Relay.Types
+{
+    public class GlobalId
+    {
+        public string Id { get; }
+
+        public string ObjectType { get; }
+
+        public GlobalId(string globalId)
+        {
+            var value = GlobalIdTools.GetEntityTypeAndId(globalId);
+            Id = value.Id;
+            ObjectType = value.Name;
+        }
+
+        public GlobalId(string id, string objectType)
+        {
+            Id = id;
+            ObjectType = objectType;
+        }
+
+        public string Deserialize()
+        {
+            return GlobalIdTools.GetGlobalId(ObjectType, Id);
+        }
+
+        public override bool Equals(object obj)
+        {
+            var value2 = obj as GlobalId;
+            return Id == value2.Id && ObjectType == value2.ObjectType;
+        }
+
+        public override int GetHashCode()
+        {
+            return (Id + ObjectType).GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return Deserialize();
+        }
+
+    }
+
+
+    internal static class GlobalIdTools
+    {
+        public static (string Id, string Name) GetEntityTypeAndId(string globalId)
+        {
+            var uniqueId = globalId.Base64Decode();
+            var split = uniqueId.Split('|');
+            var typeName = split[0];
+            var id = split[1];
+
+            return (id, typeName);
+        }
+
+        public static string GetGlobalId(string objectType, string id)
+        {
+            var typeName = objectType;
+            var uniqueId = $"{typeName}|{id}";
+            return uniqueId.Base64Encode();
+        }
+    }
+
+}
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/Types/INode.cs b/GraphqlController/GraphqlController.AspNet/Relay/Types/INode.cs
new file mode 100644
index 0000000..25613d8
--- /dev/null
+++ b/GraphqlController/GraphqlController.AspNet/Relay/Types/INode.cs
@@ -0,0 +1,13 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GraphqlController.AspNetCore.Relay.Types
+{    
+    public interface INode
+    {
+        public GlobalId Id { get; }
+    }
+}
diff --git a/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs b/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
index 2ce222e..49fec9f 100644
--- a/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
+++ b/GraphqlController/GraphqlController.WebAppTest/Types/Root.cs
@@ -38,8 +38,6 @@ public static string GenerateName(Type type)
         {
             return "ElRoot";
         }
-
-
     }
 
     public class BaseTest : GraphNodeType

From cfc0a43322148671d2e9ca8a2a633e094f341d0e Mon Sep 17 00:00:00 2001
From: Alejandro Guardiola <aleguardiolam@gmail.com>
Date: Sun, 29 Nov 2020 18:06:20 -0500
Subject: [PATCH 11/11] namespace fix

---
 .../GraphqlController.AspNet/Relay/GlobalIdType.cs              | 2 +-
 GraphqlController/GraphqlController.AspNet/Relay/Node.cs        | 2 +-
 .../GraphqlController.AspNet/Relay/NodeResolver.cs              | 2 +-
 .../GraphqlController.AspNet/Relay/ObjectResolverService.cs     | 2 +-
 .../Relay/ServiceCollectionExtensions.cs                        | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/GraphqlController/GraphqlController.AspNet/Relay/GlobalIdType.cs b/GraphqlController/GraphqlController.AspNet/Relay/GlobalIdType.cs
index bf0755f..89b372b 100644
--- a/GraphqlController/GraphqlController.AspNet/Relay/GlobalIdType.cs
+++ b/GraphqlController/GraphqlController.AspNet/Relay/GlobalIdType.cs
@@ -1,7 +1,7 @@
 ﻿using GraphQL;
 using GraphQL.Language.AST;
 using GraphQL.Types;
-using Gski.Relay.Types;
+using GraphqlController.AspNetCore.Relay.Types;
 using System;
 using System.Collections.Generic;
 using System.Text;
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/Node.cs b/GraphqlController/GraphqlController.AspNet/Relay/Node.cs
index 763a863..b8a41d4 100644
--- a/GraphqlController/GraphqlController.AspNet/Relay/Node.cs
+++ b/GraphqlController/GraphqlController.AspNet/Relay/Node.cs
@@ -1,7 +1,7 @@
 ﻿using GraphqlController;
 using GraphqlController.Attributes;
 using GraphqlController.Helpers;
-using Gski.Relay.Types;
+using GraphqlController.AspNetCore.Relay.Types;
 using System;
 using System.Collections.Generic;
 using System.Text;
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/NodeResolver.cs b/GraphqlController/GraphqlController.AspNet/Relay/NodeResolver.cs
index fbe24b7..6fb67e7 100644
--- a/GraphqlController/GraphqlController.AspNet/Relay/NodeResolver.cs
+++ b/GraphqlController/GraphqlController.AspNet/Relay/NodeResolver.cs
@@ -1,4 +1,4 @@
-﻿using Gski.Relay.Types;
+﻿using GraphqlController.AspNetCore.Relay.Types;
 using System;
 using System.Collections.Generic;
 using System.Text;
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolverService.cs b/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolverService.cs
index 4ed590d..5f0af3f 100644
--- a/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolverService.cs
+++ b/GraphqlController/GraphqlController.AspNet/Relay/ObjectResolverService.cs
@@ -1,7 +1,7 @@
 ﻿using GraphqlController.Attributes;
 using GraphqlController.Helpers;
 using GraphqlController.Services;
-using Gski.Relay.Types;
+using GraphqlController.AspNetCore.Relay.Types;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
diff --git a/GraphqlController/GraphqlController.AspNet/Relay/ServiceCollectionExtensions.cs b/GraphqlController/GraphqlController.AspNet/Relay/ServiceCollectionExtensions.cs
index 525da0f..c11b66c 100644
--- a/GraphqlController/GraphqlController.AspNet/Relay/ServiceCollectionExtensions.cs
+++ b/GraphqlController/GraphqlController.AspNet/Relay/ServiceCollectionExtensions.cs
@@ -1,6 +1,6 @@
 ﻿using GraphQL;
 using GraphqlController.Services;
-using Gski.Relay.Types;
+using GraphqlController.AspNetCore.Relay.Types;
 using Microsoft.Extensions.DependencyInjection;
 using System;
 using System.Collections.Generic;
