using FastEndpoints; using FluentValidation; using Microsoft.EntityFrameworkCore; using MissionControl.Data; using MissionControl.Domain.Entities; namespace MissionControl.Server.Endpoints.Missions.MissionLaunches; public class CreateMissionLaunch(MissionControlContext dbContext) : Endpoint { public override void Configure() { Post("/missions/{MissionId}/launch"); } public override async Task HandleAsync(CreateMissionLaunchRequest req, CancellationToken ct) { var missionExists = await dbContext.Missions.AnyAsync(mission => mission.Id == req.MissionId, ct); if (!missionExists) { await Send.NotFoundAsync(ct); return; } var missionLaunch = new MissionLaunch { ScheduledFor = req.ScheduledFor, // Validated in `CreateMissionLaunchRequestValidator` Status = Enum.Parse(req.Status, true), MissionId = req.MissionId, }; dbContext.MissionLaunches.Add(missionLaunch); await dbContext.SaveChangesAsync(ct); await Send.OkAsync(MissionLaunchResponse.FromDomain(missionLaunch), ct); } } public record CreateMissionLaunchRequest { public Guid MissionId { get; init; } public required DateTimeOffset ScheduledFor { get; init; } // Define as a string here so it can be safely deserialized into its enum type, with a validation message when unable to do so // Otherwise, we return a serialization error that is not assigned to a specific field public required string Status { get; init; } } public class CreateMissionLaunchRequestValidator : Validator { public CreateMissionLaunchRequestValidator() { RuleFor(req => req.MissionId) .NotEqual(Guid.Empty) .WithMessage("Please provide a non-zero GUID."); RuleFor(req => req.Status).IsEnumName(typeof(MissionLaunchStatus), false); } }