diff --git a/src/Medications.Api/Contracts/CreateMedicationRequest.cs b/src/Medications.Api/Contracts/CreateMedicationRequest.cs index 7d28a9f..3b05870 100644 --- a/src/Medications.Api/Contracts/CreateMedicationRequest.cs +++ b/src/Medications.Api/Contracts/CreateMedicationRequest.cs @@ -1,5 +1,13 @@ +using System.ComponentModel.DataAnnotations; + namespace Medications.Api.Contracts; public class CreateMedicationRequest { + + [Required(AllowEmptyStrings = false)] + public required string Name { get; set; } + + [Range(1, int.MaxValue, ErrorMessage = "Quantity must be greater than 0")] + public int Quantity { get; set; } } diff --git a/src/Medications.Api/Contracts/CreateMedicationResponse.cs b/src/Medications.Api/Contracts/CreateMedicationResponse.cs deleted file mode 100644 index b39badb..0000000 --- a/src/Medications.Api/Contracts/CreateMedicationResponse.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Medications.Api.Contracts; - -public class CreateMedicationResponse -{ - public int Id { get; set; } -} diff --git a/src/Medications.Api/Contracts/MedicationResponse.cs b/src/Medications.Api/Contracts/MedicationResponse.cs new file mode 100644 index 0000000..e98bcf8 --- /dev/null +++ b/src/Medications.Api/Contracts/MedicationResponse.cs @@ -0,0 +1,3 @@ +namespace Medications.Api.Contracts; + +public sealed record MedicationResponse(int Id, string Name, int Quantity, DateTimeOffset CreationDate); diff --git a/src/Medications.Api/Data/MedicationsDbContext.cs b/src/Medications.Api/Data/MedicationsDbContext.cs index b6103eb..610fa68 100644 --- a/src/Medications.Api/Data/MedicationsDbContext.cs +++ b/src/Medications.Api/Data/MedicationsDbContext.cs @@ -3,7 +3,8 @@ namespace Medications.Api.Data; -public class MedicationsDbContext(DbContextOptions options) : DbContext(options) +public class MedicationsDbContext( + DbContextOptions options) : DbContext(options) { public DbSet Medications => Set(); } diff --git a/src/Medications.Api/Endpoints/MedicationEndpoints.cs b/src/Medications.Api/Endpoints/MedicationEndpoints.cs index 1d043c9..279d1f6 100644 --- a/src/Medications.Api/Endpoints/MedicationEndpoints.cs +++ b/src/Medications.Api/Endpoints/MedicationEndpoints.cs @@ -1,5 +1,6 @@ +using Medications.Api.Contracts; using Medications.Api.Data; -using Medications.Api.Entities; +using Medications.Api.Mapping; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.EntityFrameworkCore; @@ -7,9 +8,9 @@ namespace Medications.Api.Endpoints; public static class MedicationEndpoints { - public static void MapMedicationEndpoints(this WebApplication app) + public static IEndpointRouteBuilder MapMedicationEndpoints(this IEndpointRouteBuilder endpoints) { - var group = app.MapGroup("/api/medications") + var group = endpoints.MapGroup("/api/medications") .WithTags("Medications"); group.MapGet("/", GetAllMedications) @@ -19,20 +20,47 @@ public static void MapMedicationEndpoints(this WebApplication app) group.MapGet("/{id}", GetMedication) .WithName("GetMedication") .WithDescription("Retrieve a specific medication by its ID."); + + group.MapPost("/", CreateMedication) + .WithName("CreateMedication") + .WithDescription("Create a new medication"); + + return endpoints; } - private static async Task>> GetAllMedications(MedicationsDbContext dbContext) + private static async Task>> GetAllMedications(MedicationsDbContext dbContext) { var medications = await dbContext.Medications.ToListAsync(); - return TypedResults.Ok(medications); + + var medicationResponseList = medications.Select(medication => medication.ToResponse()).ToList(); + + return TypedResults.Ok(medicationResponseList); } - private static async Task, NotFound>> GetMedication(int id, MedicationsDbContext dbContext) + private static async Task, NotFound>> GetMedication( + int id, MedicationsDbContext dbContext) { var medication = await dbContext.Medications.FindAsync(id); if (medication is null) return TypedResults.NotFound(); - return TypedResults.Ok(medication); + return TypedResults.Ok(medication.ToResponse()); + } + + private static async Task> CreateMedication( + CreateMedicationRequest request, + MedicationsDbContext dbContext, + TimeProvider timeProvider, + CancellationToken cancellationToken) + { + var medication = request.ToMedication(timeProvider); + + dbContext.Add(medication); + + await dbContext.SaveChangesAsync(cancellationToken); + + var medicationResponse = medication.ToResponse(); + + return TypedResults.CreatedAtRoute(medicationResponse, "GetMedication", new { id = medicationResponse.Id }); } } diff --git a/src/Medications.Api/Mapping/MedicationMapping.cs b/src/Medications.Api/Mapping/MedicationMapping.cs new file mode 100644 index 0000000..00bd354 --- /dev/null +++ b/src/Medications.Api/Mapping/MedicationMapping.cs @@ -0,0 +1,26 @@ +using Medications.Api.Contracts; +using Medications.Api.Entities; + +namespace Medications.Api.Mapping; + +public static class MedicationMapping +{ + public static Medication ToMedication(this CreateMedicationRequest request, TimeProvider timeProvider) + { + return new Medication + { + Name = request.Name.Trim(), + Quantity = request.Quantity, + CreationDate = timeProvider.GetUtcNow() + }; + } + + public static MedicationResponse ToResponse(this Medication medication) + { + return new MedicationResponse( + medication.Id, + medication.Name, + medication.Quantity, + medication.CreationDate); + } +} diff --git a/src/Medications.Api/Program.cs b/src/Medications.Api/Program.cs index e143637..e4d51d5 100644 --- a/src/Medications.Api/Program.cs +++ b/src/Medications.Api/Program.cs @@ -5,6 +5,9 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("Medications")); builder.Services.AddOpenApi(); +builder.Services.AddValidation(); +builder.Services.AddSingleton(TimeProvider.System); + var app = builder.Build(); if (app.Environment.IsDevelopment())