Skip to content

Commit 87f05f2

Browse files
author
Bart Koelman
authored
Merge pull request #754 from bart-degreed/default-sort-type-conversion
Fixed: error setting default sort for non-string properties
2 parents b2fdba6 + 5f5792f commit 87f05f2

File tree

2 files changed

+23
-13
lines changed

2 files changed

+23
-13
lines changed

src/JsonApiDotNetCore/Internal/ResourceGraph.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ private IEnumerable<IResourceField> Getter<T>(Expression<Func<T, dynamic>> selec
8484

8585
var targeted = new List<IResourceField>();
8686

87-
if (selector.Body is MemberExpression memberExpression)
87+
var selectorBody = RemoveConvert(selector.Body);
88+
89+
if (selectorBody is MemberExpression memberExpression)
8890
{ // model => model.Field1
8991
try
9092
{
@@ -97,8 +99,7 @@ private IEnumerable<IResourceField> Getter<T>(Expression<Func<T, dynamic>> selec
9799
}
98100
}
99101

100-
101-
if (selector.Body is NewExpression newExpression)
102+
if (selectorBody is NewExpression newExpression)
102103
{ // model => new { model.Field1, model.Field2 }
103104
string memberName = null;
104105
try
@@ -119,11 +120,17 @@ private IEnumerable<IResourceField> Getter<T>(Expression<Func<T, dynamic>> selec
119120
}
120121
}
121122

122-
throw new ArgumentException($"The expression returned by '{selector}' for '{GetType()}' is of type {selector.Body.GetType()}"
123-
+ " and cannot be used to select resource attributes. The type must be a NewExpression.Example: article => new { article.Author };");
124-
123+
throw new ArgumentException(
124+
$"The expression '{selector}' should select a single property or select multiple properties into an anonymous type. " +
125+
$"For example: 'article => article.Title' or 'article => new {{ article.Title, article.PageCount }}'.");
125126
}
126127

128+
private static Expression RemoveConvert(Expression expression)
129+
=> expression is UnaryExpression unaryExpression
130+
&& unaryExpression.NodeType == ExpressionType.Convert
131+
? RemoveConvert(unaryExpression.Operand)
132+
: expression;
133+
127134
private void ThrowNotExposedError(string memberName, FieldFilterType type)
128135
{
129136
throw new ArgumentException($"{memberName} is not an json:api exposed {type:g}.");

test/UnitTests/Models/ResourceDefinitionTests.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using JsonApiDotNetCore.Builders;
34
using JsonApiDotNetCore.Internal.Query;
@@ -23,7 +24,7 @@ public void Property_Sort_Order_Uses_NewExpression()
2324
// Assert
2425
Assert.Equal(2, sorts.Count);
2526

26-
Assert.Equal(nameof(Model.Prop), sorts[0].Attribute.PropertyInfo.Name);
27+
Assert.Equal(nameof(Model.CreatedAt), sorts[0].Attribute.PropertyInfo.Name);
2728
Assert.Equal(SortDirection.Ascending, sorts[0].SortDirection);
2829

2930
Assert.Equal(nameof(Model.Password), sorts[1].Attribute.PropertyInfo.Name);
@@ -62,7 +63,7 @@ public class Model : Identifiable
6263
{
6364
[Attr] public string AlwaysExcluded { get; set; }
6465
[Attr] public string Password { get; set; }
65-
[Attr] public string Prop { get; set; }
66+
[Attr] public DateTime CreatedAt { get; set; }
6667
}
6768

6869
public sealed class RequestFilteredResource : ResourceDefinition<Model>
@@ -72,19 +73,21 @@ public sealed class RequestFilteredResource : ResourceDefinition<Model>
7273
public RequestFilteredResource(bool isAdmin) : base(new ResourceGraphBuilder(new JsonApiOptions(), NullLoggerFactory.Instance).AddResource<Model>().Build())
7374
{
7475
if (isAdmin)
75-
HideFields(m => m.AlwaysExcluded);
76+
HideFields(model => model.AlwaysExcluded);
7677
else
77-
HideFields(m => new { m.AlwaysExcluded, m.Password });
78+
HideFields(model => new { model.AlwaysExcluded, model.Password });
7879
}
7980

8081
public override QueryFilters GetQueryFilters()
8182
=> new QueryFilters {
8283
{ "is-active", (query, value) => query.Select(x => x) }
8384
};
85+
8486
public override PropertySortOrder GetDefaultSortOrder()
85-
=> new PropertySortOrder {
86-
(t => t.Prop, SortDirection.Ascending),
87-
(t => t.Password, SortDirection.Descending)
87+
=> new PropertySortOrder
88+
{
89+
(model => model.CreatedAt, SortDirection.Ascending),
90+
(model => model.Password, SortDirection.Descending)
8891
};
8992
}
9093
}

0 commit comments

Comments
 (0)