Skip to content

Commit

Permalink
Merge pull request #83 from Research-Institute/develop
Browse files Browse the repository at this point in the history
v1.3.0
  • Loading branch information
jaredcnance authored Mar 31, 2017
2 parents 8469d2d + 53cc9db commit ea3a076
Show file tree
Hide file tree
Showing 20 changed files with 516 additions and 52 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ JsonApiDotnetCore provides a framework for building [json:api](http://jsonapi.or
- [Defining Custom Data Access Methods](#defining-custom-data-access-methods)
- [Pagination](#pagination)
- [Filtering](#filtering)
- [Custom Filters](#custom-filters)
- [Sorting](#sorting)
- [Meta](#meta)
- [Client Generated Ids](#client-generated-ids)
Expand All @@ -46,14 +47,14 @@ Install-Package JsonApiDotnetCore

- project.json
```json
"JsonApiDotNetCore": "1.2.2"
"JsonApiDotNetCore": "1.3.0"
```

- *.csproj
```xml
<ItemGroup>
<!-- ... -->
<PackageReference Include="JsonApiDotNetCore" Version="1.2.2" />
<PackageReference Include="JsonApiDotNetCore" Version="1.3.0" />
</ItemGroup>
```

Expand Down Expand Up @@ -317,6 +318,31 @@ identifier):
?filter[attribute]=like:value
```

#### Custom Filters

You can customize the filter implementation by overriding the method in the `DefaultEntityRepository` like so:

```csharp
public class MyEntityRepository : DefaultEntityRepository<MyEntity>
{
public MyEntityRepository(
AppDbContext context,
ILoggerFactory loggerFactory,
IJsonApiContext jsonApiContext)
: base(context, loggerFactory, jsonApiContext)
{ }

public override IQueryable<TEntity> Filter(IQueryable<TEntity> entities, FilterQuery filterQuery)
{
// use the base filtering method
entities = base.Filter(entities, filterQuery);

// implement custom method
return ApplyMyCustomFilter(entities, filterQuery);
}
}
```

### Sorting

Resources can be sorted by an attribute:
Expand Down
3 changes: 2 additions & 1 deletion src/JsonApiDotNetCore/Builders/DocumentBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ private List<DocumentData> GetIncludedEntities(ContextEntity contextEntity, IIde
private void AddIncludedEntity(List<DocumentData> entities, IIdentifiable entity)
{
var includedEntity = GetIncludedEntity(entity);
if(includedEntity != null)

if(includedEntity != null && !entities.Any(doc => doc.Id == includedEntity.Id && doc.Type == includedEntity.Type))
entities.Add(includedEntity);
}

Expand Down
4 changes: 3 additions & 1 deletion src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ public virtual IQueryable<TEntity> Filter(IQueryable<TEntity> entities, FilterQ
if(filterQuery == null)
return entities;

var attributeFilterQuery = new AttrFilterQuery(_jsonApiContext, filterQuery);

return entities
.Filter(filterQuery);
.Filter(attributeFilterQuery);
}

public virtual IQueryable<TEntity> Sort(IQueryable<TEntity> entities, List<SortQuery> sortQueries)
Expand Down
4 changes: 2 additions & 2 deletions src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private static IOrderedQueryable<TSource> CallGenericOrderMethod<TSource>(IQuery
return (IOrderedQueryable<TSource>)result;
}

public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> source, FilterQuery filterQuery)
public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> source, AttrFilterQuery filterQuery)
{
if (filterQuery == null)
return source;
Expand All @@ -78,7 +78,7 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
{
// convert the incoming value to the target value type
// "1" -> 1
var convertedValue = Convert.ChangeType(filterQuery.PropertyValue, property.PropertyType);
var convertedValue = TypeHelper.ConvertType(filterQuery.PropertyValue, property.PropertyType);
// {model}
var parameter = Expression.Parameter(concreteType, "model");
// {model.Id}
Expand Down
51 changes: 51 additions & 0 deletions src/JsonApiDotNetCore/Internal/Query/AttrFilterQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Linq;
using JsonApiDotNetCore.Models;
using JsonApiDotNetCore.Services;

namespace JsonApiDotNetCore.Internal.Query
{
public class AttrFilterQuery
{
private readonly IJsonApiContext _jsonApiContext;

public AttrFilterQuery(
IJsonApiContext jsonApiCopntext,
FilterQuery filterQuery)
{
_jsonApiContext = jsonApiCopntext;

var attribute = GetAttribute(filterQuery.Key);

if (attribute == null)
throw new JsonApiException("400", $"{filterQuery.Key} is not a valid property.");

FilteredAttribute = attribute;
PropertyValue = filterQuery.Value;
FilterOperation = GetFilterOperation(filterQuery.Operation);
}

public AttrAttribute FilteredAttribute { get; set; }
public string PropertyValue { get; set; }
public FilterOperations FilterOperation { get; set; }

private FilterOperations GetFilterOperation(string prefix)
{
if (prefix.Length == 0) return FilterOperations.eq;

FilterOperations opertion;
if (!Enum.TryParse<FilterOperations>(prefix, out opertion))
throw new JsonApiException("400", $"Invalid filter prefix '{prefix}'");

return opertion;
}

private AttrAttribute GetAttribute(string propertyName)
{
return _jsonApiContext.RequestEntity.Attributes
.FirstOrDefault(attr =>
attr.InternalAttributeName.ToLower() == propertyName.ToLower()
);
}
}
}
16 changes: 7 additions & 9 deletions src/JsonApiDotNetCore/Internal/Query/FilterQuery.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
using JsonApiDotNetCore.Models;

namespace JsonApiDotNetCore.Internal.Query
{
public class FilterQuery
{
public FilterQuery(AttrAttribute filteredAttribute, string propertyValue, FilterOperations filterOperation)
public FilterQuery(string key, string value, string operation)
{
FilteredAttribute = filteredAttribute;
PropertyValue = propertyValue;
FilterOperation = filterOperation;
Key = key;
Value = value;
Operation = operation;
}

public AttrAttribute FilteredAttribute { get; set; }
public string PropertyValue { get; set; }
public FilterOperations FilterOperation { get; set; }
public string Key { get; set; }
public string Value { get; set; }
public string Operation { get; set; }
}
}
33 changes: 8 additions & 25 deletions src/JsonApiDotNetCore/Internal/Query/QuerySet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,49 +74,32 @@ private List<FilterQuery> ParseFilterQuery(string key, string value)
var queries = new List<FilterQuery>();

var propertyName = key.Split('[', ']')[1].ToProperCase();
var attribute = GetAttribute(propertyName);

if (attribute == null)
throw new JsonApiException("400", $"{propertyName} is not a valid property.");

var values = value.Split(',');
foreach(var val in values)
queries.Add(ParseFilterOperation(attribute, val));
{
(var operation, var filterValue) = ParseFilterOperation(val);
queries.Add(new FilterQuery(propertyName, filterValue, operation));
}

return queries;
}

private FilterQuery ParseFilterOperation(AttrAttribute attribute, string value)
private (string operation, string value) ParseFilterOperation(string value)
{
if(value.Length < 3)
return new FilterQuery(attribute, value, FilterOperations.eq);
return (string.Empty, value);

var operation = value.Split(':');

if(operation.Length == 1)
return new FilterQuery(attribute, value, FilterOperations.eq);
return (string.Empty, value);

// remove prefix from value
var prefix = operation[0];
value = operation[1];

switch(prefix)
{
case "eq":
return new FilterQuery(attribute, value, FilterOperations.eq);
case "lt":
return new FilterQuery(attribute, value, FilterOperations.lt);
case "gt":
return new FilterQuery(attribute, value, FilterOperations.gt);
case "le":
return new FilterQuery(attribute, value, FilterOperations.le);
case "ge":
return new FilterQuery(attribute, value, FilterOperations.ge);
case "like":
return new FilterQuery(attribute, value, FilterOperations.like);
}

throw new JsonApiException("400", $"Invalid filter prefix '{prefix}'");
return (prefix, value);;
}

private PageQuery ParsePageQuery(string key, string value)
Expand Down
8 changes: 3 additions & 5 deletions src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<VersionPrefix>1.2.2</VersionPrefix>
<VersionPrefix>1.3.0</VersionPrefix>
<TargetFramework>netcoreapp1.0</TargetFramework>
<AssemblyName>JsonApiDotNetCore</AssemblyName>
<PackageId>JsonApiDotNetCore</PackageId>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50;portable-net45+win8</PackageTargetFallback>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
</ItemGroup>

</Project>
</Project>
13 changes: 13 additions & 0 deletions src/JsonApiDotNetCoreExample/Data/AppDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{ }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TodoItem>()
.HasOne(t => t.Assignee)
.WithMany(p => p.AssignedTodoItems)
.HasForeignKey(t => t.AssigneeId);

modelBuilder.Entity<TodoItem>()
.HasOne(t => t.Owner)
.WithMany(p => p.TodoItems)
.HasForeignKey(t => t.OwnerId);
}

public DbSet<TodoItem> TodoItems { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<TodoItemCollection> TodoItemCollections { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="1.1.0" />
<PackageReference Include="DotNetCoreDocs" Version="0.4.0" />
</ItemGroup>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit ea3a076

Please sign in to comment.