using FastEndpoints; using Microsoft.EntityFrameworkCore; using Socialize.Api.Data; using Socialize.Api.Modules.Comments.Data; using Socialize.Api.Modules.ContentItems.Data; using Socialize.Api.Infrastructure.Security; namespace Socialize.Api.Modules.Comments.Handlers; public record GetCommentsRequest(Guid ContentItemId); public record CommentDto( Guid Id, Guid WorkspaceId, Guid ContentItemId, Guid? ParentCommentId, Guid AuthorUserId, string AuthorDisplayName, string AuthorEmail, string? AuthorPortraitUrl, string Body, DateTimeOffset CreatedAt); public class GetCommentsHandler( AppDbContext dbContext, AccessScopeService accessScopeService) : Endpoint> { public override void Configure() { Get("/api/comments"); Options(o => o.WithTags("Comments")); } public override async Task HandleAsync(GetCommentsRequest request, CancellationToken ct) { ContentItem? item = await dbContext.ContentItems .SingleOrDefaultAsync(candidate => candidate.Id == request.ContentItemId, ct); if (item is null) { await SendNotFoundAsync(ct); return; } if (!await accessScopeService.CanReviewContentAsync(User, item.WorkspaceId, item.ClientId, item.CampaignId, ct)) { await SendForbiddenAsync(ct); return; } List comments = await dbContext.Comments .Where(comment => comment.ContentItemId == request.ContentItemId) .OrderBy(comment => comment.CreatedAt) .ToListAsync(ct); List authorIds = comments .Select(comment => comment.AuthorUserId) .Distinct() .ToList(); Dictionary authorPortraits = await dbContext.Users .Where(user => authorIds.Contains(user.Id)) .ToDictionaryAsync(user => user.Id, user => user.PortraitUrl, ct); List dtos = comments .Select(comment => new CommentDto( comment.Id, comment.WorkspaceId, comment.ContentItemId, comment.ParentCommentId, comment.AuthorUserId, comment.AuthorDisplayName, comment.AuthorEmail, authorPortraits.GetValueOrDefault(comment.AuthorUserId), comment.Body, comment.CreatedAt)) .ToList(); await SendOkAsync(dtos, ct); } }