Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Dentizone.Application/AssemblyReference.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
namespace Dentizone.Application
{
public static class AssemblyReference
/// <summary>
/// Marker interface for the Application assembly,
/// used by AutoMapper to locate and load its profiles.
/// </summary>
public interface IAssemblyReference
{
}
}
30 changes: 15 additions & 15 deletions Dentizone.Application/AutoMapper/Carts/CartProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ public CartProfile()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.PostId, opt => opt.MapFrom(src => src.PostId))
.ForMember(dest => dest.Post, opt => opt.MapFrom(src => new Post
{
Title = src.Title,
Price = src.Price,
PostAssets = new List<PostAsset>
{
new PostAsset
{
Asset = new Domain.Entity.Asset
{
Url = src.Url
}
}
}
}))
{
Title = src.Title,
Price = src.Price,
PostAssets = new List<PostAsset>
{
new()
{
Asset = new Domain.Entity.Asset
{
Url = src.Url
}
}
}
}))
.ReverseMap()
.ForMember(dest => dest.Url, opt => opt.MapFrom(src =>
src.Post.PostAssets.FirstOrDefault().Asset.Url))
src.Post.PostAssets.FirstOrDefault().Asset.Url))
Comment on lines 30 to +31
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential null reference exception in reverse mapping.

The current code could throw a NullReferenceException if PostAssets is empty or if the first PostAsset's Asset is null.

Apply this fix to handle null cases safely:

-                .ForMember(dest => dest.Url, opt => opt.MapFrom(src =>
-                    src.Post.PostAssets.FirstOrDefault().Asset.Url))
+                .ForMember(dest => dest.Url, opt => opt.MapFrom(src =>
+                    src.Post.PostAssets.FirstOrDefault()?.Asset?.Url ?? string.Empty))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.ForMember(dest => dest.Url, opt => opt.MapFrom(src =>
src.Post.PostAssets.FirstOrDefault().Asset.Url))
src.Post.PostAssets.FirstOrDefault().Asset.Url))
.ForMember(dest => dest.Url, opt => opt.MapFrom(src =>
src.Post.PostAssets.FirstOrDefault()?.Asset?.Url ?? string.Empty))
🤖 Prompt for AI Agents
In Dentizone.Application/AutoMapper/Carts/CartProfile.cs around lines 30 to 31,
the mapping code accessing src.Post.PostAssets.FirstOrDefault().Asset.Url can
throw a NullReferenceException if PostAssets is empty or the first PostAsset's
Asset is null. To fix this, modify the mapping expression to safely check for
nulls before accessing Url, for example by using conditional access operators or
null-coalescing to return a default value when any part is null.

.ForMember(dest => dest.Title, opt => opt.MapFrom(src => src.Post.Title))
.ForMember(dest => dest.Price, opt => opt.MapFrom(src => src.Post.Price));

Expand Down
2 changes: 1 addition & 1 deletion Dentizone.Application/AutoMapper/CatalogProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public CatalogProfile()
CreateMap<CategoryView, Category>().ReverseMap();
CreateMap<SubCategoryView, SubCategory>().ReverseMap();
CreateMap<SubCategoryDto, SubCategory>().ReverseMap();
CreateMap<CreatedSubCategoryDTO, SubCategory>().ReverseMap();
CreateMap<CreatedSubCategoryDto, SubCategory>().ReverseMap();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using AutoMapper;
using Dentizone.Application.Services.Payment;
using Dentizone.Application.DTOs.Payment;
using Dentizone.Domain.Entity;

namespace Dentizone.Application.AutoMapper.Payments
Expand Down
4 changes: 2 additions & 2 deletions Dentizone.Application/AutoMapper/Posts/PostProfile.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using AutoMapper;
using Dentizone.Application.DTOs.PostDTO;
using Dentizone.Application.DTOs.Post;
using Dentizone.Domain.Entity;

namespace Dentizone.Application.AutoMapper.Posts
Expand All @@ -12,7 +12,7 @@ public PostProfile()
.ReverseMap();
CreateMap<UpdatePostDto, Post>().ReverseMap();
CreateMap<PostAsset, PostAssetView>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.AssetId)) // Map AssetId to Id
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.AssetId)) // Map AssetId to Id
.ForMember(dest => dest.Url, opt => opt.MapFrom(src => src.Asset.Url)) // Map Asset.Url to Url
.ReverseMap();
CreateMap<Post, PostViewDto>()
Expand Down
4 changes: 2 additions & 2 deletions Dentizone.Application/AutoMapper/UserActivityProfile.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using AutoMapper;
using Dentizone.Application.DTOs.UserActivityDTO;
using Dentizone.Application.DTOs.UserActivity;
using Dentizone.Domain.Entity;

namespace Dentizone.Application.AutoMapper
Expand All @@ -9,7 +9,7 @@ public class UserActivityProfile : Profile
public UserActivityProfile()
{
CreateMap<CreatedUserActivityDto, UserActivity>().ReverseMap();
CreateMap<UserActivityDTO, UserActivity>()
CreateMap<UserActivityDto, UserActivity>()
.ReverseMap()
.ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.User.Username));
Comment on lines +12 to 14
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against null User navigation to avoid runtime crash

src.User may legitimately be null when the entity is projected without an Include(u => u.User).
The current mapping will throw a NullReferenceException.

- .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.User.Username));
+ .ForMember(dest => dest.UserName,
+            opt => opt.MapFrom(src => src.User?.Username));

Consider also adding .NullSubstitute(string.Empty) if an empty string is preferable over null.

🤖 Prompt for AI Agents
In Dentizone.Application/AutoMapper/UserActivityProfile.cs around lines 12 to
14, the mapping from UserActivityDto to UserActivity accesses src.User.Username
without checking if src.User is null, which can cause a NullReferenceException.
Update the mapping expression to safely handle null src.User by using a
conditional operator or null-conditional access, and optionally add
.NullSubstitute(string.Empty) to provide an empty string instead of null for
UserName.

}
Expand Down
6 changes: 3 additions & 3 deletions Dentizone.Application/AutoMapper/UserProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ public UserProfile()
CreateMap<CreateAppUser, AppUser>()
.ReverseMap();

CreateMap<KycStatusDTO, AppUser>().ReverseMap();
CreateMap<UserStateDTO, AppUser>()
CreateMap<KycStatusDto, AppUser>().ReverseMap();
CreateMap<UserStateDto, AppUser>()
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status))
Comment on lines +21 to 23
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Partial-DTO mapping risks overriding unrelated fields – ignore the rest explicitly

KycStatusDto and UserStateDto only carry one or two fields, yet the current CreateMap<…, AppUser>() will map every other property that AutoMapper can find, setting them to their default values (null, 0, false, …) when the source field is absent.
Persisting such a projection back to the database will silently wipe data.

Add explicit member mapping and ignore everything else:

-CreateMap<KycStatusDto, AppUser>().ReverseMap();
-CreateMap<UserStateDto, AppUser>()
-    .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status))
-    .ReverseMap()
-    .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status));
+CreateMap<KycStatusDto, AppUser>()
+    .ForMember(dest => dest.KycStatus, opt => opt.MapFrom(src => src.KycStatus))
+    .ForAllOtherMembers(opt => opt.Ignore())
+    .ReverseMap();
+
+CreateMap<UserStateDto, AppUser>()
+    .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status))
+    .ForAllOtherMembers(opt => opt.Ignore())
+    .ReverseMap()
+    .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status));

Same pattern should be applied wherever a “single-purpose” DTO maps to the full AppUser entity.
Failing to do so is a data-loss bug.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CreateMap<KycStatusDto, AppUser>().ReverseMap();
CreateMap<UserStateDto, AppUser>()
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status))
// Replace the default projection with explicit mappings to avoid wiping other fields
CreateMap<KycStatusDto, AppUser>()
.ForMember(dest => dest.KycStatus, opt => opt.MapFrom(src => src.KycStatus))
.ForAllOtherMembers(opt => opt.Ignore())
.ReverseMap();
CreateMap<UserStateDto, AppUser>()
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status))
.ForAllOtherMembers(opt => opt.Ignore())
.ReverseMap()
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status));
🤖 Prompt for AI Agents
In Dentizone.Application/AutoMapper/UserProfile.cs around lines 21 to 23, the
current CreateMap configurations for KycStatusDto and UserStateDto to AppUser
map all properties by default, which risks overwriting unrelated AppUser fields
with default values. To fix this, explicitly map only the intended fields from
the DTOs to AppUser and add Ignore() calls for all other members to prevent
unintended data loss. Apply this explicit member mapping pattern consistently
for all single-purpose DTO to AppUser mappings.

.ReverseMap()
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status));
CreateMap<UserView, AppUser>()
.ForPath(dest => dest.University.Name, opt => opt.MapFrom(src => src.UnversityName))
.ForPath(dest => dest.University.Name, opt => opt.MapFrom(src => src.UniversityName))
.ReverseMap();

CreateMap<DomainUserView, AppUser>()
Expand Down
12 changes: 1 addition & 11 deletions Dentizone.Application/DI/Services.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
using Dentizone.Application.Interfaces;
using Dentizone.Application.Interfaces.Analytics;
using Dentizone.Application.Interfaces.Assets;
using Dentizone.Application.Interfaces.Cart;
using Dentizone.Application.Interfaces.Catalog;
using Dentizone.Application.Interfaces.Cloudinary;
using Dentizone.Application.Interfaces.Favorites;
using Dentizone.Application.Interfaces.Order;
using Dentizone.Application.Interfaces.Post;
using Dentizone.Application.Interfaces.Review;
using Dentizone.Application.Interfaces.User;
using Dentizone.Application.Services;
using Dentizone.Application.Services.Authentication;
using Dentizone.Application.Services.Payment;
Expand Down Expand Up @@ -45,7 +35,7 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
services.AddScoped<IReviewService, ReviewService>();

services.AddScoped<IWithdrawalService, WithdrawalService>();
services.AddScoped<IQAService, QAService>();
services.AddScoped<IQaService, QaService>();
return services;
}
}
Expand Down
2 changes: 1 addition & 1 deletion Dentizone.Application/DTOs/Analytics/PostAnalyticsDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ public class PostAnalyticsDto
public int PendingPosts { get; set; }

public decimal AveragePostPrice { get; set; }
public Dictionary<string, int> PostsByCategory { get; set; } = new Dictionary<string, int>();
public Dictionary<string, int> PostsByCategory { get; set; } = new();
}
}
2 changes: 1 addition & 1 deletion Dentizone.Application/DTOs/Analytics/UserAnalyticsDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ public class UserAnalyticsDto
public int NewUsersLast7Days { get; set; }
public int NewUsersLast30Days { get; set; }

public Dictionary<string, int> UsersByUniversity { get; set; } = new Dictionary<string, int>();
public Dictionary<string, int> UsersByUniversity { get; set; } = new();
}
}
4 changes: 2 additions & 2 deletions Dentizone.Application/DTOs/Auth/ConfirmEmailRequestDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public class ConfirmEmailRequestDto
{
public string Token { get; set; }
public string UserId { get; set; }
public required string Token { get; set; }
public required string UserId { get; set; }
}
12 changes: 6 additions & 6 deletions Dentizone.Application/DTOs/Auth/RegisterRequestDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ namespace Dentizone.Application.DTOs.Auth;

public class RegisterRequestDto
{
public string Email { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
public string Username { get; set; }
public required string Email { get; set; }
public required string Password { get; set; }
public required string FullName { get; set; }
public required string Username { get; set; }
public int AcademicYear { get; set; }
public string UniversityId { get; set; }
public required string UniversityId { get; set; }
}

public class RegisterRequestDtoValidator : AbstractValidator<RegisterRequestDto>
Expand All @@ -25,7 +25,7 @@ public RegisterRequestDtoValidator()
RuleFor(x => x.FullName).NotEmpty().WithMessage("Full name is required.");
RuleFor(x => x.Username).NotEmpty().WithMessage("Username is required.");
RuleFor(x => x.AcademicYear).GreaterThan(0).LessThanOrEqualTo(5)
.WithMessage("Academic year must be greater than 0 and less than 6.");
.WithMessage("Academic year must be greater than 0 and less than 6.");
RuleFor(x => x.UniversityId).NotEmpty().WithMessage("University ID is required.");
}
}
6 changes: 3 additions & 3 deletions Dentizone.Application/DTOs/Auth/ResetPasswordDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
{
public class ResetPasswordDto
{
public string NewPassword { get; set; }
public string Token { get; set; }
public string Email { get; set; }
public required string NewPassword { get; set; }
public required string Token { get; set; }
public required string Email { get; set; }
}
}
8 changes: 4 additions & 4 deletions Dentizone.Application/DTOs/Cart/CartItemDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
{
public class CartItemDto
{
public string Id { get; set; }
public string PostId { get; set; }
public string Title { get; set; }
public required string Id { get; set; }
public required string PostId { get; set; }
public required string Title { get; set; }
public decimal Price { get; set; }
public string Url { get; set; }
public required string Url { get; set; }
}
}
2 changes: 1 addition & 1 deletion Dentizone.Application/DTOs/Catalog/CategoryDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Dentizone.Application.DTOs.Catalog
{
public class CategoryDto
{
public string Name { get; set; }
public required string Name { get; set; }
public string IconUrl { get; set; } = string.Empty;
}

Expand Down
8 changes: 4 additions & 4 deletions Dentizone.Application/DTOs/Catalog/CategoryView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ namespace Dentizone.Application.DTOs.Catalog
{
public class CategoryView
{
public string Id { get; set; }
public string Name { get; set; }
public required string Id { get; set; }
public required string Name { get; set; }
}

public class CreatedCategoryDTOValidator : AbstractValidator<CategoryView>
public class CreatedCategoryDtoValidator : AbstractValidator<CategoryView>
{
public CreatedCategoryDTOValidator()
public CreatedCategoryDtoValidator()
Comment on lines +11 to +13
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validator name/type mismatch can cause confusion

CreatedCategoryDtoValidator validates CategoryView, yet its name implies it should validate a CreatedCategoryDto. This asymmetry breaks the new naming convention and may mislead maintainers.

-    public class CreatedCategoryDtoValidator : AbstractValidator<CategoryView>
+    public class CategoryViewValidator : AbstractValidator<CategoryView>
🤖 Prompt for AI Agents
In Dentizone.Application/DTOs/Catalog/CategoryView.cs around lines 11 to 13, the
validator class name CreatedCategoryDtoValidator does not match the type it
validates, CategoryView, causing confusion. Rename the validator class to
CategoryViewValidator to align the class name with the validated type and follow
the naming convention consistently.

{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("Category name is required.")
Expand Down
10 changes: 5 additions & 5 deletions Dentizone.Application/DTOs/Catalog/CreatedSubCategoryDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

namespace Dentizone.Application.DTOs.Catalog
{
public class CreatedSubCategoryDTO
public class CreatedSubCategoryDto
{
public string Id { get; set; }
public string Name { get; set; }
public required string Id { get; set; }
public required string Name { get; set; }
}

public class CreatedSubCategoryDTOValidator : AbstractValidator<CreatedSubCategoryDTO>
public class CreatedSubCategoryDtoValidator : AbstractValidator<CreatedSubCategoryDto>
{
public CreatedSubCategoryDTOValidator()
public CreatedSubCategoryDtoValidator()
{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("SubCategory name is required.")
Expand Down
8 changes: 4 additions & 4 deletions Dentizone.Application/DTOs/Catalog/SubCategoryDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ namespace Dentizone.Application.DTOs.Catalog
{
public class SubCategoryDto
{
public string Name { get; set; }
public string CategoryId { get; set; }
public required string Name { get; set; }
public required string CategoryId { get; set; }
}

public class SubCategoryDTOValidator : AbstractValidator<SubCategoryDto>
public class SubCategoryDtoValidator : AbstractValidator<SubCategoryDto>
{
public SubCategoryDTOValidator()
public SubCategoryDtoValidator()
{
RuleFor(x => x.Name)
.NotEmpty().WithMessage("SubCategory name is required.")
Expand Down
6 changes: 3 additions & 3 deletions Dentizone.Application/DTOs/Catalog/SubCategoryView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public class SubCategoryView
{
public string Name { get; set; }
public string Id { get; set; }
public CategoryView Category { get; set; }
public required string Name { get; set; }
public required string Id { get; set; }
public required CategoryView Category { get; set; }
}
2 changes: 1 addition & 1 deletion Dentizone.Application/DTOs/ErrorResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Dentizone.Application.DTOs;

public class ErrorResponse
{
public string Message { get; set; }
public required string Message { get; set; }
public string? Details { get; set; }
public int StatusCode { get; set; }
}
6 changes: 3 additions & 3 deletions Dentizone.Application/DTOs/Favorites/FavoriteViewDto.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Dentizone.Application.DTOs.PostDTO;
using Dentizone.Application.DTOs.Post;

namespace Dentizone.Application.DTOs.Favorites
{
public class FavoriteViewDto
{
public string Id { get; set; }
public PostViewDto Post { get; set; } = new PostViewDto();
public string Id { get; set; } = string.Empty;
public PostViewDto Post { get; set; } = new();
}
}
4 changes: 2 additions & 2 deletions Dentizone.Application/DTOs/Order/CreateOrderDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ namespace Dentizone.Application.DTOs.Order
{
public class CreateOrderDto
{
public List<string> PostIds { get; set; } = new List<string>();
public ShipInfoDto ShipInfo { get; set; } = new ShipInfoDto();
public List<string> PostIds { get; set; } = new();
public ShipInfoDto ShipInfo { get; set; } = new();
}

public class ShipInfoDtoValidation : AbstractValidator<ShipInfoDto>
Expand Down
6 changes: 3 additions & 3 deletions Dentizone.Application/DTOs/Order/OrderItemDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

public class OrderItemDto
{
public string Id { get; set; }
public string PostId { get; set; }
public string PostTitle { get; set; }
public required string Id { get; set; }
public required string PostId { get; set; }
public required string PostTitle { get; set; }
public decimal Price { get; set; }

public DateTime CreatedAt { get; set; }
Expand Down
4 changes: 2 additions & 2 deletions Dentizone.Application/DTOs/Order/OrderShipInfoDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public class OrderShipInfoDto
{
public string Street { get; set; }
public string City { get; set; }
public string Street { get; set; } = string.Empty;
public string City { get; set; } = string.Empty;
}
2 changes: 1 addition & 1 deletion Dentizone.Application/DTOs/Order/OrderStatusTimeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public class OrderStatusTimeline
{
public string Status { get; set; }
public required string Status { get; set; }
public DateTime Timestamp { get; set; }
}
8 changes: 4 additions & 4 deletions Dentizone.Application/DTOs/Order/OrderViewDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
{
public class OrderViewDto
{
public string Id { get; set; }
public string BuyerName { get; set; }
public string Id { get; set; } = string.Empty;
public string BuyerName { get; set; } = string.Empty;
public int TotalAmount { get; set; }

public OrderShipInfoDto OrderShipmentAddress { get; set; } = new OrderShipInfoDto();
public OrderShipInfoDto OrderShipmentAddress { get; set; } = new();

public DateTime CreatedAt { get; set; }
public IReadOnlyCollection<OrderStatusTimeline> StatusTimeline { get; set; } = new List<OrderStatusTimeline>();
public List<OrderItemDto> OrderItems { get; set; } = new List<OrderItemDto>();
public List<OrderItemDto> OrderItems { get; set; } = new();
}
}
Loading
Loading