using FastEndpoints; using Socialize.Api.Data; using Socialize.Api.Infrastructure.Security; using Socialize.Api.Modules.Identity.Contracts; using Socialize.Api.Modules.ReleaseCommunications.Contracts; using Socialize.Api.Modules.ReleaseCommunications.Data; using Socialize.Api.Modules.ReleaseCommunications.Services; namespace Socialize.Api.Modules.ReleaseCommunications.Handlers; internal record CreateDeveloperReleaseUpdateRequest( string Title, string Summary, string? Body, string Category, string Importance, string Audience, string? DeploymentLabel, string? BuildVersion, string? CommitRange); internal class CreateDeveloperReleaseUpdateRequestValidator : Validator { public CreateDeveloperReleaseUpdateRequestValidator() { RuleFor(x => x.Title).NotEmpty().MaximumLength(160); RuleFor(x => x.Summary).NotEmpty().MaximumLength(512); RuleFor(x => x.Body).MaximumLength(8000); RuleFor(x => x.Category).NotEmpty().MaximumLength(32); RuleFor(x => x.Importance).NotEmpty().MaximumLength(32); RuleFor(x => x.Audience).NotEmpty().MaximumLength(32); RuleFor(x => x.DeploymentLabel).MaximumLength(128); RuleFor(x => x.BuildVersion).MaximumLength(128); RuleFor(x => x.CommitRange).MaximumLength(256); } } internal class CreateDeveloperReleaseUpdateHandler(AppDbContext dbContext) : Endpoint { public override void Configure() { Post("/api/developer/release-updates"); Roles(KnownRoles.Developer); Options(o => o.WithTags("Release Communications")); } public override async Task HandleAsync(CreateDeveloperReleaseUpdateRequest request, CancellationToken ct) { if (!TryParseRequest(request, out ReleaseUpdateCategory category, out ReleaseUpdateImportance importance, out ReleaseUpdateAudience audience)) { await SendErrorsAsync(StatusCodes.Status400BadRequest, ct); return; } DateTimeOffset now = DateTimeOffset.UtcNow; ReleaseUpdate update = new() { Id = Guid.NewGuid(), Title = request.Title.Trim(), Summary = request.Summary.Trim(), Body = NormalizeOptional(request.Body), Category = category, Importance = importance, Audience = audience, Status = ReleaseUpdateStatus.Draft, DeploymentLabel = NormalizeOptional(request.DeploymentLabel), BuildVersion = NormalizeOptional(request.BuildVersion), CommitRange = NormalizeOptional(request.CommitRange), CreatedByUserId = User.GetUserId(), CreatedAt = now, UpdatedAt = now, }; dbContext.ReleaseUpdates.Add(update); await dbContext.SaveChangesAsync(ct); await SendAsync(update.ToDto(false), StatusCodes.Status201Created, ct); } private bool TryParseRequest( CreateDeveloperReleaseUpdateRequest request, out ReleaseUpdateCategory category, out ReleaseUpdateImportance importance, out ReleaseUpdateAudience audience) { bool isValid = true; if (!ReleaseUpdateRules.TryParseCategory(request.Category, out category)) { AddError(x => x.Category, "The selected release update category is not valid."); isValid = false; } if (!ReleaseUpdateRules.TryParseImportance(request.Importance, out importance)) { AddError(x => x.Importance, "The selected release update importance is not valid."); isValid = false; } if (!ReleaseUpdateRules.TryParseAudience(request.Audience, out audience)) { AddError(x => x.Audience, "The selected release update audience is not valid."); isValid = false; } return isValid; } private static string? NormalizeOptional(string? value) { string? normalized = value?.Trim(); return string.IsNullOrWhiteSpace(normalized) ? null : normalized; } }