using Hutopy.Infrastructure.Security; using Hutopy.Modules.Creators.Data; using Microsoft.EntityFrameworkCore.Storage; namespace Hutopy.Modules.Creators.Features; [PublicAPI] public record ChangeSlugRequest( Guid CreatorId, Guid SlugReservationId); [PublicAPI] internal sealed class ChangeSlugRequestValidator : Validator { public ChangeSlugRequestValidator() { RuleFor(r => r.CreatorId) .NotNull().WithMessage("You should specify the CreatorId") .NotEmpty().WithMessage("You should specify a valid/not empty CreatorId"); RuleFor(r => r.SlugReservationId) .NotNull().WithMessage("You should specify the SlugReservationId") .NotEmpty().WithMessage("You should specify a valid/not empty SlugReservationId"); } } [PublicAPI] public class ChangeSlugHandler( CreatorsDbContext context) : Endpoint { public override void Configure() { Put("/api/creators/{CreatorId}/slug"); Options(o => o.WithTags("Creators")); } public override async Task HandleAsync( ChangeSlugRequest request, CancellationToken ct) { await using IDbContextTransaction transaction = await context.Database.BeginTransactionAsync(ct); try { Creator creator = await context .Creators .SingleAsync( c => c.Id == request.CreatorId, ct); if (creator.CreatedBy != User.GetUserId()) { await SendUnauthorizedAsync(ct); return; } Slugs? reservation = await context .Slugs .FirstOrDefaultAsync( s => s.Id == request.SlugReservationId, ct); if (reservation is null) { await SendNotFoundAsync(ct); return; } Slugs? previousReservation = await context .Slugs .FirstOrDefaultAsync( s => s.UsedBy == request.CreatorId, ct); if (previousReservation is null) { await SendErrorsAsync(cancellation: ct); return; } context.Remove(previousReservation); reservation.UsedBy = creator.Id; creator.Slug = reservation.NormalizedName; await context.SaveChangesAsync(ct); await transaction.CommitAsync(ct); await SendOkAsync(ct); } catch { await transaction.RollbackAsync(ct); } } }