Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically generate enums for GraphType members based on the AST or Reflection #37

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/AspNetCore/AspNetCore.sln
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/AspNetCore/Example/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Graphql.Extensions.FieldEnums.Types;
using GraphQL.Server;
using GraphQL.Types;
using Microsoft.AspNetCore.Builder;
Expand All @@ -23,6 +24,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();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace Graphql.Extensions.FieldEnums.Exceptions
{
[Serializable]
public class AmbiguousFilterException : Exception
{
public AmbiguousFilterException(string message) : base(message)
{

}
}
}
112 changes: 112 additions & 0 deletions src/Graphql.Extensions.FieldEnums/Extensions/MetaDataExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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";
}
}
12 changes: 12 additions & 0 deletions src/Graphql.Extensions.FieldEnums/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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>
40 changes: 40 additions & 0 deletions src/Graphql.Extensions.FieldEnums/SkipTakeOrderByArgument.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
62 changes: 62 additions & 0 deletions src/Graphql.Extensions.FieldEnums/Types/DefaultQueryArguments.cs
Original file line number Diff line number Diff line change
@@ -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",
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using GraphQL.Types;

namespace Graphql.Extensions.FieldEnums.Types
{
public class FieldAwareEnumValueDefinition : EnumValueDefinition
{
public FieldType FieldType { get; set; }
}
}
33 changes: 33 additions & 0 deletions src/Graphql.Extensions.FieldEnums/Types/FieldAwareGraphType.cs
Original file line number Diff line number Diff line change
@@ -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<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())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See graphql-dotnet/graphql-dotnet#1107, it will be better to use already implemented features.

Copy link
Author

@JKamsker JKamsker Mar 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I have to use it? It wasn't "already implemented" when i initially did this last Winter.
Edit: Can't use the new feature as it uses an older version of graphql-net and im unable to upgrade it without causing a webserver failure.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even after #38 ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ORIGINAL_EXPRESSION_PROPERTY_NAME now internal, OK, I'll see what I can do.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm okay didn't notice the update, sorry my fault :D
But my solution still needs the sourcetype

Copy link
Author

@JKamsker JKamsker Mar 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.WithSourceType(typeof(TSourceType));

if (result.FieldType.Name.Contains("ID"))
{
result.FieldType.Name = result.FieldType.Name.Replace("ID", "Id");
}

return result;
}
}
}
Loading