Skip to content

Commit

Permalink
add validation
Browse files Browse the repository at this point in the history
  • Loading branch information
thangchung committed Jan 3, 2020
1 parent bf8878b commit 14b4c96
Show file tree
Hide file tree
Showing 19 changed files with 252 additions and 71 deletions.
2 changes: 1 addition & 1 deletion src/CoolStore.Api/CoolStore.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<ItemGroup>
<PackageReference Include="FeatherHttp" Version="0.1.27-alpha.g10a1724f8f" />
<PackageReference Include="Grpc.AspNetCore" Version="2.25.0" />
<PackageReference Include="Grpc.AspNetCore" Version="2.26.0" />
<PackageReference Include="Microsoft.IdentityModel.Logging" Version="5.6.0" />
<PackageReference Include="Serilog" Version="2.9.1-dev-01154" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
Expand Down
5 changes: 5 additions & 0 deletions src/CoolStore.Catalog/CatalogApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
using CoolStore.Catalog.Data.Repository;
using CoolStore.Catalog.Domain;
using CoolStore.Protobuf.Catalogs.V1;
using FluentValidation;
using Grpc.Core;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moduliths.Domain;
using Moduliths.Infra;
using Moduliths.Infra.ValidationModel;
using System;
using System.Reflection;
using System.Threading.Tasks;
Expand All @@ -21,6 +24,8 @@ public static class Startup
public static IServiceCollection AddCatalogComponents(this IServiceCollection services, IConfiguration config)
{
services.AddMediatR(Assembly.GetEntryAssembly(), typeof(Startup).Assembly);
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestValidationBehavior<,>));
services.AddServiceByIntefaceInAssembly<Product>(typeof(IValidator<>));

services.AddDbContext<CatalogDbContext>(opt => opt.UseSqlServer(config.GetConnectionString("MainDb")));
services.AddScoped<IUnitOfWork, UnitOfWork>();
Expand Down
10 changes: 3 additions & 7 deletions src/CoolStore.Catalog/CoolStore.Catalog.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@
<PackageReference Include="FeatherHttp" Version="0.1.27-alpha.g10a1724f8f" />
<PackageReference Include="Google.Api.Gax.Grpc" Version="2.10.0" />
<PackageReference Include="Google.Protobuf" Version="3.11.2" />
<PackageReference Include="Grpc.Core" Version="2.25" />
<PackageReference Include="Grpc.Core" Version="2.26.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Grpc.Tools" Version="2.25.0">
<PackageReference Include="Grpc.Tools" Version="2.26.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
44 changes: 3 additions & 41 deletions src/CoolStore.Catalog/Data/Repository/ProductRepository.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,12 @@
using CoolStore.Catalog.Domain;
using Microsoft.EntityFrameworkCore;
using Moduliths.Domain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Moduliths.Infra.Data;

namespace CoolStore.Catalog.Data.Repository
{
public class ProductRepository : IProductRepository
public class ProductRepository : RepositoryBase<Product, CatalogDbContext>, IProductRepository
{
private readonly CatalogDbContext _dbContext;

public ProductRepository(CatalogDbContext dbContext)
{
_dbContext = dbContext ?? throw CoreException.NullArgument(nameof(dbContext));
}

public void Add(Product entity)
{
_dbContext.Products.Add(entity);
}

public async Task<IEnumerable<Product>> FindAllAsync(ISpecification<Product> specification)
{
// TODO fix it
//var query = specification.Includes
// .Aggregate(_dbContext.Set<Product>().AsQueryable(), (current, include) => current.Include(include));
//return await query.Where(specification.Expression).AsNoTracking().ToListAsync();

return await _dbContext.Products.ToListAsync();
}

public async Task<Product> FindOneAsync(ISpecification<Product> specification)
{
return await _dbContext.Products.FirstOrDefaultAsync(specification.Expression);
}

public void Remove(Product entity)
{
_dbContext.Products.Remove(entity);
}

public void Update(Product entity)
public ProductRepository(CatalogDbContext dbContext) : base(dbContext)
{
_dbContext.Products.Update(entity);
}
}
}
12 changes: 12 additions & 0 deletions src/CoolStore.Catalog/Domain/ProductByPriceSpec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Moduliths.Domain;

namespace CoolStore.Catalog.Domain
{
public class ProductByPriceSpec : SpecificationBase<Product>
{
public ProductByPriceSpec(double price)
: base(t => t.Price <= price && !t.IsDeleted)
{
}
}
}
11 changes: 0 additions & 11 deletions src/CoolStore.Catalog/Domain/ProductSpecifications.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using CoolStore.Catalog.Domain;
using CoolStore.Protobuf.Catalogs.V1;
using MediatR;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -18,12 +19,15 @@ public GetProductsByPriceAndNameHandler(IProductRepository productRepository)

public async Task<GetProductsResponse> Handle(GetProductsRequest request, CancellationToken cancellationToken)
{
// Todo: refactor and add validation
var products = await ProductRepository.FindAllAsync(new ProductWithIdSpecification());
var limitedProducts = products
var allProducts = new List<Product>();
await foreach (var product in ProductRepository.FindAllAsync(new ProductByPriceSpec(request.HighPrice)))
{
allProducts.Add(product);
}

var limitedProducts = allProducts
.Skip(request.CurrentPage - 1)
.Take(10)
.Where(x => !x.IsDeleted && x.Price <= request.HighPrice)
.ToList();

var response = new GetProductsResponse();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using CoolStore.Protobuf.Catalogs.V1;
using FluentValidation;

namespace CoolStore.Catalog.Usecases.GetProductsByPriceAndName
{
public class GetProductsByPriceAndNameValidator : AbstractValidator<GetProductsRequest>
{
public GetProductsByPriceAndNameValidator()
{
RuleFor(x => x.HighPrice)
.NotNull()
.NotEmpty()
.GreaterThan(0)
.WithMessage("[HighPrice] could not be null, empty and less than zero.");
}
}
}
4 changes: 2 additions & 2 deletions src/CoolStore.UI.Blazor/CoolStore.UI.Blazor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

<ItemGroup>
<PackageReference Include="Google.Api.Gax.Grpc" Version="2.10.0" />
<PackageReference Include="Grpc.AspNetCore" Version="2.25.0" />
<PackageReference Include="Grpc.Core" Version="2.25" />
<PackageReference Include="Grpc.AspNetCore" Version="2.26.0" />
<PackageReference Include="Grpc.Core" Version="2.26.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.0" Condition="'$(Configuration)' == 'Debug'" />
</ItemGroup>

Expand Down
5 changes: 4 additions & 1 deletion src/Moduliths.Domain/IRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ namespace Moduliths.Domain
public interface IRepository<T> where T : IAggregateRoot
{
Task<T> FindOneAsync(ISpecification<T> specification);
Task<IEnumerable<T>> FindAllAsync(ISpecification<T> specification);
IAsyncEnumerable<T> FindAllAsync(ISpecification<T> specification);
void Add(T entity);
void AddRange(IEnumerable<T> entities);
void Update(T entity);
void UpdateRange(IEnumerable<T> entities);
void Remove(T entity);
void RemoveRange(IEnumerable<T> entities);
}
}
4 changes: 2 additions & 2 deletions src/Moduliths.Domain/Moduliths.Domain.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MediatR" Version="7.0.0" />
<PackageReference Include="MediatR" Version="8.0.0" />
</ItemGroup>

</Project>
64 changes: 64 additions & 0 deletions src/Moduliths.Infra/Data/RepositoryBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using Microsoft.EntityFrameworkCore;
using Moduliths.Domain;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Moduliths.Infra.Data
{
public class RepositoryBase<T, TDbContext> : IRepository<T>
where T : class, IAggregateRoot
where TDbContext : DbContext
{
public RepositoryBase(TDbContext dbContext)
{
DbContext = dbContext ?? throw CoreException.NullArgument(nameof(dbContext));
}

public TDbContext DbContext { get; }

public void Add(T entity)
{
DbContext.Set<T>().Add(entity);
}

public void AddRange(IEnumerable<T> entities)
{
DbContext.Set<T>().AddRange(entities);
}

public virtual IAsyncEnumerable<T> FindAllAsync(ISpecification<T> specification)
{
return DbContext.Set<T>()
.AsQueryable()
.Where(specification.Expression)
.AsNoTracking()
.AsAsyncEnumerable();
}

public Task<T> FindOneAsync(ISpecification<T> specification)
{
return DbContext.Set<T>().FirstOrDefaultAsync(specification.Expression);
}

public void Remove(T entity)
{
DbContext.Set<T>().Remove(entity);
}

public void RemoveRange(IEnumerable<T> entities)
{
DbContext.Set<T>().RemoveRange(entities);
}

public void Update(T entity)
{
DbContext.Set<T>().Update(entity);
}

public void UpdateRange(IEnumerable<T> entities)
{
DbContext.Set<T>().UpdateRange(entities);
}
}
}
6 changes: 4 additions & 2 deletions src/Moduliths.Infra/Moduliths.Infra.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MediatR" Version="7.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
<PackageReference Include="FluentValidation" Version="8.6.1" />
<PackageReference Include="MediatR" Version="8.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.0" />
Expand All @@ -23,6 +24,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.0" />
<PackageReference Include="Scrutor" Version="3.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
19 changes: 19 additions & 0 deletions src/Moduliths.Infra/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using Microsoft.Extensions.DependencyInjection;

namespace Moduliths.Infra
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddServiceByIntefaceInAssembly<TRegisteredAssemblyType>(this IServiceCollection services, Type interfaceType)
{
services.Scan(s =>
s.FromAssemblyOf<TRegisteredAssemblyType>()
.AddClasses(c => c.AssignableTo(interfaceType))
.AsImplementedInterfaces()
.WithScopedLifetime());

return services;
}
}
}
24 changes: 24 additions & 0 deletions src/Moduliths.Infra/ValidationModel/RequestValidationBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Threading;
using System.Threading.Tasks;
using FluentValidation;
using MediatR;

namespace Moduliths.Infra.ValidationModel
{
public class RequestValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IValidator<TRequest> _validator;

public RequestValidationBehavior(IValidator<TRequest> validator)
{
_validator = validator;
}

public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
await _validator.HandleValidation(request);
return await next();
}
}
}
15 changes: 15 additions & 0 deletions src/Moduliths.Infra/ValidationModel/ValidationError.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Moduliths.Infra.ValidationModel
{
public class ValidationError
{
public string Field { get; }

public string Message { get; }

public ValidationError(string field, string message)
{
Field = field != string.Empty ? field : null;
Message = message;
}
}
}
14 changes: 14 additions & 0 deletions src/Moduliths.Infra/ValidationModel/ValidationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace Moduliths.Infra.ValidationModel
{
public class ValidationException : Exception
{
public ValidationException(ValidationResultModel validationResultModel)
{
ValidationResultModel = validationResultModel;
}

public ValidationResultModel ValidationResultModel { get; }
}
}
Loading

0 comments on commit 14b4c96

Please sign in to comment.