feat : add validators for commands , add filters in products query , add version 0.9.16.23
parent
75d53a7f76
commit
6ac4024ea9
|
@ -6,8 +6,8 @@
|
|||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<AssemblyVersion>0.8.15.22</AssemblyVersion>
|
||||
<FileVersion>0.8.15.22</FileVersion>
|
||||
<AssemblyVersion>0.9.16.23</AssemblyVersion>
|
||||
<FileVersion>0.9.16.23</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
using NetinaShop.Repository.Behaviors;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
|
||||
builder.Host.UseSerilog();
|
||||
|
@ -71,16 +73,19 @@ builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
|
|||
|
||||
builder.RegisterMediatR(MediatRConfigurationBuilder
|
||||
.Create(typeof(RepositoryConfig).Assembly)
|
||||
.WithCustomPipelineBehavior(typeof(ValidationBehavior<,>))
|
||||
.WithAllOpenGenericHandlerTypesRegistered()
|
||||
.Build());
|
||||
|
||||
builder.RegisterMediatR(MediatRConfigurationBuilder
|
||||
.Create(typeof(CoreConfig).Assembly)
|
||||
.WithCustomPipelineBehavior(typeof(ValidationBehavior<,>))
|
||||
.WithAllOpenGenericHandlerTypesRegistered()
|
||||
.Build());
|
||||
|
||||
builder.RegisterMediatR(MediatRConfigurationBuilder
|
||||
.Create(typeof(DomainConfig).Assembly)
|
||||
.WithCustomPipelineBehavior(typeof(ValidationBehavior<,>))
|
||||
.WithAllOpenGenericHandlerTypesRegistered()
|
||||
.Build());
|
||||
});
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
namespace NetinaShop.Common.Models.Exception;
|
||||
|
||||
public class ValidationException : System.Exception
|
||||
{
|
||||
public ValidationException() : base("Validation has been failed")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ValidationException(params ValidationError[] validationErrors) : base($"{string.Join(",", validationErrors.Select(v => v.ErrorMessage))}")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ValidationException(List<ValidationError> validationErrors) : base($"{string.Join(",", validationErrors.Select(v => v.ErrorMessage))}")
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
public sealed record ValidationError(string PropertyName, string ErrorMessage);
|
|
@ -0,0 +1,10 @@
|
|||
namespace NetinaShop.Domain.Dtos.FilterDtos;
|
||||
|
||||
public class BaseFilterDto
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public string QueryName { get; set; } = string.Empty;
|
||||
public QueryFilterType QueryType { get; set; }
|
||||
public List<FilterOptionDto> Options { get; set; } = new List<FilterOptionDto>();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace NetinaShop.Domain.Dtos.FilterDtos;
|
||||
|
||||
public class ExpressDeliveryFilter : BaseFilterDto
|
||||
{
|
||||
public ExpressDeliveryFilter()
|
||||
{
|
||||
Name = "ارسال سریع";
|
||||
QueryName = "expressDelivery";
|
||||
Type = "checkbox";
|
||||
QueryType = QueryFilterType.Bool;
|
||||
Options.Add(new FilterOptionDto{Id = 0,Title = "فقط ارسال سریع"});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace NetinaShop.Domain.Dtos.FilterDtos;
|
||||
|
||||
public class FilterOptionDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
namespace NetinaShop.Domain.Dtos.FilterDtos;
|
||||
|
||||
public class PriceFilterDto : BaseFilterDto
|
||||
{
|
||||
public PriceFilterDto()
|
||||
{
|
||||
Name = "قیمت";
|
||||
QueryName = "minimumPrice";
|
||||
Type = "slider";
|
||||
QueryType = QueryFilterType.Int;
|
||||
}
|
||||
|
||||
public double MinimumValue { get; set; }
|
||||
public double MaximumValue { get; set; }
|
||||
}
|
|
@ -1,8 +1,18 @@
|
|||
namespace NetinaShop.Domain.Dtos.ResponseDtos;
|
||||
using NetinaShop.Domain.Dtos.FilterDtos;
|
||||
|
||||
namespace NetinaShop.Domain.Dtos.ResponseDtos;
|
||||
|
||||
public class GetProductsResponseDto
|
||||
{
|
||||
public List<ProductSDto> Products { get; set; } = new List<ProductSDto>();
|
||||
|
||||
public PagerResponseDto Pager { get; set; } = new PagerResponseDto();
|
||||
|
||||
public FilterResponseDto Filters { get; set; } = new FilterResponseDto();
|
||||
}
|
||||
|
||||
public class FilterResponseDto
|
||||
{
|
||||
public PriceFilterDto Price { get; set; } = new PriceFilterDto();
|
||||
public ExpressDeliveryFilter ExpressDelivery { get; set; } = new ExpressDeliveryFilter();
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace NetinaShop.Domain.Enums;
|
||||
|
||||
public enum QueryFilterType
|
||||
{
|
||||
Int = 0,
|
||||
String = 1,
|
||||
Guid = 2,
|
||||
Bool = 3,
|
||||
}
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Dtos\FilterDtos\" />
|
||||
<Folder Include="Entities\StorageFiles\" />
|
||||
<Folder Include="Models\Settings\" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
using FluentValidation;
|
||||
using ValidationException = NetinaShop.Common.Models.Exception.ValidationException;
|
||||
|
||||
namespace NetinaShop.Repository.Behaviors;
|
||||
|
||||
public class ValidationBehavior <TRequest,TResponse> : IPipelineBehavior<TRequest,TResponse> where TRequest : notnull
|
||||
{
|
||||
private readonly IEnumerable<IValidator<TRequest>> _validators;
|
||||
|
||||
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
|
||||
{
|
||||
_validators = validators;
|
||||
}
|
||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||
{
|
||||
var context = new ValidationContext<TRequest>(request);
|
||||
List<ValidationError> errors = new List<ValidationError>();
|
||||
foreach (IValidator<TRequest> validator in _validators)
|
||||
{
|
||||
var result = await validator.ValidateAsync(context, cancellationToken);
|
||||
if (!result.IsValid)
|
||||
errors.AddRange(result.Errors.Select(v => new ValidationError(v.PropertyName, v.ErrorMessage)).Distinct());
|
||||
}
|
||||
|
||||
if (errors.Any())
|
||||
{
|
||||
throw new ValidationException(errors);
|
||||
}
|
||||
|
||||
|
||||
var response = await next();
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ public class CreateAddressCommandHandler : IRequestHandler<CreateAddressCommand,
|
|||
if (!Guid.TryParse(_currentUserService.UserId, out Guid userId))
|
||||
throw new AppException("User id wrong", ApiResultStatusCode.BadRequest);
|
||||
|
||||
|
||||
var ent = UserAddress.Create(request.Address, request.PostalCode, request.ReceiverFullName,
|
||||
request.ReceiverPhoneNumber, request.LocationLat, request.LocationLong, request.Province, request.City,
|
||||
request.Plaque, request.BuildingUnit, userId);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Brands.Validators;
|
||||
|
||||
public class CreateBrandCommandValidator : AbstractValidator<CreateBrandCommand>
|
||||
{
|
||||
public CreateBrandCommandValidator()
|
||||
{
|
||||
RuleFor(r => r.PersianName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام فارسی برند را وارد کنید");
|
||||
|
||||
RuleFor(r => r.EnglishName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام انگلیسی برند را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Brands.Validators;
|
||||
|
||||
public class UpdateBrandCommandValidator : AbstractValidator<UpdateBrandCommand>
|
||||
{
|
||||
public UpdateBrandCommandValidator()
|
||||
{
|
||||
RuleFor(r => r.PersianName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام فارسی برند را وارد کنید");
|
||||
|
||||
RuleFor(r => r.EnglishName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام انگلیسی برند را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Complexes.Validators;
|
||||
|
||||
public class CreateOrUpdateComplexCommandValidator : AbstractValidator<CreateOrUpdateComplexCommand>
|
||||
{
|
||||
public CreateOrUpdateComplexCommandValidator()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Discounts.Validators;
|
||||
|
||||
public class CreateDiscountCommandValidator : AbstractValidator<CreateDiscountCommand>
|
||||
{
|
||||
public CreateDiscountCommandValidator()
|
||||
{
|
||||
RuleFor(d=>d.Type)
|
||||
.Must((command, type) =>
|
||||
{
|
||||
if (type == DiscountType.Category)
|
||||
{
|
||||
if (command.CategoryId == default)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.WithMessage("برای تخفیف برروی دسته ها باید دسته مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.Type)
|
||||
.Must((command, type) =>
|
||||
{
|
||||
if (type == DiscountType.Product)
|
||||
{
|
||||
if (command.ProductId == default)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.WithMessage("برای تخفیف برروی کالای خاص باید کالای مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Discounts.Validators;
|
||||
|
||||
public class UpdateDiscountCommandValidator : AbstractValidator<CreateDiscountCommand>
|
||||
{
|
||||
public UpdateDiscountCommandValidator()
|
||||
{
|
||||
RuleFor(d => d.Type)
|
||||
.Must((command, type) =>
|
||||
{
|
||||
if (type == DiscountType.Category)
|
||||
{
|
||||
if (command.CategoryId == default)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.WithMessage("برای تخفیف برروی دسته ها باید دسته مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.Type)
|
||||
.Must((command, type) =>
|
||||
{
|
||||
if (type == DiscountType.Product)
|
||||
{
|
||||
if (command.ProductId == default)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.WithMessage("برای تخفیف برروی کالای خاص باید کالای مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using FluentValidation;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Newsletters.Validators;
|
||||
|
||||
public class CreateNewsletterMemberCommandValidator : AbstractValidator<CreateNewsletterMemberCommand>
|
||||
{
|
||||
public CreateNewsletterMemberCommandValidator()
|
||||
{
|
||||
RuleFor(command => command)
|
||||
.Must(command =>
|
||||
{
|
||||
if (command.PhoneNumber.IsNullOrEmpty() && command.Email.IsNullOrEmpty())
|
||||
return false;
|
||||
return true;
|
||||
})
|
||||
.WithMessage("شماره تلفن یا ایمیل را باید وارد کنید");
|
||||
|
||||
RuleFor(c=>c.PhoneNumber)
|
||||
.Must(phoneNumber =>
|
||||
{
|
||||
if(phoneNumber.IsNullOrEmpty()) return true;
|
||||
|
||||
return PhoneNumberExtensions.CheckPhoneNumber(phoneNumber);
|
||||
})
|
||||
.WithMessage("شماره تلفن ارسالی اشتباه است");
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ public class CreateProductCategoryCommandHandler : IRequestHandler<CreateProduct
|
|||
{
|
||||
_repositoryWrapper = repositoryWrapper;
|
||||
}
|
||||
|
||||
public async Task<ProductCategoryLDto> Handle(CreateProductCategoryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var ent = ProductCategory.Create(request.Name, request.Description, request.IsMain);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.ProductCategories.Validators;
|
||||
|
||||
public class CreateProductCategoryCommandValidator : AbstractValidator<CreateProductCategoryCommand>
|
||||
{
|
||||
public CreateProductCategoryCommandValidator()
|
||||
{
|
||||
RuleFor(d=>d.Name)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام دسته بندی مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.ProductCategories.Validators;
|
||||
|
||||
public class UpdateProductCategoryCommandValidator : AbstractValidator<CreateProductCategoryCommand>
|
||||
{
|
||||
public UpdateProductCategoryCommandValidator()
|
||||
{
|
||||
RuleFor(d => d.Name)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام دسته بندی مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -72,6 +72,11 @@ public class GetProductsQueryHandler : IRequestHandler<GetProductsQuery, GetProd
|
|||
.Select(ProductMapper.ProjectToSDto)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
response.Filters.Price.MaximumValue = await products.MaxAsync(p => p.Cost,cancellationToken);
|
||||
response.Filters.Price.MinimumValue = await products.MinAsync(p => p.Cost,cancellationToken);
|
||||
|
||||
|
||||
|
||||
foreach (var productSDto in productSDtos)
|
||||
{
|
||||
await _mediator.Send(new CalculateProductDiscountCommand(productSDto), cancellationToken);
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Products.Validators;
|
||||
|
||||
public class CreateProductCommandValidator : AbstractValidator<CreateProductCommand>
|
||||
{
|
||||
public CreateProductCommandValidator()
|
||||
{
|
||||
RuleFor(d => d.PersianName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام فارسی کالا مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.EnglishName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام انگلیسی کالا مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.Summery)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("توضیحات کوتاه کالا مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.CategoryId)
|
||||
.NotEqual(Guid.Empty)
|
||||
.NotEmpty()
|
||||
.WithMessage("دسته بندی کالا مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.BrandId)
|
||||
.NotEqual(Guid.Empty)
|
||||
.NotEmpty()
|
||||
.WithMessage("برند کالا مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Products.Validators;
|
||||
|
||||
public class UpdateProductCommandValidator : AbstractValidator<UpdateProductCommand>
|
||||
{
|
||||
public UpdateProductCommandValidator()
|
||||
{
|
||||
RuleFor(d => d.PersianName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام فارسی کالا مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.EnglishName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام انگلیسی کالا مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.Summery)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("توضیحات کوتاه کالا مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.CategoryId)
|
||||
.NotEqual(Guid.Empty)
|
||||
.NotEmpty()
|
||||
.WithMessage("دسته بندی کالا مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.BrandId)
|
||||
.NotEqual(Guid.Empty)
|
||||
.NotEmpty()
|
||||
.WithMessage("برند کالا مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Reviews.Validators;
|
||||
|
||||
public class CreateReviewCommandValidator : AbstractValidator<CreateReviewCommand>
|
||||
{
|
||||
public CreateReviewCommandValidator()
|
||||
{
|
||||
RuleFor(d => d.Title)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("عنوان نظر مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.Comment)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("متن نظر مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.ProductId)
|
||||
.NotEqual(Guid.Empty)
|
||||
.NotEmpty()
|
||||
.WithMessage("کالا مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Reviews.Validators;
|
||||
|
||||
public class UpdateReviewCommandValidator : AbstractValidator<UpdateReviewCommand>
|
||||
{
|
||||
public UpdateReviewCommandValidator()
|
||||
{
|
||||
RuleFor(d => d.Title)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("عنوان نظر مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.Comment)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("متن نظر مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.ProductId)
|
||||
.NotEqual(Guid.Empty)
|
||||
.NotEmpty()
|
||||
.WithMessage("کالا مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Warehouses.Validators;
|
||||
|
||||
public class CreateShippingCommandValidator : AbstractValidator<CreateShippingCommand>
|
||||
{
|
||||
public CreateShippingCommandValidator()
|
||||
{
|
||||
RuleFor(d => d.Name)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام روش ارسال مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.WarehouseName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("انبار ارسال مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.DeliveryCost)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("هزینه روش ارسال مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using FluentValidation;
|
||||
|
||||
namespace NetinaShop.Repository.Handlers.Warehouses.Validators;
|
||||
|
||||
public class UpdateShippingCommandValidator : AbstractValidator<UpdateShippingCommand>
|
||||
{
|
||||
public UpdateShippingCommandValidator()
|
||||
{
|
||||
RuleFor(d => d.Name)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("نام روش ارسال مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.WarehouseName)
|
||||
.NotNull()
|
||||
.NotEmpty()
|
||||
.WithMessage("انبار ارسال مورد نظر را وارد کنید");
|
||||
|
||||
RuleFor(d => d.DeliveryCost)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("هزینه روش ارسال مورد نظر را وارد کنید");
|
||||
}
|
||||
}
|
|
@ -29,8 +29,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Handlers\Brands\" />
|
||||
<Folder Include="Handlers\Complexes\" />
|
||||
<Folder Include="Models\" />
|
||||
<Folder Include="Extensions\" />
|
||||
<Folder Include="Services\Abstracts\" />
|
||||
|
|
Loading…
Reference in New Issue