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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,8 @@ MigrationBackup/
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
*.db
*.db-shm
*.db-wal
/Tests/ConsoleAppConsole
34 changes: 34 additions & 0 deletions Core/News.Application/Common/Behaviors/ValidationBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using FluentValidation;
using MediatR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace News.Application.Common.Behaviors
{
public class ValidationBehavior<TRequest, TResponce>
: IPipelineBehavior<TRequest, TResponce> where TRequest : IRequest<TResponce>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;

public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators) => _validators = validators;

public Task<TResponce> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponce> next)
{
var context = new ValidationContext<TRequest>(request);
var fails = _validators
.Select(v => v.Validate(context))
.SelectMany(res => res.Errors)
.Where(fail => fail is not null)
.ToList();

if(fails.Any())
throw new ValidationException(fails);

return next();
}
}
}
16 changes: 6 additions & 10 deletions Core/News.Application/Common/Mappings/AssemblyMappingProfiler.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using AutoMapper;
using System.Linq;
using System.Reflection;
using AutoMapper;

namespace News.Application.Common.Mappings
{
Expand All @@ -11,17 +10,14 @@ public class AssemblyMappingProfiler : Profile

private void ApplyMappingFromAssembly(Assembly assembly)
{
var assemblyTypes = assembly.GetTypes()
.Where(t =>
t.GetInterfaces().
Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapWith<>))
);
var assemblyTypes = assembly.GetExportedTypes().Where(t => t.GetCustomAttributes<MapWithAttribute>().Any()).ToList();

foreach (var assemblyType in assemblyTypes)
{

var obj = Activator.CreateInstance(assemblyType);
assemblyType.GetMethod("CreateMapping")?.Invoke(obj, new[] { this });
foreach (var attr in assemblyType.GetCustomAttributes<MapWithAttribute>())
Comment thread
devTryer31 marked this conversation as resolved.
{
this.CreateMap(attr.MapSourceType, assemblyType);
}
}
}
}
Expand Down
11 changes: 4 additions & 7 deletions Core/News.Application/Common/Mappings/IMapWith.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using AutoMapper;

namespace News.Application.Common.Mappings
{
[System.Obsolete("This method is obsolete. Use MapToAttribute", false)]
public interface IMapWith<T>
{
void CreateMapping(Profile profile) => profile.CreateMap(typeof(T), GetType());//TODO: check for error.
public void CreateMapping(Profile profile)
=> profile.CreateMap(typeof(T), GetType());
}
}
13 changes: 13 additions & 0 deletions Core/News.Application/Common/Mappings/MapToAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace News.Application.Common.Mappings
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class MapWithAttribute : Attribute
{
public Type MapSourceType { get; set; }

public MapWithAttribute() { }
public MapWithAttribute(Type mapWithType) => MapSourceType = mapWithType;
}
}
4 changes: 4 additions & 0 deletions Core/News.Application/DIConfigurator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
using FluentValidation;
using News.Application.Common.Behaviors;

namespace News.Application
{
Expand All @@ -9,6 +11,8 @@ public static class DIConfigurator
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddMediatR(Assembly.GetExecutingAssembly());
services.AddValidatorsFromAssemblies(new[] {Assembly.GetExecutingAssembly()});
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
return services;
}
}
Expand Down
2 changes: 2 additions & 0 deletions Core/News.Application/Interfaces/INewsDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ public interface INewsDbContext
DbSet<Domain.News> News { get; set; }

Task<int> SaveChangesAsync(CancellationToken cancellationToken);

bool Initialize();
}
}
2 changes: 2 additions & 0 deletions Core/News.Application/News.Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

<ItemGroup>
<PackageReference Include="AutoMapper" Version="11.0.1" />
<PackageReference Include="FluentValidation" Version="11.0.1" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.0.1" />
<PackageReference Include="MediatR" Version="10.0.1" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.16" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using News.Application.Interfaces;
using System;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using News.Application.Interfaces;

namespace News.Application.News.Commands.Create
{
public class CreateNewsCommandHandler : MediatR.IRequestHandler<CreateNewsCommandReques, Guid>
public class CreateNewsCommandHandler : MediatR.IRequestHandler<CreateNewsCommandRequest, Guid>
{
private readonly INewsDbContext newsDb;

public CreateNewsCommandHandler(INewsDbContext newsDb) => this.newsDb = newsDb;

public async Task<Guid> Handle(CreateNewsCommandReques request, CancellationToken cancellationToken)
public async Task<Guid> Handle(CreateNewsCommandRequest request, CancellationToken cancellationToken)
{
Domain.News news = new()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace News.Application.News.Commands.Create
{
public class CreateNewsCommandReques : IRequest<Guid>
public class CreateNewsCommandRequest : IRequest<Guid>
{
public Guid UserId { get; set; }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using FluentValidation;
using System;

namespace News.Application.News.Commands.Create
{
public class CreateNewsCommandValidator : AbstractValidator<CreateNewsCommandRequest>
{
public CreateNewsCommandValidator()
{
RuleFor(req => req.UserId).NotEqual(Guid.Empty);
RuleFor(req => req.Title).NotEmpty().MaximumLength(150);
Comment thread
devTryer31 marked this conversation as resolved.
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using FluentValidation;
using System;

namespace News.Application.News.Commands.Delete
{
public class DeleteNewsCommandValidator : AbstractValidator<DeleteNewsCommandRequest>
{
public DeleteNewsCommandValidator()
{
RuleFor(req => req.UserId).NotEqual(Guid.Empty);
RuleFor(req => req.Id).NotEqual(Guid.Empty);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using FluentValidation;
using System;

namespace News.Application.News.Commands.Update
{
public class UpdateNewsCommandValidator : AbstractValidator<UpdateNewsCommandRequest>
{
public UpdateNewsCommandValidator()
{
RuleFor(req => req.UserId).NotEqual(Guid.Empty);
RuleFor(req => req.Id).NotEqual(Guid.Empty);
RuleFor(req => req.Title).MinimumLength(150).NotEmpty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using FluentValidation;
using System;

namespace News.Application.News.Queries.GetDetails
{
public class GetNewsDetailsValidator : AbstractValidator<GetNewsDetailsRequest>
{
public GetNewsDetailsValidator()
{
RuleFor(req => req.UserId).NotEqual(Guid.Empty);
RuleFor(req => req.Id).NotEqual(Guid.Empty);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

namespace News.Application.News.Queries.GetDetails
{
public class NewsDetailsVm : IMapWith<Domain.News>
[MapWith(MapSourceType = typeof(Domain.News))]
public class NewsDetailsVm
{
public Guid Id { get; set; }
public string Title { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using FluentValidation;
using System;

namespace News.Application.News.Queries.GetNewsTitlesList
{
public class GetNewsTitilesListValidator : AbstractValidator<GetNewsTitilesListRequest>
{
public GetNewsTitilesListValidator()
{
RuleFor(req => req.UserId).NotEqual(Guid.Empty);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

namespace News.Application.News.Queries.GetNewsTitlesList
{
public class NewsLookupDto : IMapWith<Domain.News>
[MapWith(MapSourceType = typeof(Domain.News))]
public class NewsLookupDto
{
public Guid Id { get; set; }
public string Title { get; set; }
Expand Down
4 changes: 4 additions & 0 deletions Infrastructure/News.Persistance/NewsDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Microsoft.EntityFrameworkCore;
using News.Application.Interfaces;
using News.Persistence.EntityTypeConfigurations;
using System.Threading;
using System.Threading.Tasks;

namespace News.Persistence
{
Expand All @@ -15,5 +17,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.ApplyConfiguration(new NewsConfiguration());
base.OnModelCreating(modelBuilder);
}

public bool Initialize() => Database.EnsureCreated();
}
}
15 changes: 11 additions & 4 deletions NewsApp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Presentation", "Presentatio
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{1742E329-98BB-4C11-B913-4958AFA14B25}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "News.Domain", "Core\News.Domain\News.Domain.csproj", "{8901FDE3-7660-4A3D-989B-D12483DE150D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "News.Domain", "Core\News.Domain\News.Domain.csproj", "{8901FDE3-7660-4A3D-989B-D12483DE150D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "News.Application", "Core\News.Application\News.Application.csproj", "{32AED4DC-CA9D-4F55-9214-9478DA1A68C5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "News.Application", "Core\News.Application\News.Application.csproj", "{32AED4DC-CA9D-4F55-9214-9478DA1A68C5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "News.Persistence", "Infrastructure\News.Persistance\News.Persistence.csproj", "{46965A37-776A-45A1-9A75-2AF1FC07E5BF}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "News.Persistence", "Infrastructure\News.Persistance\News.Persistence.csproj", "{46965A37-776A-45A1-9A75-2AF1FC07E5BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "News.WebAPI", "Presentation\News.WebAPI\News.WebAPI.csproj", "{2C5DCA55-7078-4251-B8FB-FE0584495B44}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "News.WebAPI", "Presentation\News.WebAPI\News.WebAPI.csproj", "{2C5DCA55-7078-4251-B8FB-FE0584495B44}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppConsole", "Tests\ConsoleAppConsole\ConsoleAppConsole.csproj", "{3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -41,6 +43,10 @@ Global
{2C5DCA55-7078-4251-B8FB-FE0584495B44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C5DCA55-7078-4251-B8FB-FE0584495B44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C5DCA55-7078-4251-B8FB-FE0584495B44}.Release|Any CPU.Build.0 = Release|Any CPU
{3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DA46089-4245-48EF-8DC3-8FF8B7A6FA00}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -50,6 +56,7 @@ Global
{32AED4DC-CA9D-4F55-9214-9478DA1A68C5} = {5CDDE7D7-CDA8-4E19-82FB-9460BB9D6AEE}
{46965A37-776A-45A1-9A75-2AF1FC07E5BF} = {77BBFD40-0A20-40EA-B05A-1381B15C5037}
{2C5DCA55-7078-4251-B8FB-FE0584495B44} = {D64A7A3D-AA4F-42BD-B216-1403C7C34C42}
{3DA46089-4245-48EF-8DC3-8FF8B7A6FA00} = {1742E329-98BB-4C11-B913-4958AFA14B25}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F4389CF9-FA13-4412-9317-33B6E7848912}
Expand Down
25 changes: 25 additions & 0 deletions Presentation/News.WebAPI/Controllers/BaseApiController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Security.Claims;

namespace News.WebAPI.Controllers
{
[ApiController]
[Route("api/[controller]/[action]")]
public class BaseApiController : ControllerBase
{
private MediatR.IMediator _mediator;

protected MediatR.IMediator Mediator =>
_mediator ??= HttpContext.RequestServices.GetService<MediatR.IMediator>();

internal Guid UserId =>
!User.Identity.IsAuthenticated ?
Guid.Empty :
Guid.Parse(
HttpContext.RequestServices.GetService<UserManager<IdentityUser>>().GetUserId(User)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

May be mistake in using UserManager because there may be another implementation of IdentityUser.

);
}
}
Loading