121 lines
4.0 KiB
C#
121 lines
4.0 KiB
C#
using Socialize.Infrastructure.Security;
|
|
using Socialize.Modules.Notifications.Contracts;
|
|
|
|
namespace Socialize.Modules.Comments.Handlers;
|
|
|
|
public record CreateCommentRequest(
|
|
Guid WorkspaceId,
|
|
Guid ContentItemId,
|
|
Guid? ParentCommentId,
|
|
string Body);
|
|
|
|
public class CreateCommentRequestValidator
|
|
: Validator<CreateCommentRequest>
|
|
{
|
|
public CreateCommentRequestValidator()
|
|
{
|
|
RuleFor(x => x.WorkspaceId).NotEmpty();
|
|
RuleFor(x => x.ContentItemId).NotEmpty();
|
|
RuleFor(x => x.Body).NotEmpty().MaximumLength(4000);
|
|
}
|
|
}
|
|
|
|
public class CreateCommentHandler(
|
|
AppDbContext dbContext,
|
|
AccessScopeService accessScopeService,
|
|
INotificationEventWriter notificationEventWriter)
|
|
: Endpoint<CreateCommentRequest, CommentDto>
|
|
{
|
|
public override void Configure()
|
|
{
|
|
Post("/api/comments");
|
|
Options(o => o.WithTags("Comments"));
|
|
}
|
|
|
|
public override async Task HandleAsync(CreateCommentRequest request, CancellationToken ct)
|
|
{
|
|
ContentItem? contentItem = await dbContext.ContentItems
|
|
.SingleOrDefaultAsync(
|
|
candidate => candidate.Id == request.ContentItemId && candidate.WorkspaceId == request.WorkspaceId,
|
|
ct);
|
|
|
|
if (contentItem is null)
|
|
{
|
|
AddError(request => request.ContentItemId, "The selected content item does not exist in the active workspace.");
|
|
await SendErrorsAsync(StatusCodes.Status400BadRequest, ct);
|
|
return;
|
|
}
|
|
|
|
if (!accessScopeService.CanReviewContent(User, contentItem.WorkspaceId, contentItem.ClientId, contentItem.ProjectId))
|
|
{
|
|
await SendForbiddenAsync(ct);
|
|
return;
|
|
}
|
|
|
|
if (request.ParentCommentId.HasValue)
|
|
{
|
|
bool parentExists = await dbContext.Comments
|
|
.AnyAsync(
|
|
comment => comment.Id == request.ParentCommentId.Value && comment.ContentItemId == request.ContentItemId,
|
|
ct);
|
|
|
|
if (!parentExists)
|
|
{
|
|
AddError(request => request.ParentCommentId, "The selected parent comment does not exist.");
|
|
await SendErrorsAsync(StatusCodes.Status400BadRequest, ct);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Comment comment = new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
WorkspaceId = request.WorkspaceId,
|
|
ContentItemId = request.ContentItemId,
|
|
ParentCommentId = request.ParentCommentId,
|
|
AuthorUserId = User.GetUserId(),
|
|
AuthorDisplayName = User.GetAlias() ?? User.GetName(),
|
|
AuthorEmail = User.GetEmail(),
|
|
Body = request.Body.Trim(),
|
|
CreatedAt = DateTimeOffset.UtcNow,
|
|
};
|
|
|
|
dbContext.Comments.Add(comment);
|
|
await dbContext.SaveChangesAsync(ct);
|
|
|
|
string? authorPortraitUrl = await dbContext.Users
|
|
.Where(candidate => candidate.Id == comment.AuthorUserId)
|
|
.Select(candidate => candidate.PortraitUrl)
|
|
.SingleOrDefaultAsync(ct);
|
|
|
|
await notificationEventWriter.WriteAsync(
|
|
new NotificationEventWriteModel(
|
|
comment.WorkspaceId,
|
|
comment.ContentItemId,
|
|
"comment.created",
|
|
"Comment",
|
|
comment.Id,
|
|
$"{comment.AuthorDisplayName} commented on {contentItem.Title}.",
|
|
null,
|
|
null,
|
|
$$"""{"parentCommentId":"{{comment.ParentCommentId}}"}"""),
|
|
ct);
|
|
|
|
CommentDto dto = new(
|
|
comment.Id,
|
|
comment.WorkspaceId,
|
|
comment.ContentItemId,
|
|
comment.ParentCommentId,
|
|
comment.AuthorUserId,
|
|
comment.AuthorDisplayName,
|
|
comment.AuthorEmail,
|
|
authorPortraitUrl,
|
|
comment.Body,
|
|
comment.IsResolved,
|
|
comment.CreatedAt,
|
|
comment.ResolvedAt);
|
|
|
|
await SendAsync(dto, StatusCodes.Status201Created, ct);
|
|
}
|
|
}
|