Add project files.

This commit is contained in:
Sergio Matias Urquin 2025-04-29 20:16:22 -06:00
parent 4470175f26
commit 5ac06a4e15
62 changed files with 1426 additions and 0 deletions

View File

@ -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

View File

@ -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<Notification> Messages { get; set; }
public CommandResult(bool success)
{
Success = success;
}
public CommandResult(bool success, IReadOnlyCollection<Notification> messages)
{
Success = success;
Messages = messages;
}
public CommandResult(bool success, object result)
{
Result = result;
Success = success;
}
public void ValidationErrors(IEnumerable<Notification> notifications)
{
ViewModelResult = new PreconditionFailedObjectResult(notifications);
}
}
}

View File

@ -0,0 +1,23 @@

namespace Lib.Architecture.BuildingBlocks
{
public class QueryResult : IQueryResult
{
public QueryResult() { }
public QueryResult(bool success, IReadOnlyCollection<Notification> 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<Notification> Messages { get; set; }
}
}

View File

@ -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 { }
}

View File

@ -0,0 +1,21 @@

namespace Lib.Architecture.BuildingBlocks
{
public interface ICommandHandler<Tin> where Tin : ICommand
{
ValueTask<ICommandResult> ExecuteAsync(Tin command, CancellationToken cancellationToken = default);
}
public interface IComponentHandler<in TIn> where TIn : ICommand
{
ValueTask ExecuteAsync(TIn command, CancellationToken cancellationToken = default);
}
//out
public interface IComponentHandler<in TIn, TOut> where TIn : ICommand
where TOut : class
{
ValueTask<ICommandResult> ExecuteAsync<TOut>(TIn command, CancellationToken cancellationToken = default);
}
}

View File

@ -0,0 +1,12 @@

namespace Lib.Architecture.BuildingBlocks
{
public interface IAggregateRoot
{
Guid Id { get; }
}
public interface IAggregateRoot<out TType> where TType : new()
{
TType Id { get; }
}
}

View File

@ -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; }
}
}

View File

@ -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<TEvent> where TEvent : IEvent
{
ValueTask Publish(TEvent @event, CancellationToken cancellationToken);
}
}

View File

@ -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
{
}
}

View File

@ -0,0 +1,12 @@

namespace Lib.Architecture.BuildingBlocks
{
public interface IEventHandler<T> where T : IDomainEvent
{
ValueTask Execute(T @event, CancellationToken ctx);
}
public interface IEventHandler<TIn, TOut> where TIn : IDomainEvent where TOut : BaseEntity
{
ValueTask<TOut> Execute(TIn @event, CancellationToken ctx);
}
}

View File

@ -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)
{
}
}
}

View File

@ -0,0 +1,13 @@
using FluentValidation;
namespace Lib.Architecture.BuildingBlocks
{
public class BaseEntity<TValidator> : Notificator where TValidator : IValidator, new()
{
public virtual DateOnly Created { get; set; }
}
public class BaseEntity : Notificator
{
public virtual DateOnly Created { get; set; }
}
}

View File

@ -0,0 +1,40 @@

namespace Lib.Architecture.BuildingBlocks
{
public abstract class ValueObject
{
protected abstract IEnumerable<object> 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
}

View File

@ -0,0 +1,16 @@

namespace Lib.Architecture.BuildingBlocks
{
//Class For Create Domain Events
public record DomainEvent<TEntity>(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<IDomainEvent> DomainEvents { get; } = new List<IDomainEvent>();
}
}

View File

@ -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<TPort>(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<string>(refitException.Content) ?? string.Empty;
}
catch (JsonException)
{
if (!string.IsNullOrEmpty(refitException.Content))
{
errorMessage = JsonConvert.DeserializeObject<HttpError>(refitException.Content);
}
if (errorMessage?.Error.Target is null)
{
errorMessage = Newtonsoft.Json.JsonConvert.DeserializeObject<GenericErrorResponse>(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();
}
}
}
}

View File

@ -0,0 +1,32 @@
// ***********************************************************************
// <copyright file="AuthenticatedHttpClientHandler.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Lib.Architecture.BuildingBlocks.Helpers.Token
{
/// <summary>
/// Class to inject the token in all requests.
/// </summary>
public class AuthenticatedHttpClientHandler : DelegatingHandler
{
private readonly ITokenProvider _tokenProvider;
public AuthenticatedHttpClientHandler(ITokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
protected override async Task<HttpResponseMessage> 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);
}
}
}

View File

@ -0,0 +1,31 @@
// ***********************************************************************
// <copyright file="HttpContextTokenProvider.cs">
// Heath
// </copyright>
// ***********************************************************************
using Microsoft.AspNetCore.Http;
namespace Lib.Architecture.BuildingBlocks.Helpers.Token
{
/// <summary>
/// Class to return the access token to controllers.
/// </summary>
public class HttpContextTokenProvider : ITokenProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public HttpContextTokenProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// Get token from headers.
/// </summary>
public string GetToken()
{
return _httpContextAccessor.HttpContext?.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
}
}
}

View File

@ -0,0 +1,19 @@
// ***********************************************************************
// <copyright file="ITokenProvider.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Lib.Architecture.BuildingBlocks.Helpers.Token
{
/// <summary>
/// Interface for token provider.
/// </summary>
public interface ITokenProvider
{
/// <summary>
/// Get token from headers.
/// </summary>
string GetToken();
}
}

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Core.Blueprint.Mongo" Version="0.3.0-alpha0047" />
<PackageReference Include="FluentValidation" Version="11.11.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.11.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Refit" Version="8.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="9.0.1" />
<PackageReference Include="System.Text.Encodings.Web" Version="9.0.1" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
</ItemGroup>
</Project>

View File

@ -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;
}
}
}

View File

@ -0,0 +1,12 @@
namespace Lib.Architecture.BuildingBlocks
{
public abstract class Event : Message
{
public DateTime Timestamp { get; private set; }
protected Event()
{
Timestamp = DateTime.Now;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,31 @@
// ***********************************************************************
// <copyright file="ErrorDetailsDto.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Lib.Architecture.BuildingBlocks;
/// <summary>
/// The service error details transfer object.
/// </summary>
public class ErrorDetails
{
/// <summary>
/// Gets or sets the service error code.
/// </summary>
public string? ErrorCode { get; set; }
/// <summary>
/// Gets or sets the service error message.
/// </summary>
public string? Message { get; set; }
/// <summary>
/// Gets or sets the service target.
/// </summary>
public string? Target { get; set; }
}

View File

@ -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;
}
}
}
}

View File

@ -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<string, List<string>> Errors { get; set; }
public string TraceId { get; set; }
}

View File

@ -0,0 +1,39 @@
// ***********************************************************************
// <copyright file="HttpErrorDto.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Lib.Architecture.BuildingBlocks;
/// <summary>
/// The service HTTP error data transfer object.
/// </summary>
public class HttpError
{
/// <summary>
/// Gets or sets the error.
/// </summary>
public ErrorDetails Error { get; set; }
/// <summary>
/// Creates a new instance of <see cref="HttpError{TMessage}"/>
/// with custom parameters.
/// </summary>
/// <param name="message">The HTTP error message.</param>
/// <param name="errorCode">The HTTP error code.</param>
/// <param name="target">The HTTP error target.</param>
public HttpError(
string? message,
string? errorCode,
string? target)
{
Error = new ErrorDetails
{
ErrorCode = errorCode,
Message = message,
Target = target,
};
}
}

View File

@ -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;
}
}
}
}

View File

@ -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<FeatureCollection> 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<double> 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; }
}

View File

@ -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<double> Coordinates { get; set; } = null!;
}

View File

@ -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<MapsheetCollection>? 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>? MapsheetTechnicians { get; set; }
[BsonElement("geometry")]
[JsonProperty("geometry")]
public List<GeometryAdapter> Geometry { get; set; } = null!;
[BsonElement("properties")]
[JsonProperty("properties")]
public MapsheetProperties? Properties { get; set; }
}

View File

@ -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<string> Locations { get; set; } = [];
[BsonElement("project")]
[JsonPropertyName("project")]
public string Project { get; set; } = null!;
[BsonElement("surveyStatus")]
[JsonPropertyName("surveyStatus")]
public string SurveyStatus { get; set; } = null!;
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<Notification> 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);
}
}
}

View File

@ -0,0 +1,7 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface IAcceptedPort
{
void Accepted();
}
}

View File

@ -0,0 +1,7 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface IBadRequestPort
{
void BadRequest(object message);
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.AspNetCore.Mvc;
namespace Lib.Architecture.BuildingBlocks
{
public interface IBasePort
{
IActionResult ViewModel { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface IBusinessErrorPort
{
void BusinessError(string errorMessage);
}
}

View File

@ -0,0 +1,7 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface ICreatedPort<in TCommand> where TCommand : class, new()
{
void Created(TCommand output);
}
}

View File

@ -0,0 +1,8 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface IForbiddenPort
{
void Forbidden();
void Forbidden(string message);
}
}

View File

@ -0,0 +1,7 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface IInternalServerErrorPort
{
void InternalServerError(object message);
}
}

View File

@ -0,0 +1,7 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface INoContentPort
{
void NoContentSuccess();
}
}

View File

@ -0,0 +1,8 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface INotFoundPort
{
void NotFound();
void NotFound(object error);
}
}

View File

@ -0,0 +1,8 @@

namespace Lib.Architecture.BuildingBlocks
{
public interface IServiceUnavaliablePort
{
void ServiceUnavaiable(string errorMessage);
}
}

View File

@ -0,0 +1,11 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface ICommandSuccessPort<in TCommand> where TCommand : class, new()
{
void Success(TCommand output);
}
public interface IQuerySuccessPort<in TQuery> where TQuery : class, new()
{
void Success(TQuery output);
}
}

View File

@ -0,0 +1,8 @@

namespace Lib.Architecture.BuildingBlocks
{
public interface ITimeoutPort
{
void TimeoutServiceError();
}
}

View File

@ -0,0 +1,7 @@
namespace Lib.Architecture.BuildingBlocks
{
public interface IUnauthorizedPort
{
void Unauthorized();
}
}

View File

@ -0,0 +1,9 @@

namespace Lib.Architecture.BuildingBlocks
{
public interface IValidationErrorPort
{
void ValidationError(string argument, string message);
void ValidationErrors(IEnumerable<Notification> notifications);
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Lib.Architecture.BuildingBlocks
{
public interface IQueryHandler<in T> where T : IQuery
{
ValueTask<IQueryResult> ExecuteAsync(T query, CancellationToken cancellationToken = default);
}
}

View File

@ -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 { }
}

View File

@ -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<ICommandResult, CommandResult>();
services.AddScoped<IQueryResult, QueryResult>();
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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,10 @@
using FluentValidation;
namespace Lib.Architecture.BuildingBlocks
{
public interface IValidationHandler
{
bool ExecuteValidation<TV, TE>(TV validationObject, TE command) where TV : AbstractValidator<TE> where TE : Command;
}
//RuleFor
}

View File

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace Lib.Architecture.BuildingBlocks
{
public interface INotifier
{
IReadOnlyCollection<Notification> Notifications { get; }
bool HasNotifications { get; }
}
}

View File

@ -0,0 +1,8 @@
using FluentValidation;
namespace Lib.Architecture.BuildingBlocks
{
public interface IValidable
{
bool IsValid(IValidator validator);
}
}

View File

@ -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;
}
}

View File

@ -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<Notification> _notifications = [];
public IReadOnlyCollection<Notification> Notifications => _notifications.AsReadOnly();
protected Notificator()
{
_notifications = [];
}
//Validation Results
private ValidationResult _validationResult = new();
public ValidationResult GetValidationResult() => _validationResult;
private readonly Dictionary<string, string> _errors = new();
public bool IsValid(IValidator validator)
{
var context = new ValidationContext<Notificator>(this);
AddNotification(validator.Validate(context));
return !HasNotifications;
}
/// <summary>
/// Validates the entity and adds series of notification to list of errors.
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="validator"></param>
/// <param name="entity"></param>
protected void AddNotification<TEntity>(AbstractValidator<TEntity> 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);
}
}
}
/// <summary>
/// Gets the object of validation and Adds Simple Notification to Validation Result
/// </summary>
/// <param name="validation"></param>
private void AddNotification(ValidationResult validation)
{
if (!validation.IsValid)
{
foreach (var validationError in validation.Errors)
{
_notifications.Add(new Notification(
validationError.PropertyName,
validationError.ErrorMessage
));
}
}
return;
}
}
}