Skip to content
This repository has been archived by the owner on Feb 6, 2025. It is now read-only.

Commit

Permalink
Filters and generic storage client for API #19
Browse files Browse the repository at this point in the history
  • Loading branch information
spetz committed Sep 30, 2016
1 parent 0ae7128 commit c42de95
Show file tree
Hide file tree
Showing 23 changed files with 155 additions and 42 deletions.
10 changes: 9 additions & 1 deletion src/Coolector.Api/Modules/Base/ModuleBase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Coolector.Core.Commands;
using System.Collections.Generic;
using Coolector.Common.Types;
using Coolector.Core.Commands;
using Nancy;

namespace Coolector.Api.Modules.Base
Expand All @@ -12,5 +14,11 @@ public ModuleBase(ICommandDispatcher commandDispatcher, string modulePath = "")
{
CommandDispatcher = commandDispatcher;
}

//TODO: Add headers etc.
protected IEnumerable<T> FromPagedResult<T>(Maybe<PagedResult<T>> result)
{
return result.HasValue ? result.Value.Items : new List<T>();
}
}
}
3 changes: 1 addition & 2 deletions src/Coolector.Api/Modules/UserModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using Coolector.Api.Modules.Base;
using Coolector.Common.DTO.Users;
using Coolector.Core.Commands;
using Coolector.Core.Filters;
using Coolector.Core.Storages;
Expand All @@ -18,7 +17,7 @@ public UserModule(ICommandDispatcher commandDispatcher, IUserStorage userStorage
var query = this.Bind<BrowseUsers>();
var users = await userStorage.BrowseAsync(query);

return users.HasValue ? users.Value.Items : new List<UserDto>();
return FromPagedResult(users);
});
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/Coolector.Common/Extensions/PaginationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ namespace Coolector.Common.Extensions
{
public static class PaginationExtensions
{
public static PagedResult<T> Paginate<T>(this IEnumerable<T> values, PagedQueryBase query)
public static PagedResult<T> PaginateWithoutLimit<T>(this IEnumerable<T> values)
=> values.Paginate(1, int.MaxValue);

public static PagedResult<T> Paginate<T>(this IEnumerable<T> values, IPagedQuery query)
=> values.Paginate(query.Page, query.Results);

public static PagedResult<T> Paginate<T>(this IEnumerable<T> values,
Expand All @@ -30,7 +33,7 @@ public static PagedResult<T> Paginate<T>(this IEnumerable<T> values,
return PagedResult<T>.Create(data, page, resultsPerPage, totalPages, totalResults);
}

public static IEnumerable<T> Limit<T>(this IEnumerable<T> collection, PagedQueryBase query)
public static IEnumerable<T> Limit<T>(this IEnumerable<T> collection, IPagedQuery query)
=> collection.Limit(query.Page, query.Results);

public static IEnumerable<T> Limit<T>(this IEnumerable<T> collection,
Expand Down
4 changes: 2 additions & 2 deletions src/Coolector.Common/Types/IFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace Coolector.Common.Types
{
public interface IFilter<TResult, in TQuery> where TQuery : PagedQueryBase
public interface IFilter<TResult, in TQuery> where TQuery : IQuery
{
Maybe<PagedResult<TResult>> Filter(Maybe<IEnumerable<TResult>> values, TQuery query);
Maybe<IEnumerable<TResult>> Filter(Maybe<IEnumerable<TResult>> values, TQuery query);
}
}
8 changes: 8 additions & 0 deletions src/Coolector.Common/Types/IPagedQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Coolector.Common.Types
{
public interface IPagedQuery : IQuery
{
int Page { get; }
int Results { get; }
}
}
2 changes: 1 addition & 1 deletion src/Coolector.Common/Types/PagedQueryBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Coolector.Common.Types
{
public abstract class PagedQueryBase : IQuery
public abstract class PagedQueryBase : IPagedQuery
{
public int Page { get; set; }
public int Results { get; set; }
Expand Down
9 changes: 5 additions & 4 deletions src/Coolector.Core/Filters/BrowseUsersFilter.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
using System.Collections.Generic;
using Coolector.Common.DTO.Users;
using Coolector.Common.Extensions;
using Coolector.Common.Types;

namespace Coolector.Core.Filters
{
public class BrowseUsersFilter : IFilter<UserDto, BrowseUsers>
{
public Maybe<PagedResult<UserDto>> Filter(Maybe<IEnumerable<UserDto>> values, BrowseUsers query)
public Maybe<IEnumerable<UserDto>> Filter(Maybe<IEnumerable<UserDto>> values, BrowseUsers query)
{
if(values.HasNoValue)
return new Maybe<PagedResult<UserDto>>();
return new Maybe<IEnumerable<UserDto>>();
if (query == null)
return values;

return values.Value.Paginate(query);
return values;
}
}
}
14 changes: 14 additions & 0 deletions src/Coolector.Core/Filters/EmptyFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using Coolector.Common.Types;

namespace Coolector.Core.Filters
{
public class EmptyFilter<TResult, TQuery> : IFilter<TResult, TQuery> where TQuery : IQuery
{
public Maybe<IEnumerable<TResult>> Filter(Maybe<IEnumerable<TResult>> values, TQuery query)
{
return values;
}
}
}
22 changes: 22 additions & 0 deletions src/Coolector.Core/Filters/FilterResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Autofac;
using Coolector.Common.Types;

namespace Coolector.Core.Filters
{
public class FilterResolver : IFilterResolver
{
private readonly IComponentContext _context;

public FilterResolver(IComponentContext context)
{
_context = context;
}

public IFilter<TResult, TQuery> Resolve<TResult, TQuery>() where TQuery : IQuery
{
IFilter<TResult, TQuery> filter;

return _context.TryResolve(out filter) ? filter : new EmptyFilter<TResult, TQuery>();
}
}
}
9 changes: 9 additions & 0 deletions src/Coolector.Core/Filters/IFilterResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Coolector.Common.Types;

namespace Coolector.Core.Filters
{
public interface IFilterResolver
{
IFilter<TResult, TQuery> Resolve<TResult, TQuery>() where TQuery : IQuery;
}
}
2 changes: 2 additions & 0 deletions src/Coolector.Core/IoC/Modules/FilterModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Reflection;
using Autofac;
using Coolector.Common.Types;
using Coolector.Core.Filters;
using Module = Autofac.Module;

namespace Coolector.Core.IoC.Modules
Expand All @@ -9,6 +10,7 @@ public class FilterModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<FilterResolver>().As<IFilterResolver>();
var coreAssembly = Assembly.Load(new AssemblyName("Coolector.Core"));
builder.RegisterAssemblyTypes(coreAssembly).AsClosedTypesOf(typeof(IFilter<,>));
}
Expand Down
7 changes: 5 additions & 2 deletions src/Coolector.Core/Storages/IStorageClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Coolector.Common.Types;

Expand All @@ -12,7 +11,11 @@ public interface IStorageClient
Task<Maybe<T>> GetUsingCacheAsync<T>(string endpoint, string cacheKey = null, TimeSpan? expiry = null)
where T : class;

Task<Maybe<IEnumerable<T>>> GetCollectionUsingCacheAsync<T>(string endpoint, string cacheKey = null,
Task<Maybe<PagedResult<T>>> GetCollectionUsingCacheAsync<T>(string endpoint, string cacheKey = null,
TimeSpan? expiry = null) where T : class;

Task<Maybe<PagedResult<TResult>>> GetFilteredCollectionUsingCacheAsync<TResult, TQuery>(TQuery query,
string endpoint, string cacheKey = null, TimeSpan? expiry = null)
where TResult : class where TQuery : class, IPagedQuery;
}
}
3 changes: 2 additions & 1 deletion src/Coolector.Core/Storages/IUserStorage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using Coolector.Common.DTO.Users;
using Coolector.Common.Types;
using Coolector.Core.Filters;
Expand Down
68 changes: 58 additions & 10 deletions src/Coolector.Core/Storages/StorageClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@
using Coolector.Common.Extensions;
using Newtonsoft.Json;
using System.Linq;
using System.Reflection;
using System.Text;
using Coolector.Core.Filters;

namespace Coolector.Core.Storages
{
public class StorageClient : IStorageClient
{
private readonly ICache _cache;
private readonly IFilterResolver _filterResolver;
private readonly StorageSettings _settings;
private readonly HttpClient _httpClient;

private string BaseAddress
=> _settings.Url.EndsWith("/", StringComparison.CurrentCulture) ? _settings.Url : $"{_settings.Url}/";

public StorageClient(ICache cache, StorageSettings settings)
public StorageClient(ICache cache, IFilterResolver filterResolver, StorageSettings settings)
{
_cache = cache;
_filterResolver = filterResolver;
_settings = settings;
_httpClient = new HttpClient {BaseAddress = new Uri(BaseAddress)};
_httpClient.DefaultRequestHeaders.Remove("Accept");
Expand Down Expand Up @@ -67,20 +72,63 @@ public async Task<Maybe<T>> GetUsingCacheAsync<T>(string endpoint, string cacheK
return result;
}

public async Task<Maybe<IEnumerable<T>>> GetCollectionUsingCacheAsync<T>(string endpoint, string cacheKey = null,
public async Task<Maybe<PagedResult<T>>> GetCollectionUsingCacheAsync<T>(string endpoint, string cacheKey = null,
TimeSpan? expiry = null) where T : class
{
var result = await GetFromCacheAsync<IEnumerable<T>>(endpoint, cacheKey);
if (result.HasValue && result.Value.Any())
return result;
var results = await GetFromCacheAsync<IEnumerable<T>>(endpoint, cacheKey);
if (results.HasValue && results.Value.Any())
return results.Value.PaginateWithoutLimit();

result = await GetAsync<IEnumerable<T>>(endpoint);
if (result.HasNoValue || !result.Value.Any())
return new Maybe<IEnumerable<T>>();
results = await GetAsync<IEnumerable<T>>(endpoint);
if (results.HasNoValue || !results.Value.Any())
return new Maybe<PagedResult<T>>();

await StoreInCacheAsync(result, endpoint, cacheKey, expiry);
await StoreInCacheAsync(results, endpoint, cacheKey, expiry);

return result;
return results.Value.PaginateWithoutLimit();
}

public async Task<Maybe<PagedResult<TResult>>> GetFilteredCollectionUsingCacheAsync<TResult, TQuery>(
TQuery query, string endpoint, string cacheKey = null, TimeSpan? expiry = null) where TResult : class
where TQuery : class, IPagedQuery
{
var filter = _filterResolver.Resolve<TResult, TQuery>();
var results = await GetFromCacheAsync<IEnumerable<TResult>>(endpoint, cacheKey);
if (results.HasValue && results.Value.Any())
return FilterAndPaginateResults(filter, results, query);

results = await GetAsync<IEnumerable<TResult>>(GetEndpointWithQuery(endpoint, query));
if (results.HasNoValue || !results.Value.Any())
return PagedResult<TResult>.Empty;

await StoreInCacheAsync(results, endpoint, cacheKey, expiry);

return FilterAndPaginateResults(filter, results, query);
}

private static Maybe<PagedResult<TResult>> FilterAndPaginateResults<TResult, TQuery>(
IFilter<TResult, TQuery> filter,
Maybe<IEnumerable<TResult>> results, TQuery query) where TQuery : class, IPagedQuery
{
var filteredValues = filter.Filter(results, query);

return filteredValues.HasValue ? filteredValues.Value.Paginate(query) : PagedResult<TResult>.Empty;
}

private static string GetEndpointWithQuery<T>(string endpoint, T query) where T : class, IQuery
{
if (query == null)
return endpoint;

var values = new List<string>();
foreach (var property in query.GetType().GetProperties())
{
var value = property.GetValue(query, null);
values.Add($"{property.Name.ToLowerInvariant()}={value}");
}

var endpointQuery = string.Join("&", values);
return $"{endpoint}?{endpointQuery}";
}

private async Task<Maybe<T>> GetFromCacheAsync<T>(string endpoint, string cacheKey = null) where T : class
Expand Down
13 changes: 4 additions & 9 deletions src/Coolector.Core/Storages/UserStorage.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using Coolector.Common.DTO.Users;
using Coolector.Common.Types;
using Coolector.Core.Filters;
Expand All @@ -8,22 +9,16 @@ namespace Coolector.Core.Storages
public class UserStorage : IUserStorage
{
private readonly IStorageClient _storageClient;
private readonly IFilter<UserDto, BrowseUsers> _usersFilter;

public UserStorage(IStorageClient storageClient, IFilter<UserDto, BrowseUsers> usersFilter)
public UserStorage(IStorageClient storageClient)
{
_storageClient = storageClient;
_usersFilter = usersFilter;
}

public async Task<Maybe<UserDto>> GetAsync(string id)
=> await _storageClient.GetUsingCacheAsync<UserDto>($"users/{id}");

public async Task<Maybe<PagedResult<UserDto>>> BrowseAsync(BrowseUsers query)
{
var users = await _storageClient.GetCollectionUsingCacheAsync<UserDto>("users");

return _usersFilter.Filter(users, query);
}
=> await _storageClient.GetFilteredCollectionUsingCacheAsync<UserDto, BrowseUsers>(query, "users");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Coolector.Services.Mongo;
using Coolector.Services.Nancy;
using Coolector.Services.Storage.Framework.IoC;
using Coolector.Services.Storage.Providers;
using Coolector.Services.Storage.Modules.Providers;
using Coolector.Services.Storage.Repositories;
using Coolector.Services.Storage.Settings;
using Nancy.Bootstrapper;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Coolector.Common.Types;
using Coolector.Services.Storage.Mappers;

namespace Coolector.Services.Storage.Providers
namespace Coolector.Services.Storage.Modules.Providers
{
public interface IProviderClient
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Coolector.Common.Types;
using Coolector.Services.Storage.Mappers;

namespace Coolector.Services.Storage.Providers
namespace Coolector.Services.Storage.Modules.Providers
{
public interface IServiceClient
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Coolector.Common.DTO.Users;
using Coolector.Common.Types;

namespace Coolector.Services.Storage.Providers
namespace Coolector.Services.Storage.Modules.Providers
{
public interface IUserProvider
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Coolector.Common.Types;
using Coolector.Services.Storage.Mappers;

namespace Coolector.Services.Storage.Providers
namespace Coolector.Services.Storage.Modules.Providers
{
public class ProviderClient : IProviderClient
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using Coolector.Services.Storage.Mappers;
using Newtonsoft.Json;

namespace Coolector.Services.Storage.Providers
namespace Coolector.Services.Storage.Modules.Providers
{
public class ServiceClient : IServiceClient
{
Expand Down
Loading

0 comments on commit c42de95

Please sign in to comment.