Skip to content

Commit 18014f6

Browse files
MilosMilos
authored andcommitted
Back to the roots. There was few breaking changes, so I have to turn back to provide compatibility.
1 parent 487bef9 commit 18014f6

16 files changed

+396
-345
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(_jsonApiContext, sortQueries);
75+
return entities.Sort(sortQueries);
7676
}
7777

7878
/// <inheritdoc />

src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs

Lines changed: 36 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -29,60 +29,37 @@ private static MethodInfo ContainsMethod
2929
}
3030
}
3131

32-
public static IQueryable<TSource> Sort<TSource>(this IQueryable<TSource> source, IJsonApiContext jsonApiContext, List<SortQuery> sortQueries)
32+
public static IQueryable<TSource> Sort<TSource>(this IQueryable<TSource> source, List<SortQuery> sortQueries)
3333
{
3434
if (sortQueries == null || sortQueries.Count == 0)
3535
return source;
3636

37-
var orderedEntities = source.Sort(jsonApiContext, sortQueries[0]);
37+
var orderedEntities = source.Sort(sortQueries[0]);
3838

3939
if (sortQueries.Count <= 1)
4040
return orderedEntities;
4141

4242
for (var i = 1; i < sortQueries.Count; i++)
43-
orderedEntities = orderedEntities.Sort(jsonApiContext, sortQueries[i]);
43+
orderedEntities = orderedEntities.Sort(sortQueries[i]);
4444

4545
return orderedEntities;
4646
}
4747

48-
public static IOrderedQueryable<TSource> Sort<TSource>(this IQueryable<TSource> source, IJsonApiContext jsonApiContext, SortQuery sortQuery)
48+
public static IOrderedQueryable<TSource> Sort<TSource>(this IQueryable<TSource> source, SortQuery sortQuery)
4949
{
50-
if (sortQuery.IsAttributeOfRelationship)
51-
{
52-
// For now is created new instance, later resolve from cache
53-
var relatedAttrQuery = new RelatedAttrQuery(jsonApiContext, sortQuery);
54-
var path = relatedAttrQuery.GetRelatedPropertyPath();
55-
return sortQuery.Direction == SortDirection.Descending
56-
? source.OrderByDescending(path)
57-
: source.OrderBy(path);
58-
}
59-
else
60-
{
61-
var attrQuery = new AttrQuery(jsonApiContext, sortQuery);
62-
return sortQuery.Direction == SortDirection.Descending
63-
? source.OrderByDescending(attrQuery.Attribute.InternalAttributeName)
64-
: source.OrderBy(attrQuery.Attribute.InternalAttributeName);
65-
}
50+
var path = sortQuery.GetPropertyPath();
51+
return sortQuery.Direction == SortDirection.Descending
52+
? source.OrderByDescending(path)
53+
: source.OrderBy(path);
6654
}
6755

68-
public static IOrderedQueryable<TSource> Sort<TSource>(this IOrderedQueryable<TSource> source, IJsonApiContext jsonApiContext, SortQuery sortQuery)
56+
public static IOrderedQueryable<TSource> Sort<TSource>(this IOrderedQueryable<TSource> source, SortQuery sortQuery)
6957
{
70-
if (sortQuery.IsAttributeOfRelationship)
71-
{
72-
var relatedAttrQuery = new RelatedAttrQuery(jsonApiContext, sortQuery);
73-
var path = relatedAttrQuery.GetRelatedPropertyPath();
74-
return sortQuery.Direction == SortDirection.Descending
75-
? source.OrderByDescending(path)
76-
: source.OrderBy(path);
77-
}
78-
else
79-
{
80-
var attrQuery = new AttrQuery(jsonApiContext, sortQuery);
81-
return sortQuery.Direction == SortDirection.Descending
82-
? source.OrderByDescending(attrQuery.Attribute.InternalAttributeName)
83-
: source.OrderBy(attrQuery.Attribute.InternalAttributeName);
84-
}
85-
}
58+
var path = sortQuery.GetPropertyPath();
59+
return sortQuery.Direction == SortDirection.Descending
60+
? source.OrderByDescending(path)
61+
: source.OrderBy(path);
62+
}
8663

8764
public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
8865
=> CallGenericOrderMethod(source, propertyName, "OrderBy");
@@ -101,13 +78,15 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
10178
if (filterQuery == null)
10279
return source;
10380

104-
if (filterQuery.IsAttributeOfRelationship)
105-
return source.Filter(new RelatedAttrQuery(jsonApiContext, filterQuery));
81+
// Relationship.Attribute
82+
if ((filterQuery.IsStringBasedInit && filterQuery.Attribute.Contains(QueryConstants.DOT))
83+
|| filterQuery.IsAttributeOfRelationship)
84+
return source.Filter(new RelatedAttrFilterQuery(jsonApiContext, filterQuery));
10685

107-
return source.Filter(new AttrQuery(jsonApiContext, filterQuery));
86+
return source.Filter(new AttrFilterQuery(jsonApiContext, filterQuery));
10887
}
10988

110-
public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> source, BaseAttrQuery filterQuery)
89+
public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> source, BaseFilterQuery filterQuery)
11190
{
11291
if (filterQuery == null)
11392
return source;
@@ -238,7 +217,7 @@ private static IOrderedQueryable<TSource> CallGenericOrderMethod<TSource>(IQuery
238217
return (IOrderedQueryable<TSource>)result;
239218
}
240219

241-
private static IQueryable<TSource> CallGenericWhereMethod<TSource>(IQueryable<TSource> source, BaseAttrQuery filter)
220+
private static IQueryable<TSource> CallGenericWhereMethod<TSource>(IQueryable<TSource> source, BaseFilterQuery filter)
242221
{
243222
var op = filter.FilterOperation;
244223
var concreteType = typeof(TSource);
@@ -250,27 +229,27 @@ private static IQueryable<TSource> CallGenericWhereMethod<TSource>(IQueryable<TS
250229
// {model}
251230
var parameter = Expression.Parameter(concreteType, "model");
252231
// Is relationship attribute
253-
if (filter.IsAttributeOfRelationship)
232+
if (filter.FilteredRelationship != null)
254233
{
255-
relationProperty = concreteType.GetProperty(filter.RelationshipAttribute.InternalRelationshipName);
234+
relationProperty = concreteType.GetProperty(filter.FilteredRelationship.InternalRelationshipName);
256235
if (relationProperty == null)
257-
throw new ArgumentException($"'{filter.RelationshipAttribute.InternalRelationshipName}' is not a valid relationship of '{concreteType}'");
236+
throw new ArgumentException($"'{filter.FilteredRelationship.InternalRelationshipName}' is not a valid relationship of '{concreteType}'");
258237

259-
var relatedType = filter.RelationshipAttribute.Type;
260-
property = relatedType.GetProperty(filter.Attribute.InternalAttributeName);
238+
var relatedType = filter.FilteredRelationship.Type;
239+
property = relatedType.GetProperty(filter.FilteredAttribute.InternalAttributeName);
261240
if (property == null)
262-
throw new ArgumentException($"'{filter.Attribute.InternalAttributeName}' is not a valid attribute of '{filter.RelationshipAttribute.InternalRelationshipName}'");
241+
throw new ArgumentException($"'{filter.FilteredAttribute.InternalAttributeName}' is not a valid attribute of '{filter.FilteredRelationship.InternalRelationshipName}'");
263242

264-
var leftRelationship = Expression.PropertyOrField(parameter, filter.RelationshipAttribute.InternalRelationshipName);
243+
var leftRelationship = Expression.PropertyOrField(parameter, filter.FilteredRelationship.InternalRelationshipName);
265244
// {model.Relationship}
266245
left = Expression.PropertyOrField(leftRelationship, property.Name);
267246
}
268247
// Is standalone attribute
269248
else
270249
{
271-
property = concreteType.GetProperty(filter.Attribute.InternalAttributeName);
250+
property = concreteType.GetProperty(filter.FilteredAttribute.InternalAttributeName);
272251
if (property == null)
273-
throw new ArgumentException($"'{filter.Attribute.InternalAttributeName}' is not a valid property of '{concreteType}'");
252+
throw new ArgumentException($"'{filter.FilteredAttribute.InternalAttributeName}' is not a valid property of '{concreteType}'");
274253

275254
// {model.Id}
276255
left = Expression.PropertyOrField(parameter, property.Name);
@@ -300,23 +279,23 @@ private static IQueryable<TSource> CallGenericWhereMethod<TSource>(IQueryable<TS
300279
}
301280
}
302281

303-
private static IQueryable<TSource> CallGenericWhereContainsMethod<TSource>(IQueryable<TSource> source, BaseAttrQuery filter)
282+
private static IQueryable<TSource> CallGenericWhereContainsMethod<TSource>(IQueryable<TSource> source, BaseFilterQuery filter)
304283
{
305284
var concreteType = typeof(TSource);
306-
var property = concreteType.GetProperty(filter.Attribute.InternalAttributeName);
285+
var property = concreteType.GetProperty(filter.FilteredAttribute.InternalAttributeName);
307286

308287
try
309288
{
310289
var propertyValues = filter.PropertyValue.Split(QueryConstants.COMMA);
311290
ParameterExpression entity = Expression.Parameter(concreteType, "entity");
312291
MemberExpression member;
313-
if (filter.IsAttributeOfRelationship)
292+
if (filter.FilteredRelationship != null)
314293
{
315-
var relation = Expression.PropertyOrField(entity, filter.RelationshipAttribute.InternalRelationshipName);
316-
member = Expression.Property(relation, filter.Attribute.InternalAttributeName);
294+
var relation = Expression.PropertyOrField(entity, filter.FilteredRelationship.InternalRelationshipName);
295+
member = Expression.Property(relation, filter.FilteredAttribute.InternalAttributeName);
317296
}
318297
else
319-
member = Expression.Property(entity, filter.Attribute.InternalAttributeName);
298+
member = Expression.Property(entity, filter.FilteredAttribute.InternalAttributeName);
320299

321300
var method = ContainsMethod.MakeGenericMethod(member.Type);
322301
var obj = TypeHelper.ConvertListType(propertyValues, member.Type);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Linq;
2+
using JsonApiDotNetCore.Models;
3+
using JsonApiDotNetCore.Services;
4+
5+
namespace JsonApiDotNetCore.Internal.Query
6+
{
7+
public class AttrFilterQuery : BaseFilterQuery
8+
{
9+
private readonly IJsonApiContext _jsonApiContext;
10+
11+
public AttrFilterQuery(
12+
IJsonApiContext jsonApiContext,
13+
FilterQuery filterQuery)
14+
{
15+
_jsonApiContext = jsonApiContext;
16+
17+
var attribute = GetAttribute(filterQuery.Attribute);
18+
19+
if (attribute == null)
20+
throw new JsonApiException(400, $"'{filterQuery.Attribute}' is not a valid attribute.");
21+
22+
if (attribute.IsFilterable == false)
23+
throw new JsonApiException(400, $"Filter is not allowed for attribute '{attribute.PublicAttributeName}'.");
24+
25+
FilteredAttribute = attribute;
26+
PropertyValue = filterQuery.Value;
27+
FilterOperation = GetFilterOperation(filterQuery.Operation);
28+
}
29+
30+
31+
private AttrAttribute GetAttribute(string attribute) =>
32+
_jsonApiContext.RequestEntity.Attributes.FirstOrDefault(attr => attr.Is(attribute));
33+
}
34+
}

src/JsonApiDotNetCore/Internal/Query/AttrQuery.cs

Lines changed: 0 additions & 64 deletions
This file was deleted.

src/JsonApiDotNetCore/Internal/Query/BaseAttrQuery.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,28 @@
66
namespace JsonApiDotNetCore.Internal.Query
77
{
88
/// <summary>
9-
/// Abstract class to make available shared properties for AttrQuery and RelatedAttrQuery
9+
/// Abstract class to make available shared properties of all query implementations
1010
/// It elimines boilerplate of providing specified type(AttrQuery or RelatedAttrQuery)
1111
/// while filter and sort operations and eliminates plenty of methods to keep DRY principles
1212
/// </summary>
1313
public abstract class BaseAttrQuery
1414
{
15-
public AttrAttribute Attribute { get; protected set; }
16-
public RelationshipAttribute RelationshipAttribute { get; protected set; }
17-
public bool IsAttributeOfRelationship { get; protected set; }
15+
protected BaseAttrQuery(RelationshipAttribute relationship, AttrAttribute attr)
16+
{
17+
Relationship = relationship;
18+
Attr = attr;
19+
}
1820

19-
// Filter properties
20-
public string PropertyValue { get; protected set; }
21-
public FilterOperations FilterOperation { get; protected set; }
22-
// Sort properties
23-
public SortDirection Direction { get; protected set; }
21+
public AttrAttribute Attr { get; }
22+
public RelationshipAttribute Relationship { get; }
23+
public bool IsAttributeOfRelationship => Relationship != null;
24+
25+
public string GetPropertyPath()
26+
{
27+
if (IsAttributeOfRelationship)
28+
return string.Format("{0}.{1}", Relationship.InternalRelationshipName, Attr.InternalAttributeName);
29+
else
30+
return Attr.InternalAttributeName;
31+
}
2432
}
2533
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using JsonApiDotNetCore.Models;
2+
using System;
3+
4+
namespace JsonApiDotNetCore.Internal.Query
5+
{
6+
public class BaseFilterQuery
7+
{
8+
protected FilterOperations GetFilterOperation(string prefix)
9+
{
10+
if (prefix.Length == 0) return FilterOperations.eq;
11+
12+
if (Enum.TryParse(prefix, out FilterOperations opertion) == false)
13+
throw new JsonApiException(400, $"Invalid filter prefix '{prefix}'");
14+
15+
return opertion;
16+
}
17+
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; }
22+
}
23+
}

src/JsonApiDotNetCore/Internal/Query/FilterOperations.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
// ReSharper disable InconsistentNaming
2-
using System;
3-
42
namespace JsonApiDotNetCore.Internal.Query
53
{
64
public enum FilterOperations
@@ -17,5 +15,4 @@ public enum FilterOperations
1715
isnull = 9,
1816
isnotnull = 10
1917
}
20-
2118
}

0 commit comments

Comments
 (0)