Update
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
using MediatR.Pipeline;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BackOffice.BFF.Application.Common.Behaviours;
|
||||
|
||||
public class LoggingBehaviour<TRequest> : IRequestPreProcessor<TRequest> where TRequest : notnull
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public LoggingBehaviour(ILogger<TRequest> logger, ICurrentUserService currentUserService)
|
||||
{
|
||||
_logger = logger;
|
||||
_currentUserService = currentUserService;
|
||||
}
|
||||
|
||||
public async Task Process(TRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var requestName = typeof(TRequest).Name;
|
||||
var userId = _currentUserService.UserId ?? string.Empty;
|
||||
_logger.LogInformation("Request: {Name} {@UserId} {@Request}",
|
||||
requestName, userId, request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Diagnostics;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BackOffice.BFF.Application.Common.Behaviours;
|
||||
|
||||
public class PerformanceBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
{
|
||||
private readonly Stopwatch _timer;
|
||||
private readonly ILogger<TRequest> _logger;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public PerformanceBehaviour(ILogger<TRequest> logger, ICurrentUserService currentUserService)
|
||||
{
|
||||
_timer = new Stopwatch();
|
||||
_logger = logger;
|
||||
_currentUserService = currentUserService;
|
||||
}
|
||||
|
||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
_timer.Start();
|
||||
|
||||
var response = await next();
|
||||
|
||||
_timer.Stop();
|
||||
|
||||
var elapsedMilliseconds = _timer.ElapsedMilliseconds;
|
||||
|
||||
if (elapsedMilliseconds > 500)
|
||||
{
|
||||
var requestName = typeof(TRequest).Name;
|
||||
var userId = _currentUserService.UserId ?? string.Empty;
|
||||
|
||||
_logger.LogWarning("Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds) {@UserId} {@Request}",
|
||||
requestName, elapsedMilliseconds, userId, request);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BackOffice.BFF.Application.Common.Behaviours;
|
||||
|
||||
public class UnhandledExceptionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
{
|
||||
private readonly ILogger<TRequest> _logger;
|
||||
|
||||
public UnhandledExceptionBehaviour(ILogger<TRequest> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await next();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var requestName = typeof(TRequest).Name;
|
||||
|
||||
_logger.LogError(ex, "Request: Unhandled Exception for Request {Name} {@Request}", requestName, request);
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using ValidationException = BackOffice.BFF.Application.Common.Exceptions.ValidationException;
|
||||
|
||||
namespace BackOffice.BFF.Application.Common.Behaviours;
|
||||
|
||||
public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
||||
where TRequest : IRequest<TResponse>
|
||||
{
|
||||
private readonly IEnumerable<IValidator<TRequest>> _validators;
|
||||
|
||||
public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
|
||||
{
|
||||
_validators = validators;
|
||||
}
|
||||
|
||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (_validators.Any())
|
||||
{
|
||||
var context = new ValidationContext<TRequest>(request);
|
||||
|
||||
var validationResults = await Task.WhenAll(
|
||||
_validators.Select(v =>
|
||||
v.ValidateAsync(context, cancellationToken)));
|
||||
|
||||
var failures = validationResults
|
||||
.Where(r => r.Errors.Any())
|
||||
.SelectMany(r => r.Errors)
|
||||
.ToList();
|
||||
|
||||
if (failures.Any())
|
||||
throw new ValidationException(failures);
|
||||
}
|
||||
|
||||
return await next();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace BackOffice.BFF.Application.Common.Exceptions;
|
||||
|
||||
public class DuplicateException : Exception
|
||||
{
|
||||
public DuplicateException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateException(string name, object key)
|
||||
: base($"Entity \"{name}\" ({key}) already exists.")
|
||||
{
|
||||
}
|
||||
|
||||
public DuplicateException(string name, string field, object? key)
|
||||
: base($"Entity \"{name}\" field \"{field}\" ({key}) already exists.")
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BackOffice.BFF.Application.Common.Exceptions;
|
||||
|
||||
public class ForbiddenAccessException : Exception
|
||||
{
|
||||
public ForbiddenAccessException() : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace BackOffice.BFF.Application.Common.Exceptions;
|
||||
|
||||
public class NotFoundException : Exception
|
||||
{
|
||||
public NotFoundException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public NotFoundException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public NotFoundException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public NotFoundException(string name, object key)
|
||||
: base($"Entity \"{name}\" ({key}) was not found.")
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace BackOffice.BFF.Application.Common.Exceptions;
|
||||
|
||||
public class ValidationException : Exception
|
||||
{
|
||||
public ValidationException()
|
||||
: base("One or more validation failures have occurred.")
|
||||
{
|
||||
Errors = new Dictionary<string, string[]>();
|
||||
}
|
||||
|
||||
public ValidationException(IEnumerable<ValidationFailure> failures)
|
||||
: this()
|
||||
{
|
||||
Errors = failures
|
||||
.GroupBy(e => e.PropertyName, e => e.ErrorMessage)
|
||||
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
|
||||
}
|
||||
|
||||
public IDictionary<string, string[]> Errors { get; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using CMSMicroservice.Protobuf.Protos.Package;
|
||||
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
||||
|
||||
namespace BackOffice.BFF.Application.Common.Interfaces;
|
||||
|
||||
public interface IApplicationContractContext
|
||||
{
|
||||
#region FM
|
||||
|
||||
FileInfoContract.FileInfoContractClient FileInfos { get; }
|
||||
|
||||
#endregion
|
||||
#region CMS
|
||||
PackageContract.PackageContractClient Packages { get; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace BackOffice.BFF.Application.Common.Interfaces;
|
||||
|
||||
public interface ICurrentUserService
|
||||
{
|
||||
string? UserId { get; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace BackOffice.BFF.Application.Common.Interfaces;
|
||||
|
||||
public interface ITokenProvider
|
||||
{
|
||||
Task<string> GetTokenAsync();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Globalization;
|
||||
namespace BackOffice.BFF.Application.Common.Mappings;
|
||||
|
||||
public class GeneralMapping : IRegister
|
||||
{
|
||||
void IRegister.Register(TypeAdapterConfig config)
|
||||
{
|
||||
config.NewConfig<string, decimal>()
|
||||
.MapWith(src => decimal.Parse(src));
|
||||
|
||||
config.NewConfig<decimal, string>()
|
||||
.MapWith(src => src.ToString("R", new CultureInfo("en-us")));
|
||||
|
||||
config.NewConfig<decimal?, string>()
|
||||
.MapWith(src => src == null ? string.Empty : src.Value.ToString("R", new CultureInfo("en-us")));
|
||||
|
||||
config.NewConfig<string, decimal?>()
|
||||
.MapWith(src => string.IsNullOrEmpty(src) ? null : decimal.Parse(src));
|
||||
|
||||
config.NewConfig<Guid, string>()
|
||||
.MapWith(src => src == Guid.Empty ? string.Empty : src.ToString());
|
||||
|
||||
config.NewConfig<string, Guid>()
|
||||
.MapWith(src => string.IsNullOrEmpty(src) ? Guid.Empty : Guid.Parse(src));
|
||||
|
||||
config.NewConfig<string, Guid?>()
|
||||
.MapWith(src => string.IsNullOrEmpty(src) ? null : Guid.Parse(src));
|
||||
|
||||
config.NewConfig<Timestamp, DateTime>()
|
||||
.MapWith(src => src.ToDateTime());
|
||||
|
||||
config.NewConfig<Timestamp, DateTime?>()
|
||||
.MapWith(src => src == null ? null : src.ToDateTime());
|
||||
|
||||
config.NewConfig<DateTime, Timestamp>()
|
||||
.MapWith(src => Timestamp.FromDateTime(DateTime.SpecifyKind(src, DateTimeKind.Utc)));
|
||||
|
||||
config.NewConfig<DateTime?, Timestamp>()
|
||||
.MapWith(src => src.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(src.Value, DateTimeKind.Utc)) : null);
|
||||
|
||||
config.NewConfig<Duration, TimeSpan>()
|
||||
.MapWith(src => src.ToTimeSpan());
|
||||
|
||||
config.NewConfig<Duration, TimeSpan?>()
|
||||
.MapWith(src => src == null ? null : src.ToTimeSpan());
|
||||
|
||||
config.NewConfig<TimeSpan, Duration>()
|
||||
.MapWith(src => Duration.FromTimeSpan(src));
|
||||
|
||||
config.NewConfig<TimeSpan?, Duration>()
|
||||
.MapWith(src => src.HasValue ? Duration.FromTimeSpan(src.Value) : null);
|
||||
|
||||
config.Default
|
||||
.UseDestinationValue(member => member.SetterModifier == AccessModifier.None &&
|
||||
member.Type.IsGenericType &&
|
||||
member.Type.GetGenericTypeDefinition() == typeof(Google.Protobuf.Collections.RepeatedField<>));
|
||||
|
||||
config.NewConfig<Google.Protobuf.ByteString, byte[]>()
|
||||
.MapWith(src => src.ToByteArray());
|
||||
|
||||
config.NewConfig<byte[], Google.Protobuf.ByteString>()
|
||||
.MapWith(src => Google.Protobuf.ByteString.CopyFrom(src));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BackOffice.BFF.Application.Common.Mappings;
|
||||
|
||||
public class PackageProfile : IRegister
|
||||
{
|
||||
void IRegister.Register(TypeAdapterConfig config)
|
||||
{
|
||||
//config.NewConfig<Source,Destination>()
|
||||
// .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BackOffice.BFF.Application.Common.Mappings;
|
||||
|
||||
public class RoleProfile : IRegister
|
||||
{
|
||||
void IRegister.Register(TypeAdapterConfig config)
|
||||
{
|
||||
//config.NewConfig<Source,Destination>()
|
||||
// .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BackOffice.BFF.Application.Common.Mappings;
|
||||
|
||||
public class UserAddressProfile : IRegister
|
||||
{
|
||||
void IRegister.Register(TypeAdapterConfig config)
|
||||
{
|
||||
//config.NewConfig<Source,Destination>()
|
||||
// .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BackOffice.BFF.Application.Common.Mappings;
|
||||
|
||||
public class UserOrderProfile : IRegister
|
||||
{
|
||||
void IRegister.Register(TypeAdapterConfig config)
|
||||
{
|
||||
//config.NewConfig<Source,Destination>()
|
||||
// .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BackOffice.BFF.Application.Common.Mappings;
|
||||
|
||||
public class UserProfile : IRegister
|
||||
{
|
||||
void IRegister.Register(TypeAdapterConfig config)
|
||||
{
|
||||
//config.NewConfig<Source,Destination>()
|
||||
// .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BackOffice.BFF.Application.Common.Mappings;
|
||||
|
||||
public class UserRoleProfile : IRegister
|
||||
{
|
||||
void IRegister.Register(TypeAdapterConfig config)
|
||||
{
|
||||
//config.NewConfig<Source,Destination>()
|
||||
// .Map(dest => dest.FullName, src => $"{src.Firstname} {src.Lastname}");
|
||||
}
|
||||
}
|
||||
22
src/BackOffice.BFF.Application/Common/Models/MetaData.cs
Normal file
22
src/BackOffice.BFF.Application/Common/Models/MetaData.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace BackOffice.BFF.Application.Common.Models;
|
||||
|
||||
public class MetaData
|
||||
{
|
||||
//صفحه جاری
|
||||
public long CurrentPage { get; set; }
|
||||
|
||||
//تعداد کل صفحات
|
||||
public long TotalPage { get; set; }
|
||||
|
||||
//تعداد در هر صفحه
|
||||
public long PageSize { get; set; }
|
||||
|
||||
//تعداد کل آیتمها
|
||||
public long TotalCount { get; set; }
|
||||
|
||||
//قبلی دارد؟
|
||||
public bool HasPrevious { get; set; }
|
||||
|
||||
//بعدی دارد؟
|
||||
public bool HasNext { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BackOffice.BFF.Application.Common.Models;
|
||||
|
||||
public class PaginationState
|
||||
{
|
||||
//شماره صفحه
|
||||
public int PageNumber { get; set; }
|
||||
|
||||
//اندازه صفحه
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user