diff --git a/src/AspNetCore/AspNetCore.sln b/src/AspNetCore/AspNetCore.sln index 71fb4f4..c6e0ad7 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("{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 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 + {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 0fccedf..4ea9307 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; @@ -23,6 +24,8 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(typeof(TypeFieldEnumerationWithoutLists<>), typeof(TypeFieldEnumerationWithoutLists<>)); + services.AddLogging(builder => builder.AddConsole()); services.AddHttpContextAccessor(); diff --git a/src/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs b/src/Graphql.Extensions.FieldEnums/Exceptions/AmbiguousFilterException.cs new file mode 100644 index 0000000..40bceff --- /dev/null +++ b/src/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/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs b/src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs new file mode 100644 index 0000000..8e92285 --- /dev/null +++ b/src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs @@ -0,0 +1,118 @@ +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 WithOriginalName + ( + this FieldBuilder fieldBuilder, + string originalName, + bool overwrite = false + ) + { + if (!overwrite && fieldBuilder.FieldType.Metadata.ContainsKey(SharedConstants.OriginalPropertyName)) + { + return fieldBuilder; + } + + 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 WithSourceType + ( + this FieldBuilder 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 + { + + /// + /// Guesses the first type which is not a graphql lib type. + /// WARNING! THIS METHOD IS NOT SAFE + /// Its just for the lazy boyz + /// + /// + /// + 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 arguments) + { + foreach (var queryArgument in arguments) + { + queryArguments.Add(queryArgument); + } + + return queryArguments; + } + + public static FieldBuilder SkipTakeOrderByArguments + ( + this FieldBuilder 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/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs b/src/Graphql.Extensions.FieldEnums/Extensions/PropertyInfoExtensions.cs new file mode 100644 index 0000000..beb974d --- /dev/null +++ b/src/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/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs b/src/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs new file mode 100644 index 0000000..620c25b --- /dev/null +++ b/src/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/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj b/src/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj new file mode 100644 index 0000000..ac7d078 --- /dev/null +++ b/src/Graphql.Extensions.FieldEnums/Graphql.Extensions.FieldEnums.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Graphql.Extensions.FieldEnums/SharedConstants.cs b/src/Graphql.Extensions.FieldEnums/SharedConstants.cs new file mode 100644 index 0000000..3fd95ef --- /dev/null +++ b/src/Graphql.Extensions.FieldEnums/SharedConstants.cs @@ -0,0 +1,9 @@ +namespace Graphql.Extensions.FieldEnums.Types.Extensions +{ + public static class SharedConstants + { + public const string OriginalPropertyName = "ORIGINAL_EXPRESSION_PROPERTY_NAME"; + public const string SourceType = "SW_SourceType"; + public const string FieldDataRequired = "SW_FieldDataRequired"; + } +} diff --git a/src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs b/src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs new file mode 100644 index 0000000..4f3bc11 --- /dev/null +++ b/src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Text; +using GraphQL; +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(IResolveFieldContext context) + { + var result = new SkipTakeOrderByArgument + { + Skip = context.GetArgument("skip", null), + Take = context.GetArgument("take", null), + OrderBy = context.GetArgument("orderBy", null), + OrderByDesc = context.GetArgument("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/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs b/src/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs new file mode 100644 index 0000000..7a0e654 --- /dev/null +++ b/src/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 SkipTakeOrderByArguments() + => SkipTakeOrderByArguments(typeof(TTargetType)); + + public static IEnumerable 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/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareEnumValueDefinition.cs new file mode 100644 index 0000000..6d162af --- /dev/null +++ b/src/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/Graphql.Extensions.FieldEnums/Types/FieldAwareGraphType.cs b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareGraphType.cs new file mode 100644 index 0000000..aa424c5 --- /dev/null +++ b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareGraphType.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 FieldAwareGraphType : ObjectGraphType + { + public override FieldBuilder Field + ( + string name, + Expression> 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; + } + } +} diff --git a/src/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs new file mode 100644 index 0000000..875b348 --- /dev/null +++ b/src/Graphql.Extensions.FieldEnums/Types/FieldAwareQueryArgument.cs @@ -0,0 +1,11 @@ +using GraphQL.Types; + +namespace Graphql.Extensions.FieldEnums.Types +{ + public class FieldAwareQueryArgument : QueryArgument + { + public FieldAwareQueryArgument() : base(typeof(TypeFieldEnumerationWithoutLists)) + { + } + } +} \ No newline at end of file diff --git a/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs b/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs new file mode 100644 index 0000000..30213f9 --- /dev/null +++ b/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumeration.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using Graphql.Extensions.FieldEnums.Types.Extensions; +using GraphQL.Types; +using GraphQL.Utilities; + +namespace Graphql.Extensions.FieldEnums.Types +{ + public class TypeFieldEnumeration : 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 GetEnumValueDefinitions() + { + if (typeof(IComplexGraphType).IsAssignableFrom(typeof(TType))) + { + var graphType = this.serviceProvider.GetRequiredService() 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 + }; + } + } + } + } +} diff --git a/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs b/src/Graphql.Extensions.FieldEnums/Types/TypeFieldEnumerationWithoutLists.cs new file mode 100644 index 0000000..5844e58 --- /dev/null +++ b/src/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 : TypeFieldEnumeration + { + public TypeFieldEnumerationWithoutLists(IServiceProvider serviceProvider) : base(serviceProvider) + { + } + + public override IEnumerable 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/StarWars/StarWars.csproj b/src/StarWars/StarWars.csproj index 2353ebd..a3d0363 100644 --- a/src/StarWars/StarWars.csproj +++ b/src/StarWars/StarWars.csproj @@ -9,6 +9,11 @@ + + + + + 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> 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 c5b81ec..6a01fb1 100644 --- a/src/StarWars/StarWarsQuery.cs +++ b/src/StarWars/StarWarsQuery.cs @@ -1,4 +1,7 @@ using System; +using Graphql.Extensions.FieldEnums; +using Graphql.Extensions.FieldEnums.Types; +using Graphql.Extensions.FieldEnums.Types.Extensions; using GraphQL; using GraphQL.Types; using StarWars.Types; @@ -17,7 +20,13 @@ public StarWarsQuery(StarWarsData data) arguments: new QueryArguments( new QueryArgument> { Name = "id", Description = "id of the human" } ), - resolve: context => data.GetHumanByIdAsync(context.GetArgument("id")) + resolve: context => data.GetHumanByIdAsync(context.GetArgument("id"))); + + + Field>( + "humans", + arguments: new QueryArguments(DefaultQueryArguments.SkipTakeOrderByArguments()), + resolve: context => data.GetHumansAsync(SkipTakeOrderByArgument.Parse(context)) ); Func func = (context, id) => data.GetDroidByIdAsync(id); @@ -27,6 +36,7 @@ public StarWarsQuery(StarWarsData data) arguments: new QueryArguments( new QueryArgument> { Name = "id", Description = "id of the droid" } ), + resolve: func ); } diff --git a/src/StarWars/Types/DroidType.cs b/src/StarWars/Types/DroidType.cs index 44175fa..65ed993 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 + public class DroidType : FieldAwareGraphType { public DroidType(StarWarsData data) { diff --git a/src/StarWars/Types/HumanType.cs b/src/StarWars/Types/HumanType.cs index 3f7e02a..198e97b 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 + public class HumanType : FieldAwareGraphType { public HumanType(StarWarsData data) {