Skip to content

Commit 4e94214

Browse files
committed
Add orderBy support
1 parent f5a17ea commit 4e94214

File tree

9 files changed

+566
-76
lines changed

9 files changed

+566
-76
lines changed

src/Our.Umbraco.GraphQL/Adapters/GraphTypeAdapter.cs

+43-59
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Reflection;
5+
using System.Threading;
56
using System.Threading.Tasks;
67
using GraphQL;
78
using GraphQL.Types;
89
using Our.Umbraco.GraphQL.Adapters.Resolvers;
10+
using Our.Umbraco.GraphQL.Adapters.Types;
911
using Our.Umbraco.GraphQL.Adapters.Types.Relay;
1012
using Our.Umbraco.GraphQL.Adapters.Types.Resolution;
1113
using Our.Umbraco.GraphQL.Adapters.Visitors;
1214
using Our.Umbraco.GraphQL.Attributes;
15+
using Our.Umbraco.GraphQL.Reflection;
16+
using Our.Umbraco.GraphQL.Types;
1317
using Our.Umbraco.GraphQL.Types.Relay;
1418

1519
namespace Our.Umbraco.GraphQL.Adapters
@@ -38,7 +42,7 @@ public IGraphType Adapt(TypeInfo typeInfo)
3842
{
3943
if (typeInfo == null) throw new ArgumentNullException(nameof(typeInfo));
4044

41-
var unwrappedTypeInfo = UnwrapTypeInfo(typeInfo);
45+
var unwrappedTypeInfo = typeInfo.Unwrap();
4246
var graphType = TryGetFromCache(unwrappedTypeInfo, typeInfo);
4347
if (graphType != null) return graphType;
4448

@@ -65,7 +69,7 @@ public IGraphType Adapt(TypeInfo typeInfo)
6569

6670
private IGraphType AdaptInput(TypeInfo typeInfo)
6771
{
68-
var unwrappedTypeInfo = UnwrapTypeInfo(typeInfo);
72+
var unwrappedTypeInfo = typeInfo.Unwrap();
6973
var graphType = TryGetFromCache(unwrappedTypeInfo, typeInfo);
7074
if (graphType != null) return graphType;
7175
if (unwrappedTypeInfo.IsEnum)
@@ -110,7 +114,7 @@ private FieldType CreateField(MemberInfo memberInfo)
110114
{
111115
var returnType = GetReturnType(memberInfo);
112116

113-
var unwrappedReturnType = UnwrapTypeInfo(returnType);
117+
var unwrappedReturnType = returnType.Unwrap();
114118

115119
var foundType = _typeRegistry.Get(unwrappedReturnType);
116120

@@ -160,7 +164,7 @@ private FieldType CreateField(MemberInfo memberInfo)
160164
Metadata = {{nameof(MemberInfo), memberInfo}},
161165
Name = memberInfo.GetCustomAttribute<NameAttribute>()?.Name ?? memberInfo.Name,
162166
ResolvedType = resolvedType,
163-
Type = WrapTypeInfo(foundType, returnType, isNonNull, isNonNullItem)
167+
Type = foundType.Wrap(returnType, isNonNull, isNonNullItem)
164168
};
165169

166170
fieldType.Resolver = new FieldResolver(fieldType, _dependencyResolver);
@@ -188,7 +192,40 @@ private QueryArgument CreateArgument(ParameterInfo parameterInfo)
188192
parameterInfo.GetCustomAttribute<DefaultValueAttribute>() != null ||
189193
parameterType.IsValueType && parameterType.IsNullable();
190194

191-
var inputType = AdaptInput(parameterType.GetTypeInfo());
195+
var unwrappedType = parameterType.GetTypeInfo().Unwrap();
196+
197+
IGraphType inputType;
198+
if (unwrappedType == typeof(OrderBy))
199+
{
200+
var returnType = GetReturnType(parameterInfo.Member);
201+
if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Connection<>))
202+
returnType = returnType.GenericTypeArguments[0].GetTypeInfo();
203+
204+
var foundType = _typeRegistry.Get(returnType);
205+
206+
IGraphType graphType;
207+
208+
if (foundType != null)
209+
{
210+
graphType = Activator.CreateInstance(foundType) as IComplexGraphType;
211+
}
212+
else
213+
{
214+
graphType = Adapt(returnType);
215+
}
216+
217+
inputType = (IGraphType)Activator.CreateInstance(typeof(OrderByGraphType), graphType);
218+
219+
if (parameterType.IsArray)
220+
{
221+
inputType = WrapList(WrapNonNull(inputType));
222+
}
223+
}
224+
else
225+
{
226+
inputType = AdaptInput(parameterType.GetTypeInfo());
227+
}
228+
192229
if (hasDefaultValue == false && !(inputType is NonNullGraphType))
193230
{
194231
inputType = WrapNonNull(inputType);
@@ -299,45 +336,9 @@ private IGraphType TryGetFromCache(TypeInfo typeInfo, TypeInfo unwrappedTypeInfo
299336
return Wrap(unwrappedTypeInfo, (IGraphType) Activator.CreateInstance(foundType));
300337
}
301338

302-
private static TypeInfo UnwrapTypeInfo(TypeInfo typeInfo)
303-
{
304-
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Task<>))
305-
typeInfo = typeInfo.GenericTypeArguments[0].GetTypeInfo();
306-
307-
var isNullable = typeInfo.IsNullable();
308-
if (isNullable)
309-
return typeInfo.GenericTypeArguments[0].GetTypeInfo();
310-
311-
var enumerableArgument = GetEnumerableArgument(typeInfo);
312-
if (enumerableArgument != null)
313-
return enumerableArgument.GetTypeInfo();
314-
315-
return typeInfo;
316-
}
317-
318-
private TypeInfo WrapTypeInfo(TypeInfo graphType, TypeInfo typeInfo, bool isNonNull, bool isNonNullItem)
319-
{
320-
if (graphType == null)
321-
return null;
322-
323-
var enumerableArgument = GetEnumerableArgument(typeInfo);
324-
325-
if (typeInfo.IsValueType && typeInfo.IsNullable() == false || enumerableArgument != null &&
326-
(enumerableArgument.IsValueType && enumerableArgument.IsNullable() == false || isNonNullItem))
327-
graphType = typeof(NonNullGraphType<>).MakeGenericType(graphType).GetTypeInfo();
328-
329-
if (enumerableArgument != null)
330-
graphType = typeof(ListGraphType<>).MakeGenericType(graphType).GetTypeInfo();
331-
332-
if (isNonNull && typeof(NonNullGraphType).IsAssignableFrom(graphType) == false)
333-
graphType = typeof(NonNullGraphType<>).MakeGenericType(graphType).GetTypeInfo();
334-
335-
return graphType;
336-
}
337-
338339
private IGraphType Wrap(TypeInfo typeInfo, IGraphType graphType)
339340
{
340-
var enumerableArgument = GetEnumerableArgument(typeInfo);
341+
var enumerableArgument = typeInfo.GetEnumerableArgument();
341342

342343
if (typeInfo.IsValueType && typeInfo.IsNullable() == false || enumerableArgument != null &&
343344
enumerableArgument.IsValueType && enumerableArgument.IsNullable() == false)
@@ -366,23 +367,6 @@ private static IGraphType WrapNonNull(IGraphType graphType)
366367
return nonNullGraphType;
367368
}
368369

369-
private static Type GetEnumerableArgument(TypeInfo typeInfo)
370-
{
371-
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Task<>))
372-
typeInfo = typeInfo.GenericTypeArguments[0].GetTypeInfo();
373-
374-
if (typeInfo == typeof(string))
375-
return null;
376-
377-
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>))
378-
return typeInfo.GenericTypeArguments[0];
379-
380-
var enumerableInterface = typeInfo.ImplementedInterfaces.FirstOrDefault(x =>
381-
x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
382-
383-
return enumerableInterface?.GenericTypeArguments[0];
384-
}
385-
386370
private bool IsValidReturnType(Type type) =>
387371
type != typeof(void) && type != typeof(object) && type != typeof(Task);
388372
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Reflection;
3+
using GraphQL;
4+
using GraphQL.Types;
5+
using Our.Umbraco.GraphQL.Types;
6+
7+
namespace Our.Umbraco.GraphQL.Adapters.Types
8+
{
9+
public class OrderByGraphType : EnumerationGraphType
10+
{
11+
public OrderByGraphType(IComplexGraphType graphType)
12+
{
13+
if (graphType == null) throw new ArgumentNullException(nameof(graphType));
14+
15+
Name = $"{graphType.Name}Order";
16+
17+
foreach (var field in graphType.Fields)
18+
{
19+
Type fieldType = null;
20+
if (field.Type != null)
21+
{
22+
fieldType = field.Type;
23+
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(NonNullGraphType<>))
24+
fieldType = fieldType.GenericTypeArguments[0];
25+
}
26+
27+
if (field.ResolvedType != null)
28+
{
29+
fieldType = field.ResolvedType is NonNullGraphType nonNullGraphType
30+
? nonNullGraphType.ResolvedType.GetType()
31+
: field.ResolvedType.GetType();
32+
}
33+
34+
if (fieldType == null || typeof(ScalarGraphType).IsAssignableFrom(fieldType) == false) continue;
35+
36+
var name = field.GetMetadata<MethodInfo>(nameof(MethodInfo))?.Name ?? field.Name;
37+
38+
AddValue($"{field.Name}_ASC", $"Order by {field.Name} ascending.",
39+
new OrderBy(name, SortOrder.Ascending, field.Resolver.Resolve));
40+
AddValue($"{field.Name}_DESC", $"Order by {field.Name} descending.",
41+
new OrderBy(name, SortOrder.Descending, field.Resolver.Resolve));
42+
}
43+
}
44+
}
45+
46+
public class OrderByGraphType<TNodeType> : OrderByGraphType where TNodeType : IComplexGraphType
47+
{
48+
public OrderByGraphType() : base((IComplexGraphType) typeof(TNodeType).BuildNamedType())
49+
{
50+
}
51+
}
52+
}

src/Our.Umbraco.GraphQL/Models/OrderBy.cs

-14
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Reflection;
4+
using System.Threading.Tasks;
5+
using GraphQL;
6+
using GraphQL.Types;
7+
8+
namespace Our.Umbraco.GraphQL.Reflection
9+
{
10+
internal static class TypeInfoExtensions
11+
{
12+
public static TypeInfo Unwrap(this TypeInfo typeInfo)
13+
{
14+
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Task<>))
15+
typeInfo = typeInfo.GenericTypeArguments[0].GetTypeInfo();
16+
17+
var isNullable = typeInfo.IsNullable();
18+
if (isNullable)
19+
return typeInfo.GenericTypeArguments[0].GetTypeInfo();
20+
21+
var enumerableArgument = GetEnumerableArgument(typeInfo);
22+
if (enumerableArgument != null)
23+
return enumerableArgument.GetTypeInfo();
24+
25+
return typeInfo;
26+
}
27+
28+
public static TypeInfo Wrap(this TypeInfo graphType, TypeInfo typeInfo, bool isNonNull, bool isNonNullItem)
29+
{
30+
if (graphType == null)
31+
return null;
32+
33+
var enumerableArgument = GetEnumerableArgument(typeInfo);
34+
35+
if (typeInfo.IsValueType && typeInfo.IsNullable() == false || enumerableArgument != null &&
36+
(enumerableArgument.IsValueType && enumerableArgument.IsNullable() == false || isNonNullItem))
37+
graphType = typeof(NonNullGraphType<>).MakeGenericType(graphType).GetTypeInfo();
38+
39+
if (enumerableArgument != null)
40+
graphType = typeof(ListGraphType<>).MakeGenericType(graphType).GetTypeInfo();
41+
42+
if (isNonNull && typeof(NonNullGraphType).IsAssignableFrom(graphType) == false)
43+
graphType = typeof(NonNullGraphType<>).MakeGenericType(graphType).GetTypeInfo();
44+
45+
return graphType;
46+
}
47+
48+
public static TypeInfo GetEnumerableArgument(this TypeInfo typeInfo)
49+
{
50+
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Task<>))
51+
typeInfo = typeInfo.GenericTypeArguments[0].GetTypeInfo();
52+
53+
if (typeInfo == typeof(string))
54+
return null;
55+
56+
if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(IEnumerable<>))
57+
return typeInfo.GenericTypeArguments[0].GetTypeInfo();
58+
59+
var enumerableInterface = typeInfo.ImplementedInterfaces.FirstOrDefault(x =>
60+
x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>));
61+
62+
return enumerableInterface?.GenericTypeArguments[0].GetTypeInfo();
63+
}
64+
}
65+
}
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
using GraphQL.Types;
5+
6+
namespace Our.Umbraco.GraphQL.Types
7+
{
8+
public class OrderBy
9+
{
10+
private readonly Func<ResolveFieldContext, object> _resolver;
11+
12+
internal OrderBy(string field, SortOrder direction, Func<ResolveFieldContext, object> resolver)
13+
{
14+
_resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
15+
Field = field ?? throw new ArgumentNullException(nameof(field));
16+
Direction = direction;
17+
}
18+
19+
public string Field { get; }
20+
public SortOrder Direction { get; }
21+
22+
public object Resolve<TSource>(TSource source)
23+
{
24+
return _resolver(new ResolveFieldContext
25+
{
26+
Source = source,
27+
});
28+
}
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace Our.Umbraco.GraphQL.Types
5+
{
6+
public static class OrderByExtensions
7+
{
8+
public static IEnumerable<TSource> OrderBy<TSource>(this IEnumerable<TSource> source, IEnumerable<OrderBy> orderBy)
9+
{
10+
if (orderBy == null) return source;
11+
12+
foreach (var order in orderBy)
13+
{
14+
if (source is IOrderedEnumerable<TSource> ordered)
15+
{
16+
source = order.Direction == SortOrder.Ascending
17+
? ordered.ThenBy(order.Resolve)
18+
: ordered.ThenByDescending(order.Resolve);
19+
}
20+
else
21+
{
22+
source = order.Direction == SortOrder.Ascending
23+
? source.OrderBy(order.Resolve)
24+
: source.OrderByDescending(order.Resolve);
25+
}
26+
}
27+
28+
return source;
29+
}
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
namespace Our.Umbraco.GraphQL.Models
1+
namespace Our.Umbraco.GraphQL.Types
22
{
3-
43
public enum SortOrder
54
{
65
Ascending,
76
Descending
87
}
9-
}
8+
}

0 commit comments

Comments
 (0)