mirror of
https://github.com/collinbarrett/FilterLists.git
synced 2026-03-11 09:04:27 +00:00
refactor(dir): ♻️ use primary ctor syntax for responses
This commit is contained in:
parent
d668655af1
commit
2cede3e539
10 changed files with 210 additions and 408 deletions
|
|
@ -45,7 +45,7 @@ internal static void MapEndpoints(this WebApplication app)
|
|||
OperationId = nameof(GetLists)
|
||||
});
|
||||
|
||||
app.MapGet("/lists/{id:int}", async Task<Results<Ok<ListDetailsVm>, NotFound>>
|
||||
app.MapGet("/lists/{id:int}", async Task<Results<Ok<Response>, NotFound>>
|
||||
(int id, IMediator mediator, CancellationToken ct) =>
|
||||
{
|
||||
var list = await mediator.Send(new Request(id), ct);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ internal static void AddOpenApiGen(this IServiceCollection services)
|
|||
|
||||
// include OpenApiTag Description and ExternalDocs
|
||||
options.DocumentFilter<OpenApiTags.TagDescriptionsDocumentFilter>();
|
||||
|
||||
// allow re-using simple type names like "Response"
|
||||
options.CustomSchemaIds(s => s.FullName?.Replace("+", "."));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -8,59 +8,38 @@ namespace FilterLists.Directory.Application.Queries;
|
|||
|
||||
public static class GetLanguages
|
||||
{
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<LanguageVm>> Query =
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<Response>> Query =
|
||||
EF.CompileAsyncQuery((QueryDbContext ctx) =>
|
||||
ctx.Languages
|
||||
.Where(l => l.FilterListLanguages.Any())
|
||||
.OrderBy(l => l.Iso6391)
|
||||
.Select(l => new LanguageVm
|
||||
{
|
||||
Id = l.Id,
|
||||
Iso6391 = l.Iso6391,
|
||||
Name = l.Name,
|
||||
FilterListIds = l.FilterListLanguages
|
||||
.Select(l => new Response(
|
||||
l.Id,
|
||||
l.Iso6391,
|
||||
l.Name,
|
||||
l.FilterListLanguages
|
||||
.OrderBy(fl => fl.FilterListId)
|
||||
.Select(fl => fl.FilterListId)
|
||||
})
|
||||
))
|
||||
.TagWith(nameof(GetLanguages))
|
||||
);
|
||||
|
||||
public sealed record Request : IStreamRequest<LanguageVm>;
|
||||
public sealed record Request : IStreamRequest<Response>;
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, LanguageVm>
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
|
||||
{
|
||||
public async IAsyncEnumerable<LanguageVm> Handle(Request request, [EnumeratorCancellation] CancellationToken ct)
|
||||
public async IAsyncEnumerable<Response> Handle(Request request,
|
||||
[EnumeratorCancellation] CancellationToken ct)
|
||||
{
|
||||
await foreach (var language in Query(ctx).WithCancellation(ct)) yield return language;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="Id" example="37">The identifier.</param>
|
||||
/// <param name="Iso6391" example="en">The unique ISO 639-1 code.</param>
|
||||
/// <param name="Name" example="English">The unique ISO name.</param>
|
||||
/// <param name="FilterListIds" example="[ 114, 141 ]">The identifiers of the FilterLists targeted by this Language.</param>
|
||||
[PublicAPI]
|
||||
public sealed record LanguageVm
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier.
|
||||
/// </summary>
|
||||
/// <example>37</example>
|
||||
public short Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique ISO 639-1 code.
|
||||
/// </summary>
|
||||
/// <example>en</example>
|
||||
public required string Iso6391 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique ISO name.
|
||||
/// </summary>
|
||||
/// <example>English</example>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the FilterLists targeted by this Language.
|
||||
/// </summary>
|
||||
/// <example>[ 114, 141 ]</example>
|
||||
public IEnumerable<int> FilterListIds { get; init; } = [];
|
||||
}
|
||||
public sealed record Response(short Id, string Iso6391, string Name, IEnumerable<int> FilterListIds);
|
||||
}
|
||||
|
|
@ -8,80 +8,50 @@ namespace FilterLists.Directory.Application.Queries;
|
|||
|
||||
public static class GetLicenses
|
||||
{
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<LicenseVm>> Query =
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<Response>> Query =
|
||||
EF.CompileAsyncQuery((QueryDbContext ctx) =>
|
||||
ctx.Licenses
|
||||
.Where(l => l.FilterLists.Any())
|
||||
.OrderBy(l => l.Id)
|
||||
.Select(l => new LicenseVm
|
||||
{
|
||||
Id = l.Id,
|
||||
Name = l.Name,
|
||||
Url = l.Url,
|
||||
PermitsModification = l.PermitsModification,
|
||||
PermitsDistribution = l.PermitsDistribution,
|
||||
PermitsCommercialUse = l.PermitsCommercialUse,
|
||||
FilterListIds = l.FilterLists
|
||||
.Select(l => new Response(
|
||||
l.Id,
|
||||
l.Name,
|
||||
l.Url,
|
||||
l.PermitsModification,
|
||||
l.PermitsDistribution,
|
||||
l.PermitsCommercialUse,
|
||||
l.FilterLists
|
||||
.OrderBy(f => f.Id)
|
||||
.Select(f => f.Id)
|
||||
})
|
||||
))
|
||||
.TagWith(nameof(GetLicenses))
|
||||
);
|
||||
|
||||
public sealed record Request : IStreamRequest<LicenseVm>;
|
||||
public sealed record Request : IStreamRequest<Response>;
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, LicenseVm>
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
|
||||
{
|
||||
public async IAsyncEnumerable<LicenseVm> Handle(Request request, [EnumeratorCancellation] CancellationToken ct)
|
||||
public async IAsyncEnumerable<Response> Handle(Request request, [EnumeratorCancellation] CancellationToken ct)
|
||||
{
|
||||
await foreach (var license in Query(ctx).WithCancellation(ct)) yield return license;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="Id" example="5">The identifier.</param>
|
||||
/// <param name="Name" example="All Rights Reserved">The unique name.</param>
|
||||
/// <param name="Url" example="https://en.wikipedia.org/wiki/All_rights_reserved">The URL of the home page.</param>
|
||||
/// <param name="PermitsModification" example="false">If the License permits modification.</param>
|
||||
/// <param name="PermitsDistribution" example="false">If the License permits distribution.</param>
|
||||
/// <param name="PermitsCommercialUse" example="false">If the License permits commercial use.</param>
|
||||
/// <param name="FilterListIds" example="[ 6, 31 ]">The identifiers of the FilterLists released under this License.</param>
|
||||
[PublicAPI]
|
||||
public sealed record LicenseVm
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier.
|
||||
/// </summary>
|
||||
/// <example>5</example>
|
||||
public int Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique name.
|
||||
/// </summary>
|
||||
/// <example>All Rights Reserved</example>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the home page.
|
||||
/// </summary>
|
||||
/// <example>https://en.wikipedia.org/wiki/All_rights_reserved</example>
|
||||
public Uri? Url { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// If the License permits modification.
|
||||
/// </summary>
|
||||
/// <example>false</example>
|
||||
public bool PermitsModification { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// If the License permits distribution.
|
||||
/// </summary>
|
||||
/// <example>false</example>
|
||||
public bool PermitsDistribution { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// If the License permits commercial use.
|
||||
/// </summary>
|
||||
/// <example>false</example>
|
||||
public bool PermitsCommercialUse { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the FilterLists released under this License.
|
||||
/// </summary>
|
||||
/// <example>[ 6, 31 ]</example>
|
||||
public IEnumerable<int> FilterListIds { get; init; } = [];
|
||||
}
|
||||
public sealed record Response(
|
||||
int Id,
|
||||
string Name,
|
||||
Uri? Url,
|
||||
bool PermitsModification,
|
||||
bool PermitsDistribution,
|
||||
bool PermitsCommercialUse,
|
||||
IEnumerable<int> FilterListIds);
|
||||
}
|
||||
|
|
@ -2,16 +2,15 @@
|
|||
using JetBrains.Annotations;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using static FilterLists.Directory.Application.Queries.GetListDetails.ListDetailsVm;
|
||||
|
||||
namespace FilterLists.Directory.Application.Queries;
|
||||
|
||||
public static class GetListDetails
|
||||
{
|
||||
private static readonly Func<QueryDbContext, int, Task<ListDetailsVm?>> Query =
|
||||
private static readonly Func<QueryDbContext, int, Task<Response?>> Query =
|
||||
EF.CompileAsyncQuery((QueryDbContext ctx, int id) =>
|
||||
ctx.FilterLists
|
||||
.Select(f => new ListDetailsVm
|
||||
.Select(f => new Response
|
||||
{
|
||||
Id = f.Id,
|
||||
Name = f.Name,
|
||||
|
|
@ -29,12 +28,12 @@ public static class GetListDetails
|
|||
ViewUrls = f.ViewUrls
|
||||
.OrderBy(u => u.SegmentNumber)
|
||||
.ThenBy(u => u.Primariness)
|
||||
.Select(u => new ViewUrlVm
|
||||
{
|
||||
SegmentNumber = u.SegmentNumber,
|
||||
Primariness = u.Primariness,
|
||||
Url = u.Url
|
||||
}),
|
||||
.Select(u => new ViewUrlResponse
|
||||
(
|
||||
u.SegmentNumber,
|
||||
u.Primariness,
|
||||
u.Url
|
||||
)),
|
||||
HomeUrl = f.HomeUrl,
|
||||
OnionUrl = f.OnionUrl,
|
||||
PolicyUrl = f.PolicyUrl,
|
||||
|
|
@ -69,19 +68,20 @@ public static class GetListDetails
|
|||
.TagWith(nameof(GetListDetails))
|
||||
.SingleOrDefault(f => f.Id == id));
|
||||
|
||||
public sealed record Request(int Id) : IRequest<ListDetailsVm?>;
|
||||
public sealed record Request(int Id) : IRequest<Response?>;
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed class Handler(QueryDbContext ctx) : IRequestHandler<Request, ListDetailsVm?>
|
||||
private sealed class Handler(QueryDbContext ctx) : IRequestHandler<Request, Response?>
|
||||
{
|
||||
public Task<ListDetailsVm?> Handle(Request request, CancellationToken _)
|
||||
public Task<Response?> Handle(Request request, CancellationToken _)
|
||||
{
|
||||
return Query(ctx, request.Id);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor to primary ctor syntax while allowing EF Core query compilation to succeed
|
||||
[PublicAPI]
|
||||
public sealed record ListDetailsVm
|
||||
public sealed record Response
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier.
|
||||
|
|
@ -132,7 +132,7 @@ public sealed record ListDetailsVm
|
|||
/// <summary>
|
||||
/// The view URLs.
|
||||
/// </summary>
|
||||
public IEnumerable<ViewUrlVm> ViewUrls { get; init; } = [];
|
||||
public IEnumerable<ViewUrlResponse> ViewUrls { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the home page.
|
||||
|
|
@ -229,27 +229,14 @@ public sealed record ListDetailsVm
|
|||
/// </summary>
|
||||
/// <example>[]</example>
|
||||
public IEnumerable<int> DependentFilterListIds { get; init; } = [];
|
||||
|
||||
[PublicAPI]
|
||||
public sealed record ViewUrlVm
|
||||
{
|
||||
/// <summary>
|
||||
/// The segment number of the URL for the FilterList (for multi-part lists).
|
||||
/// </summary>
|
||||
/// <example>1</example>
|
||||
public short SegmentNumber { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// How primary the URL is for the FilterList segment (1 is original, 2+ is a mirror; unique per SegmentNumber)
|
||||
/// </summary>
|
||||
/// <example>1</example>
|
||||
public short Primariness { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The view URL.
|
||||
/// </summary>
|
||||
/// <example>https://easylist.to/easylist/easylist.txt</example>
|
||||
public required Uri Url { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="SegmentNumber" example="1">The segment number of the URL for the FilterList (for multi-part lists).</param>
|
||||
/// <param name="Primariness" example="1">
|
||||
/// How primary the URL is for the FilterList segment (1 is original, 2+ is a mirror;
|
||||
/// unique per SegmentNumber)
|
||||
/// </param>
|
||||
/// <param name="Url" example="https://easylist.to/easylist/easylist.txt">The view URL.</param>
|
||||
[PublicAPI]
|
||||
public sealed record ViewUrlResponse(short SegmentNumber, short Primariness, Uri Url);
|
||||
}
|
||||
|
|
@ -8,107 +8,69 @@ namespace FilterLists.Directory.Application.Queries;
|
|||
|
||||
public static class GetLists
|
||||
{
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<ListVm>> Query =
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<Response>> Query =
|
||||
EF.CompileAsyncQuery((QueryDbContext ctx) =>
|
||||
ctx.FilterLists
|
||||
.OrderBy(f => f.Id)
|
||||
.Select(f => new ListVm
|
||||
{
|
||||
Id = f.Id,
|
||||
Name = f.Name,
|
||||
Description = f.Description,
|
||||
LicenseId = f.LicenseId,
|
||||
SyntaxIds = f.FilterListSyntaxes
|
||||
.Select(f => new Response
|
||||
(
|
||||
f.Id,
|
||||
f.Name,
|
||||
f.Description,
|
||||
f.LicenseId,
|
||||
f.FilterListSyntaxes
|
||||
.OrderBy(fs => fs.SyntaxId)
|
||||
.Select(fs => fs.SyntaxId),
|
||||
LanguageIds = f.FilterListLanguages
|
||||
f.FilterListLanguages
|
||||
.OrderBy(fl => fl.LanguageId)
|
||||
.Select(fl => fl.LanguageId),
|
||||
TagIds = f.FilterListTags
|
||||
f.FilterListTags
|
||||
.OrderBy(ft => ft.TagId)
|
||||
.Select(ft => ft.TagId),
|
||||
PrimaryViewUrl = f.ViewUrls
|
||||
f.ViewUrls
|
||||
.OrderBy(u => u.SegmentNumber)
|
||||
.ThenBy(u => u.Primariness)
|
||||
.Select(u => u.Url)
|
||||
.FirstOrDefault(),
|
||||
MaintainerIds = f.FilterListMaintainers
|
||||
f.FilterListMaintainers
|
||||
.OrderBy(fm => fm.MaintainerId)
|
||||
.Select(fm => fm.MaintainerId)
|
||||
})
|
||||
))
|
||||
.TagWith(nameof(GetLists))
|
||||
);
|
||||
|
||||
public sealed record Request : IStreamRequest<ListVm>;
|
||||
public sealed record Request : IStreamRequest<Response>;
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, ListVm>
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
|
||||
{
|
||||
public async IAsyncEnumerable<ListVm> Handle(Request request, [EnumeratorCancellation] CancellationToken ct)
|
||||
public async IAsyncEnumerable<Response> Handle(Request request, [EnumeratorCancellation] CancellationToken ct)
|
||||
{
|
||||
await foreach (var list in Query(ctx).WithCancellation(ct)) yield return list;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="Id" example="301">The identifier.</param>
|
||||
/// <param name="Name" example="EasyList">The unique name in title case.</param>
|
||||
/// <param name="Description"
|
||||
/// example="EasyList is the primary filter list that removes most adverts from international web pages...">
|
||||
/// The brief description in English (preferably quoted from the project).
|
||||
/// </param>
|
||||
/// <param name="LicenseId" example="4">The identifier of the License under which this FilterList is released.</param>
|
||||
/// <param name="SyntaxIds" example="[ 3 ]">The identifiers of the Syntaxes implemented by this FilterList.</param>
|
||||
/// <param name="LanguageIds" example="[ 37 ]">The identifiers of the Languages targeted by this FilterList.</param>
|
||||
/// <param name="TagIds" example="[ 2 ]">The identifiers of the Tags applied to this FilterList.</param>
|
||||
/// <param name="PrimaryViewUrl" example="https://easylist.to/easylist/easylist.txt">The primary view URL.</param>
|
||||
/// <param name="MaintainerIds" example="[ 7 ]">The identifiers of the Maintainers of this FilterList.</param>
|
||||
[PublicAPI]
|
||||
public sealed record ListVm
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier.
|
||||
/// </summary>
|
||||
/// <example>301</example>
|
||||
public int Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique name in title case.
|
||||
/// </summary>
|
||||
/// <example>EasyList</example>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The brief description in English (preferably quoted from the project).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// EasyList is the primary filter list that removes most adverts from international web pages, including unwanted
|
||||
/// frames, images, and objects. It is the most popular list used by many ad blockers and forms the basis of over a
|
||||
/// dozen combination and supplementary filter lists.
|
||||
/// </example>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifier of the License under which this FilterList is released.
|
||||
/// </summary>
|
||||
/// <example>4</example>
|
||||
public int LicenseId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the Syntaxes implemented by this FilterList.
|
||||
/// </summary>
|
||||
/// <example>[ 3 ]</example>
|
||||
public IEnumerable<short> SyntaxIds { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the Languages targeted by this FilterList.
|
||||
/// </summary>
|
||||
/// <example>[ 37 ]</example>
|
||||
public IEnumerable<short> LanguageIds { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the Tags applied to this FilterList.
|
||||
/// </summary>
|
||||
/// <example>[ 2 ]</example>
|
||||
public IEnumerable<int> TagIds { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The primary view URL.
|
||||
/// </summary>
|
||||
/// <example>https://easylist.to/easylist/easylist.txt</example>
|
||||
public Uri? PrimaryViewUrl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the Maintainers of this FilterList.
|
||||
/// </summary>
|
||||
/// <example>[ 7 ]</example>
|
||||
public IEnumerable<int> MaintainerIds { get; init; } = [];
|
||||
}
|
||||
public sealed record Response(
|
||||
int Id,
|
||||
string Name,
|
||||
string? Description,
|
||||
int LicenseId,
|
||||
IEnumerable<short> SyntaxIds,
|
||||
IEnumerable<short> LanguageIds,
|
||||
IEnumerable<int> TagIds,
|
||||
Uri? PrimaryViewUrl,
|
||||
IEnumerable<int> MaintainerIds);
|
||||
}
|
||||
|
|
@ -8,74 +8,48 @@ namespace FilterLists.Directory.Application.Queries;
|
|||
|
||||
public static class GetMaintainers
|
||||
{
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<MaintainerVm>> Query =
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<Response>> Query =
|
||||
EF.CompileAsyncQuery((QueryDbContext ctx) =>
|
||||
ctx.Maintainers
|
||||
.Where(m => m.FilterListMaintainers.Any())
|
||||
.OrderBy(m => m.Id)
|
||||
.Select(m => new MaintainerVm
|
||||
{
|
||||
Id = m.Id,
|
||||
Name = m.Name,
|
||||
Url = m.Url,
|
||||
EmailAddress = m.EmailAddress,
|
||||
TwitterHandle = m.TwitterHandle,
|
||||
FilterListIds = m.FilterListMaintainers
|
||||
.Select(m => new Response(
|
||||
m.Id,
|
||||
m.Name,
|
||||
m.Url,
|
||||
m.EmailAddress,
|
||||
m.TwitterHandle,
|
||||
m.FilterListMaintainers
|
||||
.OrderBy(fm => fm.FilterListId)
|
||||
.Select(fm => fm.FilterListId)
|
||||
})
|
||||
))
|
||||
.TagWith(nameof(GetMaintainers))
|
||||
);
|
||||
|
||||
public sealed record Request : IStreamRequest<MaintainerVm>;
|
||||
public sealed record Request : IStreamRequest<Response>;
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, MaintainerVm>
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
|
||||
{
|
||||
public async IAsyncEnumerable<MaintainerVm> Handle(Request request,
|
||||
public async IAsyncEnumerable<Response> Handle(Request request,
|
||||
[EnumeratorCancellation] CancellationToken ct)
|
||||
{
|
||||
await foreach (var maintainer in Query(ctx).WithCancellation(ct)) yield return maintainer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="Id" example="7">The identifier.</param>
|
||||
/// <param name="Name" example="The EasyList Authors">The unique name.</param>
|
||||
/// <param name="Url" example="https://easylist.to/">The URL of the home page.</param>
|
||||
/// <param name="EmailAddress" example="null">The email address.</param>
|
||||
/// <param name="TwitterHandle" example="null">The Twitter handle.</param>
|
||||
/// <param name="FilterListIds" example="[ 11, 13, 301 ]">The identifiers of the FilterLists maintained by this Maintainer.</param>
|
||||
[PublicAPI]
|
||||
public sealed record MaintainerVm
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier.
|
||||
/// </summary>
|
||||
/// <example>7</example>
|
||||
public int Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique name.
|
||||
/// </summary>
|
||||
/// <example>The EasyList Authors</example>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the home page.
|
||||
/// </summary>
|
||||
/// <example>https://easylist.to/</example>
|
||||
public Uri? Url { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The email address.
|
||||
/// </summary>
|
||||
/// <example>null</example>
|
||||
public string? EmailAddress { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The Twitter handle.
|
||||
/// </summary>
|
||||
/// <example>null</example>
|
||||
public string? TwitterHandle { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the FilterLists maintained by this Maintainer.
|
||||
/// </summary>
|
||||
/// <example>[ 11, 13, 301 ]</example>
|
||||
public IEnumerable<int> FilterListIds { get; init; } = [];
|
||||
}
|
||||
public sealed record Response(
|
||||
int Id,
|
||||
string Name,
|
||||
Uri? Url,
|
||||
string? EmailAddress,
|
||||
string? TwitterHandle,
|
||||
IEnumerable<int> FilterListIds);
|
||||
}
|
||||
|
|
@ -8,85 +8,58 @@ namespace FilterLists.Directory.Application.Queries;
|
|||
|
||||
public static class GetSoftware
|
||||
{
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<SoftwareVm>> Query =
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<Response>> Query =
|
||||
EF.CompileAsyncQuery((QueryDbContext ctx) =>
|
||||
ctx.Software
|
||||
.Where(s => s.SoftwareSyntaxes
|
||||
.Any(ss => ss.Syntax.FilterListSyntaxes.Any()))
|
||||
.OrderBy(s => s.Id)
|
||||
.Select(s => new SoftwareVm
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = s.Name,
|
||||
Description = s.Description,
|
||||
HomeUrl = s.HomeUrl,
|
||||
DownloadUrl = s.DownloadUrl,
|
||||
SupportsAbpUrlScheme = s.SupportsAbpUrlScheme,
|
||||
SyntaxIds = s.SoftwareSyntaxes
|
||||
.Select(s => new Response
|
||||
(
|
||||
s.Id,
|
||||
s.Name,
|
||||
s.Description,
|
||||
s.HomeUrl,
|
||||
s.DownloadUrl,
|
||||
s.SupportsAbpUrlScheme,
|
||||
s.SoftwareSyntaxes
|
||||
.OrderBy(ss => ss.SyntaxId)
|
||||
.Select(ss => ss.SyntaxId)
|
||||
})
|
||||
))
|
||||
.TagWith(nameof(GetSoftware))
|
||||
);
|
||||
|
||||
public sealed record Request : IStreamRequest<SoftwareVm>;
|
||||
public sealed record Request : IStreamRequest<Response>;
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, SoftwareVm>
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
|
||||
{
|
||||
public async IAsyncEnumerable<SoftwareVm> Handle(Request request,
|
||||
public async IAsyncEnumerable<Response> Handle(Request request,
|
||||
[EnumeratorCancellation] CancellationToken ct)
|
||||
{
|
||||
await foreach (var software in Query(ctx).WithCancellation(ct)) yield return software;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="Id" example="2">The identifier.</param>
|
||||
/// <param name="Name" example="Adblock Plus">The unique name.</param>
|
||||
/// <param name="Description"
|
||||
/// example="Adblock Plus is a free extension that allows you to customize your web experience...">
|
||||
/// The description.
|
||||
/// </param>
|
||||
/// <param name="HomeUrl" example="https://adblockplus.org/">The URL of the home page.</param>
|
||||
/// <param name="DownloadUrl" example="https://adblockplus.org/">The URL of the Software download.</param>
|
||||
/// <param name="SupportsAbpUrlScheme" example="true">
|
||||
/// If the Software supports the abp: URL scheme to click-to-subscribe to a FilterList.
|
||||
/// </param>
|
||||
/// <param name="SyntaxIds" example="[ 3, 28, 38, 48 ]">The identifiers of the Syntaxes that this Software supports.</param>
|
||||
[PublicAPI]
|
||||
public sealed record SoftwareVm
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier.
|
||||
/// </summary>
|
||||
/// <example>2</example>
|
||||
public long Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique name.
|
||||
/// </summary>
|
||||
/// <example>Adblock Plus</example>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The description.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Adblock Plus is a free extension that allows you to customize your web experience. You can block annoying ads,
|
||||
/// disable tracking and lots more. It’s available for all major desktop browsers and for your mobile devices.
|
||||
/// </example>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the home page.
|
||||
/// </summary>
|
||||
/// <example>https://adblockplus.org/</example>
|
||||
public Uri? HomeUrl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the Software download.
|
||||
/// </summary>
|
||||
/// <example>https://adblockplus.org/</example>
|
||||
public Uri? DownloadUrl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// If the Software supports the abp: URL scheme to click-to-subscribe to a FilterList.
|
||||
/// </summary>
|
||||
/// <example>true</example>
|
||||
public bool SupportsAbpUrlScheme { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the Syntaxes that this Software supports.
|
||||
/// </summary>
|
||||
/// <example>[ 3, 28, 38, 48 ]</example>
|
||||
public IEnumerable<short> SyntaxIds { get; init; } = [];
|
||||
}
|
||||
public sealed record Response(
|
||||
long Id,
|
||||
string Name,
|
||||
string? Description,
|
||||
Uri? HomeUrl,
|
||||
Uri? DownloadUrl,
|
||||
bool SupportsAbpUrlScheme,
|
||||
IEnumerable<short> SyntaxIds);
|
||||
}
|
||||
|
|
@ -8,76 +8,51 @@ namespace FilterLists.Directory.Application.Queries;
|
|||
|
||||
public static class GetSyntaxes
|
||||
{
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<SyntaxVm>> Query =
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<Response>> Query =
|
||||
EF.CompileAsyncQuery((QueryDbContext ctx) =>
|
||||
ctx.Syntaxes
|
||||
.Where(s => s.FilterListSyntaxes.Any())
|
||||
.OrderBy(s => s.Id)
|
||||
.Select(s => new SyntaxVm
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = s.Name,
|
||||
Description = s.Description,
|
||||
Url = s.Url,
|
||||
FilterListIds = s.FilterListSyntaxes
|
||||
.Select(s => new Response
|
||||
(
|
||||
s.Id,
|
||||
s.Name,
|
||||
s.Description,
|
||||
s.Url,
|
||||
s.FilterListSyntaxes
|
||||
.OrderBy(fs => fs.FilterListId)
|
||||
.Select(fs => fs.FilterListId),
|
||||
SoftwareIds = s.SoftwareSyntaxes
|
||||
s.SoftwareSyntaxes
|
||||
.OrderBy(ss => ss.SoftwareId)
|
||||
.Select(ss => ss.SoftwareId)
|
||||
})
|
||||
))
|
||||
.TagWith(nameof(GetSyntaxes))
|
||||
);
|
||||
|
||||
public sealed record Request : IStreamRequest<SyntaxVm>;
|
||||
public sealed record Request : IStreamRequest<Response>;
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, SyntaxVm>
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
|
||||
{
|
||||
public async IAsyncEnumerable<SyntaxVm> Handle(Request request,
|
||||
public async IAsyncEnumerable<Response> Handle(Request request,
|
||||
[EnumeratorCancellation] CancellationToken ct)
|
||||
{
|
||||
await foreach (var syntax in Query(ctx).WithCancellation(ct)) yield return syntax;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="Id" example="3">The identifier.</param>
|
||||
/// <param name="Name" example="Adblock Plus">The unique name.</param>
|
||||
/// <param name="Description" example="null">The description.</param>
|
||||
/// <param name="Url" example="https://adblockplus.org/filters">The URL of the home page.</param>
|
||||
/// <param name="FilterListIds" example="[ 2, 6, 11 ]">The identifiers of the FilterLists implementing this Syntax.</param>
|
||||
/// <param name="SoftwareIds" example="[ 1, 2, 3 ]">The identifiers of the Software that supports this Syntax.</param>
|
||||
[PublicAPI]
|
||||
public sealed record SyntaxVm
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier.
|
||||
/// </summary>
|
||||
/// <example>3</example>
|
||||
public short Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique name.
|
||||
/// </summary>
|
||||
/// <example>Adblock Plus</example>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The description.
|
||||
/// </summary>
|
||||
/// <example>null</example>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the home page.
|
||||
/// </summary>
|
||||
/// <example>https://adblockplus.org/filters</example>
|
||||
public Uri? Url { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the FilterLists implementing this Syntax.
|
||||
/// </summary>
|
||||
/// <example>[ 2, 6, 11 ]</example>
|
||||
public IEnumerable<int> FilterListIds { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the Software that supports this Syntax.
|
||||
/// </summary>
|
||||
/// <example>[ 1, 2, 3 ]</example>
|
||||
public IEnumerable<short> SoftwareIds { get; init; } = [];
|
||||
}
|
||||
public sealed record Response(
|
||||
short Id,
|
||||
string Name,
|
||||
string? Description,
|
||||
Uri? Url,
|
||||
IEnumerable<int> FilterListIds,
|
||||
IEnumerable<short> SoftwareIds);
|
||||
}
|
||||
|
|
@ -8,60 +8,39 @@ namespace FilterLists.Directory.Application.Queries;
|
|||
|
||||
public static class GetTags
|
||||
{
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<TagVm>> Query =
|
||||
private static readonly Func<QueryDbContext, IAsyncEnumerable<Response>> Query =
|
||||
EF.CompileAsyncQuery((QueryDbContext ctx) =>
|
||||
ctx.Tags
|
||||
.Where(t => t.FilterListTags.Any())
|
||||
.OrderBy(t => t.Id)
|
||||
.Select(t => new TagVm
|
||||
{
|
||||
Id = t.Id,
|
||||
Name = t.Name,
|
||||
Description = t.Description,
|
||||
FilterListIds = t.FilterListTags
|
||||
.Select(t => new Response
|
||||
(
|
||||
t.Id,
|
||||
t.Name,
|
||||
t.Description,
|
||||
t.FilterListTags
|
||||
.OrderBy(flt => flt.FilterListId)
|
||||
.Select(flt => flt.FilterListId)
|
||||
})
|
||||
))
|
||||
.TagWith(nameof(GetTags))
|
||||
);
|
||||
|
||||
public sealed record Request : IStreamRequest<TagVm>;
|
||||
public sealed record Request : IStreamRequest<Response>;
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, TagVm>
|
||||
private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler<Request, Response>
|
||||
{
|
||||
public async IAsyncEnumerable<TagVm> Handle(Request request,
|
||||
public async IAsyncEnumerable<Response> Handle(Request request,
|
||||
[EnumeratorCancellation] CancellationToken ct)
|
||||
{
|
||||
await foreach (var tag in Query(ctx).WithCancellation(ct)) yield return tag;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="Id" example="2">The identifier.</param>
|
||||
/// <param name="Name" example="ads">The unique name.</param>
|
||||
/// <param name="Description" example="Blocks advertisements">The description.</param>
|
||||
/// <param name="FilterListIds" example="[ 1, 3, 6 ]">The identifiers of the FilterLists to which this Tag is applied.</param>
|
||||
[PublicAPI]
|
||||
public sealed record TagVm
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier.
|
||||
/// </summary>
|
||||
/// <example>2</example>
|
||||
public int Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique name.
|
||||
/// </summary>
|
||||
/// <example>ads</example>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The description.
|
||||
/// </summary>
|
||||
/// <example>Blocks advertisements</example>
|
||||
public string? Description { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The identifiers of the FilterLists to which this Tag is applied.
|
||||
/// </summary>
|
||||
/// <example>[ 1, 3, 6 ]</example>
|
||||
public IEnumerable<int> FilterListIds { get; init; } = [];
|
||||
}
|
||||
public sealed record Response(int Id, string Name, string? Description, IEnumerable<int> FilterListIds);
|
||||
}
|
||||
Loading…
Reference in a new issue