Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
4cd5b4c
create basic structure
basemkasem Feb 19, 2026
f3071a4
Create base entities and DbContext with migrations
basemkasem Feb 23, 2026
99475dd
Create UnitOfWork and Repositories Interfaces and Implementations
basemkasem Feb 24, 2026
867c034
Apply soft delete feature
basemkasem Mar 2, 2026
c77f4ce
Inject UnitOfWork to DI Container.
basemkasem Mar 2, 2026
ef660f4
Create a general response class to return for each service
basemkasem Mar 2, 2026
95637bd
Apply ISoftDeletable interface.
basemkasem Mar 2, 2026
ef30dfe
Add Category Module(DTOs, Service, Controller)
basemkasem Mar 2, 2026
2a068e4
fix update category logic
basemkasem Mar 3, 2026
c7f788d
Add pagination support to GetAllAsync and include properties to GetAl…
basemkasem Mar 7, 2026
5d0f861
Move Result<T> to Utilities folder and update references
basemkasem Mar 7, 2026
296cee7
# This is a combination of 2 commits.
basemkasem Mar 7, 2026
89df08a
Move Result<T> to Utilities folder and update references
basemkasem Mar 7, 2026
5ca2f5c
Create Product Module(Controller,Service, DTOs)
basemkasem Mar 7, 2026
32be35e
Merge remote-tracking branch 'origin/main'
basemkasem Mar 7, 2026
ded8d39
Add Product Service to DI Container.
basemkasem Mar 7, 2026
7b0751c
Add quntity attribute to product table
basemkasem Mar 8, 2026
963bb4c
fix page number issue when entered less than 1
basemkasem Mar 8, 2026
480fe77
Add Find method to IGenericRepository to support querying by conditio…
basemkasem Mar 10, 2026
b33be1e
Create Sale DTOs, Interface, and register ISaleService in DI Container.
basemkasem Mar 10, 2026
779f9dc
Create Sale DTOs, Interface, register ISaleService in DI Container, a…
basemkasem Mar 10, 2026
fd70152
Merge remote-tracking branch 'origin/main'
basemkasem Mar 10, 2026
f4eec04
Update collection navigation properties not to be nullable and change…
basemkasem Mar 13, 2026
af2fe3e
Create SaleController, add SaleService get methods
basemkasem Mar 13, 2026
92eaf59
fix empty salesDto list in GetAllSalesAsync, update GetAll endpoint t…
basemkasem Mar 13, 2026
62c8da7
Fix quantity assigned to default value and add validation for negativ…
basemkasem Mar 17, 2026
8e09dc7
Fix quantity validation for sale item quantity was not handling zero …
basemkasem Mar 17, 2026
9a3ed5b
Add README.md for E-commerce API documentation
basemkasem Mar 17, 2026
0f6c64f
Add Postman collection json file
basemkasem Mar 19, 2026
1803daa
Merge remote-tracking branch 'origin/main'
basemkasem Mar 19, 2026
a6d9433
Update README
basemkasem Mar 27, 2026
5df25cd
Add validation to CreateProductDto and UpdateProductDto, update contr…
basemkasem Mar 28, 2026
5ec82e9
Merge remote-tracking branch 'origin/main'
basemkasem Mar 28, 2026
2010954
fix quantity validation incorrect check in ProductService
basemkasem Mar 28, 2026
cf97492
fix unnecessary repeatitve validation in the same assembly assignment
basemkasem Mar 28, 2026
26ff99c
Enhance error message format in validation errors to include property…
basemkasem Mar 28, 2026
cd1d3cc
Add validation to CreateCategoryDto and UpdateCategoryDto, update con…
basemkasem Mar 28, 2026
2250178
Change names of validators to match DTOs
basemkasem Mar 28, 2026
833f12b
Create CreateSaleDtoValidator and Add FluentValidation for Sale and S…
basemkasem Mar 28, 2026
fe1ebc8
Apply global exception handling
basemkasem Mar 31, 2026
b9087c5
fix production error handling and add safe error messages
basemkasem Mar 31, 2026
5fe7a34
update postman collection
basemkasem Mar 31, 2026
5ba1eb3
Implement input validation and exception handling features
basemkasem Mar 31, 2026
fb9e8e1
Update API URL and task list in README.md
basemkasem Apr 6, 2026
c87f47e
Revise exception handling and input validation details
basemkasem Apr 6, 2026
86df173
Add Api Versioning Feature
basemkasem Apr 6, 2026
59d6ceb
Merge remote-tracking branch 'origin/main'
basemkasem Apr 6, 2026
a21fe01
Update urls to have apiVersion Variable
basemkasem Apr 6, 2026
e51adde
Enhance README with API versioning feature
basemkasem Apr 6, 2026
1b62a7f
Mark authentication & authorization task as in progress
basemkasem Apr 6, 2026
e793f57
Switch all DTOs to use record type.
basemkasem Apr 8, 2026
a46a6b5
Merge remote-tracking branch 'origin/main'
basemkasem Apr 8, 2026
94b0f82
Fix magic numbers and add validation for product and category descrei…
basemkasem Apr 9, 2026
caa9944
add ConnectionStrings section to appsettings.json
basemkasem Apr 9, 2026
7d8ea08
remove unnecessary check
basemkasem Apr 10, 2026
15c3a46
Add Jwt authentication and authorization support to the application.
basemkasem Apr 14, 2026
3a6189f
fix claims parameter call in GenerateJwt method
basemkasem Apr 14, 2026
c46a6e0
Clarify authentication details in README
basemkasem Apr 14, 2026
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
886 changes: 886 additions & 0 deletions EcommerceApi/ECommerce API.postman_collection.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Auth/AuthResponseDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Ecommerce.Core.DTOs.Auth;

public record AuthResponseDto(string Token, DateTime Expires);
7 changes: 7 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Auth/LoginDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Ecommerce.Core.DTOs.Auth;

public record LoginDto
{
public string Email { get; init; } = string.Empty;
public string Password { get; init; } = string.Empty;
}
10 changes: 10 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Auth/RegisterDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Ecommerce.Core.DTOs.Auth;

public record RegisterDto
{
public string Username { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
public string Password { get; init; } = string.Empty;
public string? Address { get; init; }
public string? PhoneNumber { get; init; }
}
8 changes: 8 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Category/CategoryDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Ecommerce.Core.DTOs.Category;

public record CategoryDto
{
public int Id { get; init; }
public string Name { get; init; } = string.Empty;
public string? Description { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Ecommerce.Core.DTOs.Category;

public record CreateCategoryDto
{
public string Name { get; init; } = string.Empty;
public string? Description { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Ecommerce.Core.DTOs.Category;

public record UpdateCategoryDto
{
public string Name { get; init; } = string.Empty;
public string? Description { get; init; }
}
11 changes: 11 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Product/CreateProductDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Ecommerce.Core.DTOs.Product;

public record CreateProductDto
{
public string Name { get; init; } = string.Empty;
public decimal Price { get; init; }
public int Quantity { get; init; } = 1;
public string? Description { get; init; }
public string? ImageUrl { get; init; }
public int CategoryId { get; init; }
}
13 changes: 13 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Product/ProductDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Ecommerce.Core.DTOs.Product;

public record ProductDto
{
public int Id { get; init; }
public string Name { get; init; } = string.Empty;
public decimal Price { get; init; }
public int Quantity { get; init; }
public string? Description { get; init; }
public string? ImageUrl { get; init; }
public int CategoryId { get; init; }
public string CategoryName { get; init; } = string.Empty;
}
11 changes: 11 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Product/UpdateProductDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Ecommerce.Core.DTOs.Product;

public record UpdateProductDto
{
public string Name { get; init; } = string.Empty;
public decimal Price { get; init; }
public int Quantity { get; init; }
public string? Description { get; init; }
public string? ImageUrl { get; init; }
public int CategoryId { get; init; }
}
12 changes: 12 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Sale/CreateSaleDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Ecommerce.Core.DTOs.Sale;

public record CreateSaleDto
{
public List<CreateSaleItemDto> Items { get; init; } = new();
}

public record CreateSaleItemDto
{
public int ProductId { get; init; }
public int Quantity { get; init; }
}
17 changes: 17 additions & 0 deletions EcommerceApi/Ecommerce.Core/DTOs/Sale/SaleDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Ecommerce.Core.DTOs.Sale;

public record SaleDto
{
public int Id { get; init; }
public DateTime CreationDate { get; init; }
public decimal TotalPrice { get; init; }
public List<SaleItemDto> Items { get; init; } = new();
}

public record SaleItemDto
{
public int ProductId { get; init; }
public string? ProductName { get; init; } = string.Empty;
public decimal UnitPrice { get; init; }
public int Quantity { get; init; }
}
19 changes: 19 additions & 0 deletions EcommerceApi/Ecommerce.Core/Ecommerce.Core.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentValidation" Version="12.1.1" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.Extensions.Identity.Stores">
<HintPath>..\..\..\..\..\..\..\..\..\..\home\basem\.nuget\packages\microsoft.extensions.identity.stores\10.0.3\lib\net10.0\Microsoft.Extensions.Identity.Stores.dll</HintPath>
</Reference>
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions EcommerceApi/Ecommerce.Core/Interfaces/Common/IBaseEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Ecommerce.Core.Interfaces.Common;

public interface IBaseEntity
{
int Id { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Ecommerce.Core.Interfaces.Common;

public interface ISoftDeletable
{
bool IsDeleted { get; set; }
DateTime? DeletedAt { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Ecommerce.Core.Models;

namespace Ecommerce.Core.Interfaces.Repositories;

public interface ICategoryRepository : IGenericRepository<Category>
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Linq.Expressions;
using Ecommerce.Core.Interfaces.Common;
using Ecommerce.Core.Utilities;

namespace Ecommerce.Core.Interfaces.Repositories;

public interface IGenericRepository<T> where T : class
Comment thread
basemkasem marked this conversation as resolved.
{
Task<List<T>> GetAllAsync(params Expression<Func<T, object?>>[] includeProperties);
Task<List<T>> GetAllAsync(PaginationParams paginationParams, params Expression<Func<T, object?>>[] includeProperties);
Task<List<T>> FindAsync(Expression<Func<T, bool>> predicate, params Expression<Func<T, object?>>[] includeProperties);
Task<T?> GetByIdAsync(int id, params Expression<Func<T, object?>>[] includes);
void Add(T entity);
void Update(T entity);
void Delete(ISoftDeletable entity);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Ecommerce.Core.Models;

namespace Ecommerce.Core.Interfaces.Repositories;

public interface IProductRepository : IGenericRepository<Product>
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Ecommerce.Core.Models;

namespace Ecommerce.Core.Interfaces.Repositories;

public interface ISaleRepository : IGenericRepository<Sale>
{
Task<Sale?> GetSaleWithItemsAsync(int id);
}
13 changes: 13 additions & 0 deletions EcommerceApi/Ecommerce.Core/Interfaces/Repositories/IUnitOfWork.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Ecommerce.Core.Models;

namespace Ecommerce.Core.Interfaces.Repositories;

public interface IUnitOfWork
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Unit of Work

❓ I know you have implemented CA and app should not know about infra, but should unit of work be defined here as a contract? Also as you are using EFCore in Infra, that already implements the UoW pattern, so is kind of pointless.

{
IProductRepository Products { get; }
ICategoryRepository Categories { get; }
ISaleRepository Sales { get; }

Task<int> CompleteAsync();
void Dispose();
}
10 changes: 10 additions & 0 deletions EcommerceApi/Ecommerce.Core/Interfaces/Services/IAuthService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Ecommerce.Core.DTOs.Auth;
using Ecommerce.Core.Utilities;

namespace Ecommerce.Core.Interfaces.Services;

public interface IAuthService
{
Task<Result<string>> Register(RegisterDto registerDto);
Task<Result<AuthResponseDto>> Login(LoginDto loginDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Ecommerce.Core.DTOs.Category;
using Ecommerce.Core.Utilities;

namespace Ecommerce.Core.Interfaces.Services;

public interface ICategoryService
{
Task<Result<CategoryDto>> CreateCategoryAsync(CreateCategoryDto category);
Task<Result<CategoryDto>> GetCategoryAsync(int id);
Task<Result<IEnumerable<CategoryDto>>> GetAllCategoriesAsync();
Task<Result<CategoryDto>> UpdateCategoryAsync(int id, UpdateCategoryDto category);
Task<Result<bool>> DeleteCategoryAsync(int id);
}
14 changes: 14 additions & 0 deletions EcommerceApi/Ecommerce.Core/Interfaces/Services/IProductService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Ecommerce.Core.DTOs;
using Ecommerce.Core.DTOs.Product;
using Ecommerce.Core.Utilities;

namespace Ecommerce.Core.Interfaces.Services;

public interface IProductService
{
Task<Result<ProductDto>> CreateProductAsync(CreateProductDto product);
Task<Result<ProductDto>> GetProductAsync(int id);
Task<Result<IEnumerable<ProductDto>>> GetAllProductsAsync(PaginationParams paginationParams);
Task<Result<ProductDto>> UpdateProductAsync(int id, UpdateProductDto product);
Task<Result<bool>> DeleteProductAsync(int id);
}
11 changes: 11 additions & 0 deletions EcommerceApi/Ecommerce.Core/Interfaces/Services/ISaleService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Ecommerce.Core.DTOs.Sale;
using Ecommerce.Core.Utilities;

namespace Ecommerce.Core.Interfaces.Services;

public interface ISaleService
{
Task<Result<SaleDto>> CreateSaleAsync(CreateSaleDto sale);
Task<Result<SaleDto>> GetSaleAsync(int id);
Task<Result<IEnumerable<SaleDto>>> GetAllSalesAsync(PaginationParams paginationParams);
}
8 changes: 8 additions & 0 deletions EcommerceApi/Ecommerce.Core/Models/ApplicationUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Identity;

namespace Ecommerce.Core.Models;

public class ApplicationUser : IdentityUser
{
public string? Address { get; set; }
}
17 changes: 17 additions & 0 deletions EcommerceApi/Ecommerce.Core/Models/Category.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Ecommerce.Core.Interfaces.Common;

namespace Ecommerce.Core.Models;

public class Category : IBaseEntity, ISoftDeletable
{
public const int MaxNameLength = 50;
public const int MaxDescriptionLength = 250;

public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Description { get; set; }
public ICollection<Product> Products { get; set; } = new List<Product>();

public bool IsDeleted { get; set; }
public DateTime? DeletedAt { get; set; }
}
21 changes: 21 additions & 0 deletions EcommerceApi/Ecommerce.Core/Models/Product.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Ecommerce.Core.Interfaces.Common;

namespace Ecommerce.Core.Models;

public class Product : IBaseEntity, ISoftDeletable
{
public const int MaxNameLength = 50;
public const int MaxDescriptionLength = 250;

public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int Quantity { get; set; }
public string? Description { get; set; }
public string? ImageUrl { get; set; }
public int CategoryId { get; set; }
public Category? Category { get; set; }

public bool IsDeleted { get; set; }
public DateTime? DeletedAt { get; set; }
}
14 changes: 14 additions & 0 deletions EcommerceApi/Ecommerce.Core/Models/Sale.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Ecommerce.Core.Interfaces.Common;

namespace Ecommerce.Core.Models;

public class Sale : IBaseEntity, ISoftDeletable
{
public int Id { get; set; }
public DateTime CreationDate { get; init; }
public decimal TotalPrice { get; init; }
public ICollection<SaleItem> Items { get; init; } = new List<SaleItem>();

public bool IsDeleted { get; set; }
public DateTime? DeletedAt { get; set; }
}
11 changes: 11 additions & 0 deletions EcommerceApi/Ecommerce.Core/Models/SaleItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Ecommerce.Core.Models;

public class SaleItem
{
public int ProductId { get; set; }
public Product? Product { get; set; }
public int SaleId { get; set; }
public Sale? Sale { get; set; }
public decimal UnitPriceAtTimeOfSale { get; set; }
public int Quantity { get; set; }
}
20 changes: 20 additions & 0 deletions EcommerceApi/Ecommerce.Core/Utilities/PaginationParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Ecommerce.Core.Utilities;

public record PaginationParams
{
private const int DefaultPageSize = 50;

public int PageNumber
{
get;
init =>
field = value > 0 ? value : 1;
} = 1;

public int PageSize
{
get;
init =>
field = value > DefaultPageSize ? DefaultPageSize : value;
} = 10;
}
Loading