diff --git a/services/Directory/FilterLists.Directory.Api/Endpoints.cs b/services/Directory/FilterLists.Directory.Api/Endpoints.cs index 7c7afecc6..ea6d71d76 100644 --- a/services/Directory/FilterLists.Directory.Api/Endpoints.cs +++ b/services/Directory/FilterLists.Directory.Api/Endpoints.cs @@ -45,7 +45,7 @@ internal static void MapEndpoints(this WebApplication app) OperationId = nameof(GetLists) }); - app.MapGet("/lists/{id:int}", async Task, NotFound>> + app.MapGet("/lists/{id:int}", async Task, NotFound>> (int id, IMediator mediator, CancellationToken ct) => { var list = await mediator.Send(new Request(id), ct); diff --git a/services/Directory/FilterLists.Directory.Api/OpenApi/OpenApiConfigurationExtension.cs b/services/Directory/FilterLists.Directory.Api/OpenApi/OpenApiConfigurationExtension.cs index e12852a64..c9e88da95 100644 --- a/services/Directory/FilterLists.Directory.Api/OpenApi/OpenApiConfigurationExtension.cs +++ b/services/Directory/FilterLists.Directory.Api/OpenApi/OpenApiConfigurationExtension.cs @@ -33,6 +33,9 @@ internal static void AddOpenApiGen(this IServiceCollection services) // include OpenApiTag Description and ExternalDocs options.DocumentFilter(); + + // allow re-using simple type names like "Response" + options.CustomSchemaIds(s => s.FullName?.Replace("+", ".")); }); } } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetLanguages.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetLanguages.cs index ea81aaf85..ecfef00fd 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetLanguages.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetLanguages.cs @@ -8,59 +8,38 @@ namespace FilterLists.Directory.Application.Queries; public static class GetLanguages { - private static readonly Func> Query = + private static readonly Func> 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; + public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) + public async IAsyncEnumerable Handle(Request request, + [EnumeratorCancellation] CancellationToken ct) { await foreach (var language in Query(ctx).WithCancellation(ct)) yield return language; } } + /// The identifier. + /// The unique ISO 639-1 code. + /// The unique ISO name. + /// The identifiers of the FilterLists targeted by this Language. [PublicAPI] - public sealed record LanguageVm - { - /// - /// The identifier. - /// - /// 37 - public short Id { get; init; } - - /// - /// The unique ISO 639-1 code. - /// - /// en - public required string Iso6391 { get; init; } - - /// - /// The unique ISO name. - /// - /// English - public required string Name { get; init; } - - /// - /// The identifiers of the FilterLists targeted by this Language. - /// - /// [ 114, 141 ] - public IEnumerable FilterListIds { get; init; } = []; - } + public sealed record Response(short Id, string Iso6391, string Name, IEnumerable FilterListIds); } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetLicenses.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetLicenses.cs index 8be9e6cf5..5d4c9ad50 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetLicenses.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetLicenses.cs @@ -8,80 +8,50 @@ namespace FilterLists.Directory.Application.Queries; public static class GetLicenses { - private static readonly Func> Query = + private static readonly Func> 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; + public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { await foreach (var license in Query(ctx).WithCancellation(ct)) yield return license; } } + /// The identifier. + /// The unique name. + /// The URL of the home page. + /// If the License permits modification. + /// If the License permits distribution. + /// If the License permits commercial use. + /// The identifiers of the FilterLists released under this License. [PublicAPI] - public sealed record LicenseVm - { - /// - /// The identifier. - /// - /// 5 - public int Id { get; init; } - - /// - /// The unique name. - /// - /// All Rights Reserved - public required string Name { get; init; } - - /// - /// The URL of the home page. - /// - /// https://en.wikipedia.org/wiki/All_rights_reserved - public Uri? Url { get; init; } - - /// - /// If the License permits modification. - /// - /// false - public bool PermitsModification { get; init; } - - /// - /// If the License permits distribution. - /// - /// false - public bool PermitsDistribution { get; init; } - - /// - /// If the License permits commercial use. - /// - /// false - public bool PermitsCommercialUse { get; init; } - - /// - /// The identifiers of the FilterLists released under this License. - /// - /// [ 6, 31 ] - public IEnumerable FilterListIds { get; init; } = []; - } + public sealed record Response( + int Id, + string Name, + Uri? Url, + bool PermitsModification, + bool PermitsDistribution, + bool PermitsCommercialUse, + IEnumerable FilterListIds); } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetListDetails.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetListDetails.cs index 6d6d9012c..3c20fcd29 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetListDetails.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetListDetails.cs @@ -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> Query = + private static readonly Func> 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; + public sealed record Request(int Id) : IRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IRequestHandler + private sealed class Handler(QueryDbContext ctx) : IRequestHandler { - public Task Handle(Request request, CancellationToken _) + public Task 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 { /// /// The identifier. @@ -132,7 +132,7 @@ public sealed record ListDetailsVm /// /// The view URLs. /// - public IEnumerable ViewUrls { get; init; } = []; + public IEnumerable ViewUrls { get; init; } = []; /// /// The URL of the home page. @@ -229,27 +229,14 @@ public sealed record ListDetailsVm /// /// [] public IEnumerable DependentFilterListIds { get; init; } = []; - - [PublicAPI] - public sealed record ViewUrlVm - { - /// - /// The segment number of the URL for the FilterList (for multi-part lists). - /// - /// 1 - public short SegmentNumber { get; init; } - - /// - /// How primary the URL is for the FilterList segment (1 is original, 2+ is a mirror; unique per SegmentNumber) - /// - /// 1 - public short Primariness { get; init; } - - /// - /// The view URL. - /// - /// https://easylist.to/easylist/easylist.txt - public required Uri Url { get; init; } - } } + + /// The segment number of the URL for the FilterList (for multi-part lists). + /// + /// How primary the URL is for the FilterList segment (1 is original, 2+ is a mirror; + /// unique per SegmentNumber) + /// + /// The view URL. + [PublicAPI] + public sealed record ViewUrlResponse(short SegmentNumber, short Primariness, Uri Url); } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetLists.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetLists.cs index e7d2117e3..303e27f76 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetLists.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetLists.cs @@ -8,107 +8,69 @@ namespace FilterLists.Directory.Application.Queries; public static class GetLists { - private static readonly Func> Query = + private static readonly Func> 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; + public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { await foreach (var list in Query(ctx).WithCancellation(ct)) yield return list; } } + /// The identifier. + /// The unique name in title case. + /// + /// The brief description in English (preferably quoted from the project). + /// + /// The identifier of the License under which this FilterList is released. + /// The identifiers of the Syntaxes implemented by this FilterList. + /// The identifiers of the Languages targeted by this FilterList. + /// The identifiers of the Tags applied to this FilterList. + /// The primary view URL. + /// The identifiers of the Maintainers of this FilterList. [PublicAPI] - public sealed record ListVm - { - /// - /// The identifier. - /// - /// 301 - public int Id { get; init; } - - /// - /// The unique name in title case. - /// - /// EasyList - public required string Name { get; init; } - - /// - /// The brief description in English (preferably quoted from the project). - /// - /// - /// 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. - /// - public string? Description { get; init; } - - /// - /// The identifier of the License under which this FilterList is released. - /// - /// 4 - public int LicenseId { get; init; } - - /// - /// The identifiers of the Syntaxes implemented by this FilterList. - /// - /// [ 3 ] - public IEnumerable SyntaxIds { get; init; } = []; - - /// - /// The identifiers of the Languages targeted by this FilterList. - /// - /// [ 37 ] - public IEnumerable LanguageIds { get; init; } = []; - - /// - /// The identifiers of the Tags applied to this FilterList. - /// - /// [ 2 ] - public IEnumerable TagIds { get; init; } = []; - - /// - /// The primary view URL. - /// - /// https://easylist.to/easylist/easylist.txt - public Uri? PrimaryViewUrl { get; init; } - - /// - /// The identifiers of the Maintainers of this FilterList. - /// - /// [ 7 ] - public IEnumerable MaintainerIds { get; init; } = []; - } + public sealed record Response( + int Id, + string Name, + string? Description, + int LicenseId, + IEnumerable SyntaxIds, + IEnumerable LanguageIds, + IEnumerable TagIds, + Uri? PrimaryViewUrl, + IEnumerable MaintainerIds); } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetMaintainers.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetMaintainers.cs index 25aae99fc..df7be0b43 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetMaintainers.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetMaintainers.cs @@ -8,74 +8,48 @@ namespace FilterLists.Directory.Application.Queries; public static class GetMaintainers { - private static readonly Func> Query = + private static readonly Func> 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; + public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { await foreach (var maintainer in Query(ctx).WithCancellation(ct)) yield return maintainer; } } + /// The identifier. + /// The unique name. + /// The URL of the home page. + /// The email address. + /// The Twitter handle. + /// The identifiers of the FilterLists maintained by this Maintainer. [PublicAPI] - public sealed record MaintainerVm - { - /// - /// The identifier. - /// - /// 7 - public int Id { get; init; } - - /// - /// The unique name. - /// - /// The EasyList Authors - public required string Name { get; init; } - - /// - /// The URL of the home page. - /// - /// https://easylist.to/ - public Uri? Url { get; init; } - - /// - /// The email address. - /// - /// null - public string? EmailAddress { get; init; } - - /// - /// The Twitter handle. - /// - /// null - public string? TwitterHandle { get; init; } - - /// - /// The identifiers of the FilterLists maintained by this Maintainer. - /// - /// [ 11, 13, 301 ] - public IEnumerable FilterListIds { get; init; } = []; - } + public sealed record Response( + int Id, + string Name, + Uri? Url, + string? EmailAddress, + string? TwitterHandle, + IEnumerable FilterListIds); } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetSoftware.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetSoftware.cs index 3413f8447..30121f1b8 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetSoftware.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetSoftware.cs @@ -8,85 +8,58 @@ namespace FilterLists.Directory.Application.Queries; public static class GetSoftware { - private static readonly Func> Query = + private static readonly Func> 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; + public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { await foreach (var software in Query(ctx).WithCancellation(ct)) yield return software; } } + /// The identifier. + /// The unique name. + /// + /// The description. + /// + /// The URL of the home page. + /// The URL of the Software download. + /// + /// If the Software supports the abp: URL scheme to click-to-subscribe to a FilterList. + /// + /// The identifiers of the Syntaxes that this Software supports. [PublicAPI] - public sealed record SoftwareVm - { - /// - /// The identifier. - /// - /// 2 - public long Id { get; init; } - - /// - /// The unique name. - /// - /// Adblock Plus - public required string Name { get; init; } - - /// - /// The description. - /// - /// - /// 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. - /// - public string? Description { get; init; } - - /// - /// The URL of the home page. - /// - /// https://adblockplus.org/ - public Uri? HomeUrl { get; init; } - - /// - /// The URL of the Software download. - /// - /// https://adblockplus.org/ - public Uri? DownloadUrl { get; init; } - - /// - /// If the Software supports the abp: URL scheme to click-to-subscribe to a FilterList. - /// - /// true - public bool SupportsAbpUrlScheme { get; init; } - - /// - /// The identifiers of the Syntaxes that this Software supports. - /// - /// [ 3, 28, 38, 48 ] - public IEnumerable SyntaxIds { get; init; } = []; - } + public sealed record Response( + long Id, + string Name, + string? Description, + Uri? HomeUrl, + Uri? DownloadUrl, + bool SupportsAbpUrlScheme, + IEnumerable SyntaxIds); } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetSyntaxes.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetSyntaxes.cs index e4c66310d..48aac9731 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetSyntaxes.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetSyntaxes.cs @@ -8,76 +8,51 @@ namespace FilterLists.Directory.Application.Queries; public static class GetSyntaxes { - private static readonly Func> Query = + private static readonly Func> 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; + public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { await foreach (var syntax in Query(ctx).WithCancellation(ct)) yield return syntax; } } + /// The identifier. + /// The unique name. + /// The description. + /// The URL of the home page. + /// The identifiers of the FilterLists implementing this Syntax. + /// The identifiers of the Software that supports this Syntax. [PublicAPI] - public sealed record SyntaxVm - { - /// - /// The identifier. - /// - /// 3 - public short Id { get; init; } - - /// - /// The unique name. - /// - /// Adblock Plus - public required string Name { get; init; } - - /// - /// The description. - /// - /// null - public string? Description { get; init; } - - /// - /// The URL of the home page. - /// - /// https://adblockplus.org/filters - public Uri? Url { get; init; } - - /// - /// The identifiers of the FilterLists implementing this Syntax. - /// - /// [ 2, 6, 11 ] - public IEnumerable FilterListIds { get; init; } = []; - - /// - /// The identifiers of the Software that supports this Syntax. - /// - /// [ 1, 2, 3 ] - public IEnumerable SoftwareIds { get; init; } = []; - } + public sealed record Response( + short Id, + string Name, + string? Description, + Uri? Url, + IEnumerable FilterListIds, + IEnumerable SoftwareIds); } \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Application/Queries/GetTags.cs b/services/Directory/FilterLists.Directory.Application/Queries/GetTags.cs index 151b9cd01..a20f6d660 100644 --- a/services/Directory/FilterLists.Directory.Application/Queries/GetTags.cs +++ b/services/Directory/FilterLists.Directory.Application/Queries/GetTags.cs @@ -8,60 +8,39 @@ namespace FilterLists.Directory.Application.Queries; public static class GetTags { - private static readonly Func> Query = + private static readonly Func> 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; + public sealed record Request : IStreamRequest; [UsedImplicitly] - private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler + private sealed class Handler(QueryDbContext ctx) : IStreamRequestHandler { - public async IAsyncEnumerable Handle(Request request, + public async IAsyncEnumerable Handle(Request request, [EnumeratorCancellation] CancellationToken ct) { await foreach (var tag in Query(ctx).WithCancellation(ct)) yield return tag; } } + /// The identifier. + /// The unique name. + /// The description. + /// The identifiers of the FilterLists to which this Tag is applied. [PublicAPI] - public sealed record TagVm - { - /// - /// The identifier. - /// - /// 2 - public int Id { get; init; } - - /// - /// The unique name. - /// - /// ads - public required string Name { get; init; } - - /// - /// The description. - /// - /// Blocks advertisements - public string? Description { get; init; } - - /// - /// The identifiers of the FilterLists to which this Tag is applied. - /// - /// [ 1, 3, 6 ] - public IEnumerable FilterListIds { get; init; } = []; - } + public sealed record Response(int Id, string Name, string? Description, IEnumerable FilterListIds); } \ No newline at end of file