diff --git a/src/MissionControl.Server/Endpoints/Missions/MissionLaunches/Contract.cs b/src/MissionControl.Server/Endpoints/Missions/MissionLaunches/Contract.cs new file mode 100644 index 0000000..9833634 --- /dev/null +++ b/src/MissionControl.Server/Endpoints/Missions/MissionLaunches/Contract.cs @@ -0,0 +1,23 @@ +using MissionControl.Domain.Entities; + +namespace MissionControl.Server.Endpoints.Missions.MissionLaunches; + +public record MissionLaunchResponse +{ + public Guid Id { get; set; } = Guid.NewGuid(); + + public DateTimeOffset ScheduledFor { get; set; } + + public MissionLaunchStatus Status { get; set; } + + public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow; + + public static MissionLaunchResponse FromDomain(MissionLaunch missionLaunch) => + new() + { + Id = missionLaunch.Id, + ScheduledFor = missionLaunch.ScheduledFor, + Status = missionLaunch.Status, + CreatedAt = missionLaunch.CreatedAt, + }; +} diff --git a/src/MissionControl.Server/Endpoints/Missions/MissionLaunches/CreateMissionLaunch.cs b/src/MissionControl.Server/Endpoints/Missions/MissionLaunches/CreateMissionLaunch.cs new file mode 100644 index 0000000..29bcd9c --- /dev/null +++ b/src/MissionControl.Server/Endpoints/Missions/MissionLaunches/CreateMissionLaunch.cs @@ -0,0 +1,53 @@ +using FastEndpoints; +using FluentValidation; +using Microsoft.AspNetCore.Mvc; +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 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); + } +}