Skip to content

Commit 61dacea

Browse files
MilosMilos
authored andcommitted
Nested sort - feature with refactor
1 parent 18014f6 commit 61dacea

File tree

16 files changed

+356
-317
lines changed

16 files changed

+356
-317
lines changed

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public virtual IQueryable<TEntity> Filter(IQueryable<TEntity> entities, FilterQu
7272
/// <inheritdoc />
7373
public virtual IQueryable<TEntity> Sort(IQueryable<TEntity> entities, List<SortQuery> sortQueries)
7474
{
75-
return entities.Sort(sortQueries);
75+
return entities.Sort(_jsonApiContext,sortQueries);
7676
}
7777

7878
/// <inheritdoc />

src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs

Lines changed: 173 additions & 114 deletions
Large diffs are not rendered by default.
Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Linq;
23
using JsonApiDotNetCore.Models;
34
using JsonApiDotNetCore.Services;
@@ -6,29 +7,25 @@ namespace JsonApiDotNetCore.Internal.Query
67
{
78
public class AttrFilterQuery : BaseFilterQuery
89
{
9-
private readonly IJsonApiContext _jsonApiContext;
10-
1110
public AttrFilterQuery(
1211
IJsonApiContext jsonApiContext,
1312
FilterQuery filterQuery)
13+
: base(jsonApiContext,
14+
null,
15+
filterQuery.Attribute,
16+
filterQuery.Value,
17+
filterQuery.OperationType)
1418
{
15-
_jsonApiContext = jsonApiContext;
16-
17-
var attribute = GetAttribute(filterQuery.Attribute);
18-
19-
if (attribute == null)
19+
if (Attribute == null)
2020
throw new JsonApiException(400, $"'{filterQuery.Attribute}' is not a valid attribute.");
2121

22-
if (attribute.IsFilterable == false)
23-
throw new JsonApiException(400, $"Filter is not allowed for attribute '{attribute.PublicAttributeName}'.");
22+
if (Attribute.IsFilterable == false)
23+
throw new JsonApiException(400, $"Filter is not allowed for attribute '{Attribute.PublicAttributeName}'.");
2424

25-
FilteredAttribute = attribute;
26-
PropertyValue = filterQuery.Value;
27-
FilterOperation = GetFilterOperation(filterQuery.Operation);
25+
FilteredAttribute = Attribute;
2826
}
2927

30-
31-
private AttrAttribute GetAttribute(string attribute) =>
32-
_jsonApiContext.RequestEntity.Attributes.FirstOrDefault(attr => attr.Is(attribute));
28+
[Obsolete("Use " + nameof(Attribute) + " property of " + nameof(BaseAttrQuery) + "class. This property is shared for all AttrQuery and RelatedAttrQuery (filter,sort..) implementations.")]
29+
public AttrAttribute FilteredAttribute { get; set; }
3330
}
3431
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Linq;
3+
using JsonApiDotNetCore.Models;
4+
using JsonApiDotNetCore.Services;
5+
6+
namespace JsonApiDotNetCore.Internal.Query
7+
{
8+
public class AttrSortQuery : BaseAttrQuery
9+
{
10+
public AttrSortQuery(
11+
IJsonApiContext jsonApiContext,
12+
SortQuery sortQuery)
13+
:base(jsonApiContext, null, sortQuery.Attribute)
14+
{
15+
if (Attribute == null)
16+
throw new JsonApiException(400, $"'{sortQuery.Attribute}' is not a valid attribute.");
17+
18+
if (Attribute.IsSortable == false)
19+
throw new JsonApiException(400, $"Sort is not allowed for attribute '{Attribute.PublicAttributeName}'.");
20+
21+
Direction = sortQuery.Direction;
22+
}
23+
24+
public SortDirection Direction { get; }
25+
}
26+
}
Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using JsonApiDotNetCore.Models;
22
using JsonApiDotNetCore.Services;
3-
using System;
43
using System.Linq;
54

65
namespace JsonApiDotNetCore.Internal.Query
@@ -12,22 +11,44 @@ namespace JsonApiDotNetCore.Internal.Query
1211
/// </summary>
1312
public abstract class BaseAttrQuery
1413
{
15-
protected BaseAttrQuery(RelationshipAttribute relationship, AttrAttribute attr)
14+
private readonly IJsonApiContext _jsonApiContext;
15+
16+
public BaseAttrQuery(IJsonApiContext jsonApiContext, string relationship, string attribute)
1617
{
17-
Relationship = relationship;
18-
Attr = attr;
18+
_jsonApiContext = jsonApiContext;
19+
if (string.IsNullOrEmpty(relationship))
20+
Attribute = GetAttribute(attribute);
21+
else
22+
{
23+
Relationship = GetRelationship(relationship);
24+
Attribute = GetAttribute(Relationship, attribute);
25+
}
26+
1927
}
2028

21-
public AttrAttribute Attr { get; }
29+
public AttrAttribute Attribute { get; }
2230
public RelationshipAttribute Relationship { get; }
2331
public bool IsAttributeOfRelationship => Relationship != null;
2432

2533
public string GetPropertyPath()
2634
{
2735
if (IsAttributeOfRelationship)
28-
return string.Format("{0}.{1}", Relationship.InternalRelationshipName, Attr.InternalAttributeName);
36+
return string.Format("{0}.{1}", Relationship.InternalRelationshipName, Attribute.InternalAttributeName);
2937
else
30-
return Attr.InternalAttributeName;
38+
return Attribute.InternalAttributeName;
39+
}
40+
41+
private AttrAttribute GetAttribute(string attribute)
42+
=> _jsonApiContext.RequestEntity.Attributes.FirstOrDefault(attr => attr.Is(attribute));
43+
44+
private RelationshipAttribute GetRelationship(string propertyName)
45+
=> _jsonApiContext.RequestEntity.Relationships.FirstOrDefault(r => r.Is(propertyName));
46+
47+
private AttrAttribute GetAttribute(RelationshipAttribute relationship, string attribute)
48+
{
49+
var relatedContextExntity = _jsonApiContext.ContextGraph.GetContextEntity(relationship.Type);
50+
return relatedContextExntity.Attributes
51+
.FirstOrDefault(a => a.Is(attribute));
3152
}
3253
}
3354
}
Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
using JsonApiDotNetCore.Models;
2+
using JsonApiDotNetCore.Services;
23
using System;
34

45
namespace JsonApiDotNetCore.Internal.Query
56
{
6-
public class BaseFilterQuery
7+
public abstract class BaseFilterQuery : BaseAttrQuery
78
{
9+
public BaseFilterQuery(
10+
IJsonApiContext jsonApiContext,
11+
string relationship,
12+
string attribute,
13+
string value,
14+
FilterOperations op)
15+
: base(jsonApiContext, relationship, attribute)
16+
{
17+
PropertyValue = value;
18+
FilterOperation = op;
19+
}
20+
21+
[Obsolete("To resolve operation use enum typed " + nameof(FilterQuery.OperationType) + " property of "+ nameof(FilterQuery) +" class")]
822
protected FilterOperations GetFilterOperation(string prefix)
923
{
1024
if (prefix.Length == 0) return FilterOperations.eq;
@@ -15,9 +29,7 @@ protected FilterOperations GetFilterOperation(string prefix)
1529
return opertion;
1630
}
1731

18-
public AttrAttribute FilteredAttribute { get; protected set; }
19-
public RelationshipAttribute FilteredRelationship { get; protected set; }
20-
public string PropertyValue { get; protected set; }
21-
public FilterOperations FilterOperation { get; protected set; }
32+
public string PropertyValue { get; }
33+
public FilterOperations FilterOperation { get; }
2234
}
2335
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using JsonApiDotNetCore.Models;
2+
using JsonApiDotNetCore.Services;
3+
using System;
4+
using System.Linq;
5+
6+
namespace JsonApiDotNetCore.Internal.Query
7+
{
8+
public abstract class BaseQuery
9+
{
10+
public BaseQuery(string attribute)
11+
{
12+
var properties = attribute.Split(QueryConstants.DOT);
13+
if(properties.Length > 1)
14+
{
15+
Relationship = properties[0];
16+
Attribute = properties[1];
17+
}
18+
else
19+
Attribute = properties[0];
20+
}
21+
22+
public string Attribute { get; }
23+
public string Relationship { get; }
24+
public bool IsAttributeOfRelationship => Relationship != null;
25+
}
26+
}

src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,35 @@
44

55
namespace JsonApiDotNetCore.Internal.Query
66
{
7-
public class FilterQuery : BaseAttrQuery
7+
public class FilterQuery : BaseQuery
88
{
9-
/// <summary>
10-
/// Temporary property while constructor based on string values exists
11-
/// </summary>
12-
internal bool IsStringBasedInit { get; } = false;
13-
14-
[Obsolete("You should use constructors with strongly typed FilterOperations and AttrAttribute or/and RelationshipAttribute parameters.")]
9+
[Obsolete("Use constructor with FilterOperations operationType paremeter. Filter operation should be provided " +
10+
"as enum type, not by string.")]
1511
public FilterQuery(string attribute, string value, string operation)
16-
:base(null, null)
12+
:base(attribute)
1713
{
18-
Attribute = attribute;
1914
Key = attribute.ToProperCase();
2015
Value = value;
2116
Operation = operation;
2217

23-
IsStringBasedInit = true;
2418
Enum.TryParse(operation, out FilterOperations opertion);
2519
OperationType = opertion;
2620
}
2721

28-
public FilterQuery(AttrAttribute attr, string value, FilterOperations operationType)
29-
:base(null, attr)
22+
public FilterQuery(string attribute, string value, FilterOperations operationType)
23+
: base(attribute)
3024
{
25+
Key = attribute.ToProperCase();
3126
Value = value;
32-
OperationType = operationType;
33-
Key = attr.PublicAttributeName.ToProperCase();
3427
Operation = operationType.ToString();
35-
}
36-
37-
public FilterQuery(RelationshipAttribute relationship, AttrAttribute attr, string value, FilterOperations operationType)
38-
:base(relationship, attr)
39-
{
40-
Value = value;
4128
OperationType = operationType;
42-
Key = string.Format("{0}.{1}", Relationship.PublicRelationshipName, Attr.PublicAttributeName);
43-
Operation = operationType.ToString();
4429
}
4530

4631
[Obsolete("Key has been replaced by '" + nameof(Attribute) + "'. Members should be located by their public name, not by coercing the provided value to the internal name.")]
4732
public string Key { get; set; }
4833
public string Value { get; set; }
4934
[Obsolete("Operation has been replaced by '" + nameof(OperationType) + "'. OperationType is typed enum value for Operation property. This should be default property for providing operation type, because of unsustainable string (not typed) value.")]
5035
public string Operation { get; set; }
51-
[Obsolete("String based Attribute was replaced by '" + nameof(Attr) + "' property ('" + nameof(AttrAttribute) + "' type) ")]
52-
public string Attribute { get; }
5336

5437
public FilterOperations OperationType { get; set; }
5538

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Linq;
23
using JsonApiDotNetCore.Models;
34
using JsonApiDotNetCore.Services;
@@ -6,40 +7,28 @@ namespace JsonApiDotNetCore.Internal.Query
67
{
78
public class RelatedAttrFilterQuery : BaseFilterQuery
89
{
9-
private readonly IJsonApiContext _jsonApiContext;
10-
1110
public RelatedAttrFilterQuery(
1211
IJsonApiContext jsonApiContext,
1312
FilterQuery filterQuery)
13+
:base(jsonApiContext, filterQuery.Relationship, filterQuery.Attribute, filterQuery.Value, filterQuery.OperationType)
1414
{
15-
_jsonApiContext = jsonApiContext;
16-
17-
var relationshipArray = filterQuery.Attribute.Split(QueryConstants.DOT);
18-
var relationship = GetRelationship(relationshipArray[0]);
19-
if (relationship == null)
20-
throw new JsonApiException(400, $"{relationshipArray[1]} is not a valid relationship on {relationshipArray[0]}.");
15+
if (Relationship == null)
16+
throw new JsonApiException(400, $"{filterQuery.Relationship} is not a valid relationship on {jsonApiContext.RequestEntity.EntityName}.");
2117

22-
var attribute = GetAttribute(relationship, relationshipArray[1]);
23-
if (attribute == null)
18+
if (Attribute == null)
2419
throw new JsonApiException(400, $"'{filterQuery.Attribute}' is not a valid attribute.");
2520

26-
if (attribute.IsFilterable == false)
27-
throw new JsonApiException(400, $"Filter is not allowed for attribute '{attribute.PublicAttributeName}'.");
21+
if (Attribute.IsFilterable == false)
22+
throw new JsonApiException(400, $"Filter is not allowed for attribute '{Attribute.PublicAttributeName}'.");
2823

29-
FilteredRelationship = relationship;
30-
FilteredAttribute = attribute;
31-
PropertyValue = filterQuery.Value;
32-
FilterOperation = GetFilterOperation(filterQuery.Operation);
24+
FilteredRelationship = Relationship;
25+
FilteredAttribute = Attribute;
3326
}
3427

35-
private RelationshipAttribute GetRelationship(string propertyName)
36-
=> _jsonApiContext.RequestEntity.Relationships.FirstOrDefault(r => r.Is(propertyName));
28+
[Obsolete("Use " + nameof(Attribute) + " property. It's shared for all implementations of BaseAttrQuery(better sort, filter) handling")]
29+
public AttrAttribute FilteredAttribute { get; set; }
3730

38-
private AttrAttribute GetAttribute(RelationshipAttribute relationship, string attribute)
39-
{
40-
var relatedContextExntity = _jsonApiContext.ContextGraph.GetContextEntity(relationship.Type);
41-
return relatedContextExntity.Attributes
42-
.FirstOrDefault(a => a.Is(attribute));
43-
}
31+
[Obsolete("Use " + nameof(Relationship) + " property. It's shared for all implementations of BaseAttrQuery(better sort, filter) handling")]
32+
public RelationshipAttribute FilteredRelationship { get; set; }
4433
}
4534
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Linq;
3+
using JsonApiDotNetCore.Models;
4+
using JsonApiDotNetCore.Services;
5+
6+
namespace JsonApiDotNetCore.Internal.Query
7+
{
8+
public class RelatedAttrSortQuery : BaseAttrQuery
9+
{
10+
public RelatedAttrSortQuery(
11+
IJsonApiContext jsonApiContext,
12+
SortQuery sortQuery)
13+
:base(jsonApiContext, sortQuery.Relationship, sortQuery.Attribute)
14+
{
15+
if (Relationship == null)
16+
throw new JsonApiException(400, $"{sortQuery.Relationship} is not a valid relationship on {jsonApiContext.RequestEntity.EntityName}.");
17+
18+
if (Attribute == null)
19+
throw new JsonApiException(400, $"'{sortQuery.Attribute}' is not a valid attribute.");
20+
21+
if (Attribute.IsSortable == false)
22+
throw new JsonApiException(400, $"Sort is not allowed for attribute '{Attribute.PublicAttributeName}'.");
23+
24+
Direction = sortQuery.Direction;
25+
}
26+
27+
public SortDirection Direction { get; }
28+
}
29+
}

0 commit comments

Comments
 (0)