Squashed commit of the following:

commit 5624d5049d0efa99dbb2d7c8453c0c756eb283aa
Author: Collin Barrett <collinbarrett@users.noreply.github.com>
Date:   Fri Sep 21 19:48:36 2018 -0500

    move index to new endpoint for testing

    ref #505

commit e49f3b6312372078f9e57a451cfae4df06145597
Author: Collin Barrett <collinbarrett@users.noreply.github.com>
Date:   Fri Sep 21 19:40:29 2018 -0500

    restore camelCase

commit 94fa3c5f4ef1e47c80108c0648a5f3597d305ba7
Author: Collin Barrett <collinbarrett@users.noreply.github.com>
Date:   Fri Sep 21 19:20:38 2018 -0500

    ignore nulls and empties from json

commit 5bc704ffad168ae90cbe232161d3b205d8855b07
Author: collinbarrett <collinbarrett@users.noreply.github.com>
Date:   Fri Sep 21 12:40:25 2018 -0500

    add maintainers

commit 68bab5bb1c3394e960ec2695c6f51f5adace1cec
Merge: 106edf5 71a3d75
Author: collinbarrett <collinbarrett@users.noreply.github.com>
Date:   Fri Sep 21 10:35:43 2018 -0500

    Merge branch 'master' into refactor-api

commit 106edf5b96d4d2296374ef60c8d31923316199d2
Author: Collin Barrett <collinbarrett@users.noreply.github.com>
Date:   Thu Sep 20 06:15:49 2018 -0500

    wip

commit 1387ef5836165f716b59df19ca6fca5959c65ee8
Author: Collin Barrett <collinbarrett@users.noreply.github.com>
Date:   Thu Sep 20 05:54:16 2018 -0500

    wip

commit fcebd19ecb583dd76112c0c4d2c1d9b1c2f0d766
Author: Collin Barrett <collinbarrett@users.noreply.github.com>
Date:   Wed Sep 19 21:11:47 2018 -0500

    wip

commit e9d78450e85120a0002d5a4f528aca6992d63646
Author: Collin Barrett <collinbarrett@users.noreply.github.com>
Date:   Wed Sep 19 18:23:08 2018 -0500

    wip

commit bb915dcfe605278a941e5ac0cfc50c363d744c41
Author: collinbarrett <collinbarrett@users.noreply.github.com>
Date:   Wed Sep 19 15:49:12 2018 -0500

    RuleCount wip

commit 8e1c0fffed9af0485dec07f33ee130d6b9ecb305
Author: collinbarrett <collinbarrett@users.noreply.github.com>
Date:   Wed Sep 19 15:17:28 2018 -0500

    use IReadOnlyCollection

    per https://stackoverflow.com/questions/52368567/optimization-of-correlated-subqueries-with-automapper?noredirect=1#comment91770378_52368567

commit 6362d5891c728cf83bf2e3bdbb4b244c56aafdc9
Author: Collin Barrett <collinbarrett@users.noreply.github.com>
Date:   Wed Sep 19 05:38:54 2018 -0500

    wip
This commit is contained in:
Collin M. Barrett 2018-09-21 19:57:01 -05:00
parent 96480cf1d8
commit 96f8c014f1
8 changed files with 141 additions and 6 deletions

View file

@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.Swagger;
namespace FilterLists.Api.DependencyInjection.Extensions
@ -32,7 +33,13 @@ private static void ConfigureCookiePolicy(this IServiceCollection services) =>
});
private static void AddMvcCustom(this IServiceCollection services) =>
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(opts =>
{
opts.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
opts.SerializerSettings.ContractResolver = new SkipEmptyContractResolver();
});
private static void AddRoutingCustom(this IServiceCollection services) =>
services.AddRouting(opts => opts.LowercaseUrls = true);

View file

@ -35,6 +35,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.5.1" />
<PackageReference Include="JetBrains.Annotations" Version="2018.2.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.4.1" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.4" />

View file

@ -0,0 +1,35 @@
using System;
using System.Collections;
using System.Reflection;
using Humanizer;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace FilterLists.Api
{
// REF: https://stackoverflow.com/a/18486790/2343739
public class SkipEmptyContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.PropertyName = property.PropertyName.Camelize();
var isDefaultValueIgnored =
((property.DefaultValueHandling ?? DefaultValueHandling.Ignore) & DefaultValueHandling.Ignore) != 0;
if (!isDefaultValueIgnored || typeof(string).IsAssignableFrom(property.PropertyType) ||
!typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
return property;
bool NewShouldSerialize(object obj)
{
return !(property.ValueProvider.GetValue(obj) is ICollection collection) || collection.Count != 0;
}
var oldShouldSerialize = property.ShouldSerialize;
property.ShouldSerialize = oldShouldSerialize != null
? o => oldShouldSerialize(o) && NewShouldSerialize(o)
: (Predicate<object>)NewShouldSerialize;
return property;
}
}
}

View file

@ -5,7 +5,7 @@
namespace FilterLists.Api.V1.Controllers
{
[ApiVersion("1.0")]
[ApiVersion("1")]
//TODO: use versioning without needing to manually specify in swagger-ui (https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/370)
[Route("v{version:apiVersion}/[controller]")]
[ResponseCache(Duration = 86400)]

View file

@ -23,9 +23,20 @@ public async Task<IActionResult> Index() =>
return filterListService.GetAllSummariesAsync();
}));
/// <summary>
/// in development for https://github.com/collinbarrett/FilterLists/issues/505
/// </summary>
[HttpGet]
[Route("alpha")]
public async Task<IActionResult> Alpha() =>
Json(await MemoryCache.GetOrCreate("ListsController_Alpha", entry =>
{
entry.AbsoluteExpirationRelativeToNow = MemoryCacheExpirationDefault;
return filterListService.GetIndexAsync();
}));
[HttpGet]
[Route("{id}")]
//TODO: respond with appropriate exception if negative id queried
public async Task<IActionResult> GetById(int id) =>
Json(await MemoryCache.GetOrCreate("ListsController_GetById_" + id, entry =>
{

View file

@ -5,12 +5,10 @@
using AutoMapper.QueryableExtensions;
using FilterLists.Data;
using FilterLists.Services.FilterList.Models;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
namespace FilterLists.Services.FilterList
{
[UsedImplicitly]
public class FilterListService : Service
{
public FilterListService(FilterListsDbContext dbContext, IConfigurationProvider mapConfig)
@ -19,7 +17,16 @@ public FilterListService(FilterListsDbContext dbContext, IConfigurationProvider
}
public async Task<IEnumerable<ListSummaryDto>> GetAllSummariesAsync() =>
await DbContext.FilterLists.OrderBy(l => l.Name).ProjectTo<ListSummaryDto>(MapConfig).ToListAsync();
await DbContext.FilterLists
.OrderBy(l => l.Name)
.ProjectTo<ListSummaryDto>(MapConfig)
.ToListAsync();
public async Task<IEnumerable<ListIndexRecord>> GetIndexAsync() =>
await DbContext.FilterLists
.OrderBy(l => l.Name)
.ProjectTo<ListIndexRecord>(MapConfig)
.ToListAsync();
public async Task<ListDetailsDto> GetDetailsAsync(uint id) =>
await DbContext.FilterLists.ProjectTo<ListDetailsDto>(MapConfig)

View file

@ -0,0 +1,53 @@
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using FilterLists.Services.FilterList.Models;
using JetBrains.Annotations;
namespace FilterLists.Services.FilterList.MappingProfiles
{
[UsedImplicitly]
public class ListIndexRecordMappingProfile : Profile
{
public ListIndexRecordMappingProfile() =>
CreateMap<Data.Entities.FilterList, ListIndexRecord>()
.ForMember(r => r.Id,
o => o.MapFrom(l =>
(int)l.Id))
.ForMember(r => r.LanguageIds,
o => o.MapFrom(l =>
l.FilterListLanguages.Select(ll => (int)ll.LanguageId)))
.ForMember(r => r.MaintainerIds,
o => o.MapFrom(l =>
l.FilterListMaintainers.Select(ll => (int)ll.MaintainerId)))
.ForMember(r => r.RuleCount,
o => o.MapFrom(l =>
l.Snapshots
.Where(s => s.WasSuccessful)
.Select(s => (int?)s.SnapshotRules.Count)
.FirstOrDefault()))
.ForMember(r => r.SyntaxId,
o => o.MapFrom(l =>
(int)l.SyntaxId))
.ForMember(r => r.TagIds,
o => o.MapFrom(l =>
l.FilterListTags.Select(lt => (int)lt.TagId)))
.ForMember(r => r.UpdatedDate,
o => o.MapFrom(l =>
l.Snapshots
.Count(s => s.WasSuccessful && s.WasUpdated) >= 2
? l.Snapshots
.Where(s => s.WasSuccessful && s.WasUpdated)
.Select(s => s.CreatedDateUtc)
.OrderByDescending(c => c)
.FirstOrDefault()
: null))
.ForMember(r => r.ViewUrlMirrors,
o => o.MapFrom(l =>
l.ViewUrlMirror1 != null
? l.ViewUrlMirror2 != null
? new List<string> {l.ViewUrlMirror1, l.ViewUrlMirror2}
: new List<string> {l.ViewUrlMirror1}
: null));
}
}

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace FilterLists.Services.FilterList.Models
{
[UsedImplicitly]
public class ListIndexRecord
{
public int Id { get; set; }
public List<int> LanguageIds { get; set; }
public List<int> MaintainerIds { get; set; }
public string Name { get; set; }
public int? RuleCount { get; set; }
public int? SyntaxId { get; set; }
public List<int> TagIds { get; set; }
public DateTime? UpdatedDate { get; set; }
public string ViewUrl { get; set; }
public List<string> ViewUrlMirrors { get; set; }
}
}