From ea18e35c313c0d69e866c29f6ff13b5e8021233f Mon Sep 17 00:00:00 2001 From: glax Date: Thu, 16 Oct 2025 20:58:28 +0200 Subject: [PATCH] Add ActionsFilter --- API/Controllers/ActionsController.cs | 16 +++++++---- API/Schema/ActionsContext/ActionsContext.cs | 20 +++++++++++++ API/openapi/API_v2.json | 31 +++++++++++++++------ 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/API/Controllers/ActionsController.cs b/API/Controllers/ActionsController.cs index 75294cb..2aee0d5 100644 --- a/API/Controllers/ActionsController.cs +++ b/API/Controllers/ActionsController.cs @@ -26,20 +26,24 @@ public class ActionsController(ActionsContext context) : Controller return TypedResults.Ok(Enum.GetValues()); } - public sealed record Interval(DateTime Start, DateTime End); + public sealed record Filter(DateTime? Start, DateTime? End, string? MangaId, string? ChapterId, ActionsEnum? Action); /// /// Returns performed in /// /// List of performed actions /// Database error - [HttpPost("Interval")] + [HttpPost("Filter")] [ProducesResponseType>(Status200OK, "application/json")] [ProducesResponseType(Status500InternalServerError)] - public async Task>, InternalServerError>> GetActionsInterval([FromBody]Interval interval) + public async Task>, InternalServerError>> GetActionsInterval([FromBody]Filter filter) { - if (await context.Actions.Where(a => a.PerformedAt >= interval.Start && a.PerformedAt <= interval.End) + if (await context.Filter(filter.MangaId, filter.ChapterId) + .Where(a => filter.Start == null || a.PerformedAt >= filter.Start) + .Where(a => filter.End == null || a.PerformedAt >= filter.End) + .Where(a => filter.Action == null || a.Action == filter.Action) .ToListAsync(HttpContext.RequestAborted) is not { } actions) return TypedResults.InternalServerError(); + return TypedResults.Ok(actions.Select(a => new ActionRecord(a))); } @@ -69,7 +73,7 @@ public class ActionsController(ActionsContext context) : Controller [ProducesResponseType(Status500InternalServerError)] public async Task>, InternalServerError>> GetActionsRelatedToManga(string MangaId) { - if(await context.Actions.FromSqlInterpolated($"""SELECT * FROM public."Actions" WHERE "MangaId" = {MangaId}""").ToListAsync(HttpContext.RequestAborted) is not { } actions) + if(await context.FilterManga(MangaId).ToListAsync(HttpContext.RequestAborted) is not { } actions) return TypedResults.InternalServerError(); return TypedResults.Ok(actions.Select(a => new ActionRecord(a))); @@ -85,7 +89,7 @@ public class ActionsController(ActionsContext context) : Controller [ProducesResponseType(Status500InternalServerError)] public async Task>, InternalServerError>> GetActionsRelatedToChapter(string ChapterId) { - if(await context.Actions.FromSqlInterpolated($"""SELECT * FROM public."Actions" WHERE "ChapterId" = {ChapterId}""").ToListAsync(HttpContext.RequestAborted) is not { } actions) + if(await context.FilterChapter(ChapterId).ToListAsync(HttpContext.RequestAborted) is not { } actions) return TypedResults.InternalServerError(); return TypedResults.Ok(actions.Select(a => new ActionRecord(a))); diff --git a/API/Schema/ActionsContext/ActionsContext.cs b/API/Schema/ActionsContext/ActionsContext.cs index fde8ab4..7ea6b88 100644 --- a/API/Schema/ActionsContext/ActionsContext.cs +++ b/API/Schema/ActionsContext/ActionsContext.cs @@ -31,4 +31,24 @@ public class ActionsContext(DbContextOptions options) : TrangaBa modelBuilder.Entity().Property(a => a.MangaId).HasColumnName("MangaId"); } + + public IQueryable FilterManga(string MangaId) => this.Actions + .FromSqlInterpolated($"""SELECT * FROM public."Actions" WHERE "MangaId" = {MangaId}"""); + + public IQueryable FilterChapter(string ChapterId) => this.Actions + .FromSqlInterpolated($"""SELECT * FROM public."Actions" WHERE "ChapterId" = {ChapterId}"""); + + public IQueryable FilterMangaAndChapter(string MangaId, string ChapterId) => this.Actions + .FromSqlInterpolated($"""SELECT * FROM public."Actions" WHERE "MangaId" = {MangaId} AND "ChapterId" = {ChapterId}"""); + + public IQueryable Filter(string? MangaId, string? ChapterId) + { + if (MangaId is { } mangaId && ChapterId is { } chapterId) + return FilterMangaAndChapter(mangaId, chapterId); + if (MangaId is { } mangaId2) + return FilterManga(mangaId2); + if (ChapterId is { } chapterId2) + return FilterChapter(chapterId2); + return this.Actions.AsQueryable(); + } } \ No newline at end of file diff --git a/API/openapi/API_v2.json b/API/openapi/API_v2.json index a7ab9a1..00cde17 100644 --- a/API/openapi/API_v2.json +++ b/API/openapi/API_v2.json @@ -28,32 +28,32 @@ } } }, - "/v2/Actions/Interval": { + "/v2/Actions/Filter": { "post": { "tags": [ "Actions" ], - "summary": "Returns API.Schema.ActionsContext.ActionRecord performed in API.Controllers.ActionsController.Interval", + "summary": "Returns API.Schema.ActionsContext.ActionRecord performed in !:Interval", "requestBody": { "content": { "application/json-patch+json; x-version=2.0": { "schema": { - "$ref": "#/components/schemas/Interval" + "$ref": "#/components/schemas/Filter" } }, "application/json; x-version=2.0": { "schema": { - "$ref": "#/components/schemas/Interval" + "$ref": "#/components/schemas/Filter" } }, "text/json; x-version=2.0": { "schema": { - "$ref": "#/components/schemas/Interval" + "$ref": "#/components/schemas/Filter" } }, "application/*+json; x-version=2.0": { "schema": { - "$ref": "#/components/schemas/Interval" + "$ref": "#/components/schemas/Filter" } } } @@ -3659,16 +3659,29 @@ }, "additionalProperties": false }, - "Interval": { + "Filter": { "type": "object", "properties": { "start": { "type": "string", - "format": "date-time" + "format": "date-time", + "nullable": true }, "end": { "type": "string", - "format": "date-time" + "format": "date-time", + "nullable": true + }, + "mangaId": { + "type": "string", + "nullable": true + }, + "chapterId": { + "type": "string", + "nullable": true + }, + "action": { + "$ref": "#/components/schemas/ActionsEnum" } }, "additionalProperties": false