Files
vegasco/src/Vegasco.Server.Api/Cars/CreateCar.cs
ThompsonNye 84a72a8557
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is failing
Add more logging and trace parameters
2025-07-21 20:58:45 +02:00

99 lines
2.6 KiB
C#

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<Response>(201)
.ProducesValidationProblem()
.Produces(409);
}
public class Validator : AbstractValidator<Request>
{
public Validator()
{
RuleFor(x => x.Name)
.NotEmpty()
.MaximumLength(CarTableConfiguration.NameMaxLength);
}
}
private static async Task<IResult> Endpoint(
Request request,
IEnumerable<IValidator<Request>> validators,
ApplicationDbContext dbContext,
UserAccessor userAccessor,
ILoggerFactory loggerFactory,
CancellationToken cancellationToken)
{
ILogger logger = loggerFactory.CreateLogger(nameof(CreateCar));
List<ValidationResult> 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);
}
}