From 5ac06a4e15ba3c42cd28b444aaae68739c526854 Mon Sep 17 00:00:00 2001 From: Sergio Matias Urquin Date: Tue, 29 Apr 2025 20:16:22 -0600 Subject: [PATCH] Add project files. --- Lib.Architecture.BuildingBlocks.sln | 25 ++++++ .../Application/CommandResult.cs | 34 +++++++ .../Application/QueryResult.cs | 23 +++++ .../Commands/ICommand.cs | 9 ++ .../Commands/ICommandHandler.cs | 21 +++++ .../Contracts/Aggregates/IAggregateRoot.cs | 12 +++ .../Contracts/Events/IDomainEvent.cs | 13 +++ .../Contracts/Events/IDomainEventPublisher.cs | 13 +++ .../Contracts/Events/IEvent.cs | 12 +++ .../Contracts/Events/IEventHandler.cs | 12 +++ .../Endpoints/BaseEndpoint.cs | 48 ++++++++++ .../Entity/BaseEntity.cs | 13 +++ .../Entity/ValueObject.cs | 40 +++++++++ .../Events/DomainEvents.cs | 16 ++++ .../Helpers/ApiResponseHelper.cs | 63 +++++++++++++ .../Token/AuthenticatedHttpClientHandler.cs | 32 +++++++ .../Helpers/Token/HttpContextTokenProvider.cs | 31 +++++++ .../Helpers/Token/ITokenProvider.cs | 19 ++++ .../Lib.Architecture.BuildingBlocks.csproj | 24 +++++ .../Messages/Command.cs | 16 ++++ .../Messages/Event.cs | 12 +++ .../Messages/Message.cs | 15 ++++ .../Presentation/Adapters/BadGatewayError.cs | 31 +++++++ .../Presentation/Adapters/BadRequestError.cs | 29 ++++++ .../Presentation/Adapters/BusinessError.cs | 24 +++++ .../Presentation/Adapters/ErrorDetails.cs | 31 +++++++ .../Adapters/GatewayTimeoutError.cs | 28 ++++++ .../Adapters/GenericErrorResponse.cs | 10 +++ .../Presentation/Adapters/HttpError.cs | 39 ++++++++ .../Adapters/InternalServerError.cs | 34 +++++++ .../Adapters/Mobile2.0/GPSPointAdapter.cs | 88 +++++++++++++++++++ .../Adapters/Mobile2.0/GeometryAdapter.cs | 15 ++++ .../Adapters/Mobile2.0/MapsheetAdapter.cs | 87 ++++++++++++++++++ .../Adapters/Mobile2.0/SurveyAdapter.cs | 29 ++++++ .../PreconditionFailedObjectResult.cs | 20 +++++ .../Adapters/ServiceUnavaliableResult.cs | 23 +++++ .../Adapters/TimeoutServiceError.cs | 21 +++++ .../Presentation/Base/BasePresenter.cs | 85 ++++++++++++++++++ .../Presentation/IAcceptedPort.cs | 7 ++ .../Presentation/IBadRequestPort.cs | 7 ++ .../Presentation/IBasePort.cs | 9 ++ .../Presentation/IBusinessErrorPort.cs | 7 ++ .../Presentation/ICreatedPort.cs | 7 ++ .../Presentation/IForbiddenPort.cs | 8 ++ .../Presentation/IInternalServerErrorPort.cs | 7 ++ .../Presentation/INoContentPort.cs | 7 ++ .../Presentation/INotFoundPort.cs | 8 ++ .../Presentation/IServiceUnavaliablePort.cs | 8 ++ .../Presentation/ISuccessPort.cs | 11 +++ .../Presentation/ITimeoutPort.cs | 8 ++ .../Presentation/IUnauthorizedPort.cs | 7 ++ .../Presentation/IValidationErrorPort.cs | 9 ++ .../Queries/Handler/IQueryHandler.cs | 13 +++ .../Queries/IQuery.cs | 9 ++ .../RegisterBuildingBlocks.cs | 44 ++++++++++ .../Utils/PrivateResolver.cs | 21 +++++ .../Utils/TypeGetter.cs | 13 +++ .../Validation/IValidationHandler.cs | 10 +++ .../Validation/Notification/INotifier.cs | 10 +++ .../Validation/Notification/IValidable.cs | 8 ++ .../Validation/Notification/Notification.cs | 11 +++ .../Validation/Notification/Notificator.cs | 80 +++++++++++++++++ 62 files changed, 1426 insertions(+) create mode 100644 Lib.Architecture.BuildingBlocks.sln create mode 100644 Lib.Architecture.BuildingBlocks/Application/CommandResult.cs create mode 100644 Lib.Architecture.BuildingBlocks/Application/QueryResult.cs create mode 100644 Lib.Architecture.BuildingBlocks/Commands/ICommand.cs create mode 100644 Lib.Architecture.BuildingBlocks/Commands/ICommandHandler.cs create mode 100644 Lib.Architecture.BuildingBlocks/Contracts/Aggregates/IAggregateRoot.cs create mode 100644 Lib.Architecture.BuildingBlocks/Contracts/Events/IDomainEvent.cs create mode 100644 Lib.Architecture.BuildingBlocks/Contracts/Events/IDomainEventPublisher.cs create mode 100644 Lib.Architecture.BuildingBlocks/Contracts/Events/IEvent.cs create mode 100644 Lib.Architecture.BuildingBlocks/Contracts/Events/IEventHandler.cs create mode 100644 Lib.Architecture.BuildingBlocks/Endpoints/BaseEndpoint.cs create mode 100644 Lib.Architecture.BuildingBlocks/Entity/BaseEntity.cs create mode 100644 Lib.Architecture.BuildingBlocks/Entity/ValueObject.cs create mode 100644 Lib.Architecture.BuildingBlocks/Events/DomainEvents.cs create mode 100644 Lib.Architecture.BuildingBlocks/Helpers/ApiResponseHelper.cs create mode 100644 Lib.Architecture.BuildingBlocks/Helpers/Token/AuthenticatedHttpClientHandler.cs create mode 100644 Lib.Architecture.BuildingBlocks/Helpers/Token/HttpContextTokenProvider.cs create mode 100644 Lib.Architecture.BuildingBlocks/Helpers/Token/ITokenProvider.cs create mode 100644 Lib.Architecture.BuildingBlocks/Lib.Architecture.BuildingBlocks.csproj create mode 100644 Lib.Architecture.BuildingBlocks/Messages/Command.cs create mode 100644 Lib.Architecture.BuildingBlocks/Messages/Event.cs create mode 100644 Lib.Architecture.BuildingBlocks/Messages/Message.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/BadGatewayError.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/BadRequestError.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/BusinessError.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/ErrorDetails.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/GatewayTimeoutError.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/GenericErrorResponse.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/HttpError.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/InternalServerError.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/GPSPointAdapter.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/GeometryAdapter.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/MapsheetAdapter.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/SurveyAdapter.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/PreconditionFailedObjectResult.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/ServiceUnavaliableResult.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Adapters/TimeoutServiceError.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/Base/BasePresenter.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IAcceptedPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IBadRequestPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IBasePort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IBusinessErrorPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/ICreatedPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IForbiddenPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IInternalServerErrorPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/INoContentPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/INotFoundPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IServiceUnavaliablePort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/ISuccessPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/ITimeoutPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IUnauthorizedPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Presentation/IValidationErrorPort.cs create mode 100644 Lib.Architecture.BuildingBlocks/Queries/Handler/IQueryHandler.cs create mode 100644 Lib.Architecture.BuildingBlocks/Queries/IQuery.cs create mode 100644 Lib.Architecture.BuildingBlocks/RegisterBuildingBlocks.cs create mode 100644 Lib.Architecture.BuildingBlocks/Utils/PrivateResolver.cs create mode 100644 Lib.Architecture.BuildingBlocks/Utils/TypeGetter.cs create mode 100644 Lib.Architecture.BuildingBlocks/Validation/IValidationHandler.cs create mode 100644 Lib.Architecture.BuildingBlocks/Validation/Notification/INotifier.cs create mode 100644 Lib.Architecture.BuildingBlocks/Validation/Notification/IValidable.cs create mode 100644 Lib.Architecture.BuildingBlocks/Validation/Notification/Notification.cs create mode 100644 Lib.Architecture.BuildingBlocks/Validation/Notification/Notificator.cs diff --git a/Lib.Architecture.BuildingBlocks.sln b/Lib.Architecture.BuildingBlocks.sln new file mode 100644 index 0000000..79ec4b8 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34309.116 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib.Architecture.BuildingBlocks", "Lib.Architecture.BuildingBlocks\Lib.Architecture.BuildingBlocks.csproj", "{FBB6B156-0EB5-4728-8B4D-7E761F7C7506}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FBB6B156-0EB5-4728-8B4D-7E761F7C7506}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBB6B156-0EB5-4728-8B4D-7E761F7C7506}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBB6B156-0EB5-4728-8B4D-7E761F7C7506}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBB6B156-0EB5-4728-8B4D-7E761F7C7506}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {48281157-94C3-4A0A-B0A9-D9BB2A94DBA7} + EndGlobalSection +EndGlobal diff --git a/Lib.Architecture.BuildingBlocks/Application/CommandResult.cs b/Lib.Architecture.BuildingBlocks/Application/CommandResult.cs new file mode 100644 index 0000000..c9e82d7 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Application/CommandResult.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Lib.Architecture.BuildingBlocks +{ + public class CommandResult : ICommandResult + { + public CommandResult() { } + protected ActionResult ViewModelResult { get; set; } + public bool Success { get; set; } + public object Result { get; set; } + public ActionResult ViewModel => ViewModelResult; + public IReadOnlyCollection Messages { get; set; } + + public CommandResult(bool success) + { + Success = success; + } + public CommandResult(bool success, IReadOnlyCollection messages) + { + Success = success; + Messages = messages; + } + public CommandResult(bool success, object result) + { + Result = result; + Success = success; + } + public void ValidationErrors(IEnumerable notifications) + { + ViewModelResult = new PreconditionFailedObjectResult(notifications); + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Application/QueryResult.cs b/Lib.Architecture.BuildingBlocks/Application/QueryResult.cs new file mode 100644 index 0000000..1b5ce36 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Application/QueryResult.cs @@ -0,0 +1,23 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public class QueryResult : IQueryResult + { + public QueryResult() { } + public QueryResult(bool success, IReadOnlyCollection messages) + { + Success = success; + Messages = messages; + } + + public QueryResult(bool success, dynamic result) + { + Success = success; + Result = result; + } + + public bool Success { get; set; } + public dynamic Result { get; set; } + public IReadOnlyCollection Messages { get; set; } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Commands/ICommand.cs b/Lib.Architecture.BuildingBlocks/Commands/ICommand.cs new file mode 100644 index 0000000..6826cee --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Commands/ICommand.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface ICommand { bool Validate(); } + public interface ICommandResult { } +} diff --git a/Lib.Architecture.BuildingBlocks/Commands/ICommandHandler.cs b/Lib.Architecture.BuildingBlocks/Commands/ICommandHandler.cs new file mode 100644 index 0000000..9456785 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Commands/ICommandHandler.cs @@ -0,0 +1,21 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public interface ICommandHandler where Tin : ICommand + { + ValueTask ExecuteAsync(Tin command, CancellationToken cancellationToken = default); + } + public interface IComponentHandler where TIn : ICommand + { + ValueTask ExecuteAsync(TIn command, CancellationToken cancellationToken = default); + } + + //out + public interface IComponentHandler where TIn : ICommand + where TOut : class + { + ValueTask ExecuteAsync(TIn command, CancellationToken cancellationToken = default); + } + + +} diff --git a/Lib.Architecture.BuildingBlocks/Contracts/Aggregates/IAggregateRoot.cs b/Lib.Architecture.BuildingBlocks/Contracts/Aggregates/IAggregateRoot.cs new file mode 100644 index 0000000..2d7bfed --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Contracts/Aggregates/IAggregateRoot.cs @@ -0,0 +1,12 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IAggregateRoot + { + Guid Id { get; } + } + public interface IAggregateRoot where TType : new() + { + TType Id { get; } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Contracts/Events/IDomainEvent.cs b/Lib.Architecture.BuildingBlocks/Contracts/Events/IDomainEvent.cs new file mode 100644 index 0000000..72226de --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Contracts/Events/IDomainEvent.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IDomainEvent + { + DateTime OccorredOn { get; } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Contracts/Events/IDomainEventPublisher.cs b/Lib.Architecture.BuildingBlocks/Contracts/Events/IDomainEventPublisher.cs new file mode 100644 index 0000000..be88f07 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Contracts/Events/IDomainEventPublisher.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IDomainEventPublisher where TEvent : IEvent + { + ValueTask Publish(TEvent @event, CancellationToken cancellationToken); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Contracts/Events/IEvent.cs b/Lib.Architecture.BuildingBlocks/Contracts/Events/IEvent.cs new file mode 100644 index 0000000..3404b82 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Contracts/Events/IEvent.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IEvent + { + } +} diff --git a/Lib.Architecture.BuildingBlocks/Contracts/Events/IEventHandler.cs b/Lib.Architecture.BuildingBlocks/Contracts/Events/IEventHandler.cs new file mode 100644 index 0000000..b510440 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Contracts/Events/IEventHandler.cs @@ -0,0 +1,12 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IEventHandler where T : IDomainEvent + { + ValueTask Execute(T @event, CancellationToken ctx); + } + public interface IEventHandler where TIn : IDomainEvent where TOut : BaseEntity + { + ValueTask Execute(TIn @event, CancellationToken ctx); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Endpoints/BaseEndpoint.cs b/Lib.Architecture.BuildingBlocks/Endpoints/BaseEndpoint.cs new file mode 100644 index 0000000..5bc4d87 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Endpoints/BaseEndpoint.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Blueprint.External +{ + public class BaseEndpoint + { + public string Url { get; set; } + public Uri Uri { get; set; } + public string Token { get; set; } + public string APIKey { get; set; } + public string APISecret { get; set; } + public string APIName { get; set; } + + } + public class AccessLog + { + private Guid CorrelationId { get; set; } = new Guid(); + protected DateTime DateTime { get; set; } = DateTime.Now; + protected string Method { get; set; } + protected object Request { get; set; } + protected string User { get; set; } + + public void GetCurrentMethodExecution() + { + var method = System.Reflection.MethodBase.GetCurrentMethod(); + + Method = string.Format("{0}.{1}({2})", + method.ReflectedType.FullName, method.Name, + string.Join(",", method.GetParameters(). + Select(o => string.Format("{0} {1}", o.ParameterType, o.Name)). + ToArray())); + } + + public void SetCurrentRequestExecution(string request) + { + + } + public void SetCurrentUserExecution(string user) + { + + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Entity/BaseEntity.cs b/Lib.Architecture.BuildingBlocks/Entity/BaseEntity.cs new file mode 100644 index 0000000..bb7d92b --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Entity/BaseEntity.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace Lib.Architecture.BuildingBlocks +{ + public class BaseEntity : Notificator where TValidator : IValidator, new() + { + public virtual DateOnly Created { get; set; } + } + public class BaseEntity : Notificator + { + public virtual DateOnly Created { get; set; } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Entity/ValueObject.cs b/Lib.Architecture.BuildingBlocks/Entity/ValueObject.cs new file mode 100644 index 0000000..f1964ef --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Entity/ValueObject.cs @@ -0,0 +1,40 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public abstract class ValueObject + { + protected abstract IEnumerable GetEqualityComponents(); + public override bool Equals(object obj) + { + + if (obj is null || obj.GetType() != GetType()) + return false; + + var other = (ValueObject)obj; + + return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); + } + + public override int GetHashCode() + { + return GetEqualityComponents() + .Select(x => x is not null ? x.GetHashCode() : 0) + .Aggregate((x, y) => x ^ y); + } + + protected static bool EqualOperator(ValueObject left, ValueObject right) + { + if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null)) + return false; + + return ReferenceEquals(left, right) || left.Equals(right); + } + + protected static bool NotEqualOperator(ValueObject left, ValueObject right) + { + return !(EqualOperator(left, right)); + } + } + + // https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects +} diff --git a/Lib.Architecture.BuildingBlocks/Events/DomainEvents.cs b/Lib.Architecture.BuildingBlocks/Events/DomainEvents.cs new file mode 100644 index 0000000..55ab2a0 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Events/DomainEvents.cs @@ -0,0 +1,16 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + //Class For Create Domain Events + public record DomainEvent(TEntity Entity) : IDomainEvent where TEntity : IEvent + { + public DateTime OccorredOn => DateTime.Now; + public Guid CorrelationId { get; init; } = Guid.NewGuid(); + + public override string ToString() + { + return typeof(TEntity).Name; + } + internal ICollection DomainEvents { get; } = new List(); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Helpers/ApiResponseHelper.cs b/Lib.Architecture.BuildingBlocks/Helpers/ApiResponseHelper.cs new file mode 100644 index 0000000..adb8aa3 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Helpers/ApiResponseHelper.cs @@ -0,0 +1,63 @@ +using Newtonsoft.Json; +using Refit; +using System.Net; + +namespace Lib.Architecture.BuildingBlocks.Helpers +{ + public static class ApiResponseHelper + { + public static void EvaluatePort(Exception exception, TPort port) + where TPort : INotFoundPort, IBusinessErrorPort, ITimeoutPort, INoContentPort, + IUnauthorizedPort, IForbiddenPort, IInternalServerErrorPort, IBadRequestPort + { + if (exception is ApiException refitException) + { + dynamic? errorMessage = string.Empty; + + if (!string.IsNullOrEmpty(refitException.Content)) + { + try + { + errorMessage = JsonConvert.DeserializeObject(refitException.Content) ?? string.Empty; + } + catch (JsonException) + { + if (!string.IsNullOrEmpty(refitException.Content)) + { + errorMessage = JsonConvert.DeserializeObject(refitException.Content); + } + + if (errorMessage?.Error.Target is null) + { + errorMessage = Newtonsoft.Json.JsonConvert.DeserializeObject(refitException.Content); + } + } + } + + if (refitException.StatusCode == HttpStatusCode.InternalServerError) + port.InternalServerError(errorMessage); + + else if (refitException.StatusCode == HttpStatusCode.NotFound) + port.NotFound(errorMessage); + + else if (refitException.StatusCode == HttpStatusCode.Forbidden && errorMessage) + port.Forbidden(errorMessage); + + else if (refitException.StatusCode == HttpStatusCode.UnprocessableContent) + port.BusinessError(errorMessage); + + else if (refitException.StatusCode == HttpStatusCode.BadRequest) + port.BadRequest(errorMessage); + + else if (refitException.StatusCode == HttpStatusCode.NoContent) + port.NoContentSuccess(); + + else if (refitException.StatusCode == HttpStatusCode.Unauthorized) + port.Unauthorized(); + + else if (refitException.StatusCode == HttpStatusCode.Forbidden) + port.Forbidden(); + } + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Helpers/Token/AuthenticatedHttpClientHandler.cs b/Lib.Architecture.BuildingBlocks/Helpers/Token/AuthenticatedHttpClientHandler.cs new file mode 100644 index 0000000..67f4b24 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Helpers/Token/AuthenticatedHttpClientHandler.cs @@ -0,0 +1,32 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Architecture.BuildingBlocks.Helpers.Token +{ + /// + /// Class to inject the token in all requests. + /// + public class AuthenticatedHttpClientHandler : DelegatingHandler + { + private readonly ITokenProvider _tokenProvider; + + public AuthenticatedHttpClientHandler(ITokenProvider tokenProvider) + { + _tokenProvider = tokenProvider; + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var token = _tokenProvider.GetToken(); + if (!string.IsNullOrEmpty(token)) + { + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + } + + return await base.SendAsync(request, cancellationToken); + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Helpers/Token/HttpContextTokenProvider.cs b/Lib.Architecture.BuildingBlocks/Helpers/Token/HttpContextTokenProvider.cs new file mode 100644 index 0000000..5abb2a0 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Helpers/Token/HttpContextTokenProvider.cs @@ -0,0 +1,31 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Microsoft.AspNetCore.Http; + +namespace Lib.Architecture.BuildingBlocks.Helpers.Token +{ + /// + /// Class to return the access token to controllers. + /// + public class HttpContextTokenProvider : ITokenProvider + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public HttpContextTokenProvider(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor; + } + + /// + /// Get token from headers. + /// + public string GetToken() + { + return _httpContextAccessor.HttpContext?.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Helpers/Token/ITokenProvider.cs b/Lib.Architecture.BuildingBlocks/Helpers/Token/ITokenProvider.cs new file mode 100644 index 0000000..427ca6a --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Helpers/Token/ITokenProvider.cs @@ -0,0 +1,19 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Architecture.BuildingBlocks.Helpers.Token +{ + /// + /// Interface for token provider. + /// + public interface ITokenProvider + { + /// + /// Get token from headers. + /// + string GetToken(); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Lib.Architecture.BuildingBlocks.csproj b/Lib.Architecture.BuildingBlocks/Lib.Architecture.BuildingBlocks.csproj new file mode 100644 index 0000000..37583a7 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Lib.Architecture.BuildingBlocks.csproj @@ -0,0 +1,24 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + diff --git a/Lib.Architecture.BuildingBlocks/Messages/Command.cs b/Lib.Architecture.BuildingBlocks/Messages/Command.cs new file mode 100644 index 0000000..19621bb --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Messages/Command.cs @@ -0,0 +1,16 @@ + +using FluentValidation.Results; + +namespace Lib.Architecture.BuildingBlocks +{ + public abstract class Command : Message + { + public DateTime Timestamp { get; private set; } + public ValidationResult ValidationResult { get; set; } + + protected Command() + { + Timestamp = DateTime.Now; + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Messages/Event.cs b/Lib.Architecture.BuildingBlocks/Messages/Event.cs new file mode 100644 index 0000000..24d7d47 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Messages/Event.cs @@ -0,0 +1,12 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public abstract class Event : Message + { + public DateTime Timestamp { get; private set; } + + protected Event() + { + Timestamp = DateTime.Now; + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Messages/Message.cs b/Lib.Architecture.BuildingBlocks/Messages/Message.cs new file mode 100644 index 0000000..ad880d8 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Messages/Message.cs @@ -0,0 +1,15 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public abstract class Message: Notificator + { + public string Result { get; protected set; } + public string MessageType { get; protected set; } + public Guid AggregateId { get; protected set; } + + protected Message() + { + MessageType = GetType().Name; + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BadGatewayError.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BadGatewayError.cs new file mode 100644 index 0000000..d23d07d --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BadGatewayError.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; + +namespace Lib.Architecture.BuildingBlocks +{ + public sealed class BadGatewayError: ObjectResult + { + + private const int DefaultStatusCode = StatusCodes.Status502BadGateway; + public string Message { get; set; } = string.Empty; + + public BadGatewayError(object value) : base(value) + { + StatusCode = DefaultStatusCode; + } + + public void GatherException(Exception exception) + { + if (exception is AggregateException) + { + var agEx = exception as AggregateException; + Message = string.Join(" | ", agEx.InnerExceptions.Select(e => e.Message)); + } + else + { + Message = exception.Message; + } + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BadRequestError.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BadRequestError.cs new file mode 100644 index 0000000..325fa6b --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BadRequestError.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Lib.Architecture.BuildingBlocks +{ + public sealed class BadRequestError: ObjectResult + { + private const int DefaultStatusCode = StatusCodes.Status400BadRequest; + public string Message { get; set; } = string.Empty; + + public BadRequestError(object value) : base(value) + { + StatusCode = DefaultStatusCode; + } + + public void GatherException(Exception exception) + { + if (exception is AggregateException) + { + var agEx = exception as AggregateException; + Message = string.Join(" | ", agEx.InnerExceptions.Select(e => e.Message)); + } + else + { + Message = exception.Message; + } + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BusinessError.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BusinessError.cs new file mode 100644 index 0000000..d091345 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/BusinessError.cs @@ -0,0 +1,24 @@ + +using System.Diagnostics.CodeAnalysis; + +namespace Lib.Architecture.BuildingBlocks +{ + [ExcludeFromCodeCoverage] + public sealed class BusinessError + { + public string Key { get; set; } + public string Message { get; set; } + + public BusinessError(string key, string message) + { + Key = key; + Message = message; + } + + public BusinessError(Exception coreException, string key) + { + Key = key; + Message = coreException.Message; + } + } +} \ No newline at end of file diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/ErrorDetails.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/ErrorDetails.cs new file mode 100644 index 0000000..d0c320f --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/ErrorDetails.cs @@ -0,0 +1,31 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Architecture.BuildingBlocks; + +/// +/// The service error details transfer object. +/// +public class ErrorDetails +{ + /// + /// Gets or sets the service error code. + /// + + public string? ErrorCode { get; set; } + + /// + /// Gets or sets the service error message. + /// + public string? Message { get; set; } + + /// + /// Gets or sets the service target. + /// + public string? Target { get; set; } +} + + diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/GatewayTimeoutError.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/GatewayTimeoutError.cs new file mode 100644 index 0000000..45122d3 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/GatewayTimeoutError.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +namespace Lib.Architecture.BuildingBlocks +{ + public sealed class GatewayTimeoutError: ObjectResult + { + private const int DefaultStatusCode = StatusCodes.Status504GatewayTimeout; + public string Message { get; set; } = string.Empty; + + public GatewayTimeoutError(object value) : base(value) + { + StatusCode = DefaultStatusCode; + } + + public void GatherException(Exception exception) + { + if (exception is AggregateException) + { + var agEx = exception as AggregateException; + Message = string.Join(" | ", agEx.InnerExceptions.Select(e => e.Message)); + } + else + { + Message = exception.Message; + } + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/GenericErrorResponse.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/GenericErrorResponse.cs new file mode 100644 index 0000000..afce608 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/GenericErrorResponse.cs @@ -0,0 +1,10 @@ +namespace Lib.Architecture.BuildingBlocks; + +public class GenericErrorResponse +{ + public string Type { get; set; } + public string Title { get; set; } + public int Status { get; set; } + public Dictionary> Errors { get; set; } + public string TraceId { get; set; } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/HttpError.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/HttpError.cs new file mode 100644 index 0000000..19656f1 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/HttpError.cs @@ -0,0 +1,39 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Architecture.BuildingBlocks; + +/// +/// The service HTTP error data transfer object. +/// +public class HttpError +{ + /// + /// Gets or sets the error. + /// + public ErrorDetails Error { get; set; } + + /// + /// Creates a new instance of + /// with custom parameters. + /// + /// The HTTP error message. + /// The HTTP error code. + /// The HTTP error target. + public HttpError( + string? message, + string? errorCode, + string? target) + { + Error = new ErrorDetails + { + ErrorCode = errorCode, + Message = message, + Target = target, + }; + } +} + diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/InternalServerError.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/InternalServerError.cs new file mode 100644 index 0000000..eac75a3 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/InternalServerError.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Lib.Architecture.BuildingBlocks +{ + [ExcludeFromCodeCoverage] + public sealed class InternalServerError : ObjectResult + { + private const int DefaultStatusCode = StatusCodes.Status500InternalServerError; + public string Message { get; set; } = string.Empty; + + public InternalServerError(object value) : base(value) + { + StatusCode = DefaultStatusCode; + } + + public void GatherException(Exception exception) + { + if (exception is AggregateException) + { + var agEx = exception as AggregateException; + Message = string.Join(" | ", agEx.InnerExceptions.Select(e => e.Message)); + } + else + { + Message = exception.Message; + } + } + } +} \ No newline at end of file diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/GPSPointAdapter.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/GPSPointAdapter.cs new file mode 100644 index 0000000..6c378dc --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/GPSPointAdapter.cs @@ -0,0 +1,88 @@ +using Core.Blueprint.Mongo; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using Newtonsoft.Json; + +namespace Lib.Architecture.BuildingBlocks.Adapters; + +[CollectionAttributeName("GPSPoints")] +public class GPSPointAdapter : Document +{ + [BsonElement("features")] + [JsonProperty("features")] + public List Features { get; set; } = null!; + + [BsonElement("timestamp")] + [JsonProperty("timestamp")] + public DateTime Timestamp { get; set; } +} +public class FeatureCollection +{ + [BsonElement("type")] + [JsonProperty("type")] + public string Type { get; set; } = null!; + + [BsonElement("geometry")] + [JsonProperty("geometry")] + public Geometry Geometry { get; set; } = null!; + + [BsonElement("properties")] + [JsonProperty("properties")] + public Properties Properties { get; set; } = null!; +} + +public class Properties +{ + [BsonElement("technicianId")] + [BsonRepresentation(BsonType.ObjectId)] + [JsonProperty("technicianId")] + public string TechnicianId { get; set; } = null!; + + [BsonElement("technicianName")] + [JsonProperty("technicianName")] + public string TechnicianName { get; set; } = null!; + + [BsonElement("sbuSurveyId")] + [BsonRepresentation(BsonType.ObjectId)] + [JsonProperty("sbuSurveyId")] + public string SbuSurveyId { get; set; } = null!; + + [BsonElement("sbuSurveyMapsheetId")] + [BsonRepresentation(BsonType.ObjectId)] + [JsonProperty("sbuSurveyMapsheetId")] + public string SbuSurveyMapsheetId { get; set; } = null!; + + [BsonElement("trailId")] + [JsonProperty("trailId")] + public string TrailId { get; set; } = null!; + + [BsonElement("deviceSerialNumber")] + [JsonProperty("deviceSerialNumber")] + public string DeviceSerialNumber { get; set; } = null!; +} +public class Geometry +{ + [BsonElement("type")] + [JsonProperty("type")] + public string Type { get; set; } = null!; + + [BsonElement("coordinates")] + [JsonProperty("coordinates")] + public List Coordinates { get; set; } = null!; + + [BsonElement("latitude")] + [JsonProperty("latitude")] + public double Latitude { get; set; } + + [BsonElement("longitude")] + [JsonProperty("longitude")] + public double Longitude { get; set; } + + [BsonElement("orientation")] + [JsonProperty("orientation")] + public double Orientation { get; set; } + + [BsonElement("accuracy")] + [JsonProperty("accuracy")] + public int Accuracy { get; set; } +} \ No newline at end of file diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/GeometryAdapter.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/GeometryAdapter.cs new file mode 100644 index 0000000..2b96fd5 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/GeometryAdapter.cs @@ -0,0 +1,15 @@ +using MongoDB.Bson.Serialization.Attributes; +using Newtonsoft.Json; + +namespace Lib.Architecture.BuildingBlocks.Adapters; + +public class GeometryAdapter +{ + [BsonElement("type")] + [JsonProperty("type")] + public string Type { get; set; } = null!; + + [BsonElement("coordinates")] + [JsonProperty("coordinates")] + public List Coordinates { get; set; } = null!; +} \ No newline at end of file diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/MapsheetAdapter.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/MapsheetAdapter.cs new file mode 100644 index 0000000..f177396 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/MapsheetAdapter.cs @@ -0,0 +1,87 @@ +using Core.Blueprint.Mongo; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using Newtonsoft.Json; +using System.Text.Json.Serialization; + +namespace Lib.Architecture.BuildingBlocks.Adapters; + +[CollectionAttributeName("Mapsheets")] +public class MapsheetAdapter : Document +{ + [BsonElement("features")] + [JsonProperty("features")] + public List? Features { get; set; } + + [BsonElement("timestamp")] + [JsonProperty("timestamp")] + public DateTime Timestamp { get; set; } +} + +public class MapsheetTechnicians +{ + [BsonElement("technicianId")] + [BsonRepresentation(BsonType.ObjectId)] + [JsonProperty("technicianId")] + public string TechnicianId { get; set; } = null!; + + [BsonElement("technicianName")] + [JsonProperty("technicianName")] + public string TechnicianName { get; set; } = null!; +} + +public class MapsheetProperties +{ + [BsonElement("surveyId")] + [BsonRepresentation(BsonType.ObjectId)] + [JsonProperty("surveyId")] + public string SurveyId { get; set; } + + [BsonElement("mapsheetName")] + [JsonProperty("mapsheetName")] + public string MapsheetName { get; set; } = null!; + + [BsonElement("workOrderNumber")] + [JsonProperty("workOrderNumber")] + public string WorkOrderNumber { get; set; } = null!; + + [BsonElement("mapsheetStatus")] + [BsonRepresentation(BsonType.String)] + [JsonProperty("mapsheetStatus")] + [System.Text.Json.Serialization.JsonConverter(typeof(JsonStringEnumConverter))] + public MapsheetStatus MapsheetStatus { get; set; } + + [BsonElement("footage")] + [JsonProperty("footage")] + public int Footage { get; set; } +} + +[System.Text.Json.Serialization.JsonConverter(typeof(JsonStringEnumConverter))] +public enum MapsheetStatus +{ + Ready = 1, + InProgress = 2, + Complete = 3, + QaPending = 4, + Closed = 5, + Reopened = 6 +} + +public class MapsheetCollection +{ + [BsonElement("type")] + [JsonProperty("type")] + public string Type { get; set; } = null!; + + [BsonElement("mapsheetTechnicians")] + [JsonProperty("mapsheetTechnicians")] + public List? MapsheetTechnicians { get; set; } + + [BsonElement("geometry")] + [JsonProperty("geometry")] + public List Geometry { get; set; } = null!; + + [BsonElement("properties")] + [JsonProperty("properties")] + public MapsheetProperties? Properties { get; set; } +} \ No newline at end of file diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/SurveyAdapter.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/SurveyAdapter.cs new file mode 100644 index 0000000..4fd251d --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/Mobile2.0/SurveyAdapter.cs @@ -0,0 +1,29 @@ +using Core.Blueprint.Mongo; +using MongoDB.Bson.Serialization.Attributes; +using System.Text.Json.Serialization; + +namespace Lib.Architecture.BuildingBlocks.Adapters; + +[CollectionAttributeName("Surveys")] +public class SurveyAdapter : Document +{ + [BsonElement("surveyName")] + [JsonPropertyName("surveyName")] + public string SurveyName { get; set; } = null!; + + [BsonElement("surveyType")] + [JsonPropertyName("surveyType")] + public string SurveyType { get; set; } = null!; + + [BsonElement("locations")] + [JsonPropertyName("locations")] + public List Locations { get; set; } = []; + + [BsonElement("project")] + [JsonPropertyName("project")] + public string Project { get; set; } = null!; + + [BsonElement("surveyStatus")] + [JsonPropertyName("surveyStatus")] + public string SurveyStatus { get; set; } = null!; +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/PreconditionFailedObjectResult.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/PreconditionFailedObjectResult.cs new file mode 100644 index 0000000..a2e7505 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/PreconditionFailedObjectResult.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using System.Diagnostics.CodeAnalysis; + +namespace Lib.Architecture.BuildingBlocks +{ + [DefaultStatusCode(DefaultStatusCode)] + [ExcludeFromCodeCoverage] + public sealed class PreconditionFailedObjectResult : ObjectResult + { + private const int DefaultStatusCode = StatusCodes.Status412PreconditionFailed; + public string Message { get; set; } = string.Empty; + public PreconditionFailedObjectResult(object value) + : base(value) + { + StatusCode = DefaultStatusCode; + } + } +} \ No newline at end of file diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/ServiceUnavaliableResult.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/ServiceUnavaliableResult.cs new file mode 100644 index 0000000..1a5dfc9 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/ServiceUnavaliableResult.cs @@ -0,0 +1,23 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; + +namespace Lib.Architecture.BuildingBlocks +{ + [DefaultStatusCode(DefaultStatusCode)] + [ExcludeFromCodeCoverage] + public sealed class ServiceUnavaliableResult : ObjectResult + { + private const int DefaultStatusCode = StatusCodes.Status503ServiceUnavailable; + public string Message { get; set; } = string.Empty; + public ServiceUnavaliableResult(object value) : base(value) + { + StatusCode = DefaultStatusCode; + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Adapters/TimeoutServiceError.cs b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/TimeoutServiceError.cs new file mode 100644 index 0000000..560ef80 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Adapters/TimeoutServiceError.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; + +namespace Lib.Architecture.BuildingBlocks +{ + [ExcludeFromCodeCoverage] + public sealed class TimeoutServiceError : ObjectResult + { + private const int DefaultStatusCode = StatusCodes.Status504GatewayTimeout; + public string Message { get; set; } = string.Empty; + public TimeoutServiceError(object value) : base(value) + { + StatusCode = DefaultStatusCode; + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/Base/BasePresenter.cs b/Lib.Architecture.BuildingBlocks/Presentation/Base/BasePresenter.cs new file mode 100644 index 0000000..15ebec6 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/Base/BasePresenter.cs @@ -0,0 +1,85 @@ +using Microsoft.AspNetCore.Mvc; +using System.Diagnostics.CodeAnalysis; + +namespace Lib.Architecture.BuildingBlocks +{ + [ExcludeFromCodeCoverage] + public abstract class BasePresenter : IBusinessErrorPort, INoContentPort, INotFoundPort, + IValidationErrorPort, ITimeoutPort, IServiceUnavaliablePort, IAcceptedPort, IForbiddenPort, + IUnauthorizedPort, IInternalServerErrorPort, IBadRequestPort + { + public required IActionResult ViewModel { get; set; } + + public void BadRequest(string message) + { + ViewModel = new BadRequestError(message); + } + public void BadRequest(object message) + { + ViewModel = new BadRequestError(message); + } + public void BusinessError(string errorMessage) + { + ViewModel = new UnprocessableEntityObjectResult(errorMessage); + } + + public void NoContentSuccess() + { + ViewModel = new NoContentResult(); + } + + public void NotFound() + { + ViewModel = new NotFoundResult(); + } + + public void NotFound(object error) + { + ViewModel = new NotFoundObjectResult(error); + } + + public void ValidationErrors(IEnumerable notifications) + { + ViewModel = new PreconditionFailedObjectResult(notifications); + } + + public void TimeoutServiceError() + { + ViewModel = new TimeoutServiceError("Timeout Error"); + } + + public void ServiceUnavaiable(string errorMessage) + { + ViewModel = new ServiceUnavaliableResult(errorMessage); + } + + public void Accepted() + { + ViewModel = new AcceptedResult(); + } + + public void ValidationError(string argument, string message) + { + var notification = new Notification(argument, message); + ViewModel = new PreconditionFailedObjectResult(notification); + } + + public void Unauthorized() + { + ViewModel = new UnauthorizedResult(); + } + public void Forbidden() + { + ViewModel = new ForbidResult(); + } + public void Forbidden(string message) + { + ViewModel = new ForbidResult(message); + } + + public void InternalServerError(object message) + { + ViewModel = new InternalServerError(message); + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IAcceptedPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IAcceptedPort.cs new file mode 100644 index 0000000..38c5a0c --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IAcceptedPort.cs @@ -0,0 +1,7 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface IAcceptedPort + { + void Accepted(); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IBadRequestPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IBadRequestPort.cs new file mode 100644 index 0000000..3347fd7 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IBadRequestPort.cs @@ -0,0 +1,7 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface IBadRequestPort + { + void BadRequest(object message); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IBasePort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IBasePort.cs new file mode 100644 index 0000000..bf34cec --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IBasePort.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IBasePort + { + IActionResult ViewModel { get; set; } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IBusinessErrorPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IBusinessErrorPort.cs new file mode 100644 index 0000000..a3a7687 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IBusinessErrorPort.cs @@ -0,0 +1,7 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface IBusinessErrorPort + { + void BusinessError(string errorMessage); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/ICreatedPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/ICreatedPort.cs new file mode 100644 index 0000000..f2f2a2d --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/ICreatedPort.cs @@ -0,0 +1,7 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface ICreatedPort where TCommand : class, new() + { + void Created(TCommand output); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IForbiddenPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IForbiddenPort.cs new file mode 100644 index 0000000..0082793 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IForbiddenPort.cs @@ -0,0 +1,8 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface IForbiddenPort + { + void Forbidden(); + void Forbidden(string message); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IInternalServerErrorPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IInternalServerErrorPort.cs new file mode 100644 index 0000000..e1b6af3 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IInternalServerErrorPort.cs @@ -0,0 +1,7 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface IInternalServerErrorPort + { + void InternalServerError(object message); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/INoContentPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/INoContentPort.cs new file mode 100644 index 0000000..31d2bc0 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/INoContentPort.cs @@ -0,0 +1,7 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface INoContentPort + { + void NoContentSuccess(); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/INotFoundPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/INotFoundPort.cs new file mode 100644 index 0000000..2c4b7a1 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/INotFoundPort.cs @@ -0,0 +1,8 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface INotFoundPort + { + void NotFound(); + void NotFound(object error); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IServiceUnavaliablePort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IServiceUnavaliablePort.cs new file mode 100644 index 0000000..3052f8f --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IServiceUnavaliablePort.cs @@ -0,0 +1,8 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IServiceUnavaliablePort + { + void ServiceUnavaiable(string errorMessage); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/ISuccessPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/ISuccessPort.cs new file mode 100644 index 0000000..aa6d4b4 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/ISuccessPort.cs @@ -0,0 +1,11 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface ICommandSuccessPort where TCommand : class, new() + { + void Success(TCommand output); + } + public interface IQuerySuccessPort where TQuery : class, new() + { + void Success(TQuery output); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/ITimeoutPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/ITimeoutPort.cs new file mode 100644 index 0000000..114791b --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/ITimeoutPort.cs @@ -0,0 +1,8 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public interface ITimeoutPort + { + void TimeoutServiceError(); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IUnauthorizedPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IUnauthorizedPort.cs new file mode 100644 index 0000000..3297647 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IUnauthorizedPort.cs @@ -0,0 +1,7 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public interface IUnauthorizedPort + { + void Unauthorized(); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Presentation/IValidationErrorPort.cs b/Lib.Architecture.BuildingBlocks/Presentation/IValidationErrorPort.cs new file mode 100644 index 0000000..ace444c --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Presentation/IValidationErrorPort.cs @@ -0,0 +1,9 @@ + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IValidationErrorPort + { + void ValidationError(string argument, string message); + void ValidationErrors(IEnumerable notifications); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Queries/Handler/IQueryHandler.cs b/Lib.Architecture.BuildingBlocks/Queries/Handler/IQueryHandler.cs new file mode 100644 index 0000000..3b4b202 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Queries/Handler/IQueryHandler.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IQueryHandler where T : IQuery + { + ValueTask ExecuteAsync(T query, CancellationToken cancellationToken = default); + } + +} diff --git a/Lib.Architecture.BuildingBlocks/Queries/IQuery.cs b/Lib.Architecture.BuildingBlocks/Queries/IQuery.cs new file mode 100644 index 0000000..3068354 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Queries/IQuery.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IQuery { bool Validate(); } + public interface IQueryResult { } +} diff --git a/Lib.Architecture.BuildingBlocks/RegisterBuildingBlocks.cs b/Lib.Architecture.BuildingBlocks/RegisterBuildingBlocks.cs new file mode 100644 index 0000000..49acde0 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/RegisterBuildingBlocks.cs @@ -0,0 +1,44 @@ +using FluentValidation; +using FluentValidation.AspNetCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Lib.Architecture.BuildingBlocks +{ + public static class RegisterBuildingBlocks + { + public static IServiceCollection AddBuildingBlocks(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + + return services; + } + public static IServiceCollection AddValidatorsFromNamespace(this IServiceCollection services, Assembly assembly, string @namespace) + { + var validators = assembly.GetTypes() + .Where(t => t.IsClass && !t.IsAbstract && t.IsPublic && t.Namespace == @namespace) + .Where(t => typeof(IValidator).IsAssignableFrom(t)) + .ToList(); + + foreach (var validator in validators) + { + var interfaceType = validator.GetInterfaces() + .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>)); + + if (interfaceType != null) + { + services.AddTransient(interfaceType, validator); + } + } + + return services; + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Utils/PrivateResolver.cs b/Lib.Architecture.BuildingBlocks/Utils/PrivateResolver.cs new file mode 100644 index 0000000..9433043 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Utils/PrivateResolver.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Reflection; + +namespace Lib.Architecture.BuildingBlocks +{ + public class PrivateResolver : DefaultContractResolver + { + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + var prop = base.CreateProperty(member, memberSerialization); + if (!prop.Writable) + { + var property = member as PropertyInfo; + var hasPrivateSetter = property?.GetSetMethod(true) is not null; + prop.Writable = hasPrivateSetter; + } + return prop; + } + } +} \ No newline at end of file diff --git a/Lib.Architecture.BuildingBlocks/Utils/TypeGetter.cs b/Lib.Architecture.BuildingBlocks/Utils/TypeGetter.cs new file mode 100644 index 0000000..6b494d9 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Utils/TypeGetter.cs @@ -0,0 +1,13 @@ +namespace Lib.Architecture.BuildingBlocks +{ + public static class TypeGetter + { + public static Type? GetTypeFromCurrencDomainAssembly(string typeName) + { + return AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes() + .Where(x => x.FullName == typeName || x.Name == typeName)) + .FirstOrDefault(); + } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Validation/IValidationHandler.cs b/Lib.Architecture.BuildingBlocks/Validation/IValidationHandler.cs new file mode 100644 index 0000000..969c9ee --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Validation/IValidationHandler.cs @@ -0,0 +1,10 @@ +using FluentValidation; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface IValidationHandler + { + bool ExecuteValidation(TV validationObject, TE command) where TV : AbstractValidator where TE : Command; + } + //RuleFor +} diff --git a/Lib.Architecture.BuildingBlocks/Validation/Notification/INotifier.cs b/Lib.Architecture.BuildingBlocks/Validation/Notification/INotifier.cs new file mode 100644 index 0000000..ad611b6 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Validation/Notification/INotifier.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Lib.Architecture.BuildingBlocks +{ + public interface INotifier + { + IReadOnlyCollection Notifications { get; } + bool HasNotifications { get; } + } +} diff --git a/Lib.Architecture.BuildingBlocks/Validation/Notification/IValidable.cs b/Lib.Architecture.BuildingBlocks/Validation/Notification/IValidable.cs new file mode 100644 index 0000000..5a32a74 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Validation/Notification/IValidable.cs @@ -0,0 +1,8 @@ +using FluentValidation; +namespace Lib.Architecture.BuildingBlocks +{ + public interface IValidable + { + bool IsValid(IValidator validator); + } +} diff --git a/Lib.Architecture.BuildingBlocks/Validation/Notification/Notification.cs b/Lib.Architecture.BuildingBlocks/Validation/Notification/Notification.cs new file mode 100644 index 0000000..a835399 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Validation/Notification/Notification.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Lib.Architecture.BuildingBlocks +{ + [ExcludeFromCodeCoverage] + public class Notification(string argument, string mensagem) + { + public string Argument { get; } = argument; + public string Message { get; } = mensagem; + } +} diff --git a/Lib.Architecture.BuildingBlocks/Validation/Notification/Notificator.cs b/Lib.Architecture.BuildingBlocks/Validation/Notification/Notificator.cs new file mode 100644 index 0000000..dc8a9c6 --- /dev/null +++ b/Lib.Architecture.BuildingBlocks/Validation/Notification/Notificator.cs @@ -0,0 +1,80 @@ +using FluentValidation; +using FluentValidation.Results; +using System.Diagnostics.CodeAnalysis; +using static System.Runtime.InteropServices.JavaScript.JSType; + + +namespace Lib.Architecture.BuildingBlocks +{ + [ExcludeFromCodeCoverage] + public abstract class Notificator : INotifier, IValidable + { + public bool HasNotifications => _notifications.Any(); + + //Notifications + private readonly List _notifications = []; + public IReadOnlyCollection Notifications => _notifications.AsReadOnly(); + + protected Notificator() + { + _notifications = []; + } + //Validation Results + private ValidationResult _validationResult = new(); + public ValidationResult GetValidationResult() => _validationResult; + + private readonly Dictionary _errors = new(); + + + public bool IsValid(IValidator validator) + { + var context = new ValidationContext(this); + + AddNotification(validator.Validate(context)); + + return !HasNotifications; + } + + /// + /// Validates the entity and adds series of notification to list of errors. + /// + /// + /// + /// + protected void AddNotification(AbstractValidator validator, TEntity entity) where TEntity : class + { + _validationResult = validator.Validate(entity); + + if (!_validationResult.IsValid) + { + foreach (var item in _validationResult.Errors) + { + _errors.Add(item.ErrorMessage, item.ErrorCode); + } + } + } + + /// + /// Gets the object of validation and Adds Simple Notification to Validation Result + /// + /// + private void AddNotification(ValidationResult validation) + { + if (!validation.IsValid) + { + foreach (var validationError in validation.Errors) + { + _notifications.Add(new Notification( + validationError.PropertyName, + validationError.ErrorMessage + )); + } + } + + + return; + + + } + } +}