Add consumption logic and endpoints

This commit is contained in:
2024-08-23 18:02:18 +02:00
parent d47e4c1971
commit 2463c11be3
22 changed files with 979 additions and 23 deletions

View File

@@ -13,12 +13,12 @@ public static class GetCar
.WithTags("Cars");
}
public static async Task<IResult> Endpoint(
private static async Task<IResult> Endpoint(
Guid id,
ApplicationDbContext dbContext,
CancellationToken cancellationToken)
{
var car = await dbContext.Cars.FindAsync([new CarId(id)], cancellationToken: cancellationToken);
Car? car = await dbContext.Cars.FindAsync([new CarId(id)], cancellationToken: cancellationToken);
if (car is null)
{

View File

@@ -0,0 +1,74 @@
using FluentValidation;
using FluentValidation.Results;
using Vegasco.WebApi.Cars;
using Vegasco.WebApi.Common;
using Vegasco.WebApi.Persistence;
namespace Vegasco.WebApi.Consumptions;
public static class CreateConsumption
{
public record Request(DateTimeOffset DateTime, double Distance, double Amount, bool IgnoreInCalculation, Guid CarId);
public record Response(Guid Id, DateTimeOffset DateTime, double Distance, double Amount, bool IgnoreInCalculation, Guid CarId);
public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder)
{
return builder
.MapPost("consumptions", Endpoint)
.WithTags("Consumptions");
}
public class Validator : AbstractValidator<Request>
{
public Validator(TimeProvider timeProvider)
{
RuleFor(x => x.DateTime.ToUniversalTime())
.LessThanOrEqualTo(timeProvider.GetUtcNow())
.WithName(nameof(Request.DateTime));
RuleFor(x => x.Distance)
.GreaterThan(0);
RuleFor(x => x.Amount)
.GreaterThan(0);
RuleFor(x => x.CarId)
.NotEmpty();
}
}
private static async Task<IResult> Endpoint(
ApplicationDbContext dbContext,
Request request,
IEnumerable<IValidator<Request>> validators,
CancellationToken cancellationToken)
{
List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken);
if (failedValidations.Count > 0)
{
return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary()));
}
Car? car = await dbContext.Cars.FindAsync([new CarId(request.CarId)], cancellationToken);
if (car is null)
{
return TypedResults.NotFound();
}
var consumption = new Consumption
{
DateTime = request.DateTime.ToUniversalTime(),
Distance = request.Distance,
Amount = request.Amount,
IgnoreInCalculation = request.IgnoreInCalculation,
CarId = new CarId(request.CarId)
};
dbContext.Consumptions.Add(consumption);
await dbContext.SaveChangesAsync(cancellationToken);
return TypedResults.Created($"consumptions/{consumption.Id.Value}",
new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance, consumption.Amount, consumption.IgnoreInCalculation, consumption.CarId.Value));
}
}

View File

@@ -0,0 +1,30 @@
using Vegasco.WebApi.Persistence;
namespace Vegasco.WebApi.Consumptions;
public static class DeleteConsumption
{
public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder)
{
return builder
.MapDelete("consumptions/{id:guid}", Endpoint)
.WithTags("Consumptions");
}
private static async Task<IResult> Endpoint(
ApplicationDbContext dbContext,
Guid id,
CancellationToken cancellationToken)
{
Consumption? consumption = await dbContext.Consumptions.FindAsync([new ConsumptionId(id)], cancellationToken);
if (consumption is null)
{
return TypedResults.NotFound();
}
dbContext.Consumptions.Remove(consumption);
await dbContext.SaveChangesAsync(cancellationToken);
return TypedResults.NoContent();
}
}

View File

@@ -0,0 +1,32 @@
using Vegasco.WebApi.Persistence;
namespace Vegasco.WebApi.Consumptions;
public static class GetConsumption
{
public record Response(Guid Id, DateTimeOffset DateTime, double Distance, double Amount, bool IgnoreInCalculation, Guid CarId);
public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder)
{
return builder
.MapGet("consumptions/{id:guid}", Endpoint)
.WithTags("Consumptions");
}
private static async Task<IResult> Endpoint(
ApplicationDbContext dbContext,
Guid id,
CancellationToken cancellationToken)
{
Consumption? consumption = await dbContext.Consumptions.FindAsync([new ConsumptionId(id)], cancellationToken);
if (consumption is null)
{
return TypedResults.NotFound();
}
var response = new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance,
consumption.Amount, consumption.IgnoreInCalculation, consumption.CarId.Value);
return TypedResults.Ok(response);
}
}

View File

@@ -0,0 +1,27 @@
using Microsoft.EntityFrameworkCore;
using Vegasco.WebApi.Persistence;
namespace Vegasco.WebApi.Consumptions;
public static class GetConsumptions
{
public record Response(Guid Id, DateTimeOffset DateTime, double Distance, double Amount, bool IgnoreInCalculation, Guid CarId);
public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder)
{
return builder
.MapGet("consumptions", Endpoint)
.WithTags("Consumptions");
}
private static async Task<IResult> Endpoint(
ApplicationDbContext dbContext,
CancellationToken cancellationToken)
{
var consumptions = await dbContext.Consumptions
.Select(x => new Response(x.Id.Value, x.DateTime, x.Distance, x.Amount, x.IgnoreInCalculation, x.CarId.Value))
.ToListAsync(cancellationToken);
return TypedResults.Ok(consumptions);
}
}

View File

@@ -0,0 +1,65 @@
using FluentValidation;
using FluentValidation.Results;
using Vegasco.WebApi.Common;
using Vegasco.WebApi.Persistence;
namespace Vegasco.WebApi.Consumptions;
public static class UpdateConsumption
{
public record Request(DateTimeOffset DateTime, double Distance, double Amount, bool IgnoreInCalculation);
public record Response(Guid Id, DateTimeOffset DateTime, double Distance, double Amount, bool IgnoreInCalculation, Guid CarId);
public static RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder builder)
{
return builder
.MapPut("consumptions/{id:guid}", Endpoint)
.WithTags("Consumptions");
}
public class Validator : AbstractValidator<Request>
{
public Validator(TimeProvider timeProvider)
{
RuleFor(x => x.DateTime.ToUniversalTime())
.LessThanOrEqualTo(timeProvider.GetUtcNow())
.WithName(nameof(Request.DateTime));
RuleFor(x => x.Distance)
.GreaterThan(0);
RuleFor(x => x.Amount)
.GreaterThan(0);
}
}
private static async Task<IResult> Endpoint(
ApplicationDbContext dbContext,
Guid id,
Request request,
IEnumerable<IValidator<Request>> validators,
CancellationToken cancellationToken)
{
List<ValidationResult> failedValidations = await validators.ValidateAllAsync(request, cancellationToken);
if (failedValidations.Count > 0)
{
return TypedResults.BadRequest(new HttpValidationProblemDetails(failedValidations.ToCombinedDictionary()));
}
Consumption? consumption = await dbContext.Consumptions.FindAsync([new ConsumptionId(id)], cancellationToken);
if (consumption is null)
{
return TypedResults.NotFound();
}
consumption.DateTime = request.DateTime.ToUniversalTime();
consumption.Distance = request.Distance;
consumption.Amount = request.Amount;
consumption.IgnoreInCalculation = request.IgnoreInCalculation;
await dbContext.SaveChangesAsync(cancellationToken);
return TypedResults.Ok(new Response(consumption.Id.Value, consumption.DateTime, consumption.Distance, consumption.Amount, consumption.IgnoreInCalculation, consumption.CarId.Value));
}
}

View File

@@ -3,6 +3,7 @@ using Asp.Versioning.Conventions;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Vegasco.WebApi.Cars;
using Vegasco.WebApi.Common;
using Vegasco.WebApi.Consumptions;
namespace Vegasco.WebApi.Endpoints;
@@ -39,5 +40,11 @@ public static class EndpointExtensions
CreateCar.MapEndpoint(versionedApis);
UpdateCar.MapEndpoint(versionedApis);
DeleteCar.MapEndpoint(versionedApis);
GetConsumptions.MapEndpoint(versionedApis);
GetConsumption.MapEndpoint(versionedApis);
CreateConsumption.MapEndpoint(versionedApis);
UpdateConsumption.MapEndpoint(versionedApis);
DeleteConsumption.MapEndpoint(versionedApis);
}
}

View File

@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using Vegasco.WebApi.Cars;
using Vegasco.WebApi.Common;
using Vegasco.WebApi.Consumptions;
using Vegasco.WebApi.Users;
namespace Vegasco.WebApi.Persistence;
@@ -11,6 +12,8 @@ public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options
public DbSet<User> Users { get; set; }
public DbSet<Consumption> Consumptions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

View File

@@ -12,7 +12,7 @@ using Vegasco.WebApi.Persistence;
namespace Vegasco.WebApi.Persistence.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240817153531_Initial")]
[Migration("20240818105918_Initial")]
partial class Initial
{
/// <inheritdoc />
@@ -70,7 +70,7 @@ namespace Vegasco.WebApi.Persistence.Migrations
b.HasIndex("CarId");
b.ToTable("Consumption");
b.ToTable("Consumptions");
});
modelBuilder.Entity("Vegasco.WebApi.Users.User", b =>

View File

@@ -42,7 +42,7 @@ namespace Vegasco.WebApi.Persistence.Migrations
});
migrationBuilder.CreateTable(
name: "Consumption",
name: "Consumptions",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
@@ -54,9 +54,9 @@ namespace Vegasco.WebApi.Persistence.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_Consumption", x => x.Id);
table.PrimaryKey("PK_Consumptions", x => x.Id);
table.ForeignKey(
name: "FK_Consumption_Cars_CarId",
name: "FK_Consumptions_Cars_CarId",
column: x => x.CarId,
principalTable: "Cars",
principalColumn: "Id",
@@ -69,8 +69,8 @@ namespace Vegasco.WebApi.Persistence.Migrations
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_Consumption_CarId",
table: "Consumption",
name: "IX_Consumptions_CarId",
table: "Consumptions",
column: "CarId");
}
@@ -78,7 +78,7 @@ namespace Vegasco.WebApi.Persistence.Migrations
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Consumption");
name: "Consumptions");
migrationBuilder.DropTable(
name: "Cars");

View File

@@ -67,7 +67,7 @@ namespace Vegasco.WebApi.Persistence.Migrations
b.HasIndex("CarId");
b.ToTable("Consumption");
b.ToTable("Consumptions");
});
modelBuilder.Entity("Vegasco.WebApi.Users.User", b =>