chore: moving towards agentic development
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user