Add ActionsFilter

This commit is contained in:
2025-10-16 20:58:28 +02:00
parent c2b0c0d02b
commit ea18e35c31
3 changed files with 52 additions and 15 deletions

View File

@@ -26,20 +26,24 @@ public class ActionsController(ActionsContext context) : Controller
return TypedResults.Ok(Enum.GetValues<ActionsEnum>()); return TypedResults.Ok(Enum.GetValues<ActionsEnum>());
} }
public sealed record Interval(DateTime Start, DateTime End); public sealed record Filter(DateTime? Start, DateTime? End, string? MangaId, string? ChapterId, ActionsEnum? Action);
/// <summary> /// <summary>
/// Returns <see cref="Schema.ActionsContext.ActionRecord"/> performed in <see cref="Interval"/> /// Returns <see cref="Schema.ActionsContext.ActionRecord"/> performed in <see cref="Interval"/>
/// </summary> /// </summary>
/// <response code="200">List of performed actions</response> /// <response code="200">List of performed actions</response>
/// <response code="500">Database error</response> /// <response code="500">Database error</response>
[HttpPost("Interval")] [HttpPost("Filter")]
[ProducesResponseType<IEnumerable<ActionRecord>>(Status200OK, "application/json")] [ProducesResponseType<IEnumerable<ActionRecord>>(Status200OK, "application/json")]
[ProducesResponseType(Status500InternalServerError)] [ProducesResponseType(Status500InternalServerError)]
public async Task<Results<Ok<IEnumerable<ActionRecord>>, InternalServerError>> GetActionsInterval([FromBody]Interval interval) public async Task<Results<Ok<IEnumerable<ActionRecord>>, 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) .ToListAsync(HttpContext.RequestAborted) is not { } actions)
return TypedResults.InternalServerError(); return TypedResults.InternalServerError();
return TypedResults.Ok(actions.Select(a => new ActionRecord(a))); return TypedResults.Ok(actions.Select(a => new ActionRecord(a)));
} }
@@ -69,7 +73,7 @@ public class ActionsController(ActionsContext context) : Controller
[ProducesResponseType(Status500InternalServerError)] [ProducesResponseType(Status500InternalServerError)]
public async Task<Results<Ok<IEnumerable<ActionRecord>>, InternalServerError>> GetActionsRelatedToManga(string MangaId) public async Task<Results<Ok<IEnumerable<ActionRecord>>, 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.InternalServerError();
return TypedResults.Ok(actions.Select(a => new ActionRecord(a))); return TypedResults.Ok(actions.Select(a => new ActionRecord(a)));
@@ -85,7 +89,7 @@ public class ActionsController(ActionsContext context) : Controller
[ProducesResponseType(Status500InternalServerError)] [ProducesResponseType(Status500InternalServerError)]
public async Task<Results<Ok<IEnumerable<ActionRecord>>, InternalServerError>> GetActionsRelatedToChapter(string ChapterId) public async Task<Results<Ok<IEnumerable<ActionRecord>>, 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.InternalServerError();
return TypedResults.Ok(actions.Select(a => new ActionRecord(a))); return TypedResults.Ok(actions.Select(a => new ActionRecord(a)));

View File

@@ -31,4 +31,24 @@ public class ActionsContext(DbContextOptions<ActionsContext> options) : TrangaBa
modelBuilder.Entity<LibraryMovedActionRecord>().Property(a => a.MangaId).HasColumnName("MangaId"); modelBuilder.Entity<LibraryMovedActionRecord>().Property(a => a.MangaId).HasColumnName("MangaId");
} }
public IQueryable<ActionRecord> FilterManga(string MangaId) => this.Actions
.FromSqlInterpolated($"""SELECT * FROM public."Actions" WHERE "MangaId" = {MangaId}""");
public IQueryable<ActionRecord> FilterChapter(string ChapterId) => this.Actions
.FromSqlInterpolated($"""SELECT * FROM public."Actions" WHERE "ChapterId" = {ChapterId}""");
public IQueryable<ActionRecord> FilterMangaAndChapter(string MangaId, string ChapterId) => this.Actions
.FromSqlInterpolated($"""SELECT * FROM public."Actions" WHERE "MangaId" = {MangaId} AND "ChapterId" = {ChapterId}""");
public IQueryable<ActionRecord> 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();
}
} }

View File

@@ -28,32 +28,32 @@
} }
} }
}, },
"/v2/Actions/Interval": { "/v2/Actions/Filter": {
"post": { "post": {
"tags": [ "tags": [
"Actions" "Actions"
], ],
"summary": "Returns API.Schema.ActionsContext.ActionRecord performed in API.Controllers.ActionsController.Interval", "summary": "Returns API.Schema.ActionsContext.ActionRecord performed in !:Interval",
"requestBody": { "requestBody": {
"content": { "content": {
"application/json-patch+json; x-version=2.0": { "application/json-patch+json; x-version=2.0": {
"schema": { "schema": {
"$ref": "#/components/schemas/Interval" "$ref": "#/components/schemas/Filter"
} }
}, },
"application/json; x-version=2.0": { "application/json; x-version=2.0": {
"schema": { "schema": {
"$ref": "#/components/schemas/Interval" "$ref": "#/components/schemas/Filter"
} }
}, },
"text/json; x-version=2.0": { "text/json; x-version=2.0": {
"schema": { "schema": {
"$ref": "#/components/schemas/Interval" "$ref": "#/components/schemas/Filter"
} }
}, },
"application/*+json; x-version=2.0": { "application/*+json; x-version=2.0": {
"schema": { "schema": {
"$ref": "#/components/schemas/Interval" "$ref": "#/components/schemas/Filter"
} }
} }
} }
@@ -3659,16 +3659,29 @@
}, },
"additionalProperties": false "additionalProperties": false
}, },
"Interval": { "Filter": {
"type": "object", "type": "object",
"properties": { "properties": {
"start": { "start": {
"type": "string", "type": "string",
"format": "date-time" "format": "date-time",
"nullable": true
}, },
"end": { "end": {
"type": "string", "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 "additionalProperties": false