diff --git a/src/SM.Medication.Api/EndPoints/MedicationEndPoints.cs b/src/SM.Medication.Api/EndPoints/MedicationEndPoints.cs index eb4d93f..d0cc255 100644 --- a/src/SM.Medication.Api/EndPoints/MedicationEndPoints.cs +++ b/src/SM.Medication.Api/EndPoints/MedicationEndPoints.cs @@ -1,3 +1,7 @@ +using System.Runtime.Versioning; +using SM.Medication.Api.Extensions; +using SM.Medication.Application.Commands; + namespace SM.Medication.Api.EndPoints; public static class MedicationEndPoints @@ -9,12 +13,43 @@ public static void MapMedicationEndPoints(this WebApplication app) async (IMedicationHandler handler) => { return await handler.Handle(); - }) - .WithTags(MEDICATION_TAG) - .WithMetadata(new SwaggerOperationAttribute(MEDICATION_TAG, "Get List of Medications")) - .WithMetadata(new SwaggerResponseAttribute(StatusCodes.Status200OK, "Success!")) + }).AddMetadata("Get List of Medications"); + + + app.MapPost("/medications", + async (IMedicationHandler handler, CreateMedicationCommand request) => + { + try + { + request.Name.GuardString(nameof(request.Name)); + + var result = await handler.Handle(request); + if (!result) + return Results.Problem( + "Failed to create Medication", + statusCode: StatusCodes.Status500InternalServerError); + + return Results.Created(); + } + catch (Exception e) + { + //Log specific exception + return Results.Problem( + e.Message, + statusCode: StatusCodes.Status500InternalServerError); + } + }).AddMetadata("Create Medication"); + + } + + public static RouteHandlerBuilder AddMetadata(this RouteHandlerBuilder builder, string description) + { + return builder.WithTags(MEDICATION_TAG) + .WithMetadata(new SwaggerOperationAttribute(MEDICATION_TAG, description)) + .WithMetadata(new SwaggerResponseAttribute(StatusCodes.Status201Created, "Created!")) + .WithMetadata(new SwaggerResponseAttribute(StatusCodes.Status400BadRequest, "Bad Request!")) .WithMetadata(new SwaggerResponseAttribute(StatusCodes.Status401Unauthorized, "You're not Authorized!")) .WithMetadata(new SwaggerResponseAttribute(StatusCodes.Status500InternalServerError, "Failed!")) - .RequireAuthorization(); ; + .RequireAuthorization(); } } diff --git a/src/SM.Medication.Api/Extensions/StringExtensions.cs b/src/SM.Medication.Api/Extensions/StringExtensions.cs new file mode 100644 index 0000000..e1d7499 --- /dev/null +++ b/src/SM.Medication.Api/Extensions/StringExtensions.cs @@ -0,0 +1,17 @@ +using Azure.Core; + +namespace SM.Medication.Api.Extensions; + +public static class StringExtensions +{ + public static void GuardString(this string? value, string nameOf) + { + ArgumentNullException.ThrowIfNull(value, nameOf); + + if(string.IsNullOrEmpty(value)) + throw new Exception($"{nameOf} cannot be null or empty"); + + if(string.IsNullOrWhiteSpace(value)) + throw new Exception($"{nameOf} cannot be empty or whitespace"); + } +} diff --git a/src/SM.Medication.Application/Commands/CreateMedicationCommand.cs b/src/SM.Medication.Application/Commands/CreateMedicationCommand.cs new file mode 100644 index 0000000..56c32d7 --- /dev/null +++ b/src/SM.Medication.Application/Commands/CreateMedicationCommand.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace SM.Medication.Application.Commands; + +public class CreateMedicationCommand +{ + [Required] + public string? Name { get; set; } + [Required] + [DefaultValue(1)] + [Range(1, int.MaxValue)] + public int Quantity { get; set; } +} diff --git a/src/SM.Medication.Application/Handlers/MedicationHandler.cs b/src/SM.Medication.Application/Handlers/MedicationHandler.cs index 193671b..5a25c4e 100644 --- a/src/SM.Medication.Application/Handlers/MedicationHandler.cs +++ b/src/SM.Medication.Application/Handlers/MedicationHandler.cs @@ -1,4 +1,6 @@ +using Mapster; using MapsterMapper; +using SM.Medication.Application.Commands; using SM.Medication.Application.Interfaces; using SM.Medication.Domain.Interfaces; @@ -13,4 +15,24 @@ public async Task> Handle() var medications = await medicationRepository.GetAll(); return mapper.Map>(medications); } + + public async Task Handle(CreateMedicationCommand command) + { + TypeAdapterConfig + .NewConfig() + .Map(dest => dest.CreatedBy, src => Environment.UserName) + .Map(dest => dest.CreatedAt, src => DateTime.UtcNow) + .Map(dest => dest.ModifiedBy, src => Environment.UserName) + .Map(dest => dest.ModifiedAt, src => DateTime.UtcNow); + + //var entity = new Domain.Entities.Medication(); + var entity = mapper.Map(command); + + var isMedicationExist = (await medicationRepository.GetByName(entity.Name!)) is null; + + if (!isMedicationExist) + throw new Exception("Medication already exist."); + + return await medicationRepository.Add(entity); + } } diff --git a/src/SM.Medication.Application/Interfaces/IMedicationHandler.cs b/src/SM.Medication.Application/Interfaces/IMedicationHandler.cs index 19147ae..175ce8e 100644 --- a/src/SM.Medication.Application/Interfaces/IMedicationHandler.cs +++ b/src/SM.Medication.Application/Interfaces/IMedicationHandler.cs @@ -1,5 +1,8 @@ +using SM.Medication.Application.Commands; + namespace SM.Medication.Application.Interfaces; public interface IMedicationHandler { Task> Handle(); + Task Handle(CreateMedicationCommand command); } diff --git a/src/SM.Medication.Domain/Common/AuditableEntity.cs b/src/SM.Medication.Domain/Common/AuditableEntity.cs index 8c22144..67ee320 100644 --- a/src/SM.Medication.Domain/Common/AuditableEntity.cs +++ b/src/SM.Medication.Domain/Common/AuditableEntity.cs @@ -2,8 +2,45 @@ namespace SM.Medication.Domain.Common; public class AuditableEntity { - public string? CreatedBy { get; set; } - public string? ModifiedBy { get; set; } - public DateTime CreatedAt { get; set; } - public DateTime ModifiedAt { get; set; } + private string? _createdBy; + private string? _modifiedBy; + private DateTime _createdAt; + private DateTime _modifiedAt; + + public string? CreatedBy + { + get { return _createdBy; } + set + { + if (string.IsNullOrEmpty(_createdBy)) + _createdBy = Environment.UserName; + } + } + public string? ModifiedBy + { + get { return _modifiedBy; } + set + { + _modifiedBy = Environment.UserName; + } + } + public DateTime CreatedAt + { + get { return _createdAt; } + set + { + if (_createdAt == DateTime.MinValue) + _createdAt = DateTime.UtcNow; + } + } + + public DateTime ModifiedAt + { + get { return _modifiedAt; } + set + { + if (_modifiedAt == DateTime.MinValue) + _modifiedAt = DateTime.UtcNow; + } + } } diff --git a/src/SM.Medication.Domain/Interfaces/IMedicationRepository.cs b/src/SM.Medication.Domain/Interfaces/IMedicationRepository.cs index 4148aff..08a147c 100644 --- a/src/SM.Medication.Domain/Interfaces/IMedicationRepository.cs +++ b/src/SM.Medication.Domain/Interfaces/IMedicationRepository.cs @@ -1,6 +1,9 @@ + namespace SM.Medication.Domain.Interfaces; public interface IMedicationRepository { + Task Add(Entities.Medication entity); Task> GetAll(); + Task GetByName(string name); } diff --git a/src/SM.Medication.Infrastructure/Services/Repositories/MedicationRepository.cs b/src/SM.Medication.Infrastructure/Services/Repositories/MedicationRepository.cs index d7f50b5..4bdb82a 100644 --- a/src/SM.Medication.Infrastructure/Services/Repositories/MedicationRepository.cs +++ b/src/SM.Medication.Infrastructure/Services/Repositories/MedicationRepository.cs @@ -5,10 +5,26 @@ namespace SM.Medication.Infrastructure.Services.Repositories; public class MedicationRepository(SmartMedMedicationDbContext context) : IMedicationRepository { + public async Task Add(Domain.Entities.Medication entity) + { + context.Medications.Add(entity); + + var result = await context.SaveChangesAsync(); + + return result > 0; + } + public async Task> GetAll() { return await context .Medications .ToListAsync(); } + + public async Task GetByName(string name) + { + return await context + .Medications + .FirstOrDefaultAsync(med => string.Equals(med.Name!.ToUpper(), name.ToUpper())); + } }