Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CorePackages Added #159

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
45 changes: 45 additions & 0 deletions NArchitecture.sln
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "corePackages", "corePackages", "{F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Application", "src\corePackages\Core.Application\Core.Application.csproj", "{41A6CDA6-B909-4BC4-8466-51EE8CC03018}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.CrossCuttingConcers", "src\corePackages\Core.CrossCuttingConcers\Core.CrossCuttingConcers.csproj", "{ED379FAF-0F5A-42E4-AA07-715F8280E2FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.ElasticSearch", "src\corePackages\Core.ElasticSearch\Core.ElasticSearch.csproj", "{4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Mailing", "src\corePackages\Core.Mailing\Core.Mailing.csproj", "{7CE04CAC-8306-4E5C-9524-67C3F3C66E88}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Persistence", "src\corePackages\Core.Persistence\Core.Persistence.csproj", "{1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Security", "src\corePackages\Core.Security\Core.Security.csproj", "{D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -62,6 +76,30 @@ Global
{A06975C0-631D-4E8B-8106-D1C8E3A05D19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A06975C0-631D-4E8B-8106-D1C8E3A05D19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A06975C0-631D-4E8B-8106-D1C8E3A05D19}.Release|Any CPU.Build.0 = Release|Any CPU
{41A6CDA6-B909-4BC4-8466-51EE8CC03018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41A6CDA6-B909-4BC4-8466-51EE8CC03018}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41A6CDA6-B909-4BC4-8466-51EE8CC03018}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41A6CDA6-B909-4BC4-8466-51EE8CC03018}.Release|Any CPU.Build.0 = Release|Any CPU
{ED379FAF-0F5A-42E4-AA07-715F8280E2FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED379FAF-0F5A-42E4-AA07-715F8280E2FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED379FAF-0F5A-42E4-AA07-715F8280E2FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED379FAF-0F5A-42E4-AA07-715F8280E2FC}.Release|Any CPU.Build.0 = Release|Any CPU
{4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D}.Release|Any CPU.Build.0 = Release|Any CPU
{7CE04CAC-8306-4E5C-9524-67C3F3C66E88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CE04CAC-8306-4E5C-9524-67C3F3C66E88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CE04CAC-8306-4E5C-9524-67C3F3C66E88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CE04CAC-8306-4E5C-9524-67C3F3C66E88}.Release|Any CPU.Build.0 = Release|Any CPU
{1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FE0F616-CEBE-4589-AE2E-DD14D0751CC2}.Release|Any CPU.Build.0 = Release|Any CPU
{D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D20AC6FA-3133-40B1-B4B2-630F6D08ECF9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -74,6 +112,13 @@ Global
{5A5C5789-75A7-4773-AB44-13721819502A} = {F4CFDEC8-18F0-4A9C-A16B-3045B325FA69}
{8696C5CF-50A9-41A3-848F-414D39A8FB21} = {F4CFDEC8-18F0-4A9C-A16B-3045B325FA69}
{A06975C0-631D-4E8B-8106-D1C8E3A05D19} = {07E15C51-014F-4C05-B709-C273D2D4E78C}
{F7AAFAB2-1A86-4672-A160-A1F8277CDE3E} = {BC7CA20F-741B-4757-8833-EA38E62FC786}
{41A6CDA6-B909-4BC4-8466-51EE8CC03018} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
{ED379FAF-0F5A-42E4-AA07-715F8280E2FC} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
{4A5607F1-D28E-4E2D-ADCE-F94A3B3BD96D} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
{7CE04CAC-8306-4E5C-9524-67C3F3C66E88} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
{1FE0F616-CEBE-4589-AE2E-DD14D0751CC2} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
{D20AC6FA-3133-40B1-B4B2-630F6D08ECF9} = {F7AAFAB2-1A86-4672-A160-A1F8277CDE3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9208CFCE-156A-49CD-9E43-32CE67AAB957}
Expand Down
26 changes: 26 additions & 0 deletions src/corePackages/Core.Application/Core.Application.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="FluentValidation" Version="10.3.6" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="10.3.6" />
<PackageReference Include="MediatR" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core.CrossCuttingConcers\Core.CrossCuttingConcers.csproj" />
<ProjectReference Include="..\Core.Security\Core.Security.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Pipelines\Caching\" />
<Folder Include="Pipelines\Authorization\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Core.CrossCuttingConcerns.Exceptions;
using Core.Security.Extensions;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;

namespace Core.Application.Pipelines.Authorization;

public class AuthorizationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, ISecuredRequest
{
private readonly IHttpContextAccessor _httpContextAccessor;

public AuthorizationBehavior(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}

public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
List<string>? roleClaims = _httpContextAccessor.HttpContext.User.ClaimRoles();

if (roleClaims == null) throw new AuthorizationException("Claims not found.");

bool isNotMatchedARoleClaimWithRequestRoles =
roleClaims.FirstOrDefault(roleClaim => request.Roles.Any(role => role == roleClaim)).IsNullOrEmpty();
if (isNotMatchedARoleClaimWithRequestRoles) throw new AuthorizationException("You are not authorized.");

TResponse response = await next();
return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Core.Application.Pipelines.Authorization;

public interface ISecuredRequest
{
public string[] Roles { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using MediatR;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Core.Application.Pipelines.Caching;

public class CacheRemovingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, ICacheRemoverRequest
{
private readonly IDistributedCache _cache;
private readonly ILogger<CacheRemovingBehavior<TRequest, TResponse>> _logger;

public CacheRemovingBehavior(IDistributedCache cache, ILogger<CacheRemovingBehavior<TRequest, TResponse>> logger
)
{
_cache = cache;
_logger = logger;
}

public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
TResponse response;
if (request.BypassCache) return await next();

async Task<TResponse> GetResponseAndRemoveCache()
{
response = await next();
await _cache.RemoveAsync(request.CacheKey, cancellationToken);
return response;
}

response = await GetResponseAndRemoveCache();
_logger.LogInformation($"Removed Cache -> {request.CacheKey}");

return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Core.Application.Pipelines.Caching;

public class CacheSettings
{
public int SlidingExpiration { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using MediatR;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Text;

namespace Core.Application.Pipelines.Caching;

public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, ICachableRequest
{
private readonly IDistributedCache _cache;
private readonly ILogger<CachingBehavior<TRequest, TResponse>> _logger;

private readonly CacheSettings _cacheSettings;

public CachingBehavior(IDistributedCache cache, ILogger<CachingBehavior<TRequest, TResponse>> logger,
IConfiguration configuration)
{
_cache = cache;
_logger = logger;
_cacheSettings = configuration.GetSection("CacheSettings").Get<CacheSettings>();
}

public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
TResponse response;
if (request.BypassCache) return await next();

async Task<TResponse> GetResponseAndAddToCache()
{
response = await next();
TimeSpan? slidingExpiration =
request.SlidingExpiration ?? TimeSpan.FromDays(_cacheSettings.SlidingExpiration);
DistributedCacheEntryOptions cacheOptions = new() { SlidingExpiration = slidingExpiration };
byte[] serializeData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(response));
await _cache.SetAsync(request.CacheKey, serializeData, cacheOptions, cancellationToken);
return response;
}

byte[]? cachedResponse = await _cache.GetAsync(request.CacheKey, cancellationToken);
if (cachedResponse != null)
{
response = JsonConvert.DeserializeObject<TResponse>(Encoding.Default.GetString(cachedResponse));
_logger.LogInformation($"Fetched from Cache -> {request.CacheKey}");
}
else
{
response = await GetResponseAndAddToCache();
_logger.LogInformation($"Added to Cache -> {request.CacheKey}");
}

return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Core.Application.Pipelines.Caching;

public interface ICachableRequest
{
bool BypassCache { get; }
string CacheKey { get; }
TimeSpan? SlidingExpiration { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Core.Application.Pipelines.Caching;

public interface ICacheRemoverRequest
{
bool BypassCache { get; }
string CacheKey { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Core.Application.Pipelines.Logging;

public interface ILoggableRequest
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Core.CrossCuttingConcerns.Logging;
using Core.CrossCuttingConcerns.Logging.Serilog;
using MediatR;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;

namespace Core.Application.Pipelines.Logging;

public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>, ILoggableRequest
{
private readonly LoggerServiceBase _loggerServiceBase;
private readonly IHttpContextAccessor _httpContextAccessor;


public LoggingBehavior(LoggerServiceBase loggerServiceBase, IHttpContextAccessor httpContextAccessor)
{
_loggerServiceBase = loggerServiceBase;
_httpContextAccessor = httpContextAccessor;
}

public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
List<LogParameter> logParameters = new();
logParameters.Add(new LogParameter
{
Type = request.GetType().Name,
Value = request
});

LogDetail logDetail = new()
{
MethodName = next.Method.Name,
Parameters = logParameters,
User = _httpContextAccessor.HttpContext == null ||
_httpContextAccessor.HttpContext.User.Identity.Name == null
? "?"
: _httpContextAccessor.HttpContext.User.Identity.Name
};

_loggerServiceBase.Info(JsonConvert.SerializeObject(logDetail));

return next();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using FluentValidation;
using FluentValidation.Results;
using MediatR;

namespace Core.Application.Pipelines.Validation;

public class RequestValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;

public RequestValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}

public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
ValidationContext<object> context = new(request);
List<ValidationFailure> failures = _validators
.Select(validator => validator.Validate(context))
.SelectMany(result => result.Errors)
.Where(failure => failure != null)
.ToList();
if (failures.Count != 0) throw new ValidationException(failures);
return next();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using FluentValidation;
using FluentValidation.Results;

namespace Core.Application.Pipelines.Validation;

public class ValidationTool
{
public static void Validate(IValidator validator, object entity)
{
ValidationContext<object> context = new(entity);
ValidationResult result = validator.Validate(context);
if (!result.IsValid) throw new ValidationException(result.Errors);
}
}
7 changes: 7 additions & 0 deletions src/corePackages/Core.Application/Requests/PageRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Core.Application.Requests;

public class PageRequest
{
public int Page { get; set; }
public int PageSize { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="FluentValidation" Version="10.3.6" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>

</Project>
Loading