From eee8234c33507fa8b12f8e718bf1e4a3934ee801 Mon Sep 17 00:00:00 2001 From: Mahmoud Nasr <239.nasr@gmail.com> Date: Fri, 4 Jul 2025 13:11:28 +0300 Subject: [PATCH 1/2] Refactor service registrations and validation logic - Removed `IWalletService`, `IReviewService`, and `IWithdrawalService` from `Services.cs`, added `IShippingService`. - Updated `ShippingService` to eliminate `AuthService` dependency and streamline email handling. - Improved Swagger configuration formatting in `SwaggerServiceExtensions.cs`. - Added `ValidateAllDependencies` call in `Program.cs` to ensure all services are registered. - Enhanced readability of `GetRequiredService` for `UserManager`. - Introduced `ServicesValidator.cs` to validate service registrations at startup. --- Dentizone.Application/DI/Services.cs | 7 +++-- Dentizone.Application/DI/ServicesValidator.cs | 28 ++++++++++++++++++ .../Services/ShippingService.cs | 15 ++++------ .../Extensions/SwaggerServiceExtensions.cs | 29 +++++++++---------- Dentizone.Presentaion/Program.cs | 8 +++-- 5 files changed, 56 insertions(+), 31 deletions(-) create mode 100644 Dentizone.Application/DI/ServicesValidator.cs diff --git a/Dentizone.Application/DI/Services.cs b/Dentizone.Application/DI/Services.cs index 83a23f7..6b1112b 100644 --- a/Dentizone.Application/DI/Services.cs +++ b/Dentizone.Application/DI/Services.cs @@ -28,14 +28,15 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); services.AddScoped(); + + services.AddScoped(); + + return services; } } diff --git a/Dentizone.Application/DI/ServicesValidator.cs b/Dentizone.Application/DI/ServicesValidator.cs new file mode 100644 index 0000000..8dbfbc8 --- /dev/null +++ b/Dentizone.Application/DI/ServicesValidator.cs @@ -0,0 +1,28 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; + +namespace Dentizone.Application.DI +{ + public static class ServiceCollectionExtensions + { + public static void ValidateAllDependencies(this IServiceCollection services, Assembly[] assembliesToScan) + { + var serviceTypes = assembliesToScan + .SelectMany(a => a.GetTypes()) + .Where(t => t.IsInterface && t.Name.EndsWith("Service")) + .ToList(); + + foreach (var serviceType in serviceTypes) + { + var isRegistered = services.Any(sd => sd.ServiceType == serviceType); + if (!isRegistered) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"❌ Service not registered: {serviceType.FullName}"); + Console.ResetColor(); + throw new InvalidOperationException($"Service not registered: {serviceType.FullName}"); + } + } + } + } +} \ No newline at end of file diff --git a/Dentizone.Application/Services/ShippingService.cs b/Dentizone.Application/Services/ShippingService.cs index da16165..a6661f6 100644 --- a/Dentizone.Application/Services/ShippingService.cs +++ b/Dentizone.Application/Services/ShippingService.cs @@ -1,5 +1,4 @@ using Dentizone.Application.Interfaces; -using Dentizone.Application.Services.Authentication; using Dentizone.Domain.Entity; using Dentizone.Domain.Enums; using Dentizone.Domain.Exceptions; @@ -11,8 +10,8 @@ namespace Dentizone.Application.Services internal class ShippingService( IOrderItemRepository orderItemRepository, IShipmentActivityRepository shipmentActivityRepository, - IMailService mailService, - AuthService authService) + IMailService mailService + ) : IShippingService { public async Task UpdateItemShipmentStatusAsync(string orderItemId, ShipmentActivityStatus newStatus, @@ -22,7 +21,7 @@ public async Task UpdateItemShipmentStatusAsync(string orderItemId, ShipmentActi var item = await orderItemRepository.FindBy( oi => oi.Id == orderItemId, - [oi => oi.ShipmentActivities]); + [oi => oi.ShipmentActivities, oi => oi.Post.Seller, oi => oi.Order.Buyer]); if (item == null) { @@ -39,15 +38,11 @@ public async Task UpdateItemShipmentStatusAsync(string orderItemId, ShipmentActi await shipmentActivityRepository.CreateAsync(shipmentActivity); - var seller = await authService.GetById(item.Post.SellerId); - - - await mailService.Send(seller.Email, $"the Status has been changed to {newStatus}", + await mailService.Send(item.Post.Seller.Email, $"the Status has been changed to {newStatus}", "New status update"); - var buyer = await authService.GetById(item.Order.BuyerId); - await mailService.Send(buyer.Email, $"the Status has been changed to {newStatus}", + await mailService.Send(item.Order.Buyer.Email, $"the Status has been changed to {newStatus}", "New status update"); } } diff --git a/Dentizone.Presentaion/Extensions/SwaggerServiceExtensions.cs b/Dentizone.Presentaion/Extensions/SwaggerServiceExtensions.cs index 9b206b7..7d214e4 100644 --- a/Dentizone.Presentaion/Extensions/SwaggerServiceExtensions.cs +++ b/Dentizone.Presentaion/Extensions/SwaggerServiceExtensions.cs @@ -11,27 +11,26 @@ public static IServiceCollection AddSwaggerWithJwt(this IServiceCollection servi opt.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { BearerFormat = "JWT", - Description = - "JWT Authorization header using the Bearer scheme.", + Description = "JWT Authorization header using the Bearer scheme.", Name = "Authorization", In = ParameterLocation.Header, Type = SecuritySchemeType.Http, Scheme = "Bearer" }); opt.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Id = "Bearer", - Type = ReferenceType.SecurityScheme - } - }, - [] - } - }); + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "Bearer", + Type = ReferenceType.SecurityScheme + } + }, + [] + } + }); }); return services; } diff --git a/Dentizone.Presentaion/Program.cs b/Dentizone.Presentaion/Program.cs index f965fb1..25777ed 100644 --- a/Dentizone.Presentaion/Program.cs +++ b/Dentizone.Presentaion/Program.cs @@ -1,5 +1,6 @@ using Dentizone.Application.DI; using Dentizone.Application.Interfaces; +using Dentizone.Application.Services; using Dentizone.Infrastructure.DependencyInjection; using Dentizone.Infrastructure.Filters; using Dentizone.Presentaion.Context; @@ -32,6 +33,7 @@ public static void Main(string[] args) builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); builder.Services.AddSwaggerWithJwt(); + builder.Services.ValidateAllDependencies([typeof(BaseService).Assembly]); var app = builder.Build(); @@ -68,15 +70,15 @@ public static void Main(string[] args) { var db = scope.ServiceProvider.GetRequiredService(); var userManager = scope.ServiceProvider - .GetRequiredService>(); + .GetRequiredService>(); try { Console.WriteLine("[Seeding] Starting full data seeding..."); Infrastructure.Persistence.Seeder.FullDataSeeder.SeedingConfig config = new(); // Use default or customize Infrastructure.Persistence.Seeder.FullDataSeeder.SeedAsync(db, userManager, config) - .GetAwaiter().GetResult(); + .GetAwaiter().GetResult(); Console.WriteLine("[Seeding] Full data seeding completed."); } catch (Exception ex) From 59097518a6fef9fc65d5ab2e6e4a928cefbcbd5b Mon Sep 17 00:00:00 2001 From: Mahmoud Nasr <239.nasr@gmail.com> Date: Fri, 4 Jul 2025 13:19:56 +0300 Subject: [PATCH 2/2] Refactor shipping status update functionality Updated IShippingService to use CreateShipmentStatusDto for shipment status updates. Adjusted ShippingService and ShippingController to reflect the new method signature and changed the HTTP method from PUT to POST. Introduced a new CreateShipmentStatusDto class to encapsulate update data. Updated namespaces and using directives accordingly. --- .../DTOs/Shipping/CreateShipmentStatusDto.cs | 11 ++++++++++ .../Interfaces/IShippingService.cs | 7 ++++--- .../Services/ShippingService.cs | 21 +++++++++++-------- .../Controllers/ShippingController.cs | 10 ++++----- 4 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 Dentizone.Application/DTOs/Shipping/CreateShipmentStatusDto.cs diff --git a/Dentizone.Application/DTOs/Shipping/CreateShipmentStatusDto.cs b/Dentizone.Application/DTOs/Shipping/CreateShipmentStatusDto.cs new file mode 100644 index 0000000..6c62c86 --- /dev/null +++ b/Dentizone.Application/DTOs/Shipping/CreateShipmentStatusDto.cs @@ -0,0 +1,11 @@ +using Dentizone.Domain.Enums; + +namespace Dentizone.Application.DTOs.Shipping +{ + public class CreateShipmentStatusDto + { + public required string OrderItemId { get; set; } + public required ShipmentActivityStatus NewStatus { get; set; } + public string? Comment { get; set; } + } +} \ No newline at end of file diff --git a/Dentizone.Application/Interfaces/IShippingService.cs b/Dentizone.Application/Interfaces/IShippingService.cs index a71bb68..b2267c5 100644 --- a/Dentizone.Application/Interfaces/IShippingService.cs +++ b/Dentizone.Application/Interfaces/IShippingService.cs @@ -1,10 +1,11 @@ -using Dentizone.Domain.Enums; +using Dentizone.Application.DTOs.Shipping; +using Dentizone.Domain.Enums; +using Microsoft.AspNetCore.Mvc; namespace Dentizone.Application.Interfaces { public interface IShippingService { - Task UpdateItemShipmentStatusAsync(string orderItemId, ShipmentActivityStatus newStatus, - string? comments); + Task UpdateItemShipmentStatusAsync(CreateShipmentStatusDto shipmentStatus); } } \ No newline at end of file diff --git a/Dentizone.Application/Services/ShippingService.cs b/Dentizone.Application/Services/ShippingService.cs index a6661f6..0d6d5b9 100644 --- a/Dentizone.Application/Services/ShippingService.cs +++ b/Dentizone.Application/Services/ShippingService.cs @@ -1,9 +1,11 @@ -using Dentizone.Application.Interfaces; +using Dentizone.Application.DTOs.Shipping; +using Dentizone.Application.Interfaces; using Dentizone.Domain.Entity; using Dentizone.Domain.Enums; using Dentizone.Domain.Exceptions; using Dentizone.Domain.Interfaces.Mail; using Dentizone.Domain.Interfaces.Repositories; +using Microsoft.AspNetCore.Mvc; namespace Dentizone.Application.Services { @@ -14,13 +16,12 @@ IMailService mailService ) : IShippingService { - public async Task UpdateItemShipmentStatusAsync(string orderItemId, ShipmentActivityStatus newStatus, - string? comments) + public async Task UpdateItemShipmentStatusAsync(CreateShipmentStatusDto shipmentStatus) { //search in DataBase if orderItemId found or not? var item = await orderItemRepository.FindBy( - oi => oi.Id == orderItemId, + oi => oi.Id == shipmentStatus.OrderItemId, [oi => oi.ShipmentActivities, oi => oi.Post.Seller, oi => oi.Order.Buyer]); if (item == null) @@ -31,18 +32,20 @@ public async Task UpdateItemShipmentStatusAsync(string orderItemId, ShipmentActi { var shipmentActivity = new ShipmentActivity { - ItemId = orderItemId, - Status = newStatus, - ActivityDescription = comments + ItemId = shipmentStatus.OrderItemId, + Status = shipmentStatus.NewStatus, + ActivityDescription = shipmentStatus.Comment ?? "No comment provided", }; await shipmentActivityRepository.CreateAsync(shipmentActivity); - await mailService.Send(item.Post.Seller.Email, $"the Status has been changed to {newStatus}", + await mailService.Send(item.Post.Seller.Email, + $"the Status has been changed to {shipmentStatus.NewStatus}", "New status update"); - await mailService.Send(item.Order.Buyer.Email, $"the Status has been changed to {newStatus}", + await mailService.Send(item.Order.Buyer.Email, + $"the Status has been changed to {shipmentStatus.NewStatus}", "New status update"); } } diff --git a/Dentizone.Presentaion/Controllers/ShippingController.cs b/Dentizone.Presentaion/Controllers/ShippingController.cs index 8088b8d..5dbb95e 100644 --- a/Dentizone.Presentaion/Controllers/ShippingController.cs +++ b/Dentizone.Presentaion/Controllers/ShippingController.cs @@ -1,4 +1,5 @@ -using Dentizone.Application.Interfaces; +using Dentizone.Application.DTOs.Shipping; +using Dentizone.Application.Interfaces; using Dentizone.Domain.Enums; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -10,11 +11,10 @@ namespace Dentizone.Presentaion.Controllers [Authorize(Policy = "IsAdmin")] public class ShippingController(IShippingService shipmentActivity) : ControllerBase { - [HttpPut] - public async Task UpdateItemShipmentStatus(string orderItemId, ShipmentActivityStatus newStatus, - string? comment) + [HttpPost] + public async Task UpdateItemShipmentStatus(CreateShipmentStatusDto shipmentStatus) { - await shipmentActivity.UpdateItemShipmentStatusAsync(orderItemId, newStatus, comment); + await shipmentActivity.UpdateItemShipmentStatusAsync(shipmentStatus); return Ok(); } }