using FluentValidation; using FluentValidation.Results; using Microsoft.EntityFrameworkCore; using Vegasco.Server.Api.Authentication; using Vegasco.Server.Api.Common; using Vegasco.Server.Api.Persistence; using Vegasco.Server.Api.Users; namespace Vegasco.Server.Api.Cars; public static class CreateCar { public record Request(string Name); public record Response(Guid Id, string Name); public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder) { return builder .MapPost("cars", Endpoint) .WithTags("Cars") .WithDescription("Creates a new car") .Produces(201) .ProducesValidationProblem() .Produces(409); } public class Validator : AbstractValidator { public Validator() { RuleFor(x => x.Name) .NotEmpty() .MaximumLength(CarTableConfiguration.NameMaxLength); } } private static async Task Endpoint( Request request, IEnumerable> validators, ApplicationDbContext dbContext, UserAccessor userAccessor, ILoggerFactory loggerFactory, CancellationToken cancellationToken) { ILogger logger = loggerFactory.CreateLogger(typeof(CreateCar)); List failedValidations = await validators.ValidateAllAsync(request, cancellationToken: cancellationToken); if (failedValidations.Count > 0) { logger.LogDebug( "Validation failed for request {@Request} with errors {@Errors}", request, failedValidations .Where(x => !x.IsValid) .SelectMany(x => x.Errors) .Select(x => x.ErrorMessage)); return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary())); } bool isDuplicate = await dbContext.Cars .AnyAsync(x => x.Name.ToUpper() == request.Name.ToUpper(), cancellationToken); if (isDuplicate) { logger.LogDebug("Car with name '{CarName}' (case insensitive) already exists", request.Name); return TypedResults.Conflict(); } string userId = userAccessor.GetUserId(); User? user = await dbContext.Users.FindAsync([userId], cancellationToken: cancellationToken); if (user is null) { logger.LogDebug("User with ID '{UserId}' not found, creating new user", userId); user = new User { Id = userId }; await dbContext.Users.AddAsync(user, cancellationToken); } Car car = new() { Name = request.Name.Trim(), UserId = userId }; await dbContext.Cars.AddAsync(car, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); logger.LogTrace("Created new car: {@Car}", car); Response response = new(car.Id.Value, car.Name); return TypedResults.Created($"/v1/cars/{car.Id}", response); } }