From 55530f482d21077f3b6fdb92e9f7bf39937e8ee8 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Sat, 28 Sep 2024 01:13:25 +0200 Subject: [PATCH] [backend/api] Add pagination data to note likes and note renotes responses --- .../Controllers/Web/NoteController.cs | 74 ++++++++++--------- .../Core/Database/Tables/NoteLike.cs | 2 +- .../Core/Extensions/QueryableExtensions.cs | 7 ++ .../Schemas/Web/PaginationWrapper.cs | 7 -- 4 files changed, 48 insertions(+), 42 deletions(-) delete mode 100644 Iceshrimp.Shared/Schemas/Web/PaginationWrapper.cs diff --git a/Iceshrimp.Backend/Controllers/Web/NoteController.cs b/Iceshrimp.Backend/Controllers/Web/NoteController.cs index 06b35d5f..0ee309ff 100644 --- a/Iceshrimp.Backend/Controllers/Web/NoteController.cs +++ b/Iceshrimp.Backend/Controllers/Web/NoteController.cs @@ -213,28 +213,31 @@ public class NoteController( return new ValueResponse(success ? --note.LikeCount : note.LikeCount); } - + [HttpGet("{id}/likes")] [Authenticate] [Authorize] + [LinkPagination(20, 40)] [ProducesResults(HttpStatusCode.OK)] [ProducesErrors(HttpStatusCode.NotFound)] - public async Task> GetNoteLikes(string id) + public async Task> GetNoteLikes(string id, PaginationQuery pq) { var user = HttpContext.GetUser(); var note = await db.Notes - .Where(p => p.Id == id) - .EnsureVisibleFor(user) - .FirstOrDefaultAsync() ?? - throw GracefulException.NotFound("Note not found"); + .Where(p => p.Id == id) + .EnsureVisibleFor(user) + .FirstOrDefaultAsync() ?? + throw GracefulException.NotFound("Note not found"); var users = await db.NoteLikes - .Where(p => p.Note == note) - .Include(p => p.User.UserProfile) - .Select(p => p.User) - .ToListAsync(); + .Where(p => p.Note == note) + .Include(p => p.User.UserProfile) + .Paginate(pq, ControllerContext) + .Wrap(p => p.User) + .ToListAsync(); - return await userRenderer.RenderMany(users); + HttpContext.SetPaginationData(users); + return await userRenderer.RenderMany(users.Select(p => p.Entity)); } [HttpPost("{id}/renote")] @@ -278,26 +281,29 @@ public class NoteController( [HttpGet("{id}/renotes")] [Authenticate] [Authorize] + [LinkPagination(20, 40)] [ProducesResults(HttpStatusCode.OK)] [ProducesErrors(HttpStatusCode.NotFound)] - public async Task> GetRenotes(string id) + public async Task> GetRenotes(string id, PaginationQuery pq) { var user = HttpContext.GetUser(); var note = await db.Notes - .Where(p => p.Id == id) - .EnsureVisibleFor(user) - .FirstOrDefaultAsync() ?? - throw GracefulException.NotFound("Note not found"); + .Where(p => p.Id == id) + .EnsureVisibleFor(user) + .FirstOrDefaultAsync() ?? + throw GracefulException.NotFound("Note not found"); var users = await db.Notes - .Where(p => p.Renote == note && p.IsPureRenote) - .EnsureVisibleFor(user) - .Include(p => p.User.UserProfile) - .FilterHidden(user, db) - .Select(p => p.User) - .ToListAsync(); + .Where(p => p.Renote == note && p.IsPureRenote) + .EnsureVisibleFor(user) + .Include(p => p.User.UserProfile) + .FilterHidden(user, db) + .Paginate(pq, ControllerContext) + .Wrap(p => p.User) + .ToListAsync(); - return await userRenderer.RenderMany(users); + HttpContext.SetPaginationData(users); + return await userRenderer.RenderMany(users.Select(p => p.Entity)); } [HttpGet("{id}/quotes")] @@ -311,21 +317,21 @@ public class NoteController( var user = HttpContext.GetUser(); var note = await db.Notes - .Where(p => p.Id == id) - .EnsureVisibleFor(user) - .FirstOrDefaultAsync() ?? - throw GracefulException.NotFound("Note not found"); + .Where(p => p.Id == id) + .EnsureVisibleFor(user) + .FirstOrDefaultAsync() ?? + throw GracefulException.NotFound("Note not found"); var renotes = await db.Notes - .Where(p => p.Renote == note && p.IsQuote) - .Include(p => p.User.UserProfile) - .EnsureVisibleFor(user) - .FilterHidden(user, db) - .Paginate(pq, ControllerContext) - .ToListAsync(); + .Where(p => p.Renote == note && p.IsQuote) + .Include(p => p.User.UserProfile) + .EnsureVisibleFor(user) + .FilterHidden(user, db) + .Paginate(pq, ControllerContext) + .ToListAsync(); return await noteRenderer.RenderMany(renotes.EnforceRenoteReplyVisibility(), user, - Filter.FilterContext.Threads); + Filter.FilterContext.Threads); } [HttpPost("{id}/react/{name}")] diff --git a/Iceshrimp.Backend/Core/Database/Tables/NoteLike.cs b/Iceshrimp.Backend/Core/Database/Tables/NoteLike.cs index 42985325..49de3e26 100644 --- a/Iceshrimp.Backend/Core/Database/Tables/NoteLike.cs +++ b/Iceshrimp.Backend/Core/Database/Tables/NoteLike.cs @@ -8,7 +8,7 @@ namespace Iceshrimp.Backend.Core.Database.Tables; [Index(nameof(UserId))] [Index(nameof(NoteId))] [Index(nameof(UserId), nameof(NoteId), IsUnique = true)] -public class NoteLike +public class NoteLike : IEntity { [Key] [Column("id")] diff --git a/Iceshrimp.Backend/Core/Extensions/QueryableExtensions.cs b/Iceshrimp.Backend/Core/Extensions/QueryableExtensions.cs index 70212b56..2cce4540 100644 --- a/Iceshrimp.Backend/Core/Extensions/QueryableExtensions.cs +++ b/Iceshrimp.Backend/Core/Extensions/QueryableExtensions.cs @@ -360,6 +360,13 @@ public static class QueryableExtensions return Paginate(query, pq, filter.DefaultLimit, filter.MaxLimit); } + public static IQueryable> Wrap( + this IQueryable query, Expression> predicate + ) where TSource : IEntity + { + return query.Select(p => new EntityWrapper { Id = p.Id, Entity = predicate.Compile().Invoke(p) }); + } + public static IQueryable HasVisibility(this IQueryable query, Note.NoteVisibility visibility) { return query.Where(note => note.Visibility == visibility); diff --git a/Iceshrimp.Shared/Schemas/Web/PaginationWrapper.cs b/Iceshrimp.Shared/Schemas/Web/PaginationWrapper.cs deleted file mode 100644 index eebfa5b0..00000000 --- a/Iceshrimp.Shared/Schemas/Web/PaginationWrapper.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Iceshrimp.Shared.Schemas.Web; - -public class PaginationWrapper -{ - public required string Id { get; set; } - public required T Entity { get; set; } -} \ No newline at end of file