Skip to content

Commit 8d46f52

Browse files
MilosMilos
authored andcommitted
Requested changes
1 parent 94fdff2 commit 8d46f52

File tree

13 files changed

+138
-189
lines changed

13 files changed

+138
-189
lines changed

src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ private bool ShouldIncludeAttribute(AttrAttribute attr, object attributeValue)
142142
return OmitNullValuedAttribute(attr, attributeValue) == false
143143
&& ((_jsonApiContext.QuerySet == null
144144
|| _jsonApiContext.QuerySet.Fields.Count == 0)
145-
|| _jsonApiContext.QuerySet.Fields.Any(i => i.Attribute == attr.InternalAttributeName));
145+
|| _jsonApiContext.QuerySet.Fields.Contains(attr.InternalAttributeName));
146146
}
147147

148148
private bool OmitNullValuedAttribute(AttrAttribute attr, object attributeValue)

src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs

Lines changed: 19 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,14 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
171171

172172
try
173173
{
174-
if (op == FilterOperationsEnum.@in || op == FilterOperationsEnum.nin)
174+
if (op == FilterOperations.@in || op == FilterOperations.nin)
175175
{
176176
string[] propertyValues = filterQuery.PropertyValue.Split(',');
177177
var lambdaIn = ArrayContainsPredicate<TSource>(propertyValues, property.Name, op);
178178

179179
return source.Where(lambdaIn);
180180
}
181-
else if (op == FilterOperationsEnum.isnotnull || op == FilterOperationsEnum.isnull) {
181+
else if (op == FilterOperations.isnotnull || op == FilterOperations.isnull) {
182182
// {model}
183183
var parameter = Expression.Parameter(concreteType, "model");
184184
// {model.Id}
@@ -231,7 +231,7 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
231231

232232
try
233233
{
234-
if (filterQuery.FilterOperation == FilterOperationsEnum.@in || filterQuery.FilterOperation == FilterOperationsEnum.nin)
234+
if (filterQuery.FilterOperation == FilterOperations.@in || filterQuery.FilterOperation == FilterOperations.nin)
235235
{
236236
string[] propertyValues = filterQuery.PropertyValue.Split(',');
237237
var lambdaIn = ArrayContainsPredicate<TSource>(propertyValues, relatedAttr.Name, filterQuery.FilterOperation, relation.Name);
@@ -271,43 +271,43 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
271271
private static bool IsNullable(Type type) => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
272272

273273

274-
private static Expression GetFilterExpressionLambda(Expression left, Expression right, FilterOperationsEnum operation)
274+
private static Expression GetFilterExpressionLambda(Expression left, Expression right, FilterOperations operation)
275275
{
276276
Expression body;
277277
switch (operation)
278278
{
279-
case FilterOperationsEnum.eq:
279+
case FilterOperations.eq:
280280
// {model.Id == 1}
281281
body = Expression.Equal(left, right);
282282
break;
283-
case FilterOperationsEnum.lt:
283+
case FilterOperations.lt:
284284
// {model.Id < 1}
285285
body = Expression.LessThan(left, right);
286286
break;
287-
case FilterOperationsEnum.gt:
287+
case FilterOperations.gt:
288288
// {model.Id > 1}
289289
body = Expression.GreaterThan(left, right);
290290
break;
291-
case FilterOperationsEnum.le:
291+
case FilterOperations.le:
292292
// {model.Id <= 1}
293293
body = Expression.LessThanOrEqual(left, right);
294294
break;
295-
case FilterOperationsEnum.ge:
295+
case FilterOperations.ge:
296296
// {model.Id >= 1}
297297
body = Expression.GreaterThanOrEqual(left, right);
298298
break;
299-
case FilterOperationsEnum.like:
299+
case FilterOperations.like:
300300
body = Expression.Call(left, "Contains", null, right);
301301
break;
302302
// {model.Id != 1}
303-
case FilterOperationsEnum.ne:
303+
case FilterOperations.ne:
304304
body = Expression.NotEqual(left, right);
305305
break;
306-
case FilterOperationsEnum.isnotnull:
306+
case FilterOperations.isnotnull:
307307
// {model.Id != null}
308308
body = Expression.NotEqual(left, right);
309309
break;
310-
case FilterOperationsEnum.isnull:
310+
case FilterOperations.isnull:
311311
// {model.Id == null}
312312
body = Expression.Equal(left, right);
313313
break;
@@ -318,7 +318,7 @@ private static Expression GetFilterExpressionLambda(Expression left, Expression
318318
return body;
319319
}
320320

321-
private static Expression<Func<TSource, bool>> ArrayContainsPredicate<TSource>(string[] propertyValues, string fieldname, FilterOperationsEnum op, string relationName = null)
321+
private static Expression<Func<TSource, bool>> ArrayContainsPredicate<TSource>(string[] propertyValues, string fieldname, FilterOperations op, string relationName = null)
322322
{
323323
ParameterExpression entity = Expression.Parameter(typeof(TSource), "entity");
324324
MemberExpression member;
@@ -333,7 +333,7 @@ private static Expression<Func<TSource, bool>> ArrayContainsPredicate<TSource>(s
333333
var method = ContainsMethod.MakeGenericMethod(member.Type);
334334
var obj = TypeHelper.ConvertListType(propertyValues, member.Type);
335335

336-
if (op == FilterOperationsEnum.@in)
336+
if (op == FilterOperations.@in)
337337
{
338338
// Where(i => arr.Contains(i.column))
339339
var contains = Expression.Call(method, new Expression[] { Expression.Constant(obj), member });
@@ -347,44 +347,20 @@ private static Expression<Func<TSource, bool>> ArrayContainsPredicate<TSource>(s
347347
}
348348
}
349349

350-
public static IQueryable<TSource> Select<TSource>(this IQueryable<TSource> source, List<QueryAttribute> columns)
350+
public static IQueryable<TSource> Select<TSource>(this IQueryable<TSource> source, List<string> columns)
351351
{
352352
if (columns == null || columns.Count == 0)
353353
return source;
354354

355355
var sourceType = source.ElementType;
356+
356357
var resultType = typeof(TSource);
357358

358359
// {model}
359360
var parameter = Expression.Parameter(sourceType, "model");
360-
var attrs = new List<string>();
361-
// Key = Relationship, Value = Attribute
362-
var relationAttrs = new Dictionary<string, string>();
363-
foreach(var item in columns)
364-
{
365-
if (item.IsAttributeOfRelationship)
366-
relationAttrs.Add(item.RelationshipAttribute, item.Attribute) ;
367-
else
368-
attrs.Add(item.Attribute);
369-
}
370-
371-
var bindings = new List<MemberAssignment>();
372-
bindings.AddRange(attrs.Select(column => Expression.Bind(resultType.GetProperty(column), Expression.PropertyOrField(parameter, column))));
373-
374-
375-
//foreach (var relationAttr in relationAttrs)
376-
//{
377-
// var relation = Expression.PropertyOrField(parameter, relationAttr.Key);
378-
// var member = Expression.Property(relation, relationAttr.Value);
379-
// var relationType = member.Type;
380-
381-
// var relationshipBindings = new List<MemberAssignment>();
382-
// relationshipBindings.AddRange(attrs.Select(column => Expression.Bind(resultType.GetProperty(column), Expression.PropertyOrField(parameter, column))));
383361

384-
// var body = Expression.MemberInit(Expression.New(relation.Type), bindings);
385-
// var ah = Expression.Bind(relation.Member, member.Expression);
386-
// bindings.Add(ah);
387-
//}
362+
var bindings = columns.Select(column => Expression.Bind(
363+
resultType.GetProperty(column), Expression.PropertyOrField(parameter, column)));
388364

389365
// { new Model () { Property = model.Property } }
390366
var body = Expression.MemberInit(Expression.New(resultType), bindings);

src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ public AttrFilterQuery(
1515
throw new JsonApiException(400, $"Filter is not allowed for attribute '{Attribute.PublicAttributeName}'.");
1616

1717
PropertyValue = filterQuery.Value;
18-
FilterOperation = FilterOperations.GetFilterOperation(filterQuery.Operation);
18+
FilterOperation = filterQuery.OperationType;
1919
}
2020

2121
public string PropertyValue { get; }
22-
public FilterOperationsEnum FilterOperation { get; }
22+
public FilterOperations FilterOperation { get; }
2323

2424
}
2525
}

src/JsonApiDotNetCore/Internal/Query/FilterOperations.cs

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace JsonApiDotNetCore.Internal.Query
55
{
6-
public enum FilterOperationsEnum
6+
public enum FilterOperations
77
{
88
eq = 0,
99
lt = 1,
@@ -18,32 +18,4 @@ public enum FilterOperationsEnum
1818
isnotnull = 10
1919
}
2020

21-
public class FilterOperations
22-
{
23-
public static FilterOperationsEnum GetFilterOperation(string prefix)
24-
{
25-
if (prefix.Length == 0) return FilterOperationsEnum.eq;
26-
27-
if (Enum.TryParse(prefix, out FilterOperationsEnum opertion) == false)
28-
throw new JsonApiException(400, $"Invalid filter prefix '{prefix}'");
29-
30-
return opertion;
31-
}
32-
33-
public static string GetFilterOperationFromQuery(string query)
34-
{
35-
var values = query.Split(QueryConstants.COLON);
36-
37-
if (values.Length == 1)
38-
return string.Empty;
39-
40-
var operation = values[0];
41-
// remove prefix from value
42-
if (Enum.TryParse(operation, out FilterOperationsEnum op) == false)
43-
return string.Empty;
44-
45-
return operation;
46-
}
47-
48-
}
4921
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// ReSharper disable InconsistentNaming
2+
using System;
3+
using System.Linq;
4+
5+
namespace JsonApiDotNetCore.Internal.Query
6+
{
7+
public class FilterOperationsHelper
8+
{
9+
/// <summary>
10+
/// Get filter operation enum and value by string value.
11+
/// Input string can contain:
12+
/// a) property value only, then FilterOperations.eq, value is returned
13+
/// b) filter prefix and value e.g. "prefix:value", then FilterOperations.prefix, value is returned
14+
/// In case of prefix is provided and is not in FilterOperations enum,
15+
/// the invalid filter prefix exception is thrown.
16+
/// </summary>
17+
/// <param name="input"></param>
18+
/// <returns></returns>
19+
public static (FilterOperations opereation,string value) GetFilterOperationAndValue(string input)
20+
{
21+
// value is empty
22+
if (input.Length == 0)
23+
return (FilterOperations.eq, input);
24+
25+
// split value
26+
var values = input.Split(QueryConstants.COLON);
27+
// value only
28+
if(values.Length == 1)
29+
return (FilterOperations.eq, input);
30+
// prefix:value
31+
else if (values.Length == 2)
32+
{
33+
var (operation, succeeded) = ParseFilterOperation(values[0]);
34+
if (succeeded == false)
35+
throw new JsonApiException(400, $"Invalid filter prefix '{values[0]}'");
36+
37+
return (operation, values[1]);
38+
}
39+
// some:colon:value OR prefix:some:colon:value (datetime)
40+
else
41+
{
42+
// succeeded = false means no prefix found => some value with colons(datetime)
43+
// succeeded = true means prefix provide + some value with colons(datetime)
44+
var (operation, succeeded) = ParseFilterOperation(values[0]);
45+
var value = "";
46+
// datetime
47+
if(succeeded == false)
48+
value = string.Join(QueryConstants.COLON_STR, values);
49+
else
50+
value = string.Join(QueryConstants.COLON_STR, values.Skip(1));
51+
return (operation, value);
52+
}
53+
}
54+
55+
/// <summary>
56+
/// Returns typed operation result and info about parsing success
57+
/// </summary>
58+
/// <param name="operation">String represented operation</param>
59+
/// <returns></returns>
60+
public static (FilterOperations operation, bool succeeded) ParseFilterOperation(string operation)
61+
{
62+
var success = Enum.TryParse(operation, out FilterOperations opertion);
63+
return (opertion, success);
64+
}
65+
}
66+
}

src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,32 @@ namespace JsonApiDotNetCore.Internal.Query
55
{
66
public class FilterQuery: QueryAttribute
77
{
8+
[Obsolete("You should use constructor with strongly typed OperationType.")]
89
public FilterQuery(string attribute, string value, string operation)
910
:base(attribute)
1011
{
1112
Key = attribute.ToProperCase();
1213
Value = value;
1314
Operation = operation;
15+
16+
Enum.TryParse(operation, out FilterOperations opertion);
17+
OperationType = opertion;
18+
}
19+
20+
public FilterQuery(string attribute, string value, FilterOperations operationType)
21+
: base(attribute)
22+
{
23+
Value = value;
24+
OperationType = operationType;
25+
Operation = operationType.ToString();
1426
}
15-
27+
1628
[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.")]
1729
public string Key { get; set; }
1830
public string Value { get; set; }
31+
[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.")]
1932
public string Operation { get; set; }
33+
public FilterOperations OperationType { get; set; }
2034

2135
}
2236
}

src/JsonApiDotNetCore/Internal/Query/QueryAttribute.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ public QueryAttribute(string attribute)
1616
Attribute = attribute;
1717
IsAttributeOfRelationship = false;
1818
}
19-
2019
}
2120

2221
public string Attribute { get; }

src/JsonApiDotNetCore/Internal/Query/QuerySet.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ public class QuerySet
88
public PageQuery PageQuery { get; set; } = new PageQuery();
99
public List<SortQuery> SortParameters { get; set; } = new List<SortQuery>();
1010
public List<string> IncludedRelationships { get; set; } = new List<string>();
11-
public List<QueryAttribute> Fields { get; set; } = new List<QueryAttribute>();
11+
public List<string> Fields { get; set; } = new List<string>();
1212
}
1313
}

src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ public RelatedAttrFilterQuery(
1313
:base(jsonApiContext, filterQuery)
1414
{
1515
PropertyValue = filterQuery.Value;
16-
FilterOperation = FilterOperations.GetFilterOperation(filterQuery.Operation);
16+
FilterOperation = filterQuery.OperationType;
1717
}
1818

1919
public string PropertyValue { get; set; }
20-
public FilterOperationsEnum FilterOperation { get; set; }
20+
public FilterOperations FilterOperation { get; set; }
2121

2222
}
2323
}

0 commit comments

Comments
 (0)