This commit is contained in:
hamid
2026-06-16 01:32:43 +03:30
commit 69bbd28bb0
298 changed files with 24728 additions and 0 deletions
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Common\ValidationBase\**" />
<EmbeddedResource Remove="Common\ValidationBase\**" />
<None Remove="Common\ValidationBase\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CleanArc.Domain\CleanArc.Domain.csproj" />
<PackageReference Include="Mediator.SourceGenerator">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Mediator.Abstractions" />
<PackageReference Include="System.Linq.Async" />
</ItemGroup>
</Project>
@@ -0,0 +1,34 @@
using CleanArc.Application.Models.Common;
using Mediator;
using Microsoft.Extensions.Logging;
namespace CleanArc.Application.Common;
public class LoggingBehavior<TRequest, TResponse>(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
: IPipelineBehavior<TRequest, TResponse>
where TResponse : class
where TRequest : IRequest<TResponse>
{
public async ValueTask<TResponse> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse> next, CancellationToken cancellationToken)
{
try
{
var response = await next(message,cancellationToken);
return response;
}
catch (Exception e)
{
logger.LogError(e, e.Message);
if (typeof(TResponse).GetGenericTypeDefinition() == typeof(OperationResult<>))
{
var response = new OperationResult<TResponse> { IsException = true };
return response as TResponse;
}
return default;
}
}
}
@@ -0,0 +1,33 @@
using System.Diagnostics;
using System.Diagnostics.Metrics;
using Mediator;
namespace CleanArc.Application.Common;
public class MetricsBehaviour<TRequest, TResponse> :
IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
private readonly Histogram<long> _requestResponseDurationHistogram;
public MetricsBehaviour(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("mediator_meter");
_requestResponseDurationHistogram = meter.CreateHistogram<long>(
"Request_Response_Duration", "ms"
, "Determines the total request response durations");
}
public async ValueTask<TResponse> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse> next, CancellationToken cancellationToken)
{
var stopWatch = Stopwatch.StartNew();
var response = await next(message,cancellationToken);
stopWatch.Stop();
_requestResponseDurationHistogram.Record(stopWatch.ElapsedMilliseconds,new []{new KeyValuePair<string, object>("Request",message.GetType().Name)});
return response;
}
}
@@ -0,0 +1,40 @@
using CleanArc.Application.Models.Common;
using FluentValidation;
using FluentValidation.Results;
using Mediator;
namespace CleanArc.Application.Common;
public class ValidateCommandBehavior<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators)
: IPipelineBehavior<TRequest, TResponse>
where TResponse : IOperationResult, new()
where TRequest : IRequest<TResponse>
{
public async ValueTask<TResponse> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse> next, CancellationToken cancellationToken)
{
var errors = new List<ValidationFailure>();
foreach (var validator in validators)
{
var validationResult =
await validator.ValidateAsync(new ValidationContext<TRequest>(message), cancellationToken);
if (!validationResult.IsValid)
errors.AddRange(validationResult.Errors);
}
if (errors.Any())
{
return new TResponse()
{
ErrorMessages = errors.Select(c => new KeyValuePair<string, string>(c.PropertyName, c.ErrorMessage))
.ToList()
};
}
return await next(message, cancellationToken);
}
}
@@ -0,0 +1,13 @@
using System.Security.Claims;
using CleanArc.Application.Models.Jwt;
using CleanArc.Domain.Entities.User;
namespace CleanArc.Application.Contracts;
public interface IJwtService
{
Task<AccessToken> GenerateAsync(User user);
Task<ClaimsPrincipal> GetPrincipalFromExpiredToken(string token);
Task<AccessToken> GenerateByPhoneNumberAsync(string phoneNumber);
Task<AccessToken> RefreshToken(Guid refreshTokenId);
}
@@ -0,0 +1,31 @@
using CleanArc.Domain.Entities.User;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Application.Contracts.Identity;
public interface IAppUserManager
{
Task<IdentityResult> CreateUser(User user);
Task<IdentityResult> CreateUser(User user,string password);
Task<bool> IsExistUser(string phoneNumber);
Task<bool> IsExistUserName(string userName);
Task<string> GeneratePhoneNumberConfirmationToken(User user, string phoneNumber);
Task<User> GetUserByCode(string code);
Task<IdentityResult> ChangePhoneNumber(User user, string phoneNumber, string code);
Task<IdentityResult> VerifyUserCode(User user,string code);
Task<string> GenerateOtpCode(User user);
Task<User> GetUserByPhoneNumber(string phoneNumber);
Task<User> GetByUserName(string userName);
Task<User> GetUserByIdAsync(int userId);
Task<List<User>> GetAllUsersAsync();
Task<IdentityResult> CreateUserWithPasswordAsync(User user,string password);
Task<IdentityResult> AddUserToRoleAsync(User user, Role role);
Task<IdentityResult> IncrementAccessFailedCountAsync(User user);
Task<bool> IsUserLockedOutAsync(User user);
Task ResetUserLockoutAsync(User user);
Task UpdateUserAsync(User user);
Task UpdateSecurityStampAsync(User user);
Task<bool> IsPasswordValidAsync(User user, string password);
Task<string[]> GetRoleAsync(User user);
}
@@ -0,0 +1,16 @@
using CleanArc.Application.Models.Identity;
using CleanArc.Domain.Entities.User;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Application.Contracts.Identity;
public interface IRoleManagerService
{
Task<List<GetRolesDto>> GetRolesAsync();
Task<IdentityResult> CreateRoleAsync(CreateRoleDto model);
Task<bool> DeleteRoleAsync(int roleId);
Task<List<ActionDescriptionDto>> GetPermissionActionsAsync();
Task<RolePermissionDto> GetRolePermissionsAsync(int roleId);
Task<bool> ChangeRolePermissionsAsync(EditRolePermissionsDto model);
Task<Role> GetRoleByIdAsync(int roleId);
}
@@ -0,0 +1,12 @@
using CleanArc.Domain.Entities.Order;
namespace CleanArc.Application.Contracts.Persistence;
public interface IOrderRepository
{
Task AddOrderAsync(Order order);
Task<List<Order>> GetAllUserOrdersAsync(int userId);
Task<List<Order>> GetAllOrdersWithRelatedUserAsync();
Task<Order> GetUserOrderByIdAndUserIdAsync(int userId,int orderId,bool trackEntity);
Task DeleteUserOrdersAsync(int userId);
}
@@ -0,0 +1,9 @@
namespace CleanArc.Application.Contracts.Persistence;
public interface IUnitOfWork
{
public IUserRefreshTokenRepository UserRefreshTokenRepository { get; }
public IOrderRepository OrderRepository { get; }
Task CommitAsync();
ValueTask RollBackAsync();
}
@@ -0,0 +1,11 @@
using CleanArc.Domain.Entities.User;
namespace CleanArc.Application.Contracts.Persistence;
public interface IUserRefreshTokenRepository
{
Task<Guid> CreateToken(int userId);
Task<UserRefreshToken> GetTokenWithInvalidation(Guid id);
Task<User> GetUserByRefreshToken(Guid tokenId);
Task RemoveUserOldTokens(int userId, CancellationToken cancellationToken);
}
@@ -0,0 +1,44 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using CleanArc.Domain.Entities.User;
using CleanArc.SharedKernel.Extensions;
using Mediator;
namespace CleanArc.Application.Features.Admin.Commands.AddAdminCommand
{
internal class AddAdminCommandHandler:IRequestHandler<AddAdminCommand,OperationResult<bool>>
{
private readonly IAppUserManager _userManager;
private readonly IRoleManagerService _roleManagerService;
public AddAdminCommandHandler(IAppUserManager userManager, IRoleManagerService roleManagerService)
{
_userManager = userManager;
_roleManagerService = roleManagerService;
}
public async ValueTask<OperationResult<bool>> Handle(AddAdminCommand request, CancellationToken cancellationToken)
{
var role = await _roleManagerService.GetRoleByIdAsync(request.RoleId);
if(role is null)
return OperationResult<bool>.NotFoundResult("Specified role not found");
var newAdmin = new User { UserName = request.UserName, Email = request.Email };
var adminCreateResult =
await _userManager.CreateUserWithPasswordAsync(
newAdmin, request.Password);
if(!adminCreateResult.Succeeded)
return OperationResult<bool>.FailureResult(adminCreateResult.Errors.StringifyIdentityResultErrors());
var addAdminToRoleResult = await _userManager.AddUserToRoleAsync(newAdmin, role);
if(addAdminToRoleResult.Succeeded)
return OperationResult<bool>.SuccessResult(true);
return OperationResult<bool>.FailureResult(addAdminToRoleResult.Errors.StringifyIdentityResultErrors());
}
}
}
@@ -0,0 +1,31 @@
using CleanArc.Application.Models.Common;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Admin.Commands.AddAdminCommand;
public record AddAdminCommand
(string UserName, string Email, string Password, int RoleId) : IRequest<OperationResult<bool>>,
IValidatableModel<AddAdminCommand>
{
public IValidator<AddAdminCommand> ValidateApplicationModel(ApplicationBaseValidationModelProvider<AddAdminCommand> validator)
{
validator.RuleFor(c => c.Email)
.EmailAddress()
.WithMessage("Please enter an valid email");
validator.RuleFor(c => c.UserName)
.NotEmpty()
.NotNull()
.WithMessage("Please specify a valid username");
validator
.RuleFor(c => c.RoleId)
.GreaterThan(0)
.WithMessage("Please select a valid role");
return validator;
}
};
@@ -0,0 +1,47 @@
using CleanArc.Application.Contracts;
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Jwt;
using Mediator;
namespace CleanArc.Application.Features.Admin.Queries.GetToken;
public class AdminGetTokenQueryHandler:IRequestHandler<AdminGetTokenQuery,OperationResult<AdminGetTokenQueryResult>>
{
private readonly IAppUserManager _userManager;
private readonly IJwtService _jwtService;
public AdminGetTokenQueryHandler(IAppUserManager userManager, IJwtService jwtService)
{
_userManager = userManager;
_jwtService = jwtService;
}
public async ValueTask<OperationResult<AdminGetTokenQueryResult>> Handle(AdminGetTokenQuery request, CancellationToken cancellationToken)
{
var user = await _userManager.GetByUserName(request.UserName);
if(user is null)
return OperationResult<AdminGetTokenQueryResult>.FailureResult("User not found");
var isUserLockedOut = await _userManager.IsUserLockedOutAsync(user);
if(isUserLockedOut)
if (user.LockoutEnd != null)
return OperationResult<AdminGetTokenQueryResult>.FailureResult(
$"User is locked out. Try in {(user.LockoutEnd-DateTimeOffset.Now).Value.Minutes} Minutes");
var userRoles = await _userManager.GetRoleAsync(user);
if(!userRoles.Any())
return OperationResult<AdminGetTokenQueryResult>.FailureResult("This user does not have any role assigned");
if(!await _userManager.IsPasswordValidAsync(user, request.Password))
return OperationResult<AdminGetTokenQueryResult>.NotFoundResult("User not found");
var token= await _jwtService.GenerateAsync(user);
return OperationResult<AdminGetTokenQueryResult>.SuccessResult(new(token,userRoles));
}
}
@@ -0,0 +1,5 @@
using CleanArc.Application.Models.Jwt;
namespace CleanArc.Application.Features.Admin.Queries.GetToken;
public record AdminGetTokenQueryResult(AccessToken Token,string[] Roles);
@@ -0,0 +1,27 @@
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Jwt;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Admin.Queries.GetToken;
public record AdminGetTokenQuery(string UserName, string Password) : IRequest<OperationResult<AdminGetTokenQueryResult>>,
IValidatableModel<AdminGetTokenQuery>
{
public IValidator<AdminGetTokenQuery> ValidateApplicationModel(ApplicationBaseValidationModelProvider<AdminGetTokenQuery> validator)
{
validator.RuleFor(c => c.UserName)
.NotEmpty()
.NotNull()
.WithMessage("Please enter admin username");
validator.RuleFor(c => c.Password)
.NotEmpty()
.NotNull()
.WithMessage("Please enter admin password");
return validator;
}
};
@@ -0,0 +1,25 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Order.Commands;
internal class AddOrderCommandHandler(IUnitOfWork unitOfWork, IAppUserManager userManager)
: IRequestHandler<AddOrderCommand, OperationResult<bool>>
{
public async ValueTask<OperationResult<bool>> Handle(AddOrderCommand request, CancellationToken cancellationToken)
{
var user = await userManager.GetUserByIdAsync(request.UserId);
if(user==null)
return OperationResult<bool>.FailureResult("User Not Found");
await unitOfWork.OrderRepository.AddOrderAsync(new Domain.Entities.Order.Order()
{ UserId = user.Id, OrderName = request.OrderName });
await unitOfWork.CommitAsync();
return OperationResult<bool>.SuccessResult(true);
}
}
@@ -0,0 +1,25 @@
using System.Text.Json.Serialization;
using CleanArc.Application.Models.Common;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Order.Commands;
public record AddOrderCommand( string OrderName) : IRequest<OperationResult<bool>>,
IValidatableModel<AddOrderCommand>
{
[JsonIgnore]
public int UserId { get; set; }
public IValidator<AddOrderCommand> ValidateApplicationModel(ApplicationBaseValidationModelProvider<AddOrderCommand> validator)
{
validator.RuleFor(c => c.OrderName)
.NotEmpty()
.NotNull()
.WithMessage("Please enter your role name");
return validator;
}
}
@@ -0,0 +1,15 @@
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Order.Commands;
public class DeleteUserOrdersCommandHandler(IUnitOfWork unitOfWork) : IRequestHandler<DeleteUserOrdersCommand,OperationResult<bool>>
{
public async ValueTask<OperationResult<bool>> Handle(DeleteUserOrdersCommand request, CancellationToken cancellationToken)
{
await unitOfWork.OrderRepository.DeleteUserOrdersAsync(request.UserId);
return OperationResult<bool>.SuccessResult(true);
}
}
@@ -0,0 +1,6 @@
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Order.Commands;
public record DeleteUserOrdersCommand(int UserId):IRequest<OperationResult<bool>>;
@@ -0,0 +1,25 @@
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Order.Commands;
public class UpdateUserOrderCommandHandler(IUnitOfWork unitOfWork) : IRequestHandler<UpdateUserOrderCommand,OperationResult<bool>>
{
public async ValueTask<OperationResult<bool>> Handle(UpdateUserOrderCommand request, CancellationToken cancellationToken)
{
var order = await unitOfWork.OrderRepository.GetUserOrderByIdAndUserIdAsync(request.UserId, request.OrderId,
true);
if(order is null)
return OperationResult<bool>.NotFoundResult("Specified Order not found");
order.OrderName=request.OrderName;
await unitOfWork.CommitAsync();
return OperationResult<bool>.SuccessResult(true);
}
}
@@ -0,0 +1,23 @@
using System.Text.Json.Serialization;
using CleanArc.Application.Models.Common;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Order.Commands;
public record UpdateUserOrderCommand
(int OrderId, string OrderName) : IRequest<OperationResult<bool>>,IValidatableModel<UpdateUserOrderCommand>
{
[JsonIgnore]
public int UserId { get; set; }
public IValidator<UpdateUserOrderCommand> ValidateApplicationModel(ApplicationBaseValidationModelProvider<UpdateUserOrderCommand> validator)
{
validator.RuleFor(c => c.OrderId).NotEmpty().GreaterThan(0);
validator.RuleFor(c => c.OrderName).NotEmpty().NotNull();
return validator;
}
};
@@ -0,0 +1,6 @@
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Order.Queries.GetAllOrders;
public record GetAllOrdersQuery():IRequest<OperationResult<List<GetAllOrdersQueryResult>>>;
@@ -0,0 +1,20 @@
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Application.Models.Common;
using MapsterMapper;
using Mediator;
namespace CleanArc.Application.Features.Order.Queries.GetAllOrders
{
internal class GetAllOrdersQueryHandler(IUnitOfWork unitOfWork, IMapper mapper)
: IRequestHandler<GetAllOrdersQuery, OperationResult<List<GetAllOrdersQueryResult>>>
{
public async ValueTask<OperationResult<List<GetAllOrdersQueryResult>>> Handle(GetAllOrdersQuery request, CancellationToken cancellationToken)
{
var orders = await unitOfWork.OrderRepository.GetAllOrdersWithRelatedUserAsync();
var result = orders.Select(mapper.Map<Domain.Entities.Order.Order,GetAllOrdersQueryResult>).ToList();
return OperationResult<List<GetAllOrdersQueryResult>>.SuccessResult(result);
}
}
}
@@ -0,0 +1,19 @@
using Mapster;
namespace CleanArc.Application.Features.Order.Queries.GetAllOrders;
public record GetAllOrdersQueryResult(int OrderId, string OrderName, int OrderOwnerId, string OrderOwnerUserName);
class GetAllOrdersQueryResultMapping : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<Domain.Entities.Order.Order, GetAllOrdersQueryResult>()
.Map(dest => dest.OrderId, src => src.Id)
.Map(dest => dest.OrderName, src => src.OrderName)
.Map(dest => dest.OrderOwnerId, src => src.User.Id)
.Map(dest => dest.OrderOwnerUserName, src => src.User.UserName);
}
}
@@ -0,0 +1,21 @@
using CleanArc.Application.Contracts.Persistence;
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Order.Queries.GetUserOrders;
internal class GetUserOrdersQueryHandler(IUnitOfWork unitOfWork)
: IRequestHandler<GetUserOrdersQueryModel, OperationResult<List<GetUsersQueryResultModel>>>
{
public async ValueTask<OperationResult<List<GetUsersQueryResultModel>>> Handle(GetUserOrdersQueryModel request, CancellationToken cancellationToken)
{
var orders = await unitOfWork.OrderRepository.GetAllUserOrdersAsync(request.UserId);
if(!orders.Any())
return OperationResult<List<GetUsersQueryResultModel>>.NotFoundResult("You Don't Have Any Orders");
var result = orders.Select(c => new GetUsersQueryResultModel(c.Id, c.OrderName));
return OperationResult<List<GetUsersQueryResultModel>>.SuccessResult(result.ToList());
}
}
@@ -0,0 +1,6 @@
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Order.Queries.GetUserOrders;
public record GetUserOrdersQueryModel(int UserId) : IRequest<OperationResult<List<GetUsersQueryResultModel>>>;
@@ -0,0 +1,3 @@
namespace CleanArc.Application.Features.Order.Queries.GetUserOrders;
public record GetUsersQueryResultModel(int OrderId, string OrderName);
@@ -0,0 +1,24 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Identity;
using Mediator;
namespace CleanArc.Application.Features.Role.Commands.AddRoleCommand
{
internal class AddRoleCommandHandler(IRoleManagerService roleManagerService)
: IRequestHandler<AddRoleCommand, OperationResult<bool>>
{
public async ValueTask<OperationResult<bool>> Handle(AddRoleCommand request, CancellationToken cancellationToken)
{
var addRoleResult =
await roleManagerService.CreateRoleAsync(new CreateRoleDto() { RoleName = request.RoleName });
if (addRoleResult.Succeeded)
return OperationResult<bool>.SuccessResult(true);
var errors = string.Join("\n", addRoleResult.Errors.Select(c => c.Description));
return OperationResult<bool>.FailureResult(errors);
}
}
}
@@ -0,0 +1,22 @@
using CleanArc.Application.Models.Common;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Role.Commands.AddRoleCommand;
public record AddRoleCommand(string RoleName) : IRequest<OperationResult<bool>>,
IValidatableModel<AddRoleCommand>
{
public IValidator<AddRoleCommand> ValidateApplicationModel(ApplicationBaseValidationModelProvider<AddRoleCommand> validator)
{
validator
.RuleFor(c => c.RoleName)
.NotEmpty()
.NotNull()
.WithMessage("Please enter role name");
return validator;
}
};
@@ -0,0 +1,21 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Identity;
using Mediator;
namespace CleanArc.Application.Features.Role.Commands.UpdateRoleClaimsCommand
{
internal class UpdateRoleClaimsCommandHandler(IRoleManagerService roleManagerService)
: IRequestHandler<UpdateRoleClaimsCommand, OperationResult<bool>>
{
public async ValueTask<OperationResult<bool>> Handle(UpdateRoleClaimsCommand request, CancellationToken cancellationToken)
{
var updateRoleResult = await roleManagerService.ChangeRolePermissionsAsync(new EditRolePermissionsDto()
{ RoleId = request.RoleId, Permissions = request.RoleClaimValue });
return updateRoleResult
? OperationResult<bool>.SuccessResult(true)
: OperationResult<bool>.FailureResult("Could Not Update Claims for given Role");
}
}
}
@@ -0,0 +1,7 @@
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Role.Commands.UpdateRoleClaimsCommand;
public record UpdateRoleClaimsCommand( int RoleId, List<string> RoleClaimValue):IRequest<OperationResult<bool>>;
@@ -0,0 +1,22 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Role.Queries.GetAllRolesQuery
{
internal class GetAllRolesQueryHandler(IRoleManagerService roleManagerService)
: IRequestHandler<GetAllRolesQuery, OperationResult<List<GetAllRolesQueryResponse>>>
{
public async ValueTask<OperationResult<List<GetAllRolesQueryResponse>>> Handle(GetAllRolesQuery request, CancellationToken cancellationToken)
{
var roles = await roleManagerService.GetRolesAsync();
if(!roles.Any())
return OperationResult<List<GetAllRolesQueryResponse>>.NotFoundResult("No Roles Found");
var result = roles.Select(c => new GetAllRolesQueryResponse(int.Parse(c.Id), c.Name)).ToList();
return OperationResult<List<GetAllRolesQueryResponse>>.SuccessResult(result);
}
}
}
@@ -0,0 +1,3 @@
namespace CleanArc.Application.Features.Role.Queries.GetAllRolesQuery;
public record GetAllRolesQueryResponse(int RoleId,string RoleName);
@@ -0,0 +1,6 @@
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Role.Queries.GetAllRolesQuery;
public record GetAllRolesQuery():IRequest<OperationResult<List<GetAllRolesQueryResponse>>>;
@@ -0,0 +1,24 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Role.Queries.GetAuthorizableRoutesQuery
{
internal class GetAuthorizableRoutesQueryHandler(IRoleManagerService roleManagerService)
: IRequestHandler<GetAuthorizableRoutesQuery, OperationResult<List<GetAuthorizableRoutesQueryResponse>>>
{
public async ValueTask<OperationResult<List<GetAuthorizableRoutesQueryResponse>>> Handle(GetAuthorizableRoutesQuery request, CancellationToken cancellationToken)
{
var authRoutes = await roleManagerService.GetPermissionActionsAsync();
if(!authRoutes.Any())
return OperationResult<List<GetAuthorizableRoutesQueryResponse>>.NotFoundResult("No Special auth route found");
var result = authRoutes.Select(c =>
new GetAuthorizableRoutesQueryResponse(c.Key, c.AreaName, c.ControllerName, c.ActionName,c.ControllerDescription))
.ToList();
return OperationResult<List<GetAuthorizableRoutesQueryResponse>>.SuccessResult(result);
}
}
}
@@ -0,0 +1,3 @@
namespace CleanArc.Application.Features.Role.Queries.GetAuthorizableRoutesQuery;
public record GetAuthorizableRoutesQueryResponse(string RouteKey,string AreaName,string ControllerName,string ActionName,string ControllerDescription);
@@ -0,0 +1,6 @@
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Role.Queries.GetAuthorizableRoutesQuery;
public record GetAuthorizableRoutesQuery():IRequest<OperationResult<List<GetAuthorizableRoutesQueryResponse>>>;
@@ -0,0 +1,54 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using CleanArc.Domain.Entities.User;
using MapsterMapper;
using Mediator;
using Microsoft.Extensions.Logging;
namespace CleanArc.Application.Features.Users.Commands.Create;
internal class UserCreateCommandHandler(
IAppUserManager userManager,
ILogger<UserCreateCommandHandler> logger,
IMapper mapper)
: IRequestHandler<UserCreateCommand, OperationResult<UserCreateCommandResult>>
{
public async ValueTask<OperationResult<UserCreateCommandResult>> Handle(UserCreateCommand request,
CancellationToken cancellationToken)
{
var userNameExist = await userManager.IsExistUser(request.PhoneNumber);
if (userNameExist)
return OperationResult<UserCreateCommandResult>.FailureResult("Phone number already exists");
var phoneNumberExist = await userManager.IsExistUserName(request.UserName);
if (phoneNumberExist)
return OperationResult<UserCreateCommandResult>.FailureResult("Username already exists");
//var user = new User { UserName = request.UserName, Name = request.FirstName, FamilyName = request.LastName, PhoneNumber = request.PhoneNumber };
var user = mapper.Map<User>(request);
var createResult =string.IsNullOrEmpty(request.Password)?
await userManager.CreateUser(user)
:await userManager.CreateUser(user, request.Password);
if (!createResult.Succeeded)
{
return OperationResult<UserCreateCommandResult>.FailureResult(string.Join(",",
createResult.Errors.Select(c => c.Description)));
}
var code = await userManager.GeneratePhoneNumberConfirmationToken(user, user.PhoneNumber);
logger.LogWarning($"Generated Code for User ID {user.Id} is {code}");
//TODO Send Code Via Sms Provider
return OperationResult<UserCreateCommandResult>.SuccessResult(new UserCreateCommandResult
{ UserGeneratedKey = user.GeneratedCode });
}
}
@@ -0,0 +1,6 @@
namespace CleanArc.Application.Features.Users.Commands.Create;
public class UserCreateCommandResult
{
public string UserGeneratedKey { get; set; }
}
@@ -0,0 +1,50 @@
using System.Text.RegularExpressions;
using CleanArc.Application.Models.Common;
using CleanArc.Domain.Entities.User;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Users.Commands.Create;
public record UserCreateCommand
(string UserName, string Name, string FamilyName, string PhoneNumber,string Password,string RepeatPassword)
: IRequest<OperationResult<UserCreateCommandResult>>
,IValidatableModel<UserCreateCommand>
{
public IValidator<UserCreateCommand> ValidateApplicationModel(ApplicationBaseValidationModelProvider<UserCreateCommand> validator)
{
validator
.RuleFor(c => c.Name)
.NotEmpty()
.NotNull()
.WithMessage("User must have first name");
validator.RuleFor(c => c.UserName)
.NotEmpty()
.NotNull()
.WithMessage("Please enter your username");
validator
.RuleFor(c => c.FamilyName)
.NotEmpty()
.NotNull()
.WithMessage("User must have last name");
validator.RuleFor(c => c.PhoneNumber).NotEmpty()
.NotNull().WithMessage("Phone Number is required.")
.MinimumLength(10).WithMessage("PhoneNumber must not be less than 10 characters.")
.MaximumLength(20).WithMessage("PhoneNumber must not exceed 50 characters.")
.Matches(new Regex(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$")).WithMessage("Phone number is not valid");
validator.RuleFor(c => c.Password)
.Matches(c => c.RepeatPassword)
.WithMessage("passwords do not match");
return validator;
}
}
@@ -0,0 +1,21 @@
using CleanArc.Application.Contracts;
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Jwt;
using Mediator;
namespace CleanArc.Application.Features.Users.Commands.RefreshUserTokenCommand
{
internal class RefreshUserTokenCommandHandler(IJwtService jwtService)
: IRequestHandler<RefreshUserTokenCommand, OperationResult<AccessToken>>
{
public async ValueTask<OperationResult<AccessToken>> Handle(RefreshUserTokenCommand request, CancellationToken cancellationToken)
{
var newToken = await jwtService.RefreshToken(request.RefreshToken);
if(newToken is null)
return OperationResult<AccessToken>.FailureResult("Invalid refresh token");
return OperationResult<AccessToken>.SuccessResult(newToken);
}
}
}
@@ -0,0 +1,22 @@
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Jwt;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Users.Commands.RefreshUserTokenCommand;
public record RefreshUserTokenCommand(Guid RefreshToken) : IRequest<OperationResult<AccessToken>>,
IValidatableModel<RefreshUserTokenCommand>
{
public IValidator<RefreshUserTokenCommand> ValidateApplicationModel(ApplicationBaseValidationModelProvider<RefreshUserTokenCommand> validator)
{
validator.RuleFor(c => c.RefreshToken)
.NotEmpty()
.NotNull()
.WithMessage("Please enter valid user refresh token");
return validator;
}
};
@@ -0,0 +1,22 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Users.Commands.RequestLogout
{
internal class RequestLogoutCommandHandler(IAppUserManager userManager)
: IRequestHandler<RequestLogoutCommand, OperationResult<bool>>
{
public async ValueTask<OperationResult<bool>> Handle(RequestLogoutCommand request, CancellationToken cancellationToken)
{
var user = await userManager.GetUserByIdAsync(request.UserId);
if (user == null)
return OperationResult<bool>.FailureResult("User not found");
await userManager.UpdateSecurityStampAsync(user);
return OperationResult<bool>.SuccessResult(true);
}
}
}
@@ -0,0 +1,6 @@
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Users.Commands.RequestLogout;
public record RequestLogoutCommand(int UserId):IRequest<OperationResult<bool>>;
@@ -0,0 +1,33 @@
using CleanArc.Application.Contracts;
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Jwt;
using CleanArc.SharedKernel.Extensions;
using Mediator;
namespace CleanArc.Application.Features.Users.Queries.GenerateUserToken;
internal class GenerateUserTokenQueryHandler(IJwtService jwtService, IAppUserManager userManager)
: IRequestHandler<GenerateUserTokenQuery, OperationResult<AccessToken>>
{
public async ValueTask<OperationResult<AccessToken>> Handle(GenerateUserTokenQuery request, CancellationToken cancellationToken)
{
var user = await userManager.GetUserByCode(request.UserKey);
if (user is null)
return OperationResult<AccessToken>.FailureResult("User Not found");
var result = user.PhoneNumberConfirmed? await userManager.VerifyUserCode(
user, request.Code):await userManager.ChangePhoneNumber(user,user.PhoneNumber,request.Code);
if (!result.Succeeded)
return OperationResult<AccessToken>.FailureResult(result.Errors.StringifyIdentityResultErrors());
await userManager.UpdateUserAsync(user);
var token = await jwtService.GenerateAsync(user);
return OperationResult<AccessToken>.SuccessResult(token);
}
}
@@ -0,0 +1,28 @@
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Jwt;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Users.Queries.GenerateUserToken;
public record GenerateUserTokenQuery(string UserKey, string Code) : IRequest<OperationResult<AccessToken>>,
IValidatableModel<GenerateUserTokenQuery>
{
public IValidator<GenerateUserTokenQuery> ValidateApplicationModel(ApplicationBaseValidationModelProvider<GenerateUserTokenQuery> validator)
{
validator.RuleFor(c => c.Code)
.NotEmpty()
.NotNull()
.Length(6)
.WithMessage("User code is not valid");
validator.RuleFor(c => c.UserKey)
.NotEmpty()
.NotNull()
.WithMessage("Invalid user key");
return validator;
}
};
@@ -0,0 +1,22 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using CleanArc.Domain.Entities.User;
using MapsterMapper;
using Mediator;
namespace CleanArc.Application.Features.Users.Queries.GetUsers;
internal class GetUsersQueryHandler(IAppUserManager userManager, IMapper mapper)
: IRequestHandler<GetUsersQuery, OperationResult<List<GetUsersQueryResponse>>>
{
public async ValueTask<OperationResult<List<GetUsersQueryResponse>>> Handle(GetUsersQuery request, CancellationToken cancellationToken)
{
var usersModel =
(await userManager.GetAllUsersAsync()).Select(mapper.Map<User, GetUsersQueryResponse>).ToList();
if(!usersModel.Any())
return OperationResult<List<GetUsersQueryResponse>>.NotFoundResult("No Users Found!");
return OperationResult<List<GetUsersQueryResponse>>.SuccessResult(usersModel);
}
}
@@ -0,0 +1,11 @@
using CleanArc.Domain.Entities.User;
namespace CleanArc.Application.Features.Users.Queries.GetUsers;
public record GetUsersQueryResponse
{
public string UserName { get; set; }
public string Email { get; set; }
public int UserId { get; set; }
}
@@ -0,0 +1,6 @@
using CleanArc.Application.Models.Common;
using Mediator;
namespace CleanArc.Application.Features.Users.Queries.GetUsers;
public record GetUsersQuery : IRequest<OperationResult<List<GetUsersQueryResponse>>>;
@@ -0,0 +1,27 @@
using CleanArc.Application.Contracts;
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Jwt;
using Mediator;
namespace CleanArc.Application.Features.Users.Queries.TokenRequest;
public class PasswordUserTokenRequestQueryResult
(IAppUserManager userManager,IJwtService jwtService)
:IRequestHandler<PasswordUserTokenRequestQuery,OperationResult<AccessToken>>
{
public async ValueTask<OperationResult<AccessToken>> Handle(PasswordUserTokenRequestQuery request, CancellationToken cancellationToken)
{
var user = await userManager.GetByUserName(request.UserName);
if(user is null)
return OperationResult<AccessToken>.NotFoundResult("User not found");
if(!await userManager.IsPasswordValidAsync(user,request.Password))
return OperationResult<AccessToken>.NotFoundResult("User not found");
var token = await jwtService.GenerateAsync(user);
return OperationResult<AccessToken>.SuccessResult(token);
}
}
@@ -0,0 +1,24 @@
using CleanArc.Application.Models.Common;
using CleanArc.Application.Models.Jwt;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
namespace CleanArc.Application.Features.Users.Queries.TokenRequest;
public record PasswordUserTokenRequestQuery
(string UserName,string Password)
:IValidatableModel<PasswordUserTokenRequestQuery>,IRequest<OperationResult<AccessToken>>
{
public IValidator<PasswordUserTokenRequestQuery> ValidateApplicationModel(ApplicationBaseValidationModelProvider<PasswordUserTokenRequestQuery> validator)
{
validator.RuleFor(c => c.UserName)
.NotEmpty();
validator.RuleFor(c => c.Password)
.NotEmpty();
return validator;
}
}
@@ -0,0 +1,30 @@
using CleanArc.Application.Contracts.Identity;
using CleanArc.Application.Models.Common;
using Mediator;
using Microsoft.Extensions.Logging;
namespace CleanArc.Application.Features.Users.Queries.TokenRequest;
public class UserTokenRequestQueryHandler(
IAppUserManager userManager,
ILogger<UserTokenRequestQueryHandler> logger)
: IRequestHandler<UserTokenRequestQuery, OperationResult<UserTokenRequestQueryResponse>>
{
public async ValueTask<OperationResult<UserTokenRequestQueryResponse>> Handle(UserTokenRequestQuery request, CancellationToken cancellationToken)
{
var user = await userManager.GetUserByPhoneNumber(request.UserPhoneNumber);
if(user is null)
return OperationResult<UserTokenRequestQueryResponse>.NotFoundResult("User Not found");
var code = user.PhoneNumberConfirmed? await userManager.GenerateOtpCode(user) : await userManager.GeneratePhoneNumberConfirmationToken(user,user.PhoneNumber);
logger.LogWarning($"Generated Code for user Id {user.Id} is {code}");
//TODO Send Code Via Sms Provider
return OperationResult<UserTokenRequestQueryResponse>.SuccessResult(new UserTokenRequestQueryResponse {UserKey = user.GeneratedCode});
}
}
@@ -0,0 +1,6 @@
namespace CleanArc.Application.Features.Users.Queries.TokenRequest;
public class UserTokenRequestQueryResponse
{
public string UserKey { get; set; }
}
@@ -0,0 +1,25 @@
using CleanArc.Application.Models.Common;
using CleanArc.SharedKernel.ValidationBase;
using CleanArc.SharedKernel.ValidationBase.Contracts;
using FluentValidation;
using Mediator;
using System.Text.RegularExpressions;
namespace CleanArc.Application.Features.Users.Queries.TokenRequest;
public record UserTokenRequestQuery(string UserPhoneNumber) : IRequest<OperationResult<UserTokenRequestQueryResponse>>,
IValidatableModel<UserTokenRequestQuery>
{
public IValidator<UserTokenRequestQuery> ValidateApplicationModel(ApplicationBaseValidationModelProvider<UserTokenRequestQuery> validator)
{
validator.RuleFor(c => c.UserPhoneNumber).NotEmpty()
.NotNull().WithMessage("Phone Number is required.")
.MinimumLength(10).WithMessage("PhoneNumber must not be less than 10 characters.")
.MaximumLength(20).WithMessage("PhoneNumber must not exceed 50 characters.")
.Matches(new Regex(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$")).WithMessage("Phone number is not valid");
return validator;
}
};
@@ -0,0 +1,37 @@
using System.Diagnostics;
using CleanArc.SharedKernel.Extensions;
namespace CleanArc.Application.Models.ApiResult;
public class ApiResult
{
public bool IsSuccess { get; set; }
public ApiResultStatusCode StatusCode { get; set; }
public string Message { get; set; }
public string RequestId { get; }
public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, string message = null)
{
IsSuccess = isSuccess;
StatusCode = statusCode;
Message = message ?? statusCode.ToDisplay();
RequestId = Activity.Current?.TraceId.ToHexString() ?? string.Empty;
}
}
public class ApiResult<TData> : ApiResult
{
public TData Data { get; set; }
public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, TData data, string message = null)
: base(isSuccess, statusCode, message)
{
Data = data;
}
}
@@ -0,0 +1,34 @@
using System.ComponentModel.DataAnnotations;
namespace CleanArc.Application.Models.ApiResult;
public enum ApiResultStatusCode
{
[Display(Name = "Success")]
Success = 200,
[Display(Name = "Server Error")]
ServerError = 500,
[Display(Name = "Bad Request Error")]
BadRequest = 400,
[Display(Name = "Not Found")]
NotFound = 404,
[Display(Name = "Request Process Error")]
EntityProcessError = 422,
[Display(Name = "Authentication Error")]
UnAuthorized = 401,
[Display(Name = "Authorization Error")]
Forbidden = 403,
[Display(Name = "Not Acceptable")]
NotAcceptable = 406,
[Display(Name = "Failed Dependency")]
FailedDependency = 424
}
@@ -0,0 +1,62 @@
namespace CleanArc.Application.Models.Common;
/// <summary>
/// Marker Interface To Mark Operation Result Response In Validation Pipeline
/// </summary>
public interface IOperationResult
{
bool IsSuccess { get; set; }
List<KeyValuePair<string,string>> ErrorMessages { get; set; }
bool IsException { get; set; }
bool IsNotFound { get; set; }
}
public class OperationResult<TResult> : IOperationResult
{
public TResult Result { get; set; }
public bool IsSuccess { get; set; }
public List<KeyValuePair<string,string>> ErrorMessages { get; set; } = new();
public bool IsException { get; set; }
public bool IsNotFound { get; set; }
public static OperationResult<TResult> SuccessResult(TResult result)
{
return new OperationResult<TResult> { Result = result, IsSuccess = true };
}
public static OperationResult<TResult> FailureResult(string propertyName, string message, TResult result = default)
{
var operationResult = new OperationResult<TResult> { Result = result, IsSuccess = false };
operationResult.ErrorMessages.Add(new(propertyName,message));
return operationResult;
}
public static OperationResult<TResult> FailureResult(string message, TResult result = default)
{
var operationResult = new OperationResult<TResult> { Result = result, IsSuccess = false };
operationResult.ErrorMessages.Add(new("GeneralError",message));
return operationResult;
}
public static OperationResult<TResult> NotFoundResult(string message)
{
var operationResult = new OperationResult<TResult> { IsSuccess = false, IsNotFound = true };
operationResult.ErrorMessages.Add(new("GeneralError", message));
return operationResult;
}
public void AddError(string propertyName, string message)
{
IsSuccess = false;
ErrorMessages.Add(new(propertyName,message));
}
public string GetErrorMessage()
=> string.Join(Environment.NewLine, ErrorMessages.Select(x => $"{x.Key}: {x.Value}"));
}
@@ -0,0 +1,16 @@
namespace CleanArc.Application.Models.Identity;
public class ActionDescriptionDto
{
public string Key => $"{AreaName}:{ControllerName}:{ActionName}";
public string AreaName { get; set; }
public string ControllerName { get; set; }
public string ControllerDisplayName { get; set; }
public string ActionName { get; set; }
public string ActionDisplayName { get; set; }
public string ControllerDescription { get; set; }
}
@@ -0,0 +1,6 @@
namespace CleanArc.Application.Models.Identity;
public class CreateRoleDto
{
public string RoleName { get; set; }
}
@@ -0,0 +1,7 @@
namespace CleanArc.Application.Models.Identity;
public class EditRolePermissionsDto
{
public int RoleId { get; set; }
public List<string> Permissions { get; set; }
}
@@ -0,0 +1,9 @@
using CleanArc.Domain.Entities.User;
namespace CleanArc.Application.Models.Identity;
public class GetRolesDto
{
public string Id { get; set; }
public string Name { get; set; }
}
@@ -0,0 +1,14 @@
using CleanArc.Domain.Entities.User;
namespace CleanArc.Application.Models.Identity;
public class RolePermissionDto
{
public List<string> Keys { get; set; } = new List<string>();
public Role Role { get; set; }
public int RoleId { get; set; }
public List<ActionDescriptionDto> Actions { get; set; }
}
@@ -0,0 +1,19 @@
using System.IdentityModel.Tokens.Jwt;
namespace CleanArc.Application.Models.Jwt;
public class AccessToken
{
public string access_token { get; set; }
public string refresh_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public AccessToken(JwtSecurityToken securityToken,string refreshToken="")
{
access_token = new JwtSecurityTokenHandler().WriteToken(securityToken);
token_type = "Bearer";
expires_in = (int)(securityToken.ValidTo - DateTime.UtcNow).TotalSeconds;
refresh_token = refreshToken;
}
}
@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace CleanArc.Application.Models.Jwt;
public class TokenRequest
{
[Required]
public string grant_type { get; set; }
public string username { get; set; }
public string password { get; set; }
public string refresh_token { get; set; }
public string scope { get; set; }
public string client_id { get; set; }
public string client_secret { get; set; }
}
@@ -0,0 +1,29 @@
using System.Reflection;
using CleanArc.Application.Common;
using Mapster;
using Mediator;
using Microsoft.Extensions.DependencyInjection;
namespace CleanArc.Application.ServiceConfiguration;
public static class ServiceCollectionExtension
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
services.AddMediator(options =>
{
options.ServiceLifetime = ServiceLifetime.Scoped;
options.Namespace = "CleanArc.Application.Mediator";
});
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(MetricsBehaviour<,>));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidateCommandBehavior<,>));
return services;
}
}
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\CleanArc.SharedKernel\CleanArc.SharedKernel.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,59 @@
namespace CleanArc.Domain.Common;
public interface IEntity
{
}
public interface ITimeModification
{
DateTime CreatedTime { get; set; }
DateTime? ModifiedDate { get; set; }
}
public abstract class BaseEntity<TKey> : IEntity, ITimeModification
{
public TKey Id { get; protected set; }
public override bool Equals(object obj)
{
if (!(obj is BaseEntity<TKey> other))
return false;
if (ReferenceEquals(this, other))
return true;
if (GetType() != other.GetType())
return false;
return Id.Equals(other.Id);
}
public static bool operator ==(BaseEntity<TKey> a, BaseEntity<TKey> b)
{
if (a is null && b is null)
return true;
if (a is null || b is null)
return false;
return a.Equals(b);
}
public static bool operator !=(BaseEntity<TKey> a, BaseEntity<TKey> b)
{
return !(a == b);
}
public override int GetHashCode()
{
return (GetType().ToString() + Id).GetHashCode();
}
public DateTime CreatedTime { get; set; }
public DateTime? ModifiedDate { get; set; }
}
public abstract class BaseEntity : BaseEntity<int>
{
}
@@ -0,0 +1,16 @@
using CleanArc.Domain.Common;
namespace CleanArc.Domain.Entities.Order;
public class Order:BaseEntity
{
public string OrderName { get; set; }
public bool IsDeleted { get; set; }
#region Navigation Properties
public User.User User { get; set; }
public int UserId { get; set; }
#endregion
}
@@ -0,0 +1,19 @@
using CleanArc.Domain.Common;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Domain.Entities.User;
public class Role:IdentityRole<int>,IEntity
{
public Role()
{
CreatedDate=DateTime.Now;
}
public string DisplayName { get; set; }
public DateTime CreatedDate { get; set; }
public ICollection<RoleClaim> Claims { get; set; }
public ICollection<UserRole> Users { get; set; }
}
@@ -0,0 +1,16 @@
using CleanArc.Domain.Common;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Domain.Entities.User;
public class RoleClaim:IdentityRoleClaim<int>,IEntity
{
public RoleClaim()
{
CreatedClaim=DateTime.Now;
}
public DateTime CreatedClaim { get; set; }
public Role Role { get; set; }
}
@@ -0,0 +1,29 @@
using CleanArc.Domain.Common;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Domain.Entities.User;
public class User:IdentityUser<int>,IEntity
{
public User()
{
this.GeneratedCode = Guid.NewGuid().ToString().Substring(0, 8);
}
public string Name { get; set; }
public string FamilyName { get; set; }
public string GeneratedCode { get; set; }
public ICollection<UserRole> UserRoles { get; set; }
public ICollection<UserLogin> Logins { get; set; }
public ICollection<UserClaim> Claims { get; set; }
public ICollection<UserToken> Tokens { get; set; }
public ICollection<UserRefreshToken> UserRefreshTokens { get; set; }
#region Navigation Properties
public IList<Order.Order> Orders { get; set; }
#endregion
}
@@ -0,0 +1,9 @@
using CleanArc.Domain.Common;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Domain.Entities.User;
public class UserClaim:IdentityUserClaim<int>,IEntity
{
public User User { get; set; }
}
@@ -0,0 +1,15 @@
using CleanArc.Domain.Common;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Domain.Entities.User;
public class UserLogin:IdentityUserLogin<int>,IEntity
{
public UserLogin()
{
LoggedOn=DateTime.Now;
}
public User User { get; set; }
public DateTime LoggedOn { get; set; }
}
@@ -0,0 +1,16 @@
using CleanArc.Domain.Common;
namespace CleanArc.Domain.Entities.User;
public class UserRefreshToken:BaseEntity<Guid>
{
public UserRefreshToken()
{
CreatedAt=DateTime.Now;
}
public int UserId { get; set; }
public User User { get; set; }
public DateTime CreatedAt { get; set; }
public bool IsValid { get; set; }
}
@@ -0,0 +1,12 @@
using CleanArc.Domain.Common;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Domain.Entities.User;
public class UserRole : IdentityUserRole<int>,IEntity
{
public User User { get; set; }
public Role Role { get; set; }
public DateTime CreatedUserRoleDate { get; set; }
}
@@ -0,0 +1,16 @@
using CleanArc.Domain.Common;
using Microsoft.AspNetCore.Identity;
namespace CleanArc.Domain.Entities.User;
public class UserToken:IdentityUserToken<int>,IEntity
{
public UserToken()
{
GeneratedTime=DateTime.Now;
}
public User User { get; set; }
public DateTime GeneratedTime { get; set; }
}