149 lines
5.0 KiB
C#
149 lines
5.0 KiB
C#
using Socialize.Infrastructure.Security;
|
|
using Socialize.Modules.Notifications.Contracts;
|
|
|
|
namespace Socialize.Modules.ContentItems.Handlers;
|
|
|
|
public record CreateContentItemRequest(
|
|
Guid WorkspaceId,
|
|
Guid ClientId,
|
|
Guid ProjectId,
|
|
string Title,
|
|
string PublicationMessage,
|
|
string PublicationTargets,
|
|
string? Hashtags,
|
|
DateTimeOffset? DueDate);
|
|
|
|
public class CreateContentItemRequestValidator
|
|
: Validator<CreateContentItemRequest>
|
|
{
|
|
public CreateContentItemRequestValidator()
|
|
{
|
|
RuleFor(x => x.WorkspaceId).NotEmpty();
|
|
RuleFor(x => x.ClientId).NotEmpty();
|
|
RuleFor(x => x.ProjectId).NotEmpty();
|
|
RuleFor(x => x.Title).NotEmpty().MaximumLength(256);
|
|
RuleFor(x => x.PublicationMessage).NotEmpty().MaximumLength(4000);
|
|
RuleFor(x => x.PublicationTargets).NotEmpty().MaximumLength(512);
|
|
RuleFor(x => x.Hashtags).MaximumLength(1024);
|
|
}
|
|
}
|
|
|
|
public class CreateContentItemHandler(
|
|
AppDbContext dbContext,
|
|
AccessScopeService accessScopeService,
|
|
INotificationEventWriter notificationEventWriter)
|
|
: Endpoint<CreateContentItemRequest, ContentItemDto>
|
|
{
|
|
public override void Configure()
|
|
{
|
|
Post("/api/content-items");
|
|
Options(o => o.WithTags("Content Items"));
|
|
}
|
|
|
|
public override async Task HandleAsync(CreateContentItemRequest request, CancellationToken ct)
|
|
{
|
|
if (!accessScopeService.CanContributeToProject(User, request.WorkspaceId, request.ClientId, request.ProjectId))
|
|
{
|
|
await SendForbiddenAsync(ct);
|
|
return;
|
|
}
|
|
|
|
bool workspaceExists = await dbContext.Workspaces
|
|
.AnyAsync(workspace => workspace.Id == request.WorkspaceId, ct);
|
|
|
|
if (!workspaceExists)
|
|
{
|
|
AddError(request => request.WorkspaceId, "The selected workspace does not exist.");
|
|
await SendErrorsAsync(StatusCodes.Status400BadRequest, ct);
|
|
return;
|
|
}
|
|
|
|
bool clientExists = await dbContext.Clients
|
|
.AnyAsync(
|
|
client => client.Id == request.ClientId && client.WorkspaceId == request.WorkspaceId,
|
|
ct);
|
|
|
|
if (!clientExists)
|
|
{
|
|
AddError(request => request.ClientId, "The selected client does not belong to the active workspace.");
|
|
await SendErrorsAsync(StatusCodes.Status400BadRequest, ct);
|
|
return;
|
|
}
|
|
|
|
bool projectExists = await dbContext.Projects
|
|
.AnyAsync(
|
|
project => project.Id == request.ProjectId &&
|
|
project.WorkspaceId == request.WorkspaceId &&
|
|
project.ClientId == request.ClientId,
|
|
ct);
|
|
|
|
if (!projectExists)
|
|
{
|
|
AddError(request => request.ProjectId, "The selected project does not belong to the selected client.");
|
|
await SendErrorsAsync(StatusCodes.Status400BadRequest, ct);
|
|
return;
|
|
}
|
|
|
|
ContentItem item = new()
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
WorkspaceId = request.WorkspaceId,
|
|
ClientId = request.ClientId,
|
|
ProjectId = request.ProjectId,
|
|
Title = request.Title.Trim(),
|
|
PublicationMessage = request.PublicationMessage.Trim(),
|
|
PublicationTargets = request.PublicationTargets.Trim(),
|
|
Hashtags = string.IsNullOrWhiteSpace(request.Hashtags) ? null : request.Hashtags.Trim(),
|
|
Status = "Draft",
|
|
DueDate = request.DueDate,
|
|
CurrentRevisionLabel = "v1",
|
|
CurrentRevisionNumber = 1,
|
|
CreatedAt = DateTimeOffset.UtcNow,
|
|
};
|
|
|
|
dbContext.ContentItems.Add(item);
|
|
dbContext.ContentItemRevisions.Add(new ContentItemRevision
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
ContentItemId = item.Id,
|
|
RevisionNumber = 1,
|
|
RevisionLabel = "v1",
|
|
Title = item.Title,
|
|
PublicationMessage = item.PublicationMessage,
|
|
PublicationTargets = item.PublicationTargets,
|
|
Hashtags = item.Hashtags,
|
|
CreatedAt = DateTimeOffset.UtcNow,
|
|
});
|
|
await dbContext.SaveChangesAsync(ct);
|
|
|
|
await notificationEventWriter.WriteAsync(
|
|
new NotificationEventWriteModel(
|
|
item.WorkspaceId,
|
|
item.Id,
|
|
"content-item.created",
|
|
"ContentItem",
|
|
item.Id,
|
|
$"Content item {item.Title} was created.",
|
|
null,
|
|
null,
|
|
$$"""{"status":"{{item.Status}}","revisionLabel":"{{item.CurrentRevisionLabel}}"}"""),
|
|
ct);
|
|
|
|
ContentItemDto dto = new(
|
|
item.Id,
|
|
item.WorkspaceId,
|
|
item.ClientId,
|
|
item.ProjectId,
|
|
item.Title,
|
|
item.PublicationMessage,
|
|
item.PublicationTargets,
|
|
item.Hashtags,
|
|
item.Status,
|
|
item.DueDate,
|
|
item.CurrentRevisionLabel,
|
|
item.CurrentRevisionNumber);
|
|
|
|
await SendAsync(dto, StatusCodes.Status201Created, ct);
|
|
}
|
|
}
|