From 3a727d8f906e053fd0a34732a98ecef77c051bab Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Fri, 28 Feb 2020 08:41:35 +0100
Subject: [PATCH 1/9] Implemented automatic enum generation for types

---
 src/AspNetCore/AspNetCore.sln                 |  18 ++-
 src/AspNetCore/Example/Startup.cs             |   2 +
 .../Extensions/MetaDataExtensions.cs          | 112 ++++++++++++++++++
 .../Extensions/PropertyInfoExtensions.cs      |  32 +++++
 .../Extensions/SharedConstants.cs             |   9 ++
 .../Extensions/TypeExtensions.cs              |  12 ++
 .../Graphql.Extensions.FieldEnums.csproj      |  11 ++
 .../Types/DefaultQueryArguments.cs            |  62 ++++++++++
 .../Types/FieldAwareEnumValueDefinition.cs    |   9 ++
 .../Types/FieldAwareQueryArgument.cs          |  11 ++
 .../Types/ServiceProviderExtensions.cs        |  12 ++
 .../Types/TypeFieldEnumeration.cs             |  58 +++++++++
 .../Types/TypeFieldEnumerationWithoutLists.cs |  49 ++++++++
 .../Types/UtilityGraphType.cs                 |  33 ++++++
 src/StarWars/StarWars.csproj                  |   4 +
 src/StarWars/StarWarsQuery.cs                 |   4 +-
 src/StarWars/Types/DroidType.cs               |   3 +-
 src/StarWars/Types/HumanType.cs               |   3 +-
 18 files changed, 439 insertions(+), 5 deletions(-)
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs

diff --git a/src/AspNetCore/AspNetCore.sln b/src/AspNetCore/AspNetCore.sln
index 71fb4f4..ee25327 100644
--- a/src/AspNetCore/AspNetCore.sln
+++ b/src/AspNetCore/AspNetCore.sln
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2036
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29613.14
 MinimumVisualStudioVersion = 15.0.26124.0
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StarWars", "..\StarWars\StarWars.csproj", "{E579287D-EFD6-4803-9B52-23BE9854A9AE}"
 EndProject
@@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		run.sh = run.sh
 	EndProjectSection
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Graphql.Extensions.FieldEnums", "Graphql.Extensions.FieldEnums\Graphql.Extensions.FieldEnums.csproj", "{3F6540A1-0539-431B-B1B2-630161A00661}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -48,6 +50,18 @@ Global
 		{46C914F6-4432-4D1F-8B47-18F0C9AA65DD}.Release|x64.Build.0 = Release|Any CPU
 		{46C914F6-4432-4D1F-8B47-18F0C9AA65DD}.Release|x86.ActiveCfg = Release|Any CPU
 		{46C914F6-4432-4D1F-8B47-18F0C9AA65DD}.Release|x86.Build.0 = Release|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|x64.Build.0 = Debug|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|x86.Build.0 = Debug|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|x64.ActiveCfg = Release|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|x64.Build.0 = Release|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|x86.ActiveCfg = Release|Any CPU
+		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/src/AspNetCore/Example/Startup.cs b/src/AspNetCore/Example/Startup.cs
index 587594e..5e62a08 100644
--- a/src/AspNetCore/Example/Startup.cs
+++ b/src/AspNetCore/Example/Startup.cs
@@ -23,6 +23,8 @@ public void ConfigureServices(IServiceCollection services)
             services.AddSingleton<EpisodeEnum>();
             services.AddSingleton<ISchema, StarWarsSchema>();
 
+            services.AddSingleton(typeof(TypeFieldEnumerationWithoutLists<>), typeof(TypeFieldEnumerationWithoutLists<>));
+
             services.AddLogging(builder => builder.AddConsole());
             services.AddHttpContextAccessor();
 
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
new file mode 100644
index 0000000..cf3e819
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using GraphQL.Builders;
+using GraphQL.Types;
+
+namespace Graphql.Extensions.FieldEnums.Types.Extensions
+{
+    public static class MetaDataExtensions
+    {
+        public static FieldBuilder<TSourceType, TProperty> WithOriginalName<TSourceType, TProperty>
+        (
+            this FieldBuilder<TSourceType, TProperty> fieldBuilder,
+            string originalName
+        )
+        {
+            fieldBuilder.FieldType.Metadata[SharedConstants.OriginalPropertyName] = originalName;
+            return fieldBuilder;
+        }
+
+        public static string GetOriginalName(this FieldType field)
+        {
+            if (field.Metadata.TryGetValue(SharedConstants.OriginalPropertyName, out var originalName))
+            {
+                return originalName.ToString();
+            }
+
+            return field.Name;
+        }
+
+
+        public static FieldBuilder<TSourceType, TProperty> WithSourceType<TSourceType, TProperty>
+        (
+            this FieldBuilder<TSourceType, TProperty> fieldBuilder,
+            Type type = null
+        )
+        {
+            fieldBuilder.FieldType.Metadata[SharedConstants.SourceType] = type ?? typeof(TSourceType);
+            return fieldBuilder;
+        }
+
+        public static Type GetSourceType(this FieldType field)
+        {
+            if (field.Metadata.TryGetValue(SharedConstants.SourceType, out var sourceTypeRaw) && sourceTypeRaw is Type sourceType)
+            {
+                return sourceType;
+            }
+
+            return null;
+        }
+    }
+
+    public static class GraphQLExtensions
+    {
+
+        /// <summary>
+        /// Guesses the first type which is not a graphql lib type.
+        /// WARNING! THIS METHOD IS NOT SAFE
+        /// Its just for the lazy boyz
+        /// </summary>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        public static Type EnsureNoGraphQlCoreType(this Type type)
+        {
+            var assembly = typeof(ListGraphType).Assembly;
+            if (type.Assembly == assembly)
+            {
+                return EnsureNoListGraphType(type.GetGenericArguments().First());
+            }
+
+            return type;
+        }
+
+        public static Type EnsureNoListGraphType(this Type type)
+        {
+            if (type.BaseType == typeof(ListGraphType))
+            {
+                return EnsureNoListGraphType(type.GetGenericArguments().First());
+            }
+
+            return type;
+        }
+
+        public static QueryArguments AddRange(this QueryArguments queryArguments, IEnumerable<QueryArgument> arguments)
+        {
+            foreach (var queryArgument in arguments)
+            {
+                queryArguments.Add(queryArgument);
+            }
+
+            return queryArguments;
+        }
+
+        public static FieldBuilder<TSourceType, TReturnType> SkipTakeOrderByArguments<TSourceType, TReturnType>
+        (
+            this FieldBuilder<TSourceType, TReturnType> source
+        )
+        {
+            var guessedGraphType = source.FieldType.Type.EnsureNoGraphQlCoreType();
+            var typedArg = typeof(TypeFieldEnumerationWithoutLists<>).MakeGenericType(guessedGraphType);
+
+            source.FieldType.Arguments.AddRange(new[] {
+                DefaultQueryArguments.Skip,
+                DefaultQueryArguments.Take,
+                DefaultQueryArguments.GetOrderBy(typedArg),
+                DefaultQueryArguments.GetOrderByDesc(typedArg),
+            });
+
+            return source;
+        }
+    }
+}
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs
new file mode 100644
index 0000000..beb974d
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Linq;
+using System.Reflection;
+
+namespace Graphql.Extensions.FieldEnums.Types.Extensions
+{
+    public static class PropertyInfoExtensions
+    {
+        private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
+
+        public static PropertyInfo GetProperty
+        (
+            this Type type,
+            string name,
+            BindingFlags bindingAttr = DefaultLookup,
+            StringComparison comparisonType = StringComparison.Ordinal
+        )
+        {
+            return type.GetProperties(bindingAttr).SingleOrDefault(x => string.Equals(name, x.Name, comparisonType));
+        }
+
+        public static PropertyInfo EnsureDeclaringPropertyInfo(this PropertyInfo propertyInfo)
+        {
+            if (propertyInfo.DeclaringType == null || propertyInfo.ReflectedType == propertyInfo.DeclaringType)
+            {
+                return propertyInfo;
+            }
+
+            return propertyInfo.DeclaringType.GetProperty(propertyInfo.Name, DefaultLookup);
+        }
+    }
+}
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
new file mode 100644
index 0000000..d516177
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
@@ -0,0 +1,9 @@
+namespace Graphql.Extensions.FieldEnums.Types.Extensions
+{
+    public static class SharedConstants
+    {
+        public const string OriginalPropertyName = "SW_OriginalPropertyName";
+        public const string SourceType = "SW_SourceType";
+        public const string FieldDataRequired = "SW_FieldDataRequired";
+    }
+}
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs
new file mode 100644
index 0000000..620c25b
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Graphql.Extensions.FieldEnums.Types.Extensions
+{
+    public static class TypeExtensions
+    {
+        public static bool InheritsFromGenericType(this Type type, Type genericTypeDefinition)
+        {
+            return type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == genericTypeDefinition;
+        }
+    }
+}
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj b/src/AspNetCore/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
new file mode 100644
index 0000000..1d629fd
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="GraphQL" Version="3.0.0-preview-1357" />
+  </ItemGroup>
+
+</Project>
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs
new file mode 100644
index 0000000..7a0e654
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using GraphQL.Types;
+
+namespace Graphql.Extensions.FieldEnums.Types
+{
+    public static class DefaultQueryArguments
+    {
+        public static IEnumerable<QueryArgument> SkipTakeOrderByArguments<TTargetType>()
+            => SkipTakeOrderByArguments(typeof(TTargetType));
+
+        public static IEnumerable<QueryArgument> SkipTakeOrderByArguments(Type targetType)
+        {
+            yield return Skip;
+            yield return Take;
+            yield return GetOrderBy(targetType);
+            yield return GetOrderByDesc(targetType);
+        }
+
+        public static QueryArgument Skip => new QueryArgument(typeof(IntGraphType))
+        {
+            Name = "skip",
+            Description = "skip n entries",
+        };
+
+        public static QueryArgument Take => new QueryArgument(typeof(IntGraphType))
+        {
+            Name = "take",
+            Description = "take n entries",
+        };
+
+        public static QueryArgument GetOrderBy(Type type)
+        {
+            var typedArg = type;
+            if (type.IsGenericType == false || type.GetGenericTypeDefinition() != typeof(TypeFieldEnumerationWithoutLists<>))
+            {
+                typedArg = typeof(TypeFieldEnumerationWithoutLists<>).MakeGenericType(type);
+            }
+
+            return new QueryArgument(typedArg)
+            {
+                Name = "orderBy",
+                Description = "order by",
+            };
+        }
+
+        public static QueryArgument GetOrderByDesc(Type type)
+        {
+            var typedArg = type;
+            if (type.IsGenericType == false || type.GetGenericTypeDefinition() != typeof(TypeFieldEnumerationWithoutLists<>))
+            {
+                typedArg = typeof(TypeFieldEnumerationWithoutLists<>).MakeGenericType(type);
+            }
+
+            return new QueryArgument(typedArg)
+            {
+                Name = "orderByDesc",
+                Description = "order by desc",
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs
new file mode 100644
index 0000000..6d162af
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs
@@ -0,0 +1,9 @@
+using GraphQL.Types;
+
+namespace Graphql.Extensions.FieldEnums.Types
+{
+    public class FieldAwareEnumValueDefinition : EnumValueDefinition
+    {
+        public FieldType FieldType { get; set; }
+    }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs
new file mode 100644
index 0000000..875b348
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs
@@ -0,0 +1,11 @@
+using GraphQL.Types;
+
+namespace Graphql.Extensions.FieldEnums.Types
+{
+    public class FieldAwareQueryArgument<TType> : QueryArgument
+    {
+        public FieldAwareQueryArgument() : base(typeof(TypeFieldEnumerationWithoutLists<TType>))
+        {
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs
new file mode 100644
index 0000000..1051b89
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Graphql.Extensions.FieldEnums.Types
+{
+    public static class ServiceProviderExtensions
+    {
+        public static T GetService<T>(this IServiceProvider serviceProvider)
+        {
+            return (T)serviceProvider.GetService(typeof(T));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
new file mode 100644
index 0000000..df8963b
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using Graphql.Extensions.FieldEnums.Types.Extensions;
+using GraphQL.Types;
+
+namespace Graphql.Extensions.FieldEnums.Types
+{
+    public class TypeFieldEnumeration<TType> : EnumerationGraphType
+    {
+        private readonly IServiceProvider serviceProvider;
+
+        public TypeFieldEnumeration(IServiceProvider serviceProvider)
+        {
+            this.serviceProvider = serviceProvider;
+            this.Name = $"{typeof(TType).Name}_Enumeration";
+
+            // ReSharper disable once VirtualMemberCallInConstructor
+            foreach (var enumValueDefinition in this.GetEnumValueDefinitions())
+            {
+                base.AddValue(enumValueDefinition);
+            }
+        }
+
+        public virtual IEnumerable<EnumValueDefinition> GetEnumValueDefinitions()
+        {
+            if (typeof(IComplexGraphType).IsAssignableFrom(typeof(TType)))
+            {
+                var graphType = this.serviceProvider.GetService<TType>() as IComplexGraphType;
+                var fields = graphType.Fields;
+
+                foreach (var field in fields)
+                {
+                    yield return new FieldAwareEnumValueDefinition
+                    {
+                        Name = field.Name,
+                        Description = field.Description,
+                        Value = field.GetOriginalName(),
+                        DeprecationReason = null,
+                        FieldType = field
+                    };
+                }
+            }
+            else
+            {
+                var fields = typeof(TType).GetProperties();
+                foreach (var field in fields)
+                {
+                    yield return new EnumValueDefinition
+                    {
+                        Name = field.Name,
+                        Value = field.Name,
+                        Description = field.Name
+                    };
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs
new file mode 100644
index 0000000..5844e58
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Graphql.Extensions.FieldEnums.Types.Extensions;
+using GraphQL.Types;
+
+namespace Graphql.Extensions.FieldEnums.Types
+{
+    public class TypeFieldEnumerationWithoutLists<TType> : TypeFieldEnumeration<TType>
+    {
+        public TypeFieldEnumerationWithoutLists(IServiceProvider serviceProvider) : base(serviceProvider)
+        {
+        }
+
+        public override IEnumerable<EnumValueDefinition> GetEnumValueDefinitions()
+        {
+            return base.GetEnumValueDefinitions().Where(x => ValidateEnumValueDefinition(x));
+        }
+
+        private bool ValidateEnumValueDefinition(EnumValueDefinition valueDefinition)
+        {
+            if (!(valueDefinition is FieldAwareEnumValueDefinition fieldDefinition))
+            {
+                return true;
+            }
+            if (typeof(ListGraphType).IsAssignableFrom(fieldDefinition.FieldType.Type))
+            {
+                return false;
+            }
+            else
+            {
+                var originalName = fieldDefinition.FieldType.GetOriginalName();
+                var sourceType = fieldDefinition.FieldType.GetSourceType();
+                if (sourceType == null)
+                {
+                    return true; //TODO or false, not sure, need more brain to evaluate
+                }
+
+                var requiredProperty = sourceType.GetProperty(originalName, comparisonType: StringComparison.OrdinalIgnoreCase);
+                if (requiredProperty == null)
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs
new file mode 100644
index 0000000..e474182
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Linq.Expressions;
+using GraphQL;
+using GraphQL.Builders;
+using Graphql.Extensions.FieldEnums.Types.Extensions;
+using GraphQL.Types;
+
+namespace Graphql.Extensions.FieldEnums.Types
+{
+    public class UtilityGraphType<TSourceType> : ObjectGraphType<TSourceType>
+    {
+        public override FieldBuilder<TSourceType, TProperty> Field<TProperty>
+        (
+            string name,
+            Expression<Func<TSourceType, TProperty>> expression,
+            bool nullable = false,
+            Type type = null
+        )
+        {
+            var result = base
+                .Field(name, expression, nullable, type)
+                .WithOriginalName(expression.NameOf())
+                .WithSourceType(typeof(TSourceType));
+
+            if (result.FieldType.Name.Contains("ID"))
+            {
+                result.FieldType.Name = result.FieldType.Name.Replace("ID", "Id");
+            }
+
+            return result;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/StarWars/StarWars.csproj b/src/StarWars/StarWars.csproj
index d9581d7..3fa3f01 100644
--- a/src/StarWars/StarWars.csproj
+++ b/src/StarWars/StarWars.csproj
@@ -11,4 +11,8 @@
     <PackageReference Include="GraphQL" Version="3.0.0-preview-1357" />
   </ItemGroup>
 
+  <ItemGroup>
+    <ProjectReference Include="..\AspNetCore\Graphql.Extensions.FieldEnums\Graphql.Extensions.FieldEnums.csproj" />
+  </ItemGroup>
+
 </Project>
diff --git a/src/StarWars/StarWarsQuery.cs b/src/StarWars/StarWarsQuery.cs
index 112568c..3e0cdaa 100644
--- a/src/StarWars/StarWarsQuery.cs
+++ b/src/StarWars/StarWarsQuery.cs
@@ -1,4 +1,6 @@
 using System;
+using Graphql.Extensions.FieldEnums.Types;
+using Graphql.Extensions.FieldEnums.Types.Extensions;
 using GraphQL.Types;
 using StarWars.Types;
 
@@ -15,7 +17,7 @@ public StarWarsQuery(StarWarsData data)
                 "human",
                 arguments: new QueryArguments(
                     new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the human" }
-                ),
+                ).AddRange(DefaultQueryArguments.SkipTakeOrderByArguments<HumanType>()),
                 resolve: context => data.GetHumanByIdAsync(context.GetArgument<string>("id"))
             );
 
diff --git a/src/StarWars/Types/DroidType.cs b/src/StarWars/Types/DroidType.cs
index 44175fa..76cbf47 100644
--- a/src/StarWars/Types/DroidType.cs
+++ b/src/StarWars/Types/DroidType.cs
@@ -1,8 +1,9 @@
+using Graphql.Extensions.FieldEnums.Types;
 using GraphQL.Types;
 
 namespace StarWars.Types
 {
-    public class DroidType : ObjectGraphType<Droid>
+    public class DroidType : UtilityGraphType<Droid>
     {
         public DroidType(StarWarsData data)
         {
diff --git a/src/StarWars/Types/HumanType.cs b/src/StarWars/Types/HumanType.cs
index 3f7e02a..fd74357 100644
--- a/src/StarWars/Types/HumanType.cs
+++ b/src/StarWars/Types/HumanType.cs
@@ -1,8 +1,9 @@
+using Graphql.Extensions.FieldEnums.Types;
 using GraphQL.Types;
 
 namespace StarWars.Types
 {
-    public class HumanType : ObjectGraphType<Human>
+    public class HumanType : UtilityGraphType<Human>
     {
         public HumanType(StarWarsData data)
         {

From 549785b629b0b7dfbefd8228d017ad21158f00ac Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Fri, 28 Feb 2020 08:48:21 +0100
Subject: [PATCH 2/9] Added SkipTakeOrderBy parser

---
 .../Exceptions/AmbiguousFilterException.cs    | 13 ++++++
 .../SkipTakeOrderByArgument.cs                | 40 +++++++++++++++++++
 src/StarWars/StarWarsQuery.cs                 | 18 +++++++--
 3 files changed, 67 insertions(+), 4 deletions(-)
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs
 create mode 100644 src/AspNetCore/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs

diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs
new file mode 100644
index 0000000..40bceff
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Graphql.Extensions.FieldEnums.Exceptions
+{
+    [Serializable]
+    public class AmbiguousFilterException : Exception
+    {
+        public AmbiguousFilterException(string message) : base(message)
+        {
+
+        }
+    }
+}
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs b/src/AspNetCore/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
new file mode 100644
index 0000000..dd78354
--- /dev/null
+++ b/src/AspNetCore/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using System.Text;
+using Graphql.Extensions.FieldEnums.Exceptions;
+using GraphQL.Types;
+
+namespace Graphql.Extensions.FieldEnums
+{
+    public class SkipTakeOrderByArgument
+    {
+        public int? Skip { get; set; }
+        public int? Take { get; set; }
+        public string OrderBy { get; set; }
+        public string OrderByDesc { get; set; }
+
+        public SkipTakeOrderByArgument()
+        {
+        }
+
+        public static SkipTakeOrderByArgument Parse<T>(ResolveFieldContext<T> context)
+        {
+            var result = new SkipTakeOrderByArgument
+            {
+                Skip = context.GetArgument<int?>("skip", null),
+                Take = context.GetArgument<int?>("take", null),
+                OrderBy = context.GetArgument<string>("orderBy", null),
+                OrderByDesc = context.GetArgument<string>("orderByDesc", null),
+            };
+
+            var hasOrderBy = !string.IsNullOrEmpty(result.OrderBy);
+            var hasOrderByDesc = !string.IsNullOrEmpty(result.OrderByDesc);
+
+            if (hasOrderBy == true && hasOrderByDesc == true)
+            {
+                throw new AmbiguousFilterException($"Cannot order by {result.OrderBy} and {result.OrderByDesc} at the same time");
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/src/StarWars/StarWarsQuery.cs b/src/StarWars/StarWarsQuery.cs
index 3e0cdaa..91b123f 100644
--- a/src/StarWars/StarWarsQuery.cs
+++ b/src/StarWars/StarWarsQuery.cs
@@ -1,4 +1,5 @@
 using System;
+using Graphql.Extensions.FieldEnums;
 using Graphql.Extensions.FieldEnums.Types;
 using Graphql.Extensions.FieldEnums.Types.Extensions;
 using GraphQL.Types;
@@ -18,16 +19,25 @@ public StarWarsQuery(StarWarsData data)
                 arguments: new QueryArguments(
                     new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the human" }
                 ).AddRange(DefaultQueryArguments.SkipTakeOrderByArguments<HumanType>()),
-                resolve: context => data.GetHumanByIdAsync(context.GetArgument<string>("id"))
-            );
+                resolve: context =>
+                {
+                    var skipTakeArgs = SkipTakeOrderByArgument.Parse(context);
+
+                    return data.GetHumanByIdAsync(context.GetArgument<string>("id"));
+                });
 
-            Func<ResolveFieldContext, string, object> func = (context, id) => data.GetDroidByIdAsync(id);
+            Func<ResolveFieldContext, string, object> func = (context, id) =>
+            {
+                var skipTakeArgs = SkipTakeOrderByArgument.Parse(context);
+                return data.GetDroidByIdAsync(id);
+            };
 
             FieldDelegate<DroidType>(
                 "droid",
                 arguments: new QueryArguments(
                     new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the droid" }
-                ),
+                ).AddRange(DefaultQueryArguments.SkipTakeOrderByArguments<DroidType>()),
+
                 resolve: func
             );
         }

From 275ad4b207db11b21bf47392fff5af2b0422df24 Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Fri, 28 Feb 2020 08:52:13 +0100
Subject: [PATCH 3/9] Moved Extension Project

---
 src/AspNetCore/AspNetCore.sln                 | 26 +++++++++----------
 src/AspNetCore/Example/Startup.cs             |  1 +
 .../Exceptions/AmbiguousFilterException.cs    |  0
 .../Extensions/MetaDataExtensions.cs          |  0
 .../Extensions/PropertyInfoExtensions.cs      |  0
 .../Extensions/SharedConstants.cs             |  0
 .../Extensions/TypeExtensions.cs              |  0
 .../Graphql.Extensions.FieldEnums.csproj      |  0
 .../SkipTakeOrderByArgument.cs                |  0
 .../Types/DefaultQueryArguments.cs            |  0
 .../Types/FieldAwareEnumValueDefinition.cs    |  0
 .../Types/FieldAwareQueryArgument.cs          |  0
 .../Types/ServiceProviderExtensions.cs        |  0
 .../Types/TypeFieldEnumeration.cs             |  0
 .../Types/TypeFieldEnumerationWithoutLists.cs |  0
 .../Types/UtilityGraphType.cs                 |  0
 src/StarWars/StarWars.csproj                  |  2 +-
 17 files changed, 15 insertions(+), 14 deletions(-)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs (100%)
 rename src/{AspNetCore => }/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs (100%)

diff --git a/src/AspNetCore/AspNetCore.sln b/src/AspNetCore/AspNetCore.sln
index ee25327..c6e0ad7 100644
--- a/src/AspNetCore/AspNetCore.sln
+++ b/src/AspNetCore/AspNetCore.sln
@@ -14,7 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		run.sh = run.sh
 	EndProjectSection
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Graphql.Extensions.FieldEnums", "Graphql.Extensions.FieldEnums\Graphql.Extensions.FieldEnums.csproj", "{3F6540A1-0539-431B-B1B2-630161A00661}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Graphql.Extensions.FieldEnums", "..\Graphql.Extensions.FieldEnums\Graphql.Extensions.FieldEnums.csproj", "{25647A77-01C4-47C4-8409-4BD2616563D7}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -50,18 +50,18 @@ Global
 		{46C914F6-4432-4D1F-8B47-18F0C9AA65DD}.Release|x64.Build.0 = Release|Any CPU
 		{46C914F6-4432-4D1F-8B47-18F0C9AA65DD}.Release|x86.ActiveCfg = Release|Any CPU
 		{46C914F6-4432-4D1F-8B47-18F0C9AA65DD}.Release|x86.Build.0 = Release|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|x64.Build.0 = Debug|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Debug|x86.Build.0 = Debug|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|x64.ActiveCfg = Release|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|x64.Build.0 = Release|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|x86.ActiveCfg = Release|Any CPU
-		{3F6540A1-0539-431B-B1B2-630161A00661}.Release|x86.Build.0 = Release|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Debug|x64.Build.0 = Debug|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Debug|x86.Build.0 = Debug|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Release|x64.ActiveCfg = Release|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Release|x64.Build.0 = Release|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Release|x86.ActiveCfg = Release|Any CPU
+		{25647A77-01C4-47C4-8409-4BD2616563D7}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/src/AspNetCore/Example/Startup.cs b/src/AspNetCore/Example/Startup.cs
index 5e62a08..7d5142a 100644
--- a/src/AspNetCore/Example/Startup.cs
+++ b/src/AspNetCore/Example/Startup.cs
@@ -1,3 +1,4 @@
+using Graphql.Extensions.FieldEnums.Types;
 using GraphQL.Server;
 using GraphQL.Types;
 using Microsoft.AspNetCore.Builder;
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs b/src/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs
rename to src/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs b/src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
rename to src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs b/src/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs
rename to src/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs b/src/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
rename to src/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs b/src/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs
rename to src/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj b/src/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
rename to src/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs b/src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
rename to src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs b/src/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs
rename to src/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs
rename to src/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs
rename to src/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs b/src/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs
rename to src/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs b/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
rename to src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs b/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs
rename to src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs
diff --git a/src/AspNetCore/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs b/src/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs
similarity index 100%
rename from src/AspNetCore/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs
rename to src/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs
diff --git a/src/StarWars/StarWars.csproj b/src/StarWars/StarWars.csproj
index 3fa3f01..ae1a415 100644
--- a/src/StarWars/StarWars.csproj
+++ b/src/StarWars/StarWars.csproj
@@ -12,7 +12,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <ProjectReference Include="..\AspNetCore\Graphql.Extensions.FieldEnums\Graphql.Extensions.FieldEnums.csproj" />
+    <ProjectReference Include="..\Graphql.Extensions.FieldEnums\Graphql.Extensions.FieldEnums.csproj" />
   </ItemGroup>
 
 </Project>

From 00ae52cbab57285e6124dce31b99246e3a9adb41 Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Fri, 28 Feb 2020 08:53:34 +0100
Subject: [PATCH 4/9] Being a bit more creative

---
 .../Types/{UtilityGraphType.cs => FieldAwareGraphType.cs}     | 4 ++--
 src/StarWars/Types/DroidType.cs                               | 2 +-
 src/StarWars/Types/HumanType.cs                               | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)
 rename src/Graphql.Extensions.FieldEnums/Types/{UtilityGraphType.cs => FieldAwareGraphType.cs} (91%)

diff --git a/src/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareGraphType.cs
similarity index 91%
rename from src/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs
rename to src/Graphql.Extensions.FieldEnums/Types/FieldAwareGraphType.cs
index e474182..aa424c5 100644
--- a/src/Graphql.Extensions.FieldEnums/Types/UtilityGraphType.cs
+++ b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareGraphType.cs
@@ -7,7 +7,7 @@
 
 namespace Graphql.Extensions.FieldEnums.Types
 {
-    public class UtilityGraphType<TSourceType> : ObjectGraphType<TSourceType>
+    public class FieldAwareGraphType<TSourceType> : ObjectGraphType<TSourceType>
     {
         public override FieldBuilder<TSourceType, TProperty> Field<TProperty>
         (
@@ -30,4 +30,4 @@ public override FieldBuilder<TSourceType, TProperty> Field<TProperty>
             return result;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/StarWars/Types/DroidType.cs b/src/StarWars/Types/DroidType.cs
index 76cbf47..65ed993 100644
--- a/src/StarWars/Types/DroidType.cs
+++ b/src/StarWars/Types/DroidType.cs
@@ -3,7 +3,7 @@
 
 namespace StarWars.Types
 {
-    public class DroidType : UtilityGraphType<Droid>
+    public class DroidType : FieldAwareGraphType<Droid>
     {
         public DroidType(StarWarsData data)
         {
diff --git a/src/StarWars/Types/HumanType.cs b/src/StarWars/Types/HumanType.cs
index fd74357..198e97b 100644
--- a/src/StarWars/Types/HumanType.cs
+++ b/src/StarWars/Types/HumanType.cs
@@ -3,7 +3,7 @@
 
 namespace StarWars.Types
 {
-    public class HumanType : UtilityGraphType<Human>
+    public class HumanType : FieldAwareGraphType<Human>
     {
         public HumanType(StarWarsData data)
         {

From 7c6981645ad60ec0f2ba10566a9b6b15e9998e18 Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Tue, 3 Mar 2020 15:06:43 +0100
Subject: [PATCH 5/9] Made SkipTakeOrderByArgument to be used somewhere

---
 src/StarWars/StarWars.csproj  |  1 +
 src/StarWars/StarWarsData.cs  | 41 +++++++++++++++++++++++++++++++++--
 src/StarWars/StarWarsQuery.cs | 22 +++++++++----------
 3 files changed, 50 insertions(+), 14 deletions(-)

diff --git a/src/StarWars/StarWars.csproj b/src/StarWars/StarWars.csproj
index ae1a415..2195cc0 100644
--- a/src/StarWars/StarWars.csproj
+++ b/src/StarWars/StarWars.csproj
@@ -9,6 +9,7 @@
 
   <ItemGroup>
     <PackageReference Include="GraphQL" Version="3.0.0-preview-1357" />
+    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.21" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/StarWars/StarWarsData.cs b/src/StarWars/StarWarsData.cs
index 5d67e72..15a14ca 100644
--- a/src/StarWars/StarWarsData.cs
+++ b/src/StarWars/StarWarsData.cs
@@ -1,9 +1,14 @@
+using Graphql.Extensions.FieldEnums;
+
+using GraphQL;
+
+using StarWars.Types;
+
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Linq.Dynamic.Core;
 using System.Threading.Tasks;
-using GraphQL;
-using StarWars.Types;
 
 namespace StarWars
 {
@@ -80,5 +85,37 @@ public Human AddHuman(Human human)
             _humans.Add(human);
             return human;
         }
+
+        public Task<IEnumerable<Human>> GetHumansAsync(SkipTakeOrderByArgument skipTakeArgs)
+        {
+            var query = _humans.AsQueryable();
+            if (!string.IsNullOrEmpty(skipTakeArgs.OrderBy))
+            {
+                query = query.OrderBy(skipTakeArgs.OrderBy);
+            }
+            else if (!string.IsNullOrEmpty(skipTakeArgs.OrderByDesc))
+            {
+                query = query.OrderBy($"{skipTakeArgs.OrderByDesc} descending");
+            }
+            else
+            {
+                if (skipTakeArgs.Skip != null || skipTakeArgs.Take != null)
+                {
+                    query = query.OrderBy(x => x.Id);
+                }
+            }
+
+            if (skipTakeArgs.Skip != null)
+            {
+                query = query.Skip(skipTakeArgs.Skip.Value);
+            }
+
+            if (skipTakeArgs.Take != null)
+            {
+                query = query.Take(skipTakeArgs.Take.Value);
+            }
+
+            return Task.FromResult(query.ToList().AsEnumerable());
+        }
     }
 }
diff --git a/src/StarWars/StarWarsQuery.cs b/src/StarWars/StarWarsQuery.cs
index 91b123f..4f2bda3 100644
--- a/src/StarWars/StarWarsQuery.cs
+++ b/src/StarWars/StarWarsQuery.cs
@@ -18,25 +18,23 @@ public StarWarsQuery(StarWarsData data)
                 "human",
                 arguments: new QueryArguments(
                     new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the human" }
-                ).AddRange(DefaultQueryArguments.SkipTakeOrderByArguments<HumanType>()),
-                resolve: context =>
-                {
-                    var skipTakeArgs = SkipTakeOrderByArgument.Parse(context);
+                ),
+                resolve: context => data.GetHumanByIdAsync(context.GetArgument<string>("id")));
 
-                    return data.GetHumanByIdAsync(context.GetArgument<string>("id"));
-                });
 
-            Func<ResolveFieldContext, string, object> func = (context, id) =>
-            {
-                var skipTakeArgs = SkipTakeOrderByArgument.Parse(context);
-                return data.GetDroidByIdAsync(id);
-            };
+            Field<ListGraphType<HumanType>>(
+                "humans",
+                arguments: new QueryArguments(DefaultQueryArguments.SkipTakeOrderByArguments<HumanType>()),
+                resolve: context => data.GetHumansAsync(SkipTakeOrderByArgument.Parse(context))
+            );
+
+            Func<ResolveFieldContext, string, object> func = (context, id) => data.GetDroidByIdAsync(id);
 
             FieldDelegate<DroidType>(
                 "droid",
                 arguments: new QueryArguments(
                     new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the droid" }
-                ).AddRange(DefaultQueryArguments.SkipTakeOrderByArguments<DroidType>()),
+                ),
 
                 resolve: func
             );

From 8a0cc2b61091f332956a1bbc21a22fe373f0cc70 Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Tue, 3 Mar 2020 15:09:26 +0100
Subject: [PATCH 6/9] Got rid of the ServiceProviderExtensions class

---
 .../Types/ServiceProviderExtensions.cs               | 12 ------------
 .../Types/TypeFieldEnumeration.cs                    |  5 +++--
 2 files changed, 3 insertions(+), 14 deletions(-)
 delete mode 100644 src/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs

diff --git a/src/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs b/src/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs
deleted file mode 100644
index 1051b89..0000000
--- a/src/Graphql.Extensions.FieldEnums/Types/ServiceProviderExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace Graphql.Extensions.FieldEnums.Types
-{
-    public static class ServiceProviderExtensions
-    {
-        public static T GetService<T>(this IServiceProvider serviceProvider)
-        {
-            return (T)serviceProvider.GetService(typeof(T));
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs b/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
index df8963b..30213f9 100644
--- a/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
+++ b/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs
@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using Graphql.Extensions.FieldEnums.Types.Extensions;
 using GraphQL.Types;
+using GraphQL.Utilities;
 
 namespace Graphql.Extensions.FieldEnums.Types
 {
@@ -25,7 +26,7 @@ public virtual IEnumerable<EnumValueDefinition> GetEnumValueDefinitions()
         {
             if (typeof(IComplexGraphType).IsAssignableFrom(typeof(TType)))
             {
-                var graphType = this.serviceProvider.GetService<TType>() as IComplexGraphType;
+                var graphType = this.serviceProvider.GetRequiredService<TType>() as IComplexGraphType;
                 var fields = graphType.Fields;
 
                 foreach (var field in fields)
@@ -55,4 +56,4 @@ public virtual IEnumerable<EnumValueDefinition> GetEnumValueDefinitions()
             }
         }
     }
-}
\ No newline at end of file
+}

From 55bb061da462fe7c98d2ba244867ef6086d4765f Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Tue, 3 Mar 2020 15:45:13 +0100
Subject: [PATCH 7/9] Fixed merge

---
 .../Graphql.Extensions.FieldEnums.csproj                       | 2 +-
 src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs   | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj b/src/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
index 1d629fd..ac7d078 100644
--- a/src/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
+++ b/src/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj
@@ -5,7 +5,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="GraphQL" Version="3.0.0-preview-1357" />
+    <PackageReference Include="GraphQL" Version="3.0.0-preview-1558" />
   </ItemGroup>
 
 </Project>
diff --git a/src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs b/src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
index dd78354..4f3bc11 100644
--- a/src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
+++ b/src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Text;
+using GraphQL;
 using Graphql.Extensions.FieldEnums.Exceptions;
 using GraphQL.Types;
 
@@ -16,7 +17,7 @@ public SkipTakeOrderByArgument()
         {
         }
 
-        public static SkipTakeOrderByArgument Parse<T>(ResolveFieldContext<T> context)
+        public static SkipTakeOrderByArgument Parse<T>(IResolveFieldContext<T> context)
         {
             var result = new SkipTakeOrderByArgument
             {

From 4e080763ef34597c25786f9347430e62d4074cd0 Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Tue, 3 Mar 2020 15:49:31 +0100
Subject: [PATCH 8/9] Changed SharedConstants.OriginalPropertyName to use the
 internal string

---
 .../Extensions/MetaDataExtensions.cs                      | 8 +++++++-
 .../Extensions/SharedConstants.cs                         | 2 +-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs b/src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
index cf3e819..8e92285 100644
--- a/src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
+++ b/src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
@@ -11,9 +11,15 @@ public static class MetaDataExtensions
         public static FieldBuilder<TSourceType, TProperty> WithOriginalName<TSourceType, TProperty>
         (
             this FieldBuilder<TSourceType, TProperty> fieldBuilder,
-            string originalName
+            string originalName,
+            bool overwrite = false
         )
         {
+            if (!overwrite && fieldBuilder.FieldType.Metadata.ContainsKey(SharedConstants.OriginalPropertyName))
+            {
+                return fieldBuilder;
+            }
+
             fieldBuilder.FieldType.Metadata[SharedConstants.OriginalPropertyName] = originalName;
             return fieldBuilder;
         }
diff --git a/src/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs b/src/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
index d516177..3fd95ef 100644
--- a/src/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
+++ b/src/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
@@ -2,7 +2,7 @@ namespace Graphql.Extensions.FieldEnums.Types.Extensions
 {
     public static class SharedConstants
     {
-        public const string OriginalPropertyName = "SW_OriginalPropertyName";
+        public const string OriginalPropertyName = "ORIGINAL_EXPRESSION_PROPERTY_NAME";
         public const string SourceType = "SW_SourceType";
         public const string FieldDataRequired = "SW_FieldDataRequired";
     }

From cbb5e77beefd8a21186d82123848f79aadbadb12 Mon Sep 17 00:00:00 2001
From: Jonas Kamsker <jonas.kamsker@lints.at>
Date: Tue, 3 Mar 2020 15:50:36 +0100
Subject: [PATCH 9/9] Moved sharedConstants

---
 .../{Extensions => }/SharedConstants.cs                           | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename src/Graphql.Extensions.FieldEnums/{Extensions => }/SharedConstants.cs (100%)

diff --git a/src/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs b/src/Graphql.Extensions.FieldEnums/SharedConstants.cs
similarity index 100%
rename from src/Graphql.Extensions.FieldEnums/Extensions/SharedConstants.cs
rename to src/Graphql.Extensions.FieldEnums/SharedConstants.cs