From 6358f5f1995febc523418097ef17163e684a634e Mon Sep 17 00:00:00 2001 From: Sergio Matias Urquin Date: Tue, 29 Apr 2025 18:39:57 -0600 Subject: [PATCH] Add project files. --- .dockerignore | 30 ++ .../Controllers/BlobStorageController.cs | 61 +++ .../Controllers/KeyVaultController.cs | 44 ++ .../Controllers/MongoBlueprintController.cs | 67 +++ .../Controllers/UserProjectController.cs | 63 +++ .../Core.Blueprint.DAL.API.csproj | 31 ++ .../Core.Blueprint.DAL.API.http | 6 + Core.Blueprint.DAL.API/Dockerfile | 25 ++ .../Extensions/ConfigurationExtension.cs | 36 ++ .../Extensions/SwaggerExtensions.cs | 71 ++++ Core.Blueprint.DAL.API/Program.cs | 103 +++++ .../Properties/launchSettings.json | 52 +++ .../appsettings.Development.json | 11 + Core.Blueprint.DAL.API/appsettings.Local.json | 11 + Core.Blueprint.DAL.API/appsettings.json | 11 + .../Core.Blueprint.DAL.Hangfire.csproj | 24 ++ .../Configure/DependencyInjection.cs | 75 ++++ .../Configure/LoggerExtension.cs | 12 + .../Context/MongoContext.cs | 135 ++++++ .../Context/SqlServerContext.cs | 16 + .../Contracts/IBlueprintRepository.cs | 9 + .../Contracts/IMongoContext.cs | 13 + .../Contracts/IProxyBlobStorage.cs | 11 + .../Contracts/IRepositoryBase.cs | 11 + .../Contracts/IRepositoryIdentityBase.cs | 9 + .../Contracts/ISampleImageRepository.cs | 11 + .../Contracts/ISampleItemRepository.cs | 8 + .../Contracts/ISecretRepository.cs | 11 + .../Core.Blueprint.DAL.Infrastructure.csproj | 31 ++ .../Proxies/ProxyBlobStorage.cs | 95 +++++ .../Repository/BluePrintRepository.cs | 52 +++ .../Repository/RepositorySqlBase.cs | 54 +++ .../Repository/SampleImageRepository.cs | 33 ++ .../Repository/SampleItemRepository.cs | 16 + .../Repository/SecretRepository.cs | 41 ++ .../Contracts/IKeyVaultService.cs | 12 + .../Core.Blueprint.DAL.KeyVault.csproj | 17 + .../Services/KeyVaultService.cs | 31 ++ .../BlueprintSerilogLogger.cs | 50 +++ .../LogExtensionConfiguration.cs | 15 + .../Contracts/IBlueprintLogger.cs | 9 + .../Contracts/IBlueprintSerilogLogger.cs | 12 + .../Core.Blueprint.DAL.Logs.csproj | 14 + Core.Blueprint.DAL.Logs/Log.cs | 24 ++ .../Contracts/IBlueprintService.cs | 18 + .../Core.Blueprint.DAL.Mongo.csproj | 22 + .../Collections/BlueprintCollection.cs | 11 + .../Entities/Requests/BlueprintRequest.cs | 9 + .../Service/BlueprintService.cs | 80 ++++ .../Core.Blueprint.DAL.Provider.csproj | 9 + Core.Blueprint.DAL.Redis/CacheService.cs | 134 ++++++ .../Configuration/CacheSettings.cs | 7 + .../Configuration/RedisExtension.cs | 27 ++ .../Contracts/ICacheService.cs | 11 + .../Core.Blueprint.DAL.Redis.csproj | 17 + .../Helpers/CacheHelper.cs | 30 ++ .../Helpers/CacheKeyHelper.cs | 46 ++ .../Context/SqlServerContext.cs | 25 ++ .../Contracts/IUserProjectService.cs | 19 + .../Core.Blueprint.DAL.SQLServer.csproj | 19 + .../Entities/Request/UserProjectRequest.cs | 9 + .../Entities/UserProject.cs | 13 + .../Services/UserProjectService.cs | 82 ++++ .../BlueprintService.cs | 125 ++++++ .../Configure/DependencyInjection.cs | 22 + .../Contracts/IBlueprintService.cs | 13 + .../Contracts/ISampleImageService.cs | 11 + .../Contracts/ISampleItemService.cs | 14 + .../Contracts/ISecretService.cs | 11 + .../Core.Blueprint.DAL.Service.csproj | 14 + .../SampleImageService.cs | 74 ++++ .../SampleItemService.cs | 114 +++++ Core.Blueprint.DAL.Service/SecretService.cs | 76 ++++ .../Contracts/IBlobStorageService.cs | 16 + .../Core.Blueprint.DAL.Storage.csproj | 22 + .../Provider/BlobStorageService.cs | 41 ++ .../Core.Blueprint.DAL.Testing.csproj | 25 ++ Core.Blueprint.DAL.sln | 81 ++++ .../SQLServer/BaseConfig/BaseSQLEntity.cs | 18 + .../Core.Blueprint.Domain.csproj | 20 + Core.Blueprint.Domain/Dtos/ImageUrlDto.cs | 7 + .../Dtos/KeyVaultSecretDto.cs | 14 + Core.Blueprint.Domain/Entities/AbsEntity.cs | 14 + .../Entities/AbsFileEntity.cs | 7 + .../Entities/BlueprintCollection.cs | 15 + Core.Blueprint.Domain/Entities/SampleImage.cs | 7 + Core.Blueprint.Domain/Entities/SampleItem.cs | 17 + Core.Blueprint.Domain/Entities/Secret.cs | 13 + .../Shared/CollectionAttributeName.cs | 12 + .../Constants/Claims.cs | 48 +++ .../Constants/DisplayNames.cs | 397 ++++++++++++++++++ .../Constants/EnvironmentVariables.cs | 21 + .../Constants/ErrorCodes.cs | 79 ++++ .../Constants/Headers.cs | 10 + .../Constants/MimeTypes.cs | 148 +++++++ .../Constants/Responses.cs | 29 ++ .../Error/ErrorDetailsDto.cs | 44 ++ .../DataTranferObjects/Error/HttpErrorDto.cs | 46 ++ .../DataTranferObjects/Logger/LogDetail.cs | 121 ++++++ .../DataTranferObjects/Logger/LogOperation.cs | 56 +++ .../DataTranferObjects/Logger/LogSeverity.cs | 42 ++ .../DataTranferObjects/Logger/LogTarget.cs | 42 ++ .../Extensions/HttpException/HttpException.cs | 41 ++ .../Lib.Common.LoggingAPI.Service.csproj | 32 ++ .../HttpException/HttpErrorMiddleware.cs | 96 +++++ .../HttpErrorMiddlewareExtension.cs | 20 + .../Middleware/HttpLogger/HttpLogger.cs | 239 +++++++++++ .../HttpLogger/HttpLoggingMiddleware.cs | 70 +++ .../HttpLoggingMiddlewareExtension.cs | 20 + .../Settings/ServiceSettings.cs | 19 + 110 files changed, 4484 insertions(+) create mode 100644 .dockerignore create mode 100644 Core.Blueprint.DAL.API/Controllers/BlobStorageController.cs create mode 100644 Core.Blueprint.DAL.API/Controllers/KeyVaultController.cs create mode 100644 Core.Blueprint.DAL.API/Controllers/MongoBlueprintController.cs create mode 100644 Core.Blueprint.DAL.API/Controllers/UserProjectController.cs create mode 100644 Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.csproj create mode 100644 Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.http create mode 100644 Core.Blueprint.DAL.API/Dockerfile create mode 100644 Core.Blueprint.DAL.API/Extensions/ConfigurationExtension.cs create mode 100644 Core.Blueprint.DAL.API/Extensions/SwaggerExtensions.cs create mode 100644 Core.Blueprint.DAL.API/Program.cs create mode 100644 Core.Blueprint.DAL.API/Properties/launchSettings.json create mode 100644 Core.Blueprint.DAL.API/appsettings.Development.json create mode 100644 Core.Blueprint.DAL.API/appsettings.Local.json create mode 100644 Core.Blueprint.DAL.API/appsettings.json create mode 100644 Core.Blueprint.DAL.Hangfire/Core.Blueprint.DAL.Hangfire.csproj create mode 100644 Core.Blueprint.DAL.Infrastrcture/Configure/DependencyInjection.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Configure/LoggerExtension.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Context/MongoContext.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Context/SqlServerContext.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Contracts/IBlueprintRepository.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Contracts/IMongoContext.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Contracts/IProxyBlobStorage.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Contracts/IRepositoryBase.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Contracts/IRepositoryIdentityBase.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Contracts/ISampleImageRepository.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Contracts/ISampleItemRepository.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Contracts/ISecretRepository.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Core.Blueprint.DAL.Infrastructure.csproj create mode 100644 Core.Blueprint.DAL.Infrastrcture/Proxies/ProxyBlobStorage.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Repository/BluePrintRepository.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Repository/RepositorySqlBase.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Repository/SampleImageRepository.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Repository/SampleItemRepository.cs create mode 100644 Core.Blueprint.DAL.Infrastrcture/Repository/SecretRepository.cs create mode 100644 Core.Blueprint.DAL.KeyVault/Contracts/IKeyVaultService.cs create mode 100644 Core.Blueprint.DAL.KeyVault/Core.Blueprint.DAL.KeyVault.csproj create mode 100644 Core.Blueprint.DAL.KeyVault/Services/KeyVaultService.cs create mode 100644 Core.Blueprint.DAL.Logs/BlueprintSerilogLogger.cs create mode 100644 Core.Blueprint.DAL.Logs/Configuration/LogExtensionConfiguration.cs create mode 100644 Core.Blueprint.DAL.Logs/Contracts/IBlueprintLogger.cs create mode 100644 Core.Blueprint.DAL.Logs/Contracts/IBlueprintSerilogLogger.cs create mode 100644 Core.Blueprint.DAL.Logs/Core.Blueprint.DAL.Logs.csproj create mode 100644 Core.Blueprint.DAL.Logs/Log.cs create mode 100644 Core.Blueprint.DAL.Mongo/Contracts/IBlueprintService.cs create mode 100644 Core.Blueprint.DAL.Mongo/Core.Blueprint.DAL.Mongo.csproj create mode 100644 Core.Blueprint.DAL.Mongo/Entities/Collections/BlueprintCollection.cs create mode 100644 Core.Blueprint.DAL.Mongo/Entities/Requests/BlueprintRequest.cs create mode 100644 Core.Blueprint.DAL.Mongo/Service/BlueprintService.cs create mode 100644 Core.Blueprint.DAL.Provider/Core.Blueprint.DAL.Provider.csproj create mode 100644 Core.Blueprint.DAL.Redis/CacheService.cs create mode 100644 Core.Blueprint.DAL.Redis/Configuration/CacheSettings.cs create mode 100644 Core.Blueprint.DAL.Redis/Configuration/RedisExtension.cs create mode 100644 Core.Blueprint.DAL.Redis/Contracts/ICacheService.cs create mode 100644 Core.Blueprint.DAL.Redis/Core.Blueprint.DAL.Redis.csproj create mode 100644 Core.Blueprint.DAL.Redis/Helpers/CacheHelper.cs create mode 100644 Core.Blueprint.DAL.Redis/Helpers/CacheKeyHelper.cs create mode 100644 Core.Blueprint.DAL.SQLServer/Context/SqlServerContext.cs create mode 100644 Core.Blueprint.DAL.SQLServer/Contracts/IUserProjectService.cs create mode 100644 Core.Blueprint.DAL.SQLServer/Core.Blueprint.DAL.SQLServer.csproj create mode 100644 Core.Blueprint.DAL.SQLServer/Entities/Request/UserProjectRequest.cs create mode 100644 Core.Blueprint.DAL.SQLServer/Entities/UserProject.cs create mode 100644 Core.Blueprint.DAL.SQLServer/Services/UserProjectService.cs create mode 100644 Core.Blueprint.DAL.Service/BlueprintService.cs create mode 100644 Core.Blueprint.DAL.Service/Configure/DependencyInjection.cs create mode 100644 Core.Blueprint.DAL.Service/Contracts/IBlueprintService.cs create mode 100644 Core.Blueprint.DAL.Service/Contracts/ISampleImageService.cs create mode 100644 Core.Blueprint.DAL.Service/Contracts/ISampleItemService.cs create mode 100644 Core.Blueprint.DAL.Service/Contracts/ISecretService.cs create mode 100644 Core.Blueprint.DAL.Service/Core.Blueprint.DAL.Service.csproj create mode 100644 Core.Blueprint.DAL.Service/SampleImageService.cs create mode 100644 Core.Blueprint.DAL.Service/SampleItemService.cs create mode 100644 Core.Blueprint.DAL.Service/SecretService.cs create mode 100644 Core.Blueprint.DAL.Storage/Contracts/IBlobStorageService.cs create mode 100644 Core.Blueprint.DAL.Storage/Core.Blueprint.DAL.Storage.csproj create mode 100644 Core.Blueprint.DAL.Storage/Provider/BlobStorageService.cs create mode 100644 Core.Blueprint.DAL.Testing/Core.Blueprint.DAL.Testing.csproj create mode 100644 Core.Blueprint.DAL.sln create mode 100644 Core.Blueprint.Domain/Contexts/SQLServer/BaseConfig/BaseSQLEntity.cs create mode 100644 Core.Blueprint.Domain/Core.Blueprint.Domain.csproj create mode 100644 Core.Blueprint.Domain/Dtos/ImageUrlDto.cs create mode 100644 Core.Blueprint.Domain/Dtos/KeyVaultSecretDto.cs create mode 100644 Core.Blueprint.Domain/Entities/AbsEntity.cs create mode 100644 Core.Blueprint.Domain/Entities/AbsFileEntity.cs create mode 100644 Core.Blueprint.Domain/Entities/BlueprintCollection.cs create mode 100644 Core.Blueprint.Domain/Entities/SampleImage.cs create mode 100644 Core.Blueprint.Domain/Entities/SampleItem.cs create mode 100644 Core.Blueprint.Domain/Entities/Secret.cs create mode 100644 Core.Blueprint.Domain/Shared/CollectionAttributeName.cs create mode 100644 Lib.Common.LoggingAPI.Service/Constants/Claims.cs create mode 100644 Lib.Common.LoggingAPI.Service/Constants/DisplayNames.cs create mode 100644 Lib.Common.LoggingAPI.Service/Constants/EnvironmentVariables.cs create mode 100644 Lib.Common.LoggingAPI.Service/Constants/ErrorCodes.cs create mode 100644 Lib.Common.LoggingAPI.Service/Constants/Headers.cs create mode 100644 Lib.Common.LoggingAPI.Service/Constants/MimeTypes.cs create mode 100644 Lib.Common.LoggingAPI.Service/Constants/Responses.cs create mode 100644 Lib.Common.LoggingAPI.Service/DataTranferObjects/Error/ErrorDetailsDto.cs create mode 100644 Lib.Common.LoggingAPI.Service/DataTranferObjects/Error/HttpErrorDto.cs create mode 100644 Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogDetail.cs create mode 100644 Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogOperation.cs create mode 100644 Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogSeverity.cs create mode 100644 Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogTarget.cs create mode 100644 Lib.Common.LoggingAPI.Service/Extensions/HttpException/HttpException.cs create mode 100644 Lib.Common.LoggingAPI.Service/Lib.Common.LoggingAPI.Service.csproj create mode 100644 Lib.Common.LoggingAPI.Service/Middleware/HttpException/HttpErrorMiddleware.cs create mode 100644 Lib.Common.LoggingAPI.Service/Middleware/HttpException/HttpErrorMiddlewareExtension.cs create mode 100644 Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLogger.cs create mode 100644 Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLoggingMiddleware.cs create mode 100644 Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLoggingMiddlewareExtension.cs create mode 100644 Lib.Common.LoggingAPI.Service/Settings/ServiceSettings.cs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/Core.Blueprint.DAL.API/Controllers/BlobStorageController.cs b/Core.Blueprint.DAL.API/Controllers/BlobStorageController.cs new file mode 100644 index 0000000..522e073 --- /dev/null +++ b/Core.Blueprint.DAL.API/Controllers/BlobStorageController.cs @@ -0,0 +1,61 @@ +using Asp.Versioning; +using Core.Blueprint.DAL.Storage.Contracts; +using Core.Blueprint.Storage; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Blueprint.DAL.API.Controllers +{ + [ApiVersion("1.0")] + [Route("api/v{api-version:apiVersion}/[controller]")] + [Produces("application/json")] + [ApiController] + [AllowAnonymous] + public class BlobStorageController(IBlobStorageService storageService) : ControllerBase + { + [HttpPost("UploadBlobFromFileBrowser")] + [Consumes("multipart/form-data")] + public async Task UploadBlobAsync([FromQuery] string blobName, IFormFile file) + { + if (file == null || file.Length == 0) + return BadRequest("No file uploaded."); + + using var stream = file.OpenReadStream(); + + var result = await storageService.UploadBlobAsync(blobName, stream); + + return Ok(result); + } + + [HttpPost("UploadBlob")] + public async Task UploadBlobAsync([FromBody] BlobAddDto newBlob) + { + var result = await storageService.UploadBlobAsync(newBlob); + return Ok(result); + } + + [HttpGet("GetBlobList")] + public async Task GetBlobsListAsync([FromQuery] string? prefix) + { + var result = await storageService.GetBlobsListAsync(prefix).ConfigureAwait(false); + return Ok(result); + } + + [HttpGet("DownloadBlob")] + public IActionResult DownloadBlobAsync([FromQuery] string blobName) + { + var result = storageService.DownloadBlobAsync(blobName); + + return Ok(result); + } + + [HttpDelete("DeleteBlob")] + public async Task DeleteFileAsync([FromQuery] string blobName) + { + var result = await storageService.DeleteBlobAsync(blobName).ConfigureAwait(false); + + if (result is null) return NotFound($"Blob {blobName} doesn't exist"); + return Ok(result); + } + } +} \ No newline at end of file diff --git a/Core.Blueprint.DAL.API/Controllers/KeyVaultController.cs b/Core.Blueprint.DAL.API/Controllers/KeyVaultController.cs new file mode 100644 index 0000000..3b4ac83 --- /dev/null +++ b/Core.Blueprint.DAL.API/Controllers/KeyVaultController.cs @@ -0,0 +1,44 @@ +using Asp.Versioning; +using Core.Blueprint.DAL.KeyVault.Contracts; +using Core.Blueprint.KeyVault; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Blueprint.DAL.API.Controllers +{ + [ApiVersion("1.0")] + [Route("api/v{api-version:apiVersion}/[controller]")] + [Produces("application/json")] + [ApiController] + [AllowAnonymous] + public class KeyVaultController(IKeyVaultService service) : ControllerBase + { + [HttpPost("CreateSecret")] + public async Task CreateSecret([FromBody] KeyVaultRequest newSecret, CancellationToken cancellationToken) + { + var result = await service.CreateSecretAsync(newSecret, cancellationToken); + return Ok(result); + } + + [HttpGet("{secretName}/GetSecret")] + public async Task GetSecret([FromRoute] string secretName, CancellationToken cancellationToken) + { + var result = await service.GetSecretAsync(secretName, cancellationToken); + return Ok(result); + } + + [HttpPut("UpdateSecret")] + public async Task UpdateSecret([FromBody] KeyVaultRequest newSecret, CancellationToken cancellationToken) + { + var result = await service.UpdateSecretAsync(newSecret, cancellationToken); + return Ok(result); + } + + [HttpDelete("{secretName}/DeleteSecret")] + public async Task DeleteSecret([FromRoute] string secretName, CancellationToken cancellationToken) + { + var result = await service.DeleteSecretAsync(secretName, cancellationToken); + return Ok(result); + } + } +} diff --git a/Core.Blueprint.DAL.API/Controllers/MongoBlueprintController.cs b/Core.Blueprint.DAL.API/Controllers/MongoBlueprintController.cs new file mode 100644 index 0000000..c93c1fd --- /dev/null +++ b/Core.Blueprint.DAL.API/Controllers/MongoBlueprintController.cs @@ -0,0 +1,67 @@ +using Asp.Versioning; +using Core.Blueprint.DAL.Mongo.Contracts; +using Core.Blueprint.DAL.Mongo.Entities.Collections; +using Core.Blueprint.DAL.Mongo.Entities.Requests; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Blueprint.DAL.API.Controllers +{ + [ApiVersion("1.0")] + [Route("api/v{api-version:apiVersion}/[controller]")] + [Produces("application/json")] + [ApiController] + [AllowAnonymous] + public class MongoBlueprintController(IBlueprintService service) : ControllerBase + { + [HttpPost("Create")] + public async Task CreateBlueprint([FromBody] BlueprintRequest entity, CancellationToken cancellationToken) + { + var result = await service.CreateBlueprint(entity, cancellationToken).ConfigureAwait(false); + return Created("CreatedWithIdAsync", result); + } + + [HttpGet("GetAll")] + public async Task GetEntities(CancellationToken cancellationToken) + { + var result = await service.GetAllBlueprints(cancellationToken).ConfigureAwait(false); + return Ok(result); + } + + [HttpGet("{_id}/GetBy_Id")] + public async Task GetBlueprint([FromRoute] string _id, CancellationToken cancellationToken) + { + var result = await service.GetBlueprintById(_id, cancellationToken).ConfigureAwait(false); + + if (result == null) + { + return NotFound("Entity not found"); + } + + return Ok(result); + } + + [HttpPut("{_id}/Update")] + public async Task UpdateBlueprint([FromRoute] string _id, [FromBody] BlueprintCollection entity, CancellationToken cancellationToken) + { + if (_id != entity._Id?.ToString()) + { + return BadRequest("Blueprint ID mismatch"); + } + + var result = await service.UpdateBlueprint(_id, entity, cancellationToken).ConfigureAwait(false); + + return Ok(result); + } + + [HttpDelete("{_id}/Delete")] + public async Task DeleteBlueprint([FromRoute] string _id, CancellationToken cancellationToken) + { + var result = await service.DeleteBlueprint(_id, cancellationToken).ConfigureAwait(false); + + if (result is null) return NotFound(); + + return Ok(result); + } + } +} diff --git a/Core.Blueprint.DAL.API/Controllers/UserProjectController.cs b/Core.Blueprint.DAL.API/Controllers/UserProjectController.cs new file mode 100644 index 0000000..3d767b3 --- /dev/null +++ b/Core.Blueprint.DAL.API/Controllers/UserProjectController.cs @@ -0,0 +1,63 @@ +using Asp.Versioning; +using Core.Blueprint.DAL.SQLServer.Contracts; +using Core.Blueprint.DAL.SQLServer.Entities; +using Core.Blueprint.DAL.SQLServer.Entities.Request; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Core.Blueprint.DAL.API.Controllers +{ + [ApiVersion("1.0")] + [Route("api/v{api-version:apiVersion}/[controller]")] + [Produces("application/json")] + [ApiController] + [AllowAnonymous] + public class UserProjectController(IUserProjectService service) : ControllerBase + { + [HttpPost("Create")] + public async Task> CreateEntity([FromBody] UserProjectRequest request) + { + var result = await service.AddUserProject(request).ConfigureAwait(false); + return Created("CreatedWithIdAsync", result); + } + + [HttpGet("GetAll")] + public async Task>> GetEntities() + { + var result = await service.GetAllUserProjects().ConfigureAwait(false); + return Ok(result); + } + + [HttpGet("{id}/GetById")] + public async Task> GetEntity(int id) + { + var result = await service.GetUserProjectById(id).ConfigureAwait(false); + + if (result is null) return NotFound("User Project not found"); + + return Ok(result); + } + + [HttpPut("{id}/Update")] + public async Task> UpdateEntity(int id, UserProject entity) + { + if (id != entity.Id) + { + return BadRequest("ID mismatch"); + } + + var result = await service.UpdateUserProject(entity).ConfigureAwait(false); + return Ok(entity); + } + + [HttpDelete("{id}/Delete")] + public async Task DeleteEntity(int id) + { + var result = await service.DeleteUserProject(id).ConfigureAwait(false); + + if (result is null) return NotFound(); + + return Ok(result); + } + } +} diff --git a/Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.csproj b/Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.csproj new file mode 100644 index 0000000..604dc72 --- /dev/null +++ b/Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.csproj @@ -0,0 +1,31 @@ + + + + net8.0 + enable + enable + b441578c-ea54-49f9-ba82-09c2b23bd830 + Linux + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.http b/Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.http new file mode 100644 index 0000000..d88ee96 --- /dev/null +++ b/Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.http @@ -0,0 +1,6 @@ +@Core.Blueprint.DAL.API_HostAddress = http://localhost:5013 + +GET {{Core.Blueprint.DAL.API_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Core.Blueprint.DAL.API/Dockerfile b/Core.Blueprint.DAL.API/Dockerfile new file mode 100644 index 0000000..82cc162 --- /dev/null +++ b/Core.Blueprint.DAL.API/Dockerfile @@ -0,0 +1,25 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 +EXPOSE 8081 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["Core.Blueprint.DAL.API/Core.Blueprint.DAL.API.csproj", "Core.Blueprint.DAL.API/"] +RUN dotnet restore "./Core.Blueprint.DAL.API/./Core.Blueprint.DAL.API.csproj" +COPY . . +WORKDIR "/src/Core.Blueprint.DAL.API" +RUN dotnet build "./Core.Blueprint.DAL.API.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./Core.Blueprint.DAL.API.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Core.Blueprint.DAL.API.dll"] \ No newline at end of file diff --git a/Core.Blueprint.DAL.API/Extensions/ConfigurationExtension.cs b/Core.Blueprint.DAL.API/Extensions/ConfigurationExtension.cs new file mode 100644 index 0000000..cbaef4e --- /dev/null +++ b/Core.Blueprint.DAL.API/Extensions/ConfigurationExtension.cs @@ -0,0 +1,36 @@ +using Core.Blueprint.DAL.KeyVault.Contracts; +using Core.Blueprint.DAL.KeyVault.Services; +using Core.Blueprint.DAL.Mongo.Contracts; +using Core.Blueprint.DAL.Mongo.Entities.Collections; +using Core.Blueprint.DAL.Mongo.Service; +using Core.Blueprint.DAL.SQLServer.Context; +using Core.Blueprint.DAL.SQLServer.Contracts; +using Core.Blueprint.DAL.Storage.Contracts; +using Core.Blueprint.DAL.Storage.Service; +using Core.Blueprint.Mongo; +using Microsoft.EntityFrameworkCore; + +namespace Core.Blueprint.DAL.API.Extensions +{ + public static class ConfigurationExtension + { + public static IServiceCollection AddDALLayerServices(this IServiceCollection services, IConfiguration configuration) + { + //Mongo + services.AddScoped(); + services.AddScoped>(); + + //SQL + services.AddDbContext(options => options.UseSqlServer(configuration.GetConnectionString("SQLServer"))); + services.AddScoped(); + + //Storage + services.AddScoped(); + + //KeyVault + services.AddScoped(); + + return services; + } + } +} diff --git a/Core.Blueprint.DAL.API/Extensions/SwaggerExtensions.cs b/Core.Blueprint.DAL.API/Extensions/SwaggerExtensions.cs new file mode 100644 index 0000000..81cc72d --- /dev/null +++ b/Core.Blueprint.DAL.API/Extensions/SwaggerExtensions.cs @@ -0,0 +1,71 @@ +using Asp.Versioning.ApiExplorer; +using Microsoft.Extensions.Options; +using Microsoft.OpenApi.Any; +using Swashbuckle.AspNetCore.SwaggerGen; +using Swashbuckle.AspNetCore.SwaggerUI; + +namespace Core.Blueprint.DAL.API.Extensions +{ + public static class SwaggerExtensions + { + public static void AddSwagger(this IServiceCollection services) + { + services.AddEndpointsApiExplorer(); + services.AddSwaggerGen(); + services.AddTransient, ConfigureSwaggerOptions>(); + } + + public static void ConfigureSwagger(this WebApplication app) + { + app.UseSwagger(); + app.UseSwaggerUI(options => + { + foreach (var version in app.DescribeApiVersions().Select(version => version.GroupName)) + options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version); + + options.DisplayRequestDuration(); + options.EnableTryItOutByDefault(); + options.DocExpansion(DocExpansion.None); + }); + } + public static IServiceCollection AddVersioning(this IServiceCollection services) + { + services.AddApiVersioning(options => options.ReportApiVersions = true) + .AddApiExplorer(options => + { + options.GroupNameFormat = "'v'VVV"; + options.SubstituteApiVersionInUrl = true; + }); + + return services; + } + } + public class ConfigureSwaggerOptions : IConfigureOptions + { + private readonly IApiVersionDescriptionProvider _provider; + + public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) + { + _provider = provider; + } + + public void Configure(SwaggerGenOptions options) + { + foreach (var description in _provider.ApiVersionDescriptions) + options.SwaggerDoc(description.GroupName, new() + { + Title = AppDomain.CurrentDomain.FriendlyName, + Version = description.ApiVersion.ToString() + }); + + //Map ALL Values Format TODO + options.MapType(() => new() + { + Format = "date", + Example = new OpenApiString(DateOnly.MinValue.ToString()) + }); + + options.CustomSchemaIds(type => type.ToString().Replace("+", ".")); + } + } +} diff --git a/Core.Blueprint.DAL.API/Program.cs b/Core.Blueprint.DAL.API/Program.cs new file mode 100644 index 0000000..772e985 --- /dev/null +++ b/Core.Blueprint.DAL.API/Program.cs @@ -0,0 +1,103 @@ +using Azure.Identity; +using Core.Blueprint.DAL.API.Extensions; +using Core.Blueprint.DAL.Mongo.Configuration; +using Core.Blueprint.KeyVault.Configuration; +using Core.Blueprint.Logging.Configuration; +using Core.Blueprint.Redis.Configuration; +using Core.Blueprint.SQLServer.Configuration; +using Core.Blueprint.Storage.Configuration; +using Microsoft.AspNetCore.HttpLogging; +using Microsoft.Extensions.Configuration.AzureAppConfiguration; +using System.Reflection; +using System.Text.Json.Serialization; + +var builder = WebApplication.CreateBuilder(args); + +builder.Configuration.AddAzureAppConfiguration(options => +{ + var endpoint = builder.Configuration.GetSection("Endpoints:AppConfigurationURI").Value; + + if (string.IsNullOrEmpty(endpoint)) + throw new ArgumentException("The app configuration is missing"); + + options.Connect(new Uri(endpoint), new DefaultAzureCredential()) + .Select(KeyFilter.Any, "blueprint_dal"); + + options.ConfigureKeyVault(keyVaultOptions => + { + keyVaultOptions.SetCredential(new DefaultAzureCredential()); + }); +}); + +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +builder.Configuration + .AddUserSecrets(Assembly.GetExecutingAssembly()) + .AddEnvironmentVariables(); + +builder.Services.AddResponseCompression(); +builder.Services.AddProblemDetails(); +builder.Services.AddMemoryCache(); +builder.Services.AddLogs(builder); +builder.Services.AddKeyVault(builder.Configuration); +builder.Services.AddBlobStorage(builder.Configuration); +builder.Services.AddRedis(builder.Configuration); +builder.Services.AddMongoLayer(builder.Configuration); +builder.Services.AddSQLServer(builder.Configuration); +builder.Services.AddDALLayerServices(builder.Configuration); + +builder.Host.ConfigureServices((context, services) => +{ + + services.AddLogging(); + services.AddControllers(); + services.AddProblemDetails(); + services.AddCors(options + => options.AddDefaultPolicy(policyBuilder + => policyBuilder + .AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod())); + + builder.Services.Configure(options => + { + options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); + }); + + services + .AddEndpointsApiExplorer() + .AddVersioning() + .AddSwagger(); + + services.AddHealthChecks(); + services.AddHttpLogging(options => options.LoggingFields = HttpLoggingFields.All); + + builder.Services.AddOutputCache(options => + { + options.AddBasePolicy(builder => + builder.Expire(TimeSpan.FromSeconds(10))); + options.AddPolicy("Expire20", builder => + builder.Expire(TimeSpan.FromSeconds(20))); + options.AddPolicy("Expire30", builder => + builder.Expire(TimeSpan.FromSeconds(30))); + }); +}); + +var app = builder.Build(); + +app.UseSwagger(); +app.UseSwaggerUI(); + +app.MapControllers(); +app.UseCors(); +app.ConfigureSwagger(); +app.UseHttpsRedirection(); +app.UseStaticFiles(); +app.UseRouting(); +app.UseResponseCompression(); +app.UseOutputCache(); +app.UseResponseCaching(); +app.UseLogging(builder.Configuration); +app.MapHealthChecks("/health"); + +app.Run(); \ No newline at end of file diff --git a/Core.Blueprint.DAL.API/Properties/launchSettings.json b/Core.Blueprint.DAL.API/Properties/launchSettings.json new file mode 100644 index 0000000..3db0cd2 --- /dev/null +++ b/Core.Blueprint.DAL.API/Properties/launchSettings.json @@ -0,0 +1,52 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5013" + }, + "https": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7075;http://localhost:5013" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Local" + } + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "environmentVariables": { + "ASPNETCORE_HTTPS_PORTS": "8081", + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true, + "useSSL": true + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:13526", + "sslPort": 44366 + } + } +} \ No newline at end of file diff --git a/Core.Blueprint.DAL.API/appsettings.Development.json b/Core.Blueprint.DAL.API/appsettings.Development.json new file mode 100644 index 0000000..dc08592 --- /dev/null +++ b/Core.Blueprint.DAL.API/appsettings.Development.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "CacheSettings": { + "DefaultCacheDurationInMinutes": 3 + } +} \ No newline at end of file diff --git a/Core.Blueprint.DAL.API/appsettings.Local.json b/Core.Blueprint.DAL.API/appsettings.Local.json new file mode 100644 index 0000000..dc08592 --- /dev/null +++ b/Core.Blueprint.DAL.API/appsettings.Local.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "CacheSettings": { + "DefaultCacheDurationInMinutes": 3 + } +} \ No newline at end of file diff --git a/Core.Blueprint.DAL.API/appsettings.json b/Core.Blueprint.DAL.API/appsettings.json new file mode 100644 index 0000000..adacba1 --- /dev/null +++ b/Core.Blueprint.DAL.API/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "Endpoints": { + "AppConfigurationURI": "https://sandbox-hci-usc-appcg.azconfig.io" + } +} diff --git a/Core.Blueprint.DAL.Hangfire/Core.Blueprint.DAL.Hangfire.csproj b/Core.Blueprint.DAL.Hangfire/Core.Blueprint.DAL.Hangfire.csproj new file mode 100644 index 0000000..3aaa39c --- /dev/null +++ b/Core.Blueprint.DAL.Hangfire/Core.Blueprint.DAL.Hangfire.csproj @@ -0,0 +1,24 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + diff --git a/Core.Blueprint.DAL.Infrastrcture/Configure/DependencyInjection.cs b/Core.Blueprint.DAL.Infrastrcture/Configure/DependencyInjection.cs new file mode 100644 index 0000000..e70bcb6 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Configure/DependencyInjection.cs @@ -0,0 +1,75 @@ +using Azure.Identity; +using Core.Blueprint.DAL.Infrastructure.Context; +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.DAL.Infrastructure.Proxies; +using Core.Blueprint.DAL.Infrastructure.Repository; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Azure; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System.Reflection; +using Serilog; +using Serilog.Events; +using Serilog.Exceptions; + +namespace Core.Blueprint.DAL.Infrastructure.Configure +{ + public static class DependencyInjection + { + public static IServiceCollection RegisterInfrastructure(this IServiceCollection services, IConfiguration configuration) + { + var chainedCredentials = new ChainedTokenCredential( + new ManagedIdentityCredential(), + new SharedTokenCacheCredential(), + new VisualStudioCredential(), + new VisualStudioCodeCredential() + ); + services.AddAzureClients(cfg => + { + cfg.AddBlobServiceClient(configuration.GetRequiredSection("Azure:BlobStorage")).WithCredential(chainedCredentials); + cfg.AddSecretClient(configuration.GetRequiredSection("Azure:KeyVault")).WithCredential(chainedCredentials); + }); + + services.AddDbContext(options => options.UseSqlServer(configuration.GetConnectionString("SQLServerConnString"))); + + // LogFactory + services.ConfigureLogging(); + + //MongoDB + services.Configure(configuration.GetRequiredSection("MongoDbSettings")); + services.AddScoped(serviceProvider => + serviceProvider.GetRequiredService>().Value); + services.AddScoped(); + services.AddScoped(serviceProvider => + { + var mongoContext = serviceProvider.GetRequiredService(); + return mongoContext.Database; + }); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } + + public static void ConfigureLogging(this IServiceCollection services) + { + Log.Logger = new LoggerConfiguration() + .Enrich.FromLogContext() + .Enrich.WithMachineName() + .Enrich.WithProcessId() + .Enrich.WithThreadId() + .Enrich.WithProperty("Application", Assembly.GetExecutingAssembly().GetName().Name) + .Enrich.WithExceptionDetails() + .MinimumLevel.Is(LogEventLevel.Information) + .WriteTo.Console() + .CreateLogger(); + + services.AddSingleton(Log.Logger); + } + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Configure/LoggerExtension.cs b/Core.Blueprint.DAL.Infrastrcture/Configure/LoggerExtension.cs new file mode 100644 index 0000000..7dec044 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Configure/LoggerExtension.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Blueprint.DAL.Infrastructure.Configure +{ + class LoggerExtension + { + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Context/MongoContext.cs b/Core.Blueprint.DAL.Infrastrcture/Context/MongoContext.cs new file mode 100644 index 0000000..a59ffc1 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Context/MongoContext.cs @@ -0,0 +1,135 @@ +using Azure.Core; +using Azure.Identity; +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.Domain.Shared; +using Microsoft.Extensions.Configuration; +using MongoDB.Driver; +using MongoDB.Driver.Authentication.Oidc; +using System.Reflection; + +namespace Core.Blueprint.DAL.Infrastructure.Context +{ + public interface IMongoDbSettings + { + string DatabaseName { get; set; } + string ConnectionString { get; set; } + } + + public class MongoDbSettings : IMongoDbSettings + { + public string DatabaseName { get; set; } + public string ConnectionString { get; set; } + + } + + public class HeathOidcCallback : IOidcCallback + { + private readonly string _audience; + public HeathOidcCallback(string audience) + { + _audience = audience; + } + + public OidcAccessToken GetOidcAccessToken(OidcCallbackParameters parameters, CancellationToken cancellationToken) + { + TokenRequestContext tokenRequestContext = + new TokenRequestContext( + new[] { _audience } + ); + + AccessToken token = + new ChainedTokenCredential( + new ManagedIdentityCredential(), + new SharedTokenCacheCredential(), + new VisualStudioCredential(), + new VisualStudioCodeCredential() + ) + .GetToken( + tokenRequestContext + ); + + return new(token.Token, expiresIn: null); + } + + public async Task GetOidcAccessTokenAsync(OidcCallbackParameters parameters, CancellationToken cancellationToken) + { + TokenRequestContext tokenRequestContext = + new TokenRequestContext( + new[] { _audience } + ); + + AccessToken token = + await new ChainedTokenCredential( + new ManagedIdentityCredential(), + new SharedTokenCacheCredential(), + new VisualStudioCredential(), + new VisualStudioCodeCredential() + ) + .GetTokenAsync( + tokenRequestContext, cancellationToken + ).ConfigureAwait(false); + + return new(token.Token, expiresIn: null); + } + } + public sealed class MongoContext : IMongoContext + { + private IMongoDatabase _database; + public IMongoClient _client { get; private set; } + public IMongoDatabase Database { get { return _database; } } + public MongoUrl _mongoUrl { get; private set; } + + //Constructors + public MongoContext(IConfiguration configuration) + { + var mongoClientSettings = MongoClientSettings.FromConnectionString(configuration.GetConnectionString("MongoDBConnString")); + mongoClientSettings.Credential = MongoCredential.CreateOidcCredential(new HeathOidcCallback(configuration.GetValue("MongoDb:Audience"))); + _database = new MongoClient(mongoClientSettings).GetDatabase(configuration.GetValue("MongoDb:DatabaseName")); //Mongo Database + _client = Database.Client; + } + public MongoContext(string connectionString, string audience, string databaseName) + { + var mongoClientSettings = MongoClientSettings.FromConnectionString(connectionString); + mongoClientSettings.Credential = MongoCredential.CreateOidcCredential(new HeathOidcCallback(audience)); + _client = new MongoClient(mongoClientSettings); + _database = _client.GetDatabase(databaseName); + } + public MongoContext(MongoClient client, string databaseName) + { + _client = client; + _database = client.GetDatabase(databaseName); + } + public void SetUpDatabase(string database) + { + _database = new MongoClient(_mongoUrl).GetDatabase(database); + _client = Database.Client; + } + + + //Methods + public void DropCollection(string? partitionKey = null) where TDocument : class + { + Database.DropCollection(GetCollectionName(partitionKey)); + } + public IMongoCollection GetCollection(string collection) + { + return _database.GetCollection(collection); + } + + private string GetCollectionName(string partitionKey) + { + var collectionName = GetAttributeCollectionName(); + if (string.IsNullOrEmpty(partitionKey)) + { + return collectionName; + } + return $"{partitionKey}-{collectionName}"; + } + private string? GetAttributeCollectionName() + { + return (typeof(TDocument).GetTypeInfo() + .GetCustomAttributes(typeof(CollectionAttributeName)) + .FirstOrDefault() as CollectionAttributeName)?.Name; + } + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Context/SqlServerContext.cs b/Core.Blueprint.DAL.Infrastrcture/Context/SqlServerContext.cs new file mode 100644 index 0000000..35820e3 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Context/SqlServerContext.cs @@ -0,0 +1,16 @@ +using Core.Blueprint.Domain.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Core.Blueprint.DAL.Infrastructure.Context +{ + public sealed class SqlServerContext : DbContext + { + public SqlServerContext(DbContextOptions options) : base(options) { } + public DbSet BlueprintTests { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.Entity().Property(p => p.Id).HasConversion(a => a, b => b); + } + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Contracts/IBlueprintRepository.cs b/Core.Blueprint.DAL.Infrastrcture/Contracts/IBlueprintRepository.cs new file mode 100644 index 0000000..bb5a07e --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Contracts/IBlueprintRepository.cs @@ -0,0 +1,9 @@ +using Core.Blueprint.Domain.Entities; + +namespace Core.Blueprint.DAL.Infrastructure.Contracts +{ + public interface IBlueprintRepository : IRepositoryIdentityBase + { + + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Contracts/IMongoContext.cs b/Core.Blueprint.DAL.Infrastrcture/Contracts/IMongoContext.cs new file mode 100644 index 0000000..5828554 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Contracts/IMongoContext.cs @@ -0,0 +1,13 @@ +using MongoDB.Driver; + +namespace Core.Blueprint.DAL.Infrastructure.Contracts +{ + public interface IMongoContext + { + + IMongoClient _client { get; } + IMongoDatabase Database { get; } + void DropCollection(string partitionKey = null) where TDocument : class; + void SetUpDatabase(string database); + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Contracts/IProxyBlobStorage.cs b/Core.Blueprint.DAL.Infrastrcture/Contracts/IProxyBlobStorage.cs new file mode 100644 index 0000000..966831f --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Contracts/IProxyBlobStorage.cs @@ -0,0 +1,11 @@ +using Core.Blueprint.Domain.Dtos; + +namespace Core.Blueprint.DAL.Infrastructure.Contracts +{ + public interface IProxyBlobStorage + { + IAsyncEnumerable ListAllItemsAsync(); + Task GetFirstImageUrlAsync(); + Task GetUploadUrl(); + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Contracts/IRepositoryBase.cs b/Core.Blueprint.DAL.Infrastrcture/Contracts/IRepositoryBase.cs new file mode 100644 index 0000000..670d00a --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Contracts/IRepositoryBase.cs @@ -0,0 +1,11 @@ +namespace Core.Blueprint.DAL.Infrastructure.Contracts +{ + public interface IRepositoryBase where T : class + { + ValueTask GetByIdAsync(string id); + ValueTask> GetAllAsync(); + ValueTask CreateAsync(T blueprint); + Task UpdateAsync(string id, T blueprint); + Task DeleteAsync(string id); + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Contracts/IRepositoryIdentityBase.cs b/Core.Blueprint.DAL.Infrastrcture/Contracts/IRepositoryIdentityBase.cs new file mode 100644 index 0000000..2aca196 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Contracts/IRepositoryIdentityBase.cs @@ -0,0 +1,9 @@ +using Core.Blueprint.Domain.Entities; + +namespace Core.Blueprint.DAL.Infrastructure.Contracts +{ + public interface IRepositoryIdentityBase : IRepositoryBase where T : AbsEntity + { + ValueTask GetByIdAsync(Guid id) => GetByIdAsync(id.ToString()); + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Contracts/ISampleImageRepository.cs b/Core.Blueprint.DAL.Infrastrcture/Contracts/ISampleImageRepository.cs new file mode 100644 index 0000000..5ffab80 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Contracts/ISampleImageRepository.cs @@ -0,0 +1,11 @@ +using Core.Blueprint.Domain.Dtos; + +namespace Core.Blueprint.DAL.Infrastructure.Contracts +{ + public interface ISampleImageRepository + { + IAsyncEnumerable GetAllImagesUrls(); + Task GetFirstImageUrl(); + Task GetUploadUrl(); + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Contracts/ISampleItemRepository.cs b/Core.Blueprint.DAL.Infrastrcture/Contracts/ISampleItemRepository.cs new file mode 100644 index 0000000..1d74371 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Contracts/ISampleItemRepository.cs @@ -0,0 +1,8 @@ +using Core.Blueprint.Domain.Entities; + +namespace Core.Blueprint.DAL.Infrastructure.Contracts +{ + public interface ISampleItemRepository : IRepositoryIdentityBase + { + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Contracts/ISecretRepository.cs b/Core.Blueprint.DAL.Infrastrcture/Contracts/ISecretRepository.cs new file mode 100644 index 0000000..32d41bb --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Contracts/ISecretRepository.cs @@ -0,0 +1,11 @@ +using Core.Blueprint.Domain.Entities; + +namespace Core.Blueprint.DAL.Infrastructure.Contracts +{ + public interface ISecretRepository + { + Task GetSecret(string secretName, CancellationToken cancellationToken); + Task SetSecret(string secretName, string secretValue, CancellationToken cancellationToken); + Task RemoveSecret(string secretName, CancellationToken cancellationToken); + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Core.Blueprint.DAL.Infrastructure.csproj b/Core.Blueprint.DAL.Infrastrcture/Core.Blueprint.DAL.Infrastructure.csproj new file mode 100644 index 0000000..735ea1b --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Core.Blueprint.DAL.Infrastructure.csproj @@ -0,0 +1,31 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core.Blueprint.DAL.Infrastrcture/Proxies/ProxyBlobStorage.cs b/Core.Blueprint.DAL.Infrastrcture/Proxies/ProxyBlobStorage.cs new file mode 100644 index 0000000..e0f56e5 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Proxies/ProxyBlobStorage.cs @@ -0,0 +1,95 @@ +using Azure.Storage.Blobs; +using Azure.Storage.Blobs.Models; +using Azure.Storage.Sas; +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.Domain.Dtos; +using Microsoft.Extensions.Azure; +using System.Reflection.Metadata; + +namespace Core.Blueprint.DAL.Infrastructure.Proxies; + +public class ProxyBlobStorage : IProxyBlobStorage +{ + + private readonly BlobServiceClient _blobServiceClient; + private readonly BlobContainerClient _blobContainerClient; + private const string TIMESTAMP_TAG_NAME = "Timestamp"; + private UserDelegationKey _blobDelegationKey; + + public ProxyBlobStorage(BlobServiceClient blobServiceClient) + { + _blobServiceClient = blobServiceClient; + _blobContainerClient = blobServiceClient.GetBlobContainerClient("blueprint"); + _blobDelegationKey = _blobServiceClient.GetUserDelegationKey(DateTimeOffset.UtcNow, + DateTimeOffset.UtcNow.AddHours(2)); + } + + public async IAsyncEnumerable ListAllItemsAsync() + { + await using (var blobs = _blobContainerClient.GetBlobsAsync().GetAsyncEnumerator()) + { + while (await blobs.MoveNextAsync()) + { + yield return new ImageUrlDto + { + Url = _blobContainerClient.GenerateSasUri(BlobContainerSasPermissions.Read, DateTimeOffset.UtcNow.AddMinutes(5)) + }; + } + } + } + + public async Task GetFirstImageUrlAsync() + { + await using (var blob = _blobContainerClient.GetBlobsAsync().GetAsyncEnumerator()) + { + System.Console.WriteLine(_blobContainerClient.Uri.ToString()); + await blob.MoveNextAsync(); + + var blobClient = _blobContainerClient.GetBlobClient(blob.Current.Name); + + var sasBuilder = new BlobSasBuilder() + { + BlobContainerName = blobClient.BlobContainerName, + BlobName = blobClient.Name, + Resource = "b", + StartsOn = DateTimeOffset.UtcNow, + ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5), + }; + sasBuilder.SetPermissions(BlobAccountSasPermissions.Read); + + var blobUriBuilder = new BlobUriBuilder(blobClient.Uri){ + Sas = sasBuilder.ToSasQueryParameters(_blobDelegationKey, _blobServiceClient.AccountName) + }; + + var uri = blobUriBuilder.ToUri(); + System.Console.WriteLine(uri); + return new ImageUrlDto { Url = uri }; + } + } + + public async Task GetUploadUrl() + { + await using (var blobs = _blobContainerClient.GetBlobsAsync().GetAsyncEnumerator()) + { + await blobs.MoveNextAsync(); + + var blobClient = _blobContainerClient.GetBlobClient(blobs.Current.Name); + + var sasBuilder = new BlobSasBuilder(){ + BlobContainerName = blobClient.BlobContainerName, + BlobName = blobClient.Name, + Resource = "b", + StartsOn = DateTimeOffset.UtcNow, + ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5), + }; + sasBuilder.SetPermissions(BlobAccountSasPermissions.Read); + + var blobUriBuilder = new BlobUriBuilder(blobClient.Uri){ + Sas = sasBuilder.ToSasQueryParameters(_blobDelegationKey, _blobServiceClient.AccountName) + }; + + var uri = blobUriBuilder.ToUri(); + return new ImageUrlDto { Url = uri }; + } + } +} \ No newline at end of file diff --git a/Core.Blueprint.DAL.Infrastrcture/Repository/BluePrintRepository.cs b/Core.Blueprint.DAL.Infrastrcture/Repository/BluePrintRepository.cs new file mode 100644 index 0000000..69bd90d --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Repository/BluePrintRepository.cs @@ -0,0 +1,52 @@ +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.Domain.Entities; +using Microsoft.Extensions.Logging; +using MongoDB.Driver; + +namespace Core.Blueprint.DAL.Infrastructure.Repository +{ + public sealed class BlueprintRepository: IBlueprintRepository + { + private readonly IMongoContext _context; + private readonly ILogger _logger; + private readonly IMongoCollection _collection; + + public BlueprintRepository(IMongoContext context, IProxyBlobStorage blobProxy, ILogger logger) + { + _context = context; + _logger = logger; + _collection = _context.Database.GetCollection("Blueprints"); + } + + public async ValueTask GetByIdAsync(string id) + { + var result = await _collection.FindAsync(b => b.Id == id); + return result.First(); + } + + async ValueTask> IRepositoryBase.GetAllAsync() + { + var result = await _collection.FindAsync(_ => true); + return (await result.ToListAsync()).AsQueryable(); + } + + async ValueTask IRepositoryBase.CreateAsync(BlueprintCollection blueprint) + { + blueprint.Id = null; + await _collection.InsertOneAsync(blueprint); + return blueprint; + } + + public async Task UpdateAsync(string id, BlueprintCollection blueprint) + { + var result = await _collection.ReplaceOneAsync(b => b.Id == id, blueprint); + return result.IsAcknowledged && result.ModifiedCount > 0; + } + + public async Task DeleteAsync(string id) + { + var result = await _collection.DeleteOneAsync(b => b.Id == id); + return result.IsAcknowledged && result.DeletedCount > 0; + } + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Repository/RepositorySqlBase.cs b/Core.Blueprint.DAL.Infrastrcture/Repository/RepositorySqlBase.cs new file mode 100644 index 0000000..1f99d88 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Repository/RepositorySqlBase.cs @@ -0,0 +1,54 @@ +using Core.Blueprint.DAL.Infrastructure.Context; +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.Domain.Entities; + +namespace Core.Blueprint.DAL.Infrastructure.Repository +{ + public abstract class RepositorySqlBase : IRepositoryIdentityBase where T: AbsEntity + { + protected SqlServerContext _dbContext; + + protected RepositorySqlBase(SqlServerContext dbContext) + { + _dbContext = dbContext; + } + + public async ValueTask CreateAsync(T blueprint) + { + blueprint.Id = Guid.NewGuid().ToString(); + _dbContext.Add(blueprint); + await _dbContext.SaveChangesAsync(); + return blueprint; + } + + public async Task DeleteAsync(string id) + { + var item = await GetByIdAsync(id); + if (item == null) + { + return false; + } + _dbContext.Remove(item); + await _dbContext.SaveChangesAsync(); + return true; + } + + public async ValueTask> GetAllAsync() + { + return _dbContext.Set(); + } + + public async ValueTask GetByIdAsync(string id) + { + var result = await _dbContext.FindAsync(id); + return result; + } + + public async Task UpdateAsync(string id, T entity) + { + _dbContext.Update(entity); + await _dbContext.SaveChangesAsync(); + return true; + } + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Repository/SampleImageRepository.cs b/Core.Blueprint.DAL.Infrastrcture/Repository/SampleImageRepository.cs new file mode 100644 index 0000000..9e48502 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Repository/SampleImageRepository.cs @@ -0,0 +1,33 @@ +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.Domain.Dtos; + +namespace Core.Blueprint.DAL.Infrastructure.Repository +{ + public class SampleImageRepository : ISampleImageRepository + { + private readonly IProxyBlobStorage _proxyBlobStorage; + public SampleImageRepository(IProxyBlobStorage proxyBlobStorage) + { + _proxyBlobStorage = proxyBlobStorage; + } + + public async IAsyncEnumerable GetAllImagesUrls() + { + await using (var images = _proxyBlobStorage.ListAllItemsAsync().GetAsyncEnumerator()) + { + await images.MoveNextAsync(); + yield return images.Current; + } + } + + public async Task GetFirstImageUrl() + { + return await _proxyBlobStorage.GetFirstImageUrlAsync(); + } + + public async Task GetUploadUrl() + { + return await _proxyBlobStorage.GetUploadUrl(); + } + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Repository/SampleItemRepository.cs b/Core.Blueprint.DAL.Infrastrcture/Repository/SampleItemRepository.cs new file mode 100644 index 0000000..d33f020 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Repository/SampleItemRepository.cs @@ -0,0 +1,16 @@ +using Core.Blueprint.DAL.Infrastructure.Context; +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.Domain.Entities; +using Microsoft.Extensions.Logging; + +namespace Core.Blueprint.DAL.Infrastructure.Repository +{ + public class SampleItemRepository : RepositorySqlBase, ISampleItemRepository + { + private readonly ILogger _logger; + public SampleItemRepository(SqlServerContext dbContext, ILogger logger) : base(dbContext) + { + _logger = logger; + } + } +} diff --git a/Core.Blueprint.DAL.Infrastrcture/Repository/SecretRepository.cs b/Core.Blueprint.DAL.Infrastrcture/Repository/SecretRepository.cs new file mode 100644 index 0000000..9ca2e74 --- /dev/null +++ b/Core.Blueprint.DAL.Infrastrcture/Repository/SecretRepository.cs @@ -0,0 +1,41 @@ +using Azure.Security.KeyVault.Secrets; +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Blueprint.DAL.Infrastructure.Repository +{ + public class SecretRepository : ISecretRepository + { + private readonly SecretClient _client; + public SecretRepository(SecretClient client) + { + _client = client; + } + + public async Task GetSecret(string secretName, CancellationToken cancellationToken) + { + var ret = await _client.GetSecretAsync(secretName, cancellationToken: cancellationToken); + return new Secret() { Value = ret.Value?.Value }; + } + + public async Task SetSecret(string secretName, string secretValue, CancellationToken cancellationToken) + { + + await _client.SetSecretAsync(new KeyVaultSecret(secretName, secretValue), cancellationToken: cancellationToken); + + } + + public async Task RemoveSecret(string secretName, CancellationToken cancellationToken) + { + + await _client.StartDeleteSecretAsync(secretName, cancellationToken: cancellationToken); + + } + + } +} diff --git a/Core.Blueprint.DAL.KeyVault/Contracts/IKeyVaultService.cs b/Core.Blueprint.DAL.KeyVault/Contracts/IKeyVaultService.cs new file mode 100644 index 0000000..0c2aacf --- /dev/null +++ b/Core.Blueprint.DAL.KeyVault/Contracts/IKeyVaultService.cs @@ -0,0 +1,12 @@ +using Core.Blueprint.KeyVault; + +namespace Core.Blueprint.DAL.KeyVault.Contracts +{ + public interface IKeyVaultService + { + Task CreateSecretAsync(KeyVaultRequest secret, CancellationToken cancellationToken); + Task> GetSecretAsync(string secret, CancellationToken cancellationToken); + Task> DeleteSecretAsync(string secretName, CancellationToken cancellationToken); + Task> UpdateSecretAsync(KeyVaultRequest secretName, CancellationToken cancellationToken); + } +} diff --git a/Core.Blueprint.DAL.KeyVault/Core.Blueprint.DAL.KeyVault.csproj b/Core.Blueprint.DAL.KeyVault/Core.Blueprint.DAL.KeyVault.csproj new file mode 100644 index 0000000..a547b73 --- /dev/null +++ b/Core.Blueprint.DAL.KeyVault/Core.Blueprint.DAL.KeyVault.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/Core.Blueprint.DAL.KeyVault/Services/KeyVaultService.cs b/Core.Blueprint.DAL.KeyVault/Services/KeyVaultService.cs new file mode 100644 index 0000000..50158c8 --- /dev/null +++ b/Core.Blueprint.DAL.KeyVault/Services/KeyVaultService.cs @@ -0,0 +1,31 @@ +using Core.Blueprint.DAL.KeyVault.Contracts; +using Core.Blueprint.KeyVault; + +namespace Core.Blueprint.DAL.KeyVault.Services +{ + public class KeyVaultService(IKeyVaultProvider keyVaultProvider) : IKeyVaultService + { + public async Task CreateSecretAsync(KeyVaultRequest newSecret, CancellationToken cancellationToken) + { + var result = await keyVaultProvider.CreateSecretAsync(newSecret, cancellationToken); + return result; + } + public async Task> GetSecretAsync(string secretName, CancellationToken cancellationToken) + { + var result = await keyVaultProvider.GetSecretAsync(secretName, cancellationToken: cancellationToken); + return result; + } + + public async Task> UpdateSecretAsync(KeyVaultRequest newSecret, CancellationToken cancellationToken) + { + var result = await keyVaultProvider.UpdateSecretAsync(newSecret, cancellationToken); + return result; + } + + public async Task> DeleteSecretAsync(string secretName, CancellationToken cancellationToken) + { + var result = await keyVaultProvider.DeleteSecretAsync(secretName, cancellationToken); + return result; + } + } +} diff --git a/Core.Blueprint.DAL.Logs/BlueprintSerilogLogger.cs b/Core.Blueprint.DAL.Logs/BlueprintSerilogLogger.cs new file mode 100644 index 0000000..9184f4c --- /dev/null +++ b/Core.Blueprint.DAL.Logs/BlueprintSerilogLogger.cs @@ -0,0 +1,50 @@ +using Core.Blueprint.DAL.Logs.Contracts; +using Serilog; + +namespace Core.Blueprint.DAL.Logs +{ + public class BlueprintSerilogLogger : IBlueprintSerilogLogger + { + private readonly ILogger logger; + + public BlueprintSerilogLogger(ILogger logger) + { + this.logger = logger; + } + + public void LogInformation(string service, params object[] args) + { + logger.Information("Starting operation in {service} service", service, args); + } + + public void LogOperationStarted(string service, params object[] args) + { + logger.Information("Starting operation in {Service} service with parameters: {@Args}", service, args); + } + public void LogOperationFinished(string service, params object[] args) + { + logger.Information("Finishing operation in {Service} service with parameters: {@Args}", service, args); + } + + public void LogInformation(string message) + { + logger.Information(message); + } + + public void LogWarning(string message, params object[] args) + { + logger.Warning(message, args); + } + + public void LogError(string service, params object[] args) + { + logger.Error("An error occurred in `{service}` Exception: {@Args}", service, args); + } + + public void LogCritical(Exception exception, string message, params object[] args) + { + logger.Fatal(exception, message, args); + } + } +} + diff --git a/Core.Blueprint.DAL.Logs/Configuration/LogExtensionConfiguration.cs b/Core.Blueprint.DAL.Logs/Configuration/LogExtensionConfiguration.cs new file mode 100644 index 0000000..47d6236 --- /dev/null +++ b/Core.Blueprint.DAL.Logs/Configuration/LogExtensionConfiguration.cs @@ -0,0 +1,15 @@ +using Core.Blueprint.DAL.Logs.Contracts; +using Microsoft.Extensions.DependencyInjection; + +namespace Core.Blueprint.DAL.Logs.Configuration +{ + public static class LogExtensionConfiguration + { + public static IServiceCollection AddLogLayer(this IServiceCollection services) + { + services.AddScoped(); + + return services; + } + } +} diff --git a/Core.Blueprint.DAL.Logs/Contracts/IBlueprintLogger.cs b/Core.Blueprint.DAL.Logs/Contracts/IBlueprintLogger.cs new file mode 100644 index 0000000..1192464 --- /dev/null +++ b/Core.Blueprint.DAL.Logs/Contracts/IBlueprintLogger.cs @@ -0,0 +1,9 @@ +namespace Core.Blueprint.DAL.Logs.Contracts +{ + public interface IBlueprintLogger where T : class, new() + { + //logger + //serilog... + //patterns of structuie logging + } +} diff --git a/Core.Blueprint.DAL.Logs/Contracts/IBlueprintSerilogLogger.cs b/Core.Blueprint.DAL.Logs/Contracts/IBlueprintSerilogLogger.cs new file mode 100644 index 0000000..779d7ad --- /dev/null +++ b/Core.Blueprint.DAL.Logs/Contracts/IBlueprintSerilogLogger.cs @@ -0,0 +1,12 @@ +namespace Core.Blueprint.DAL.Logs.Contracts +{ + public interface IBlueprintSerilogLogger + { + public void LogInformation(string service, params object[] args); + public void LogOperationStarted(string service, params object[] args); + public void LogOperationFinished(string service, params object[] args); + public void LogWarning(string message, params object[] args); + public void LogError(string servicee, params object[] args); + public void LogCritical(Exception exception, string message, params object[] args); + } +} \ No newline at end of file diff --git a/Core.Blueprint.DAL.Logs/Core.Blueprint.DAL.Logs.csproj b/Core.Blueprint.DAL.Logs/Core.Blueprint.DAL.Logs.csproj new file mode 100644 index 0000000..bc4910c --- /dev/null +++ b/Core.Blueprint.DAL.Logs/Core.Blueprint.DAL.Logs.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/Core.Blueprint.DAL.Logs/Log.cs b/Core.Blueprint.DAL.Logs/Log.cs new file mode 100644 index 0000000..1f21d82 --- /dev/null +++ b/Core.Blueprint.DAL.Logs/Log.cs @@ -0,0 +1,24 @@ +namespace Core.Blueprint.DAL.Logs; +public static partial class Log +{ + //[LoggerMessage( + // EventId = 1, + // Level = LogLevel.Information, + // Message = "Starting operation in`{service}` service with parameters: {@Args}" + //)] + //static partial void OperationStarted(ILogger logger, string service, params object[] args); + + //[LoggerMessage( + // EventId = 1, + // Level = LogLevel.Information, + // Message = "Operation finished in`{service}` service with parameters: {@Args}" + //)] + //static partial void OperationFinished(ILogger logger, string service, params object[] args); + + //[LoggerMessage( + // EventId = 2, + // Level = LogLevel.Error, + // Message = "An error occurred in `{service}` service" + //)] + //static partial void OperationFailed(ILogger logger, string service, Exception exception); +} diff --git a/Core.Blueprint.DAL.Mongo/Contracts/IBlueprintService.cs b/Core.Blueprint.DAL.Mongo/Contracts/IBlueprintService.cs new file mode 100644 index 0000000..90502b2 --- /dev/null +++ b/Core.Blueprint.DAL.Mongo/Contracts/IBlueprintService.cs @@ -0,0 +1,18 @@ +using Core.Blueprint.DAL.Mongo.Entities.Collections; +using Core.Blueprint.DAL.Mongo.Entities.Requests; + +namespace Core.Blueprint.DAL.Mongo.Contracts +{ + public interface IBlueprintService + { + ValueTask CreateBlueprint(BlueprintRequest newBlueprint, CancellationToken cancellationToken); + + ValueTask GetBlueprintById(string _id, CancellationToken cancellationToken); + + ValueTask> GetAllBlueprints(CancellationToken cancellationToken); + + ValueTask UpdateBlueprint(string _id, BlueprintCollection entity, CancellationToken cancellationToken); + + ValueTask DeleteBlueprint(string _id, CancellationToken cancellationToken); + } +} diff --git a/Core.Blueprint.DAL.Mongo/Core.Blueprint.DAL.Mongo.csproj b/Core.Blueprint.DAL.Mongo/Core.Blueprint.DAL.Mongo.csproj new file mode 100644 index 0000000..b593439 --- /dev/null +++ b/Core.Blueprint.DAL.Mongo/Core.Blueprint.DAL.Mongo.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/Core.Blueprint.DAL.Mongo/Entities/Collections/BlueprintCollection.cs b/Core.Blueprint.DAL.Mongo/Entities/Collections/BlueprintCollection.cs new file mode 100644 index 0000000..30b23b4 --- /dev/null +++ b/Core.Blueprint.DAL.Mongo/Entities/Collections/BlueprintCollection.cs @@ -0,0 +1,11 @@ +using Core.Blueprint.Mongo; + +namespace Core.Blueprint.DAL.Mongo.Entities.Collections +{ + [CollectionAttributeName("Blueprints")] + public class BlueprintCollection : Document + { + public string Name { get; set; } = null!; + public string? Description { get; set; } + } +} diff --git a/Core.Blueprint.DAL.Mongo/Entities/Requests/BlueprintRequest.cs b/Core.Blueprint.DAL.Mongo/Entities/Requests/BlueprintRequest.cs new file mode 100644 index 0000000..3cf6881 --- /dev/null +++ b/Core.Blueprint.DAL.Mongo/Entities/Requests/BlueprintRequest.cs @@ -0,0 +1,9 @@ +namespace Core.Blueprint.DAL.Mongo.Entities.Requests +{ + public class BlueprintRequest + { + public string Name { get; set; } = null!; + + public string? Description { get; set; } + } +} diff --git a/Core.Blueprint.DAL.Mongo/Service/BlueprintService.cs b/Core.Blueprint.DAL.Mongo/Service/BlueprintService.cs new file mode 100644 index 0000000..6aeb023 --- /dev/null +++ b/Core.Blueprint.DAL.Mongo/Service/BlueprintService.cs @@ -0,0 +1,80 @@ +using Core.Blueprint.DAL.Mongo.Contracts; +using Core.Blueprint.DAL.Mongo.Entities.Collections; +using Core.Blueprint.DAL.Mongo.Entities.Requests; +using Core.Blueprint.Mongo; +using Core.Blueprint.Redis; +using Core.Blueprint.Redis.Helpers; +using Mapster; +using Microsoft.Extensions.Options; + +namespace Core.Blueprint.DAL.Mongo.Service +{ + public class BlueprintService : IBlueprintService + { + private readonly CollectionRepository repository; + private readonly CacheSettings cacheSettings; + private readonly IRedisCacheProvider cacheProvider; + + public BlueprintService(CollectionRepository repository, + IRedisCacheProvider cacheProvider, IOptions cacheSettings) + { + this.repository = repository; + this.repository.CollectionInitialization(); + this.cacheSettings = cacheSettings.Value; + this.cacheProvider = cacheProvider; + } + + public async ValueTask CreateBlueprint(BlueprintRequest newBlueprint, CancellationToken cancellationToken) + { + var blueprintCollection = newBlueprint.Adapt(); + + await this.repository.InsertOneAsync(blueprintCollection); + + return blueprintCollection; + } + + public async ValueTask GetBlueprintById(string _id, CancellationToken cancellationToken) + { + + var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetBlueprintById", _id); + var cachedData = await cacheProvider.GetAsync(cacheKey); + + if (cachedData is not null) { return cachedData; } + + var blueprint = await this.repository.FindByIdAsync(_id); + + await cacheProvider.SetAsync(cacheKey, blueprint); + + return blueprint; + } + + public async ValueTask> GetAllBlueprints(CancellationToken cancellationToken) + { + + var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetAllBlueprints"); + var cachedData = await cacheProvider.GetAsync>(cacheKey) ?? []; + + if (cachedData.Any()) return cachedData; + + var blueprints = await this.repository.AsQueryable(); + + await cacheProvider.SetAsync(cacheKey, blueprints); + + return blueprints; + } + + public async ValueTask UpdateBlueprint(string _id, BlueprintCollection entity, CancellationToken cancellationToken) + { + await this.repository.ReplaceOneAsync(entity); + + return entity; + } + + public async ValueTask DeleteBlueprint(string _id, CancellationToken cancellationToken) + { + var entity = await this.repository.DeleteOneAsync(doc => doc._Id == _id); + + return entity; + } + } +} diff --git a/Core.Blueprint.DAL.Provider/Core.Blueprint.DAL.Provider.csproj b/Core.Blueprint.DAL.Provider/Core.Blueprint.DAL.Provider.csproj new file mode 100644 index 0000000..fa71b7a --- /dev/null +++ b/Core.Blueprint.DAL.Provider/Core.Blueprint.DAL.Provider.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/Core.Blueprint.DAL.Redis/CacheService.cs b/Core.Blueprint.DAL.Redis/CacheService.cs new file mode 100644 index 0000000..28defbb --- /dev/null +++ b/Core.Blueprint.DAL.Redis/CacheService.cs @@ -0,0 +1,134 @@ +using Azure.Identity; +using Core.Blueprint.DAL.Redis.Contracts; +using Microsoft.Extensions.Logging; +using StackExchange.Redis; +using System.Text.Json; + + +namespace Core.Blueprint.DAL.Redis +{ + public class CacheService : ICacheService + { + private IDatabase _cacheDatabase = null!; + private readonly ILogger _logger; + + + public CacheService(string connectionString, ILogger logger) + { + _logger = logger; + Task.Run(async () => + { + _cacheDatabase = await GetRedisDatabase(connectionString); + }).Wait(); + } + + private async Task GetRedisDatabase(string connectionString) + { + try + { + var configurationOptions = await ConfigurationOptions.Parse($"{connectionString}") + .ConfigureForAzureWithTokenCredentialAsync(new DefaultAzureCredential()); + + configurationOptions.AbortOnConnectFail = false; + var connectionMultiplexer = await ConnectionMultiplexer.ConnectAsync(configurationOptions); + + _logger.LogInformation("Successfully connected to Redis."); + + return connectionMultiplexer.GetDatabase(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error establishing Redis connection."); + throw; + } + } + + + public async Task GetAsync(string key) + { + try + { + var value = await _cacheDatabase.StringGetAsync(key); + if (value.IsNullOrEmpty) + { + _logger.LogInformation($"Cache miss for key: {key}"); + return default; + } + + _logger.LogInformation($"Cache hit for key: {key}"); + return JsonSerializer.Deserialize(value); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error getting cache item with key {key}"); + throw; + } + } + + public async Task SetAsync(string key, T value, TimeSpan? expiry = null) + { + try + { + var json = JsonSerializer.Serialize(value); + await _cacheDatabase.StringSetAsync(key, json, expiry); + _logger.LogInformation($"Cache item set with key: {key}"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error setting cache item with key {key}"); + throw; + } + } + + public async Task RemoveAsync(string key) + { + try + { + await _cacheDatabase.KeyDeleteAsync(key); + _logger.LogInformation($"Cache item removed with key: {key}"); + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error removing cache item with key {key}"); + throw; + } + } + + public async Task ExistsAsync(string key) + { + try + { + var exists = await _cacheDatabase.KeyExistsAsync(key); + _logger.LogInformation($"Cache item exists check for key: {key} - {exists}"); + return exists; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error checking existence of cache item with key {key}"); + throw; + } + } + + public async Task RefreshAsync(string key, TimeSpan? expiry = null) + { + try + { + var value = await _cacheDatabase.StringGetAsync(key); + if (!value.IsNullOrEmpty) + { + await _cacheDatabase.StringSetAsync(key, value, expiry); + _logger.LogInformation($"Cache item refreshed with key: {key}"); + } + else + { + _logger.LogWarning($"Cache item with key: {key} does not exist, cannot refresh"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error refreshing cache item with key {key}"); + throw; + } + } + } +} diff --git a/Core.Blueprint.DAL.Redis/Configuration/CacheSettings.cs b/Core.Blueprint.DAL.Redis/Configuration/CacheSettings.cs new file mode 100644 index 0000000..110e680 --- /dev/null +++ b/Core.Blueprint.DAL.Redis/Configuration/CacheSettings.cs @@ -0,0 +1,7 @@ +namespace Core.Blueprint.DAL.Redis.Configuration +{ + public class CacheSettings + { + public int DefaultCacheDurationInMinutes { get; set; } + } +} \ No newline at end of file diff --git a/Core.Blueprint.DAL.Redis/Configuration/RedisExtension.cs b/Core.Blueprint.DAL.Redis/Configuration/RedisExtension.cs new file mode 100644 index 0000000..48a7368 --- /dev/null +++ b/Core.Blueprint.DAL.Redis/Configuration/RedisExtension.cs @@ -0,0 +1,27 @@ +using Core.Blueprint.DAL.Redis.Contracts; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Core.Blueprint.DAL.Redis.Configuration +{ + public static class RedisExtension + { + public static IServiceCollection AddRedisLayer(this IServiceCollection services, IConfiguration configuration) + { + var source = configuration.GetSection("ConnectionStrings"); + + var redisConnectionString = source["Redis"]?.ToString(); + + if (string.IsNullOrEmpty(redisConnectionString)) + { + throw new InvalidOperationException("Redis connection string is not configured."); + } + + services.AddSingleton(provider => + new CacheService(redisConnectionString, provider.GetRequiredService>())); + + return services; + } + } +} diff --git a/Core.Blueprint.DAL.Redis/Contracts/ICacheService.cs b/Core.Blueprint.DAL.Redis/Contracts/ICacheService.cs new file mode 100644 index 0000000..ec0ce30 --- /dev/null +++ b/Core.Blueprint.DAL.Redis/Contracts/ICacheService.cs @@ -0,0 +1,11 @@ +namespace Core.Blueprint.DAL.Redis.Contracts +{ + public interface ICacheService + { + Task GetAsync(string key); + Task SetAsync(string key, T value, TimeSpan? expiry = null); + Task RemoveAsync(string key); + Task ExistsAsync(string key); + Task RefreshAsync(string key, TimeSpan? expiry = null); + } +} diff --git a/Core.Blueprint.DAL.Redis/Core.Blueprint.DAL.Redis.csproj b/Core.Blueprint.DAL.Redis/Core.Blueprint.DAL.Redis.csproj new file mode 100644 index 0000000..ca0032c --- /dev/null +++ b/Core.Blueprint.DAL.Redis/Core.Blueprint.DAL.Redis.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/Core.Blueprint.DAL.Redis/Helpers/CacheHelper.cs b/Core.Blueprint.DAL.Redis/Helpers/CacheHelper.cs new file mode 100644 index 0000000..602155b --- /dev/null +++ b/Core.Blueprint.DAL.Redis/Helpers/CacheHelper.cs @@ -0,0 +1,30 @@ +using Core.Blueprint.DAL.Redis.Configuration; + +namespace Core.Blueprint.DAL.Redis.Helpers +{ + public static class CacheHelper + { + /// + /// Determines the cache duration based on specific duration, settings, or a default value. + /// + /// Specific cache duration in minutes, if provided. + /// General cache settings containing default duration values. + /// The cache duration as a TimeSpan. + public static TimeSpan GetCacheDuration(CacheSettings cacheSettings, int? specificCacheDuration = 0) + { + var defaultCacheDuration = TimeSpan.FromMinutes(.5); + + if (specificCacheDuration.HasValue && specificCacheDuration.Value > 0) + { + return TimeSpan.FromMinutes(specificCacheDuration.Value); + } + + if (cacheSettings.DefaultCacheDurationInMinutes > 0) + { + return TimeSpan.FromMinutes(cacheSettings.DefaultCacheDurationInMinutes); + } + + return defaultCacheDuration; + } + } +} diff --git a/Core.Blueprint.DAL.Redis/Helpers/CacheKeyHelper.cs b/Core.Blueprint.DAL.Redis/Helpers/CacheKeyHelper.cs new file mode 100644 index 0000000..fbacbb4 --- /dev/null +++ b/Core.Blueprint.DAL.Redis/Helpers/CacheKeyHelper.cs @@ -0,0 +1,46 @@ +using System.Text; +using System.Text.RegularExpressions; + +namespace Core.Blueprint.DAL.Redis.Helpers +{ + public static class CacheKeyHelper + { + public static string GenerateCacheKey(object instance, string methodName, params object[] parameters) + { + var className = instance.GetType().Name; + var keyBuilder = new StringBuilder($"{className}.{methodName}"); + + foreach (var param in parameters) + { + string normalizedParam = NormalizeParameter(param); + keyBuilder.Append($".{normalizedParam}"); + } + + return keyBuilder.ToString(); + } + + private static string NormalizeParameter(object param) + { + if (param == null) + { + return "null"; + } + + string paramString; + + if (param is DateTime dateTime) + { + paramString = dateTime.ToString("yyyyMMdd"); + } + else + { + paramString = param.ToString(); + } + + // Replace special characters with an underscore + string normalizedParam = Regex.Replace(paramString, @"[^a-zA-Z0-9]", "_"); + + return normalizedParam; + } + } +} diff --git a/Core.Blueprint.DAL.SQLServer/Context/SqlServerContext.cs b/Core.Blueprint.DAL.SQLServer/Context/SqlServerContext.cs new file mode 100644 index 0000000..2eb2f2c --- /dev/null +++ b/Core.Blueprint.DAL.SQLServer/Context/SqlServerContext.cs @@ -0,0 +1,25 @@ +using Core.Blueprint.DAL.SQLServer.Entities; +using Core.Blueprint.SQLServer.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Core.Blueprint.DAL.SQLServer.Context +{ + public sealed class SqlServerContext : DbContext + { + public SqlServerContext(DbContextOptions options) : base(options) { } + public DbSet UserProjects { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + foreach (var entityType in modelBuilder.Model.GetEntityTypes() + .Where(t => typeof(BaseSQLAdapter).IsAssignableFrom(t.ClrType))) + { + modelBuilder.Entity(entityType.ClrType) + .HasKey("Id"); + + modelBuilder.Entity(entityType.ClrType).Property("Status") + .HasConversion(); + } + } + } +} diff --git a/Core.Blueprint.DAL.SQLServer/Contracts/IUserProjectService.cs b/Core.Blueprint.DAL.SQLServer/Contracts/IUserProjectService.cs new file mode 100644 index 0000000..04dc118 --- /dev/null +++ b/Core.Blueprint.DAL.SQLServer/Contracts/IUserProjectService.cs @@ -0,0 +1,19 @@ +using Core.Blueprint.DAL.SQLServer.Entities; +using Core.Blueprint.DAL.SQLServer.Entities.Request; + +namespace Core.Blueprint.DAL.SQLServer.Contracts +{ + public interface IUserProjectService + { + Task> GetAllUserProjects(); + + Task GetUserProjectById(int id); + + Task AddUserProject(UserProjectRequest newUserProject); + + Task UpdateUserProject(UserProject userProject); + + Task DeleteUserProject(int id); + + } +} diff --git a/Core.Blueprint.DAL.SQLServer/Core.Blueprint.DAL.SQLServer.csproj b/Core.Blueprint.DAL.SQLServer/Core.Blueprint.DAL.SQLServer.csproj new file mode 100644 index 0000000..7aa64c5 --- /dev/null +++ b/Core.Blueprint.DAL.SQLServer/Core.Blueprint.DAL.SQLServer.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/Core.Blueprint.DAL.SQLServer/Entities/Request/UserProjectRequest.cs b/Core.Blueprint.DAL.SQLServer/Entities/Request/UserProjectRequest.cs new file mode 100644 index 0000000..4d1d8a7 --- /dev/null +++ b/Core.Blueprint.DAL.SQLServer/Entities/Request/UserProjectRequest.cs @@ -0,0 +1,9 @@ +namespace Core.Blueprint.DAL.SQLServer.Entities.Request +{ + public class UserProjectRequest + { + public string ProjectCode { get; set; } = null!; + public string ProjectDescription { get; set; } = null!; + public string UserId { get; set; } = null!; + } +} diff --git a/Core.Blueprint.DAL.SQLServer/Entities/UserProject.cs b/Core.Blueprint.DAL.SQLServer/Entities/UserProject.cs new file mode 100644 index 0000000..43fb1e5 --- /dev/null +++ b/Core.Blueprint.DAL.SQLServer/Entities/UserProject.cs @@ -0,0 +1,13 @@ +using Core.Blueprint.SQLServer.Entities; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Core.Blueprint.DAL.SQLServer.Entities +{ + [Table("UserProjects")] + public class UserProject : BaseSQLAdapter + { + public string ProjectCode { get; set; } = null!; + public string ProjectDescription { get; set; } = null!; + public string UserId { get; set; } = null!; + } +} \ No newline at end of file diff --git a/Core.Blueprint.DAL.SQLServer/Services/UserProjectService.cs b/Core.Blueprint.DAL.SQLServer/Services/UserProjectService.cs new file mode 100644 index 0000000..615b47b --- /dev/null +++ b/Core.Blueprint.DAL.SQLServer/Services/UserProjectService.cs @@ -0,0 +1,82 @@ +using Core.Blueprint.DAL.SQLServer; +using Core.Blueprint.DAL.SQLServer.Context; +using Core.Blueprint.DAL.SQLServer.Contracts; +using Core.Blueprint.DAL.SQLServer.Entities; +using Core.Blueprint.DAL.SQLServer.Entities.Request; +using Core.Blueprint.Redis; +using Core.Blueprint.Redis.Helpers; +using Mapster; +using Microsoft.Extensions.Options; + +public class UserProjectService : IUserProjectService +{ + private readonly IEntityRepository _userProjectRepository; + private readonly CacheSettings cacheSettings; + private readonly IRedisCacheProvider cacheProvider; + + public UserProjectService(IEntityRepository userProjectRepository, IRedisCacheProvider cacheProvider, IOptions cacheSettings) + { + _userProjectRepository = userProjectRepository; + this.cacheSettings = cacheSettings.Value; + this.cacheProvider = cacheProvider; + } + + public async Task> GetAllUserProjects() + { + var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetAllUserProjects"); + var cachedData = await cacheProvider.GetAsync>(cacheKey) ?? []; + + if (cachedData.Any()) return cachedData; + + var userProjects = await _userProjectRepository.GetAllAsync(); + + await cacheProvider.SetAsync(cacheKey, userProjects); + + return userProjects; + } + + public async Task GetUserProjectById(int id) + { + var cacheKey = CacheKeyHelper.GenerateCacheKey(this, "GetUserProjectById", id); + var cachedData = await cacheProvider.GetAsync(cacheKey); + + if (cachedData is not null) { return cachedData; } + + var userProject = await _userProjectRepository.GetByIdAsync(id); + + await cacheProvider.SetAsync(cacheKey, userProject); + + return userProject; + } + + public async Task AddUserProject(UserProjectRequest newUserProject) + { + var userProject = newUserProject.Adapt(); + + await _userProjectRepository.AddAsync(userProject); + await _userProjectRepository.SaveAsync(); + return userProject; + } + + public async Task UpdateUserProject(UserProject userProject) + { + var existingEntity = await _userProjectRepository.GetByIdAsync(userProject.Id); + + if (existingEntity is null) return existingEntity; + + _userProjectRepository.Update(userProject); + await _userProjectRepository.SaveAsync(); + return userProject; + } + + public async Task DeleteUserProject(int id) + { + var userProject = await _userProjectRepository.GetByIdAsync(id); + if (userProject == null) + return null; + + _userProjectRepository.Delete(userProject); + await _userProjectRepository.SaveAsync(); + return userProject; + } +} diff --git a/Core.Blueprint.DAL.Service/BlueprintService.cs b/Core.Blueprint.DAL.Service/BlueprintService.cs new file mode 100644 index 0000000..a3e94ec --- /dev/null +++ b/Core.Blueprint.DAL.Service/BlueprintService.cs @@ -0,0 +1,125 @@ +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.DAL.Service.Contracts; +using Core.Blueprint.Domain.Entities; +using Microsoft.AspNetCore.Http.HttpResults; +using Serilog; + + +namespace Core.Blueprint.DAL.Service +{ + public class BlueprintService : IBlueprintService + { + private readonly ILogger _logger; + private readonly IRepositoryIdentityBase _repository; + + public BlueprintService(IBlueprintRepository repository, ILogger logger) + { + _repository = repository; + _logger = logger; + } + + public virtual async ValueTask CreateAsync(BlueprintCollection entity) + { + try + { + _logger.Information("Start to create the BluePrintCollection | Method: {method} | Class: {class}", nameof(CreateAsync), nameof(BlueprintService)); + + + var ret = await _repository.CreateAsync(entity); + + _logger.Information("The blueprint collection was created | Method: {method} | Class: {class}", nameof(CreateAsync), nameof(BlueprintService)); + + return ret; + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(CreateAsync), nameof(BlueprintService)); + return default; + + + } + + } + + public virtual async ValueTask DeleteAsync(string id) + { + try + { + _logger.Information("Start to delete BluePrintCollection | Method: {method} | Class: {class}", nameof(DeleteAsync), nameof(BlueprintService)); + + var ret = await _repository.DeleteAsync(id); + + _logger.Information("The blueprintcollection delete Finished | Method: {method} | Class: {class}", nameof(DeleteAsync), nameof(BlueprintService)); + + return ret; + } + catch (Exception ex) + { + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(DeleteAsync), nameof(BlueprintService)); + return default; + } + + } + + public virtual async ValueTask> GetAllAsync() + { + try + { + _logger.Information("Start to get the BluePrintCollection list | Method: {method} | Class: {class}", nameof(GetAllAsync), nameof(BlueprintService)); + + var ret = await _repository.GetAllAsync(); + + _logger.Information("The get blueprintcollection list Finished | Method: {method} | Class: {class}", nameof(GetAllAsync), nameof(BlueprintService)); + + return ret; + } + catch (Exception ex) + { + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(GetAllAsync), nameof(BlueprintService)); + return default; + } + + } + + public virtual async ValueTask GetByIdAsync(string id) + { + try + { + _logger.Information("Start to get a sigle BluePrintCollection | Method: {method} | Class: {class}", nameof(GetByIdAsync), nameof(BlueprintService)); + + var ret = await _repository.GetByIdAsync(id); + + _logger.Information("The single blueprintcollection got | Method: {method} | Class: {class}", nameof(GetByIdAsync), nameof(BlueprintService)); + + return ret; + } + catch (Exception ex) + { + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(GetByIdAsync), nameof(BlueprintService)); + return default; + } + + } + + public virtual async Task UpdateAsync(string id, BlueprintCollection entity) + { + try + { + _logger.Information("Start to update BluePrintCollection | Method: {method} | Class: {class}", nameof(UpdateAsync), nameof(BlueprintService)); + + var ret = await _repository.UpdateAsync(id, entity); + + _logger.Information("The blueprintcollection update Finished | Method: {method} | Class: {class}", nameof(UpdateAsync), nameof(BlueprintService)); + + return ret; + } + catch (Exception ex) + { + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(UpdateAsync), nameof(BlueprintService)); + return default; + } + + } + } +} diff --git a/Core.Blueprint.DAL.Service/Configure/DependencyInjection.cs b/Core.Blueprint.DAL.Service/Configure/DependencyInjection.cs new file mode 100644 index 0000000..1f2aa06 --- /dev/null +++ b/Core.Blueprint.DAL.Service/Configure/DependencyInjection.cs @@ -0,0 +1,22 @@ +using Core.Blueprint.DAL.Infrastructure.Configure; +using Core.Blueprint.DAL.Service.Contracts; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Core.Blueprint.DAL.Service.Configure +{ + public static class DependencyInjection + { + public static IServiceCollection RegisterServices(this IServiceCollection services, IConfiguration configuration) + { + services.RegisterInfrastructure(configuration); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } + } +} diff --git a/Core.Blueprint.DAL.Service/Contracts/IBlueprintService.cs b/Core.Blueprint.DAL.Service/Contracts/IBlueprintService.cs new file mode 100644 index 0000000..3f6ab52 --- /dev/null +++ b/Core.Blueprint.DAL.Service/Contracts/IBlueprintService.cs @@ -0,0 +1,13 @@ +using Core.Blueprint.Domain.Entities; + +namespace Core.Blueprint.DAL.Service.Contracts +{ + public interface IBlueprintService + { + ValueTask CreateAsync(BlueprintCollection entity); + ValueTask DeleteAsync(string id); + ValueTask> GetAllAsync(); + ValueTask GetByIdAsync(string id); + Task UpdateAsync(string id, BlueprintCollection entity); + } +} diff --git a/Core.Blueprint.DAL.Service/Contracts/ISampleImageService.cs b/Core.Blueprint.DAL.Service/Contracts/ISampleImageService.cs new file mode 100644 index 0000000..3cb4015 --- /dev/null +++ b/Core.Blueprint.DAL.Service/Contracts/ISampleImageService.cs @@ -0,0 +1,11 @@ +using Core.Blueprint.Domain.Dtos; + +namespace Core.Blueprint.DAL.Service.Contracts +{ + public interface ISampleImageService + { + IAsyncEnumerable GetAllUrl(); + Task GetFirstUrl(); + Task GetUploadUrl(); + } +} diff --git a/Core.Blueprint.DAL.Service/Contracts/ISampleItemService.cs b/Core.Blueprint.DAL.Service/Contracts/ISampleItemService.cs new file mode 100644 index 0000000..aca78c5 --- /dev/null +++ b/Core.Blueprint.DAL.Service/Contracts/ISampleItemService.cs @@ -0,0 +1,14 @@ +using Core.Blueprint.Domain.Entities; + +namespace Core.Blueprint.DAL.Service.Contracts +{ + public interface ISampleItemService + { + ValueTask CreateAsync(SampleItem entity); + ValueTask DeleteAsync(string id); + ValueTask> GetAllAsync(); + ValueTask GetByIdAsync(string id); + Task UpdateAsync(string id, SampleItem entity); + + } +} diff --git a/Core.Blueprint.DAL.Service/Contracts/ISecretService.cs b/Core.Blueprint.DAL.Service/Contracts/ISecretService.cs new file mode 100644 index 0000000..fbc500f --- /dev/null +++ b/Core.Blueprint.DAL.Service/Contracts/ISecretService.cs @@ -0,0 +1,11 @@ +using Core.Blueprint.Domain.Entities; + +namespace Core.Blueprint.DAL.Service.Contracts +{ + public interface ISecretService + { + Task GetSecret(string secretName, CancellationToken cancellationToken); + Task SetSecret(string secretName, string secretValue, CancellationToken cancellationToken); + Task RemoveSecret(string secretName, CancellationToken cancellationToken); + } +} diff --git a/Core.Blueprint.DAL.Service/Core.Blueprint.DAL.Service.csproj b/Core.Blueprint.DAL.Service/Core.Blueprint.DAL.Service.csproj new file mode 100644 index 0000000..e54d779 --- /dev/null +++ b/Core.Blueprint.DAL.Service/Core.Blueprint.DAL.Service.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/Core.Blueprint.DAL.Service/SampleImageService.cs b/Core.Blueprint.DAL.Service/SampleImageService.cs new file mode 100644 index 0000000..130ef30 --- /dev/null +++ b/Core.Blueprint.DAL.Service/SampleImageService.cs @@ -0,0 +1,74 @@ +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.DAL.Service.Contracts; +using Core.Blueprint.Domain.Dtos; +using Serilog; + +namespace Core.Blueprint.DAL.Service +{ + public class SampleImageService : ISampleImageService + { + private readonly ISampleImageRepository _sampleImageRepository; + private readonly ILogger _logger; + + public SampleImageService(ISampleImageRepository sampleImageRepository, ILogger logger) + { + _sampleImageRepository = sampleImageRepository; + _logger = logger; + } + + public IAsyncEnumerable GetAllUrl() + { + try + { + _logger.Information("Starting to get the images urls list | Method: {method} | Class: {class}", nameof(GetAllUrl), nameof(SampleImageService)); + var ret = _sampleImageRepository.GetAllImagesUrls(); + _logger.Information("Finishing to get the images urls list | Method: {method} | Class: {class}", nameof(GetAllUrl), nameof(SampleImageService)); + return ret; + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(GetAllUrl), nameof(SampleImageService)); + return default; + } + + } + + public async Task GetFirstUrl() + { + try + { + _logger.Information("Starting to get the first image url | Method: {method} | Class: {class}", nameof(GetFirstUrl), nameof(SampleImageService)); + var ret = await _sampleImageRepository.GetFirstImageUrl(); + _logger.Information("Finishing to get the first image url | Method: {method} | Class: {class}", nameof(GetFirstUrl), nameof(SampleImageService)); + return ret; + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(GetFirstUrl), nameof(SampleImageService)); + return default; + } + + } + + public async Task GetUploadUrl() + { + try + { + _logger.Information("Starting to get the upload image url | Method: {method} | Class: {class}", nameof(GetUploadUrl), nameof(SampleImageService)); + var ret = await _sampleImageRepository.GetUploadUrl(); + _logger.Information("Finishing to get the upload image url | Method: {method} | Class: {class}", nameof(GetUploadUrl), nameof(SampleImageService)); + return ret; + + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(GetUploadUrl), nameof(SampleImageService)); + return default; + } + + } + } +} diff --git a/Core.Blueprint.DAL.Service/SampleItemService.cs b/Core.Blueprint.DAL.Service/SampleItemService.cs new file mode 100644 index 0000000..66b1994 --- /dev/null +++ b/Core.Blueprint.DAL.Service/SampleItemService.cs @@ -0,0 +1,114 @@ +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.DAL.Service.Contracts; +using Core.Blueprint.Domain.Entities; +using Serilog; + + +namespace Core.Blueprint.DAL.Service +{ + //[LoggingAttribute] + public class SampleItemService : ISampleItemService + { + private readonly ILogger _logger; + private readonly IRepositoryIdentityBase _repository; + public SampleItemService(ISampleItemRepository repository, ILogger logger) + { + _repository = repository; + _logger = logger; + } + + //[LoggingAttribute] //Time of request, class name, class method... memory usage.... + public virtual async ValueTask CreateAsync(SampleItem entity) + { + try + { + _logger.Information("Starting to create the Sample item"); + var ret = await _repository.CreateAsync(entity); + _logger.Information("Finishing to create the Sample item | Method: {method} | Class: {class}", nameof(CreateAsync), nameof(SampleItemService)); + return ret; + + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(CreateAsync), nameof(SampleItemService)); + return default; + } + + } + + public virtual async ValueTask DeleteAsync(string id) + { + try + { + _logger.Information("Starting to delete the Sample item | Method: {method} | Class: {class}", nameof(DeleteAsync), nameof(SampleItemService)); + var ret = await _repository.DeleteAsync(id); + _logger.Information("Finishing to delete the Sample item | Method: {method} | Class: {class}", nameof(DeleteAsync), nameof(SampleItemService)); + return ret; + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(DeleteAsync), nameof(SampleItemService)); + return default; + } + + } + + public virtual async ValueTask> GetAllAsync() + { + try + { + _logger.Information("Starting to get all Sample item | Method: {method} | Class: {class}", nameof(GetAllAsync), nameof(SampleItemService)); + var ret = await _repository.GetAllAsync(); + _logger.Information("Finishing to get all Sample item | Method: {method} | Class: {class}", nameof(GetAllAsync), nameof(SampleItemService)); + return ret; + + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(GetAllAsync), nameof(SampleItemService)); + return default; + } + + } + + public virtual async ValueTask GetByIdAsync(string id) + { + try + { + _logger.Information("Starting to get Sample item by id | Method: {method} | Class: {class}", nameof(GetByIdAsync), nameof(SampleItemService)); + var ret = await _repository.GetByIdAsync(id); + _logger.Information("Finishing to get all Sample item | Method: {method} | Class: {class}", nameof(GetByIdAsync), nameof(SampleItemService)); + return ret; + + } + catch (Exception ex) + { + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(GetByIdAsync), nameof(SampleItemService)); + return default; + } + + } + + public virtual async Task UpdateAsync(string id, SampleItem entity) + { + try + { + _logger.Information("Starting to update Sample item | Method: {method} | Class: {class}", nameof(UpdateAsync), nameof(SampleItemService)); + var ret = await _repository.UpdateAsync(id, entity); + _logger.Information("Finishing to update Sample item | Method: {method} | Class: {class}", nameof(UpdateAsync), nameof(SampleItemService)); + return ret; + + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(UpdateAsync), nameof(SampleItemService)); + return default; + } + + } + } +} diff --git a/Core.Blueprint.DAL.Service/SecretService.cs b/Core.Blueprint.DAL.Service/SecretService.cs new file mode 100644 index 0000000..63018b3 --- /dev/null +++ b/Core.Blueprint.DAL.Service/SecretService.cs @@ -0,0 +1,76 @@ +using Core.Blueprint.DAL.Infrastructure.Contracts; +using Core.Blueprint.DAL.Service.Contracts; +using Core.Blueprint.Domain.Entities; +using Serilog; +using SharpCompress.Common; + +namespace Core.Blueprint.DAL.Service +{ + public class SecretService : ISecretService + { + private readonly ISecretRepository _repository; + private readonly ILogger _logger; + + public SecretService(ISecretRepository repository, ILogger logger) + { + _repository = repository; + _logger = logger; + } + + public async Task GetSecret(string secretName, CancellationToken cancellationToken) + { + + try + { + _logger.Information("Starting to get the secret | Method: {method} | Class: {class}", nameof(GetSecret), nameof(SecretService)); + var ret = await _repository.GetSecret(secretName, cancellationToken); + _logger.Information("Finishing to get the secret | Method: {method} | Class: {class}", nameof(GetSecret), nameof(SecretService)); + return ret; + + + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(GetSecret), nameof(SecretService)); + return default; + } + } + + public async Task SetSecret(string secretName, string secretValue, CancellationToken cancellationToken) + { + + try + { + _logger.Information("Starting to set the secret | Method: {method} | Class: {class}", nameof(SetSecret), nameof(SecretService)); + await _repository.SetSecret(secretName, secretValue, cancellationToken); + _logger.Information("Finishing to set the secret | Method: {method} | Class: {class}", nameof(SetSecret), nameof(SecretService)); + + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(SetSecret), nameof(SecretService)); + + } + } + + public async Task RemoveSecret(string secretName, CancellationToken cancellationToken) + { + + try + { + _logger.Information("Starting to remove the secret | Method: {method} | Class: {class}", nameof(RemoveSecret), nameof(SecretService)); + await _repository.RemoveSecret(secretName, cancellationToken); + _logger.Information("Finishing to remove the secret | Method: {method} | Class: {class}", nameof(RemoveSecret), nameof(SecretService)); + + } + catch (Exception ex) + { + + _logger.Error(ex, "{error} | Method: {method} | Class: {class}", ex.Message, nameof(RemoveSecret), nameof(SecretService)); + + } + } + } +} diff --git a/Core.Blueprint.DAL.Storage/Contracts/IBlobStorageService.cs b/Core.Blueprint.DAL.Storage/Contracts/IBlobStorageService.cs new file mode 100644 index 0000000..dda2c78 --- /dev/null +++ b/Core.Blueprint.DAL.Storage/Contracts/IBlobStorageService.cs @@ -0,0 +1,16 @@ +using Azure; +using Azure.Storage.Blobs.Models; +using Core.Blueprint.Storage; +using Core.Blueprint.Storage.Adapters; + +namespace Core.Blueprint.DAL.Storage.Contracts +{ + public interface IBlobStorageService + { + Task> GetBlobsListAsync(string? prefix); + Task> UploadBlobAsync(string blobName, Stream content); + Task UploadBlobAsync(BlobAddDto newBlob); + BlobDownloadUriAdapter DownloadBlobAsync(string blobName); + Task DeleteBlobAsync(string fileName); + } +} diff --git a/Core.Blueprint.DAL.Storage/Core.Blueprint.DAL.Storage.csproj b/Core.Blueprint.DAL.Storage/Core.Blueprint.DAL.Storage.csproj new file mode 100644 index 0000000..99e1303 --- /dev/null +++ b/Core.Blueprint.DAL.Storage/Core.Blueprint.DAL.Storage.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/Core.Blueprint.DAL.Storage/Provider/BlobStorageService.cs b/Core.Blueprint.DAL.Storage/Provider/BlobStorageService.cs new file mode 100644 index 0000000..fdeb732 --- /dev/null +++ b/Core.Blueprint.DAL.Storage/Provider/BlobStorageService.cs @@ -0,0 +1,41 @@ +using Azure; +using Azure.Storage.Blobs.Models; +using Core.Blueprint.DAL.Storage.Contracts; +using Core.Blueprint.Storage; +using Core.Blueprint.Storage.Adapters; +using Core.Blueprint.Storage.Contracts; + +namespace Core.Blueprint.DAL.Storage.Service +{ + public class BlobStorageService(IBlobStorageProvider blobStorageProvider) : IBlobStorageService + { + + public async Task> UploadBlobAsync(string blobName, Stream content) + { + var result = await blobStorageProvider.UploadBlobAsync(blobName, content); + return result; + } + + public async Task UploadBlobAsync(BlobAddDto newBlob) + { + var result = await blobStorageProvider.UploadBlobAsync(newBlob); + return result; + } + + public async Task> GetBlobsListAsync(string? prefix) + { + return await blobStorageProvider.ListBlobsAsync(prefix); + } + + public BlobDownloadUriAdapter DownloadBlobAsync(string blobName) + { + var result = blobStorageProvider.GenerateBlobDownloadUri(blobName); + return result; + } + + public async Task DeleteBlobAsync(string blobName) + { + return await blobStorageProvider.DeleteBlobsAsync(blobName); + } + } +} diff --git a/Core.Blueprint.DAL.Testing/Core.Blueprint.DAL.Testing.csproj b/Core.Blueprint.DAL.Testing/Core.Blueprint.DAL.Testing.csproj new file mode 100644 index 0000000..9e468ee --- /dev/null +++ b/Core.Blueprint.DAL.Testing/Core.Blueprint.DAL.Testing.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/Core.Blueprint.DAL.sln b/Core.Blueprint.DAL.sln new file mode 100644 index 0000000..66135eb --- /dev/null +++ b/Core.Blueprint.DAL.sln @@ -0,0 +1,81 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34309.116 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.DAL.API", "Core.Blueprint.DAL.API\Core.Blueprint.DAL.API.csproj", "{D66611EE-2AE0-48B2-8D79-AF10A5E11C91}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Presentation", "Presentation", "{0DD43B65-17E6-4AA2-BE35-F9C7FCB5DD18}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{4D299A91-3EDD-4ABF-B134-CE4966E8C990}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.DAL.SQLServer", "Core.Blueprint.DAL.SQLServer\Core.Blueprint.DAL.SQLServer.csproj", "{546B418A-2DBD-499D-B7AA-57078231B950}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.DAL.Storage", "Core.Blueprint.DAL.Storage\Core.Blueprint.DAL.Storage.csproj", "{B744C1BC-7EDA-47C5-BC04-91D0FB061985}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.DAL.Hangfire", "Core.Blueprint.DAL.Hangfire\Core.Blueprint.DAL.Hangfire.csproj", "{5D3F8418-7BFA-4EA7-ADE7-7285A91FB1D9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.DAL.Mongo", "Core.Blueprint.DAL.Mongo\Core.Blueprint.DAL.Mongo.csproj", "{14F77CBD-952F-40F1-8568-A084EF7D2224}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.DAL.Redis", "Core.Blueprint.DAL.Redis\Core.Blueprint.DAL.Redis.csproj", "{CD655B60-8C94-494A-87BA-F689CDD65F2B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.DAL.Logs", "Core.Blueprint.DAL.Logs\Core.Blueprint.DAL.Logs.csproj", "{B91E1D72-FB2E-4532-83E9-3E9EF2231740}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.DAL.KeyVault", "Core.Blueprint.DAL.KeyVault\Core.Blueprint.DAL.KeyVault.csproj", "{ACE11F5D-338D-4DF0-B2A1-D8886DEC4B1E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D66611EE-2AE0-48B2-8D79-AF10A5E11C91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D66611EE-2AE0-48B2-8D79-AF10A5E11C91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D66611EE-2AE0-48B2-8D79-AF10A5E11C91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D66611EE-2AE0-48B2-8D79-AF10A5E11C91}.Release|Any CPU.Build.0 = Release|Any CPU + {546B418A-2DBD-499D-B7AA-57078231B950}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {546B418A-2DBD-499D-B7AA-57078231B950}.Debug|Any CPU.Build.0 = Debug|Any CPU + {546B418A-2DBD-499D-B7AA-57078231B950}.Release|Any CPU.ActiveCfg = Release|Any CPU + {546B418A-2DBD-499D-B7AA-57078231B950}.Release|Any CPU.Build.0 = Release|Any CPU + {B744C1BC-7EDA-47C5-BC04-91D0FB061985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B744C1BC-7EDA-47C5-BC04-91D0FB061985}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B744C1BC-7EDA-47C5-BC04-91D0FB061985}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B744C1BC-7EDA-47C5-BC04-91D0FB061985}.Release|Any CPU.Build.0 = Release|Any CPU + {5D3F8418-7BFA-4EA7-ADE7-7285A91FB1D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D3F8418-7BFA-4EA7-ADE7-7285A91FB1D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D3F8418-7BFA-4EA7-ADE7-7285A91FB1D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D3F8418-7BFA-4EA7-ADE7-7285A91FB1D9}.Release|Any CPU.Build.0 = Release|Any CPU + {14F77CBD-952F-40F1-8568-A084EF7D2224}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14F77CBD-952F-40F1-8568-A084EF7D2224}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14F77CBD-952F-40F1-8568-A084EF7D2224}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14F77CBD-952F-40F1-8568-A084EF7D2224}.Release|Any CPU.Build.0 = Release|Any CPU + {CD655B60-8C94-494A-87BA-F689CDD65F2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD655B60-8C94-494A-87BA-F689CDD65F2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD655B60-8C94-494A-87BA-F689CDD65F2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD655B60-8C94-494A-87BA-F689CDD65F2B}.Release|Any CPU.Build.0 = Release|Any CPU + {B91E1D72-FB2E-4532-83E9-3E9EF2231740}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B91E1D72-FB2E-4532-83E9-3E9EF2231740}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B91E1D72-FB2E-4532-83E9-3E9EF2231740}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B91E1D72-FB2E-4532-83E9-3E9EF2231740}.Release|Any CPU.Build.0 = Release|Any CPU + {ACE11F5D-338D-4DF0-B2A1-D8886DEC4B1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACE11F5D-338D-4DF0-B2A1-D8886DEC4B1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACE11F5D-338D-4DF0-B2A1-D8886DEC4B1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACE11F5D-338D-4DF0-B2A1-D8886DEC4B1E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D66611EE-2AE0-48B2-8D79-AF10A5E11C91} = {0DD43B65-17E6-4AA2-BE35-F9C7FCB5DD18} + {546B418A-2DBD-499D-B7AA-57078231B950} = {4D299A91-3EDD-4ABF-B134-CE4966E8C990} + {B744C1BC-7EDA-47C5-BC04-91D0FB061985} = {4D299A91-3EDD-4ABF-B134-CE4966E8C990} + {5D3F8418-7BFA-4EA7-ADE7-7285A91FB1D9} = {4D299A91-3EDD-4ABF-B134-CE4966E8C990} + {14F77CBD-952F-40F1-8568-A084EF7D2224} = {4D299A91-3EDD-4ABF-B134-CE4966E8C990} + {CD655B60-8C94-494A-87BA-F689CDD65F2B} = {4D299A91-3EDD-4ABF-B134-CE4966E8C990} + {B91E1D72-FB2E-4532-83E9-3E9EF2231740} = {4D299A91-3EDD-4ABF-B134-CE4966E8C990} + {ACE11F5D-338D-4DF0-B2A1-D8886DEC4B1E} = {4D299A91-3EDD-4ABF-B134-CE4966E8C990} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A74EEA93-F8F3-4607-92C8-D94D5E7F305C} + EndGlobalSection +EndGlobal diff --git a/Core.Blueprint.Domain/Contexts/SQLServer/BaseConfig/BaseSQLEntity.cs b/Core.Blueprint.Domain/Contexts/SQLServer/BaseConfig/BaseSQLEntity.cs new file mode 100644 index 0000000..ba0c033 --- /dev/null +++ b/Core.Blueprint.Domain/Contexts/SQLServer/BaseConfig/BaseSQLEntity.cs @@ -0,0 +1,18 @@ +namespace Core.Blueprint.Domain.Contexts.SQLServer.BaseConfig +{ + public abstract class BaseSQLEntity + { + public Guid Id { get; set; } = Guid.NewGuid(); + + public Guid TransactionalId { get; private set; } + public DateTime Created = DateTime.Now; + public DateTime Update { get; private set; } + + + public void IsUpdate() + { + TransactionalId = Guid.NewGuid(); + Update = DateTime.Now; + } + } +} \ No newline at end of file diff --git a/Core.Blueprint.Domain/Core.Blueprint.Domain.csproj b/Core.Blueprint.Domain/Core.Blueprint.Domain.csproj new file mode 100644 index 0000000..7e956bf --- /dev/null +++ b/Core.Blueprint.Domain/Core.Blueprint.Domain.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + diff --git a/Core.Blueprint.Domain/Dtos/ImageUrlDto.cs b/Core.Blueprint.Domain/Dtos/ImageUrlDto.cs new file mode 100644 index 0000000..6e3efeb --- /dev/null +++ b/Core.Blueprint.Domain/Dtos/ImageUrlDto.cs @@ -0,0 +1,7 @@ +namespace Core.Blueprint.Domain.Dtos +{ + public class ImageUrlDto + { + public Uri Url { get; set; } + } +} diff --git a/Core.Blueprint.Domain/Dtos/KeyVaultSecretDto.cs b/Core.Blueprint.Domain/Dtos/KeyVaultSecretDto.cs new file mode 100644 index 0000000..982c469 --- /dev/null +++ b/Core.Blueprint.Domain/Dtos/KeyVaultSecretDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Blueprint.Domain.Dtos +{ + public class KeyVaultSecretDto + { + public string Key { get; set; } + public string Value { get; set; } + } +} diff --git a/Core.Blueprint.Domain/Entities/AbsEntity.cs b/Core.Blueprint.Domain/Entities/AbsEntity.cs new file mode 100644 index 0000000..3827c87 --- /dev/null +++ b/Core.Blueprint.Domain/Entities/AbsEntity.cs @@ -0,0 +1,14 @@ +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson; + +namespace Core.Blueprint.Domain.Entities +{ + public abstract class AbsEntity + { + [BsonId] + [BsonRepresentation(BsonType.ObjectId)] + [BsonElement("_id")] + public string? Id { get; set; } + public char StatusCode { get; set; } = 'P'; + } +} diff --git a/Core.Blueprint.Domain/Entities/AbsFileEntity.cs b/Core.Blueprint.Domain/Entities/AbsFileEntity.cs new file mode 100644 index 0000000..fff82ac --- /dev/null +++ b/Core.Blueprint.Domain/Entities/AbsFileEntity.cs @@ -0,0 +1,7 @@ +namespace Core.Blueprint.Domain.Entities +{ + public abstract class AbsFileEntity : AbsEntity + { + public byte[] FileContent { get; set; } = []; + } +} diff --git a/Core.Blueprint.Domain/Entities/BlueprintCollection.cs b/Core.Blueprint.Domain/Entities/BlueprintCollection.cs new file mode 100644 index 0000000..8b4def3 --- /dev/null +++ b/Core.Blueprint.Domain/Entities/BlueprintCollection.cs @@ -0,0 +1,15 @@ +using Core.Blueprint.Domain.Shared; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Core.Blueprint.Domain.Entities +{ + [CollectionAttributeName("blueprint")] + public class BlueprintCollection : AbsEntity + { + public Guid CorrelationId { get; set; } + + public DateTime Created { get; set; } + + } +} diff --git a/Core.Blueprint.Domain/Entities/SampleImage.cs b/Core.Blueprint.Domain/Entities/SampleImage.cs new file mode 100644 index 0000000..751abbb --- /dev/null +++ b/Core.Blueprint.Domain/Entities/SampleImage.cs @@ -0,0 +1,7 @@ +namespace Core.Blueprint.Domain.Entities +{ + public class SampleImage : AbsFileEntity + { + + } +} diff --git a/Core.Blueprint.Domain/Entities/SampleItem.cs b/Core.Blueprint.Domain/Entities/SampleItem.cs new file mode 100644 index 0000000..7bcacab --- /dev/null +++ b/Core.Blueprint.Domain/Entities/SampleItem.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Core.Blueprint.Domain.Entities +{ + [Table("Blueprint_Test01")] + public class SampleItem : AbsEntity + { + public Guid Id { get; set; } = Guid.NewGuid(); + public string TestName { get; set; } = string.Empty; + public char StatusCode { get; set; } = 'P'; + public int NumValue { get; set; } = 0; + public DateTime CreatedAt { get; set; } = DateTime.Now; + } +} + + +//User - Project \ No newline at end of file diff --git a/Core.Blueprint.Domain/Entities/Secret.cs b/Core.Blueprint.Domain/Entities/Secret.cs new file mode 100644 index 0000000..45c9a1c --- /dev/null +++ b/Core.Blueprint.Domain/Entities/Secret.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Core.Blueprint.Domain.Entities +{ + public class Secret + { + public string? Value { get; set; } + } +} diff --git a/Core.Blueprint.Domain/Shared/CollectionAttributeName.cs b/Core.Blueprint.Domain/Shared/CollectionAttributeName.cs new file mode 100644 index 0000000..47d6977 --- /dev/null +++ b/Core.Blueprint.Domain/Shared/CollectionAttributeName.cs @@ -0,0 +1,12 @@ +namespace Core.Blueprint.Domain.Shared +{ + [AttributeUsage(AttributeTargets.Class)] + public class CollectionAttributeName : Attribute + { + public string Name { get; set; } + public CollectionAttributeName(string name) + { + Name = name; + } + } +} diff --git a/Lib.Common.LoggingAPI.Service/Constants/Claims.cs b/Lib.Common.LoggingAPI.Service/Constants/Claims.cs new file mode 100644 index 0000000..6823936 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Constants/Claims.cs @@ -0,0 +1,48 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** +namespace Lib.Common.LoggingAPI.Service.Constants +{ + /// + /// Constants for claims used in JWT tokens. + /// + public class Claims + { + /// + /// Claim name for user's name. + /// + public const string Name = "name"; + + /// + /// Claim name for user's name. + /// + public const string Email = "email"; + + /// + /// Claim name for user's ID. + /// + public const string Id = "id"; + + /// + /// Claim name for user's role ID. + /// + public const string Rol = "rol"; + + /// + /// Claim name for user's companies. + /// + public const string Companies = "companies"; + + /// + /// Claim name for user's projects. + /// + public const string Projects = "projects"; + + /// + /// Claim name for user's surveys. + /// + public const string Surveys = "surveys"; + } +} diff --git a/Lib.Common.LoggingAPI.Service/Constants/DisplayNames.cs b/Lib.Common.LoggingAPI.Service/Constants/DisplayNames.cs new file mode 100644 index 0000000..32382d6 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Constants/DisplayNames.cs @@ -0,0 +1,397 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Common.LoggingAPI.Service.Constants +{ + /// + /// Constants of the display names for this service. + /// + public static class DisplayNames + { + /// + /// The active patameter. + /// + public const string Active = "active"; + + + /// + /// The client identifier parameter. + /// + public const string ClientId = "clientId"; + + /// + /// The client request parameter. + /// + public const string ClientRequest = "clientRequest"; + + /// + /// The client response parameter. + /// + public const string ClientResponse = "clientResponse"; + + /// + /// The creation date. + /// + public const string CreationDate = "creationDate"; + + /// + /// The content parameter. + /// + public const string Content = "content"; + + /// + /// The delete parameter. + /// + public const string Delete = "delete"; + + /// + /// The description parameter. + /// + public const string Description = "description"; + + /// + /// The detail parameter. + /// + public const string Detail = "detail"; + + + /// + /// The environment parameter. + /// + public const string Environment = "environment"; + + /// + /// The error log severity level parameter. + /// + public const string Error = "error"; + + /// + /// The error code parameter. + /// + public const string ErrorCode = "errorCode"; + + /// + /// The external request parameter. + /// + public const string ExternalRequest = "externalRequest"; + + /// + /// The external response parameter. + /// + public const string ExternalResponse = "externalResponse"; + + /// + /// The fatal log severity level parameter. + /// + public const string Fatal = "fatal"; + + /// + /// The host parameter. + /// + public const string Host = "host"; + + /// + /// The identifier parameter. + /// + public const string Id = "id"; + + /// + /// The inactive parameter. + /// + public const string Inactive = "inactive"; + + /// + /// The info log severity level parameter. + /// + public const string Info = "info"; + + /// + /// The information log severity level parameter. + /// + public const string Information = "information"; + + /// + /// The media parameter. + /// + public const string Media = "media"; + + /// + /// The media type parameter. + /// + public const string MediaType = "mediaType"; + + /// + /// The media use type parameter. + /// + public const string MediaUseType = "mediaUseType"; + + /// + /// Th message parameter. + /// + public const string Message = "message"; + + /// + /// The method parameter. + /// + public const string Method = "method"; + + /// + /// The monday parameter. + /// + public const string Monday = "monday"; + + /// + /// The MXN parameter. + /// + public const string MXN = "MXN"; + + /// + /// The name parameter. + /// + public const string Name = "name"; + + /// + /// The next page parameter. + /// + public const string NextPage = "nextPage"; + + /// + /// The nick name parameter. + /// + public const string NickName = "nickName"; + + /// + /// The note parameter. + /// + public const string Note = "note"; + + /// + /// The not so affordable parameter. + /// + public const string NotSoAffordable = "notSoAffordable"; + + /// + /// The object status parameter. + /// + public const string ObjectStatus = "objectStatus"; + + /// + /// The opening time parameter. + /// + public const string OpeningTime = "openingTime"; + + /// + /// The operation days parameter. + /// + public const string OperationDays = "operationDays"; + + /// + /// The page parameter. + /// + public const string Page = "page"; + + /// + /// The page count parameter. + /// + public const string PageCount = "pageCount"; + + /// + /// The page metadata parameter. + /// + public const string PageMetadata = "pageMetadata"; + + /// + /// The page size parameter. + /// + public const string PageSize = "pageSize"; + + /// + /// The parent identifier parameter. + /// + public const string ParentId = "ParentId"; + + /// + /// The pet ticket price parameter. + /// + public const string PetTicketPrice = "petTicketPrice"; + + /// + /// The place parameter. + /// + public const string Place = "place"; + + /// + /// The place type parameter. + /// + public const string PlaceType = "placeType"; + + /// + /// The previous page parameter. + /// + public const string PreviousPage = "previousPage"; + + /// + /// The provider identifier parameter. + /// + public const string ProviderId = "providerId"; + + /// + /// The provider type identifier parameter. + /// + public const string ProviderTypeId = "providerTypeId"; + + /// + /// The request identifier parameter. + /// + public const string RequestId = "requestId"; + + /// + /// The RNT identifier parameter. + /// + public const string RntId = "rntId"; + + /// + /// The route parameter. + /// + public const string Route = "route"; + + /// + /// The operation parameter. + /// + public const string Operation = "operation"; + + /// + /// The other ticket price parameter. + /// + public const string OtherTicketPrice = "otherTicketPrice"; + + /// + /// The path parameter. + /// + public const string Path = "path"; + + /// + /// The saturday parameter. + /// + public const string Saturday = "saturday"; + + /// + /// The secondary parameter. + /// + public const string Secondary = "secondary"; + + /// + /// The service health parameter. + /// + public const string ServiceHealth = "serviceHealth"; + + /// + /// The service identifier parameter. + /// + public const string ServiceId = "serviceId"; + + /// + /// The severity parameter. + /// + public const string Severity = "severity"; + + /// + /// The state identifier parameter. + /// + public const string StateId = "stateId"; + + /// + /// The region identifier parameter. + /// + public const string StateProvinceRegionId = "regionId"; + + /// + /// The sunday parameter. + /// + public const string Sunday = "sunday"; + + /// + /// The tax identifier parameter. + /// + public const string TaxId = "taxId"; + + /// + /// The target parameter. + /// + public const string Target = "target"; + + /// + /// The thursday parameter. + /// + public const string Thursday = "thursday"; + + /// + /// The timestamp parameter. + /// + public const string Timestamp = "timestamp"; + + /// + /// The total items parameter. + /// + public const string TotalItems = "totalItems"; + + /// + /// Gets or sets the transaction identifier parameter. + /// + public const string TransactionId = "transactionId"; + + /// + /// The tuesday parameter. + /// + public const string Tuesday = "tuesday"; + + /// + /// The URI parameter. + /// + public const string Uri = "uri"; + + /// + /// The update parameter. + /// + public const string Update = "update"; + + /// + /// The x-forwarded-for header parameter. + /// + public const string XForwardedFor = "xForwardedFor"; + + /// + /// The x-forwarded-for header parameter. + /// + public const string XForwardedForHeader = "X-Forwarded-For"; + + /// + /// The final currency identifier parameter. + /// + public const string CurrencyId = "currencyId"; + + /// + /// The user identifier parameter. + /// + public const string UserId = "userId"; + + /// + /// The user parameter. + /// + public const string User = "user"; + + /// + /// The warning log severity level. + /// + public const string Warning = "warning"; + + /// + /// The email parameter. + /// + public const string Email = "email"; + + } +} diff --git a/Lib.Common.LoggingAPI.Service/Constants/EnvironmentVariables.cs b/Lib.Common.LoggingAPI.Service/Constants/EnvironmentVariables.cs new file mode 100644 index 0000000..9e88b52 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Constants/EnvironmentVariables.cs @@ -0,0 +1,21 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Common.LoggingAPI.Service.Constants +{ + /// + /// Constants of the environment variables for this service. + /// + public static class EnvironmentVariables + { + /// + /// The stage environment vriable. + /// + public const string Stage = "ASPNETCORE_ENVIRONMENT"; + } +} + + diff --git a/Lib.Common.LoggingAPI.Service/Constants/ErrorCodes.cs b/Lib.Common.LoggingAPI.Service/Constants/ErrorCodes.cs new file mode 100644 index 0000000..fe08df9 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Constants/ErrorCodes.cs @@ -0,0 +1,79 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Common.LoggingAPI.Service.Constants +{ + /// + /// Constants for the error codes. + /// + public static class ErrorCodes + { + /// + /// The generic entities not found error code. + /// + public const string EntitiesNotFound = "{0}EntitiesNotFound"; + + /// + /// The entity already exsits error message. + /// + public const string EntityAlreadyExists = "{0}EntityAlreadyExists"; + + /// + /// The generic entity not found error code. + /// + public const string EntityNotFound = "{0}EntityNotFound"; + + /// + /// The generic not supported error code. + /// + public const string EntityNotSupported = "{0}NotSupported"; + + /// + /// The internal server error code. + /// + public const string InternalServerError = "InternalServerError"; + + /// + /// The invalid parameters in mapper error code. + /// + public const string InvalidParametersMapper = "InvalidParametersMapper"; + + /// + /// The page size invalid value error code. + /// + public const string PageSizeInvalidValue = "PageSizeInvalidValue"; + + /// + /// The page ot of range error code. + /// + public const string PageOutOfRange = "PageOutOfRange"; + + /// + /// The property does not match the regular expresion error code. + /// + public const string PropertyDoesNotMatchRegex = "{0}PropertyDoesNotMatchRegex"; + + /// + /// The property is required error code. + /// + public const string PropertyIsRequired = "{0}PropertyIsRequired"; + + /// + /// The property length invalid error code. + /// + public const string PropertyLengthInvalid = "{0}PropertyLengthInvalid"; + + /// + /// The property must be in range error code. + /// + public const string PropertyMustBeInRange = "{0}PropertyMustBeInRange"; + + /// + /// The route not found error code. + /// + public const string RouteNotFound = "RouteNotFound"; + } +} diff --git a/Lib.Common.LoggingAPI.Service/Constants/Headers.cs b/Lib.Common.LoggingAPI.Service/Constants/Headers.cs new file mode 100644 index 0000000..b5aaf15 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Constants/Headers.cs @@ -0,0 +1,10 @@ +namespace Lib.Common.LoggingAPI.Service.Constants +{ + public static class Headers + { + /// + /// The authorization header. + /// + public const string Authorization = "Authorization"; + } +} diff --git a/Lib.Common.LoggingAPI.Service/Constants/MimeTypes.cs b/Lib.Common.LoggingAPI.Service/Constants/MimeTypes.cs new file mode 100644 index 0000000..fbc3f36 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Constants/MimeTypes.cs @@ -0,0 +1,148 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using System.Globalization; + +namespace Lib.Common.LoggingAPI.Service.Constants +{ + /// + /// Constants for the mime types. + /// + public static class MimeTypes + { + /// + /// The service application/json mime type. + /// + public const string ApplicationJson = "application/json"; + + /// + /// The application/pdf mime type. + /// + public const string ApplicationPdf = "application/pdf"; + + /// + /// The end index. + /// + public const int EndIndex = 5; + + /// + /// The JPEG extension. + /// + public const string ExtensionGif = "gif"; + + /// + /// The JPEG extension. + /// + public const string ExtensionJpeg = "jpeg"; + + /// + /// The PNG extension. + /// + public const string ExtensionPng = "png"; + + /// + /// The SVG extension. + /// + public const string ExtensionSvg = "svg"; + + /// + /// The image/gif mime type. + /// + public const string ImageGif = "image/gif"; + + /// + /// The image/jpeg mime type. + /// + public const string ImageJpeg = "image/jpeg"; + + /// + /// The image/png mime type. + /// + public const string ImagePng = "image/png"; + + /// + /// The image/svg+xml mime type. + /// + public const string ImageSvg = "image/svg+xml"; + + /// + /// The identifier GIF. + /// + public const string IdentifierGif = "R0LGO"; + + /// + /// The identifier PNG. + /// + public const string IdentifierJpeg = "/9J/4"; + + /// + /// The identifier PDF. + /// + public const string IdentifierPdf = "JVBER"; + + /// + /// The identifier PNG. + /// + public const string IdentifierPng = "IVBOR"; + + /// + /// The identifier SVG. + /// + public const string IdentifierSvg = "PHN2Z"; + + /// + /// The parameter name. + /// + public const string ParameterName = "MimeType"; + + /// + /// The start index. + /// + public const int StartIndex = 0; + + /// + /// The mime type dictionary. + /// + public static readonly Dictionary Dictionary = new Dictionary + { + { IdentifierJpeg, ImageJpeg }, + { IdentifierPng, ImagePng }, + { IdentifierGif, ImageGif }, + { IdentifierSvg, ImageSvg }, + }; + + /// + /// The mime type dictionary. + /// + public static readonly Dictionary DictionaryExtension = new Dictionary + { + { IdentifierJpeg, ExtensionJpeg }, + { IdentifierPng, ExtensionPng }, + { IdentifierGif, ExtensionGif }, + { IdentifierSvg, ExtensionSvg }, + }; + + /// + /// Gets the mime type. + /// + /// The cpntent with mime type identifier, substring 0, 5 from content. + /// A representing the value. + public static string GetMimeType(this string content) + { + return Dictionary.FirstOrDefault(_ => _.Key == content[..EndIndex].ToUpper(CultureInfo.InvariantCulture)).Value; + } + + /// + /// Gets the extension. + /// + /// The mime type identifier, substring 0, 5 from content. + /// A representing the value. + public static string GetExtension(this string content) + { + return DictionaryExtension.FirstOrDefault(_ => _.Key == content[..EndIndex].ToUpper(CultureInfo.InvariantCulture)).Value; + } + } +} diff --git a/Lib.Common.LoggingAPI.Service/Constants/Responses.cs b/Lib.Common.LoggingAPI.Service/Constants/Responses.cs new file mode 100644 index 0000000..f6776f6 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Constants/Responses.cs @@ -0,0 +1,29 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Common.LoggingAPI.Service.Constants +{ + /// + /// Constants of the responses for this service. + /// + public static class Responses + { + /// + /// The health response. + /// + public const string HealthyService = "healthy"; + + /// + /// The route does not exist response. + /// + public const string RouteDoesNotExist = "The specified route '{0}' does not exist for method '{1}' in this service."; + + /// + /// The target response. + /// + public const string Target = "{0}|{1}://{2}{3}"; + } +} diff --git a/Lib.Common.LoggingAPI.Service/DataTranferObjects/Error/ErrorDetailsDto.cs b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Error/ErrorDetailsDto.cs new file mode 100644 index 0000000..54e045f --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Error/ErrorDetailsDto.cs @@ -0,0 +1,44 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Service.Constants; +using System.ComponentModel; +using System.Text.Json.Serialization; + +namespace Lib.Common.LoggingAPI.Service.DataTransferObjects.Error +{ + /// + /// The service error details transfer object. + /// + public class ErrorDetailsDto + { + /// + /// Gets or sets the service error code. + /// + /// healthy + [DisplayName(DisplayNames.ErrorCode)] + [JsonPropertyName(DisplayNames.ErrorCode)] + public string? ErrorCode { get; set; } + + /// + /// Gets or sets the service error message. + /// + /// This is an example message. + [DisplayName(DisplayNames.Message)] + [JsonPropertyName(DisplayNames.Message)] + public string? Message { get; set; } + + /// + /// Gets or sets the service target. + /// + /// healthy + [DisplayName(DisplayNames.Target)] + [JsonPropertyName(DisplayNames.Target)] + public string? Target { get; set; } + } +} + + diff --git a/Lib.Common.LoggingAPI.Service/DataTranferObjects/Error/HttpErrorDto.cs b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Error/HttpErrorDto.cs new file mode 100644 index 0000000..6043ca7 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Error/HttpErrorDto.cs @@ -0,0 +1,46 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Service.Constants; +using System.ComponentModel; +using System.Text.Json.Serialization; + +namespace Lib.Common.LoggingAPI.Service.DataTransferObjects.Error +{ + /// + /// The service HTTP error data transfer object. + /// + public class HttpErrorDto + { + /// + /// Gets or sets the error. + /// + [DisplayName(DisplayNames.Error)] + [JsonPropertyName(DisplayNames.Error)] + public ErrorDetailsDto Error { get; set; } + + /// + /// Creates a new instance of + /// with custom parameters. + /// + /// The HTTP error message. + /// The HTTP error code. + /// The HTTP error target. + public HttpErrorDto( + string? message, + string? errorCode, + string? target) + { + this.Error = new ErrorDetailsDto + { + ErrorCode = errorCode, + Message = message, + Target = target, + }; + } + } +} + diff --git a/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogDetail.cs b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogDetail.cs new file mode 100644 index 0000000..a6856f7 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogDetail.cs @@ -0,0 +1,121 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Service.Constants; +using System.ComponentModel; +using System.Text.Json.Serialization; + +namespace Lib.Common.LoggingAPI.Service.DataTransferObjects.Logger +{ + /// + /// The service logger detail object. + /// + /// The generic message type. + public class LogDetail + { + /// + /// Gets or sets the log severity. + /// + /// info + [DisplayName(DisplayNames.Severity)] + [JsonPropertyName(DisplayNames.Severity)] + public LogSeverity Severity { get; set; } + + /// + /// Gets or sets the timestamp. + /// + [DisplayName(DisplayNames.Timestamp)] + [JsonPropertyName(DisplayNames.Timestamp)] + public DateTime Timestamp { get; set; } + + /// + /// Gets or sets the environment. + /// + /// Development + [DisplayName(DisplayNames.Environment)] + [JsonPropertyName(DisplayNames.Environment)] + public string? Environment { get; set; } + + /// + /// Gets or sets the target. + /// + [DisplayName(DisplayNames.Target)] + [JsonPropertyName(DisplayNames.Target)] + public LogTarget? Target { get; set; } + + /// + /// Gets or sets the x-forwarded-for header. + /// + /// localhost + [DisplayName(DisplayNames.XForwardedFor)] + [JsonPropertyName(DisplayNames.XForwardedFor)] + public string? XForwardedFor { get; set; } + + /// + /// Gets or sets the service identifier. + /// + /// + [DisplayName(DisplayNames.ServiceId)] + [JsonPropertyName(DisplayNames.ServiceId)] + public string? ServiceId { get; set; } + + /// + /// Gets or sets the request identifier. + /// + /// + [DisplayName(DisplayNames.RequestId)] + [JsonPropertyName(DisplayNames.RequestId)] + public string? RequestId { get; set; } + + /// + /// Gets or sets the client identifier. + /// + /// + [DisplayName(DisplayNames.ClientId)] + [JsonPropertyName(DisplayNames.ClientId)] + public string? ClientId { get; set; } + + /// + /// Gets or sets the client identifier. + /// + /// clientRequest + [DisplayName(DisplayNames.Operation)] + [JsonPropertyName(DisplayNames.Operation)] + public LogOperation Operation { get; set; } + + /// + /// Gets or sets the user name. + /// + /// clientRequest + [DisplayName(DisplayNames.User)] + [JsonPropertyName(DisplayNames.User)] + public string? User { get; set; } + + /// + /// Gets or sets user's email. + /// + /// clientRequest + [DisplayName(DisplayNames.Email)] + [JsonPropertyName(DisplayNames.Email)] + public string? Email { get; set; } + + /// + /// Gets or sets the user identifier. + /// + /// clientRequest + [DisplayName(DisplayNames.UserId)] + [JsonPropertyName(DisplayNames.UserId)] + public string? UserId { get; set; } + + /// + /// Gets or sets the message. + /// + /// A custom log message. + [DisplayName(DisplayNames.Message)] + [JsonPropertyName(DisplayNames.Message)] + public TMessage? Message { get; set; } + } +} diff --git a/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogOperation.cs b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogOperation.cs new file mode 100644 index 0000000..fa625ee --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogOperation.cs @@ -0,0 +1,56 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Service.Constants; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace Lib.Common.LoggingAPI.Service.DataTransferObjects.Logger +{ + /// + /// Represents all possible values for log operation. + /// + [DataContract] + public enum LogOperation + { + /// + /// The client request log operation type. + /// + [EnumMember(Value = DisplayNames.ClientRequest)] + [JsonPropertyName(DisplayNames.ClientRequest)] + ClientRequest = 0, + + /// + /// The client response log operation type. + /// + [EnumMember(Value = DisplayNames.ClientResponse)] + ClientResponse = 1, + + /// + /// The external request log operation type. + /// + [EnumMember(Value = DisplayNames.ExternalRequest)] + ExternalRequest = 2, + + /// + /// The external response log operation type. + /// + [EnumMember(Value = DisplayNames.ExternalResponse)] + ExternalResponse = 3, + + /// + /// The error log operation type. + /// + [EnumMember(Value = DisplayNames.Error)] + Error = 4, + + /// + /// The info log operation type. + /// + [EnumMember(Value = DisplayNames.Info)] + Info = 5, + } +} diff --git a/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogSeverity.cs b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogSeverity.cs new file mode 100644 index 0000000..e41a513 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogSeverity.cs @@ -0,0 +1,42 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Service.Constants; +using System.Runtime.Serialization; + +namespace Lib.Common.LoggingAPI.Service.DataTransferObjects.Logger +{ + /// + /// Represents all possible values for log severity. + /// + [DataContract] + public enum LogSeverity + { + /// + /// The information severity level. + /// + [EnumMember(Value = DisplayNames.Information)] + Info = 0, + + /// + /// The warning severity level. + /// + [EnumMember(Value = DisplayNames.Warning)] + Warn = 1, + + /// + /// The error severity level. + /// + [EnumMember(Value = DisplayNames.Error)] + Error = 2, + + /// + /// The fatal severity level. + /// + [EnumMember(Value = DisplayNames.Fatal)] + Fatal = 3, + } +} diff --git a/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogTarget.cs b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogTarget.cs new file mode 100644 index 0000000..33f1339 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/DataTranferObjects/Logger/LogTarget.cs @@ -0,0 +1,42 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Service.Constants; +using System.ComponentModel; +using System.Text.Json.Serialization; + +namespace Lib.Common.LoggingAPI.Service.DataTransferObjects.Logger +{ + /// + /// The service logger target object. + /// + public class LogTarget + { + /// + /// Gets or sets the log target method. + /// + /// GET + [DisplayName(DisplayNames.Method)] + [JsonPropertyName(DisplayNames.Method)] + public string? Method { get; set; } + + /// + /// Gets or sets the log target host. + /// + /// GET + [DisplayName(DisplayNames.Host)] + [JsonPropertyName(DisplayNames.Host)] + public string? Host { get; set; } + + /// + /// Gets or sets the log target route. + /// + /// GET + [DisplayName(DisplayNames.Route)] + [JsonPropertyName(DisplayNames.Route)] + public string? Route { get; set; } + } +} diff --git a/Lib.Common.LoggingAPI.Service/Extensions/HttpException/HttpException.cs b/Lib.Common.LoggingAPI.Service/Extensions/HttpException/HttpException.cs new file mode 100644 index 0000000..eaa7770 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Extensions/HttpException/HttpException.cs @@ -0,0 +1,41 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Common.LoggingAPI.Service.Extensions +{ + /// + /// The service HTTP exception. + /// Extends the class. + /// + public class HttpException : Exception + { + /// + /// Gets or sets the exception error code. + /// + public string? ErrorCode { get; set; } + + /// + /// Gets or sets the exception status code. + /// + public int StatusCode { get; set; } + + /// + /// Creates a new instance of . + /// + /// The exception status code. + /// The exception error code. + /// The exception message. + public HttpException( + int statusCode, + string errorCode, + string message) + : base(message) + { + this.ErrorCode = errorCode; + this.StatusCode = statusCode; + } + } +} diff --git a/Lib.Common.LoggingAPI.Service/Lib.Common.LoggingAPI.Service.csproj b/Lib.Common.LoggingAPI.Service/Lib.Common.LoggingAPI.Service.csproj new file mode 100644 index 0000000..0cb5ccb --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Lib.Common.LoggingAPI.Service.csproj @@ -0,0 +1,32 @@ + + + + net8.0 + enable + enable + True + true + Lib.Common.LoggingAPI.Service + Upstart + Heath + Tracks incoming request and outgoing responses + HeatUs.com + C:\NugetPackages + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/Lib.Common.LoggingAPI.Service/Middleware/HttpException/HttpErrorMiddleware.cs b/Lib.Common.LoggingAPI.Service/Middleware/HttpException/HttpErrorMiddleware.cs new file mode 100644 index 0000000..d11058c --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Middleware/HttpException/HttpErrorMiddleware.cs @@ -0,0 +1,96 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Common.Settings; +using Lib.Common.LoggingAPI.Service.Constants; +using Lib.Common.LoggingAPI.Service.DataTransferObjects.Error; +using Lib.Common.LoggingAPI.Service.Middleware.HttpLogger; +using Microsoft.AspNetCore.Http; +using Serilog; +using System.Text.Json; + +namespace Lib.Common.LoggingAPI.Service.Middleware.HttpException +{ + /// + /// Handles HTTP logging. + /// + public class HttpErrorMiddleware + { + private readonly ILogger logger; + private readonly RequestDelegate requestProcess; + public readonly ServiceSettings settings; + + /// + /// Creates a new instrance of . + /// + /// The logger representig an instance of . + /// The request delegate process. + public HttpErrorMiddleware(ILogger logger, RequestDelegate requestProcess, ServiceSettings settings) + { + this.logger = logger; + this.requestProcess = requestProcess; + this.settings = settings; + } + + /// + /// Invoke method. + /// + /// The HTTP context. + /// A representing the asynchronous operation. + public async Task Invoke(HttpContext context) + { + try + { + await this.requestProcess(context).ConfigureAwait(false); + } + catch (Extensions.HttpException exception) + { + await HandleErrorResponse( + context, + exception.Message, + exception.ErrorCode, + exception.StatusCode).ConfigureAwait(false); + } + catch (Exception defaultException) + { + await HandleErrorResponse( + context, + defaultException.Message, + ErrorCodes.InternalServerError, + StatusCodes.Status500InternalServerError).ConfigureAwait(false); + } + } + + /// + /// Handles error responses. + /// + /// The HTTP context. + /// The error message. + /// The error code. + /// The HTTP status code. + /// A representing the asynchronous operation. + private async Task HandleErrorResponse(HttpContext context, string? message, string? errorCode, int statusCode) + { + var errorMessage = new HttpErrorDto( + message, + errorCode, + string.Format( + Responses.Target, + context.Request.Method, + context.Request.Scheme, + context.Request.Host.Host, + context.Request.Path)); + + this.logger.LogError(context, errorMessage, settings.ServiceId); + + context.Response.ContentType = MimeTypes.ApplicationJson; + context.Response.StatusCode = statusCode; + + await context.Response.WriteAsync(JsonSerializer.Serialize(errorMessage)).ConfigureAwait(false); + } + } +} + diff --git a/Lib.Common.LoggingAPI.Service/Middleware/HttpException/HttpErrorMiddlewareExtension.cs b/Lib.Common.LoggingAPI.Service/Middleware/HttpException/HttpErrorMiddlewareExtension.cs new file mode 100644 index 0000000..aa8845d --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Middleware/HttpException/HttpErrorMiddlewareExtension.cs @@ -0,0 +1,20 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Common.Settings; +using Microsoft.AspNetCore.Builder; + +namespace Lib.Common.LoggingAPI.Service.Middleware.HttpException +{ + public static class HttpErrorMiddlewareExtension + { + public static IApplicationBuilder UseHttpExceptionHandler(this IApplicationBuilder builder, ServiceSettings settings) + { + return builder.UseMiddleware(settings); + } + } +} + diff --git a/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLogger.cs b/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLogger.cs new file mode 100644 index 0000000..91ab6ee --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLogger.cs @@ -0,0 +1,239 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Service.Constants; +using Lib.Common.LoggingAPI.Service.DataTransferObjects.Logger; +using Microsoft.AspNetCore.Http; +using Microsoft.IO; +using Serilog; +using System.IdentityModel.Tokens.Jwt; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Lib.Common.LoggingAPI.Service.Middleware.HttpLogger +{ + /// + /// Handles all logging scenarios. + /// + public static class HttpLogger + { + /// + /// The JSON serializer options for logging methods. + /// + public static JsonSerializerOptions serializerOptions = new JsonSerializerOptions + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = { + new JsonStringEnumConverter( JsonNamingPolicy.CamelCase), + }, + }; + + /// + /// Logs an error message. + /// + /// The generic message parameter. + /// The HTTP context. + /// The message. + /// The service identifier. + public static void LogError(this ILogger logger, HttpContext context, TMessage message, string? serviceId) + { + var logMessage = CreateErrorLog(context, message, serviceId); + logger.Error(logMessage); + } + + /// + /// Logs an information message. + /// + /// The generic message parameter. + /// The HTTP context. + /// The message. + /// The service identifier. + public static void LogInfo(this ILogger logger, HttpContext context, TMessage message, string? serviceId) + { + var logMessage = CreateInfoLog(context, message, serviceId); + logger.Information(logMessage); + } + + /// + /// Logs an incoming HTTP request. + /// + /// The logger. + /// The HTTP context. + /// The recyclable mmory stream manager. + /// The service identifier. + /// A representing the asynchronous operation. + public static async Task LogRequest( + this ILogger logger, + HttpContext context, + RecyclableMemoryStreamManager recyclableMemoryStreamManager, + string? serviceId) + { + context.Request.EnableBuffering(); + await using var requestStream = recyclableMemoryStreamManager.GetStream(); + await context.Request.Body.CopyToAsync(requestStream); + + var logMessage = CreateRequestLog( + context, + ReadStream(requestStream), + serviceId); + logger.Information(logMessage); + + context.Request.Body.Position = 0; + } + + /// + /// Logs an outcome HTTP response. + /// + /// The logger. + /// The HTTP context. + /// The recyclable mmory stream manager. + /// The request delegate process. + /// The service identifier. + /// A representing the asynchronous operation. + /// + public static async Task LogResponse( + this ILogger logger, + HttpContext context, + RecyclableMemoryStreamManager recyclableMemoryStreamManager, + RequestDelegate requestProcess, + string? serviceId) + { + var originalBodyStream = context.Response.Body; + await using var responseBody = recyclableMemoryStreamManager.GetStream(); + context.Response.Body = responseBody; + + await requestProcess(context); + + context.Response.Body.Seek(0, SeekOrigin.Begin); + var text = await new StreamReader(context.Response.Body).ReadToEndAsync(); + context.Response.Body.Seek(0, SeekOrigin.Begin); + + var logMessage = CreateResponseLog(context, text, serviceId); + logger.Information(logMessage); + + await responseBody.CopyToAsync(originalBodyStream); + } + + /// + /// Creates an error log. + /// + /// The generic message. + /// The HTTP context. + /// The error message. + /// The service identifier. + /// A representig the error log. + private static string CreateErrorLog(HttpContext context, TMessage message, string? serviceId) + => CreateLog(context, LogSeverity.Error, LogOperation.Error, message, serviceId); + + /// + /// Creates an info log. + /// + /// The generic message. + /// The HTTP context. + /// The info message. + /// The service identifier. + /// A representig the info log. + private static string CreateInfoLog(HttpContext context, TMessage message, string? serviceId) + => CreateLog(context, LogSeverity.Info, LogOperation.Info, message, serviceId); + + /// + /// Creates a request log. + /// + /// The HTTP context. + /// The request body. + /// The service identifier. + /// A representig the request log. + private static string CreateRequestLog(HttpContext context, string? requestBody, string? serviceId) + => CreateLog(context, LogSeverity.Info, LogOperation.ClientRequest, requestBody, serviceId); + + /// + /// Creates a response log. + /// + /// The HTTP context. + /// The response body. + /// The service identifier. + /// A representig the response log. + private static string CreateResponseLog(HttpContext context, string? responseBody, string? serviceId) + => CreateLog(context, LogSeverity.Info, LogOperation.ClientResponse, responseBody, serviceId); + + /// + /// Creates a generic log. + /// + /// The HTTP context. + /// The log severity. + /// The log operation. + /// The log message + /// The service identifier. + /// A representing a generic log. + private static string CreateLog( + HttpContext context, + LogSeverity severity, + LogOperation operation, + TMessage message, + string? serviceId) + { + var tokenHeader = context.Request.Headers[Headers.Authorization].FirstOrDefault()?.Split(" ").Last(); + var tokenHandler = new JwtSecurityTokenHandler(); + var token = (tokenHeader is not null) ? tokenHandler.ReadJwtToken(tokenHeader) : null; + + var log = new LogDetail + { + Severity = severity, + Target = new LogTarget + { + Method = context.Request.Method, + Host = context.Request.Host.Host, + Route = context.Request.Path, + }, + Email = token?.Claims.FirstOrDefault(c => c.Type == Claims.Email)?.Value, + User = token?.Claims.FirstOrDefault(c => c.Type == Claims.Name)?.Value, + UserId = token?.Claims.FirstOrDefault(c => c.Type == Claims.Id)?.Value, + Environment = Environment.GetEnvironmentVariable(EnvironmentVariables.Stage), + Operation = operation, + RequestId = context.Request.Headers[DisplayNames.RequestId], + ServiceId = serviceId, + XForwardedFor = context.Request.Headers[DisplayNames.XForwardedForHeader], + Timestamp = DateTime.Now, + Message = message, + }; + + var serializedLog = JsonSerializer.Serialize(log, serializerOptions); + + return serializedLog + .Replace("\\u0022", "\"") + .Replace("\"{", "{") + .Replace("}\"", "}") + .Replace("\\u0027", "'") + .Replace("\\\u0027", "'") + .Replace("\n", ""); + } + + /// + /// Reads the stream. + /// + /// The stream to be read. + /// A representig the request body. + private static string? ReadStream(Stream stream) + { + const int readChunkBufferLength = 4096; + stream.Seek(0, SeekOrigin.Begin); + using var textWriter = new StringWriter(); + using var reader = new StreamReader(stream); + var readChunk = new char[readChunkBufferLength]; + int readChunkLength; + do + { + readChunkLength = reader.ReadBlock(readChunk, 0, readChunkBufferLength); + textWriter.Write(readChunk, 0, readChunkLength); + } while (readChunkLength > 0); + + var stringItem = textWriter.ToString(); + + return stringItem != string.Empty ? stringItem : null; + } + } +} + diff --git a/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLoggingMiddleware.cs b/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLoggingMiddleware.cs new file mode 100644 index 0000000..d30516c --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLoggingMiddleware.cs @@ -0,0 +1,70 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Common.Settings; +using Microsoft.AspNetCore.Http; +using Microsoft.IO; +using Serilog; + +namespace Lib.Common.LoggingAPI.Service.Middleware.HttpLogger +{ + /// + /// Handles HTTP logging. + /// + public class HttpLoggingMiddleware + { + private readonly ILogger logger; + private readonly RequestDelegate requestProcess; + private readonly ServiceSettings settings; + private readonly RecyclableMemoryStreamManager recyclableMemoryStreamManager; + + /// + /// Creates a new instrance of . + /// + /// The request delegate process. + /// The logger representig an instance of . + /// The service settings. + public HttpLoggingMiddleware(RequestDelegate requestProcess, ILogger logger, ServiceSettings settings) + { + this.logger = logger; + this.requestProcess = requestProcess; + this.settings = settings; + recyclableMemoryStreamManager = new RecyclableMemoryStreamManager(); + } + + /// + /// Invoke method. + /// + /// The HTTP context. + /// + public async Task Invoke(HttpContext context) + { + await LogRequest(context); + await LogResponse(context); + } + + /// + /// Logs an incoming HTTP request. + /// + /// The HTTP context. + /// A representing the asynchronous operation. + private async Task LogRequest(HttpContext context) + { + await this.logger.LogRequest(context, this.recyclableMemoryStreamManager, this.settings.ServiceId); + } + + /// + /// Logs an outcome HTTP response. + /// + /// The HTTP context. + /// A representing the asynchronous operation. + private async Task LogResponse(HttpContext context) + { + await this.logger.LogResponse(context, recyclableMemoryStreamManager, requestProcess, this.settings.ServiceId); + } + } +} + diff --git a/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLoggingMiddlewareExtension.cs b/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLoggingMiddlewareExtension.cs new file mode 100644 index 0000000..0798e30 --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Middleware/HttpLogger/HttpLoggingMiddlewareExtension.cs @@ -0,0 +1,20 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +using Lib.Common.LoggingAPI.Common.Settings; +using Microsoft.AspNetCore.Builder; + +namespace Lib.Common.LoggingAPI.Service.Middleware.HttpLogger +{ + public static class HttpLoggingMiddlewareExtension + { + public static IApplicationBuilder UseCustomHttpLogging(this IApplicationBuilder builder, ServiceSettings settings) + { + return builder.UseMiddleware(settings); + } + } +} + diff --git a/Lib.Common.LoggingAPI.Service/Settings/ServiceSettings.cs b/Lib.Common.LoggingAPI.Service/Settings/ServiceSettings.cs new file mode 100644 index 0000000..25a58fc --- /dev/null +++ b/Lib.Common.LoggingAPI.Service/Settings/ServiceSettings.cs @@ -0,0 +1,19 @@ +// *********************************************************************** +// +// Heath +// +// *********************************************************************** + +namespace Lib.Common.LoggingAPI.Common.Settings +{ + /// + /// The service settings. + /// + public class ServiceSettings + { + /// + /// Gets or sets the service identifier. + /// + public string? ServiceId { get; set; } + } +}