Add project files.
This commit is contained in:
		
							
								
								
									
										55
									
								
								Core.BluePrint.Packages.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								Core.BluePrint.Packages.sln
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  |  | ||||||
|  | Microsoft Visual Studio Solution File, Format Version 12.00 | ||||||
|  | # Visual Studio Version 17 | ||||||
|  | VisualStudioVersion = 17.9.34728.123 | ||||||
|  | MinimumVisualStudioVersion = 10.0.40219.1 | ||||||
|  | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Blueprint.KeyVault", "Core.Blueprint.KeyVault\Core.Blueprint.KeyVault.csproj", "{0B4D475C-6A41-443C-8FB4-21C759EDCE63}" | ||||||
|  | EndProject | ||||||
|  | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Blueprint.Mongo", "Core.Blueprint.Mongo\Core.Blueprint.Mongo.csproj", "{27A8E3E1-D613-4D5B-8105-485699409F1E}" | ||||||
|  | EndProject | ||||||
|  | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Blueprint.Redis", "Core.Blueprint.Redis\Core.Blueprint.Redis.csproj", "{11F2AA11-FB98-4A33-AEE4-CD49588D2FE1}" | ||||||
|  | EndProject | ||||||
|  | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Blueprint.Storage", "Core.Blueprint.Storage\Core.Blueprint.Storage.csproj", "{636E4520-79F9-46C8-990D-08F2D24A151C}" | ||||||
|  | EndProject | ||||||
|  | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Blueprint.SQLServer", "Core.Blueprint.SQLServer\Core.Blueprint.SQLServer.csproj", "{A9CC126A-C187-49EF-9784-0F9F5B8ABDB1}" | ||||||
|  | EndProject | ||||||
|  | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Blueprint.Logging", "Core.Blueprint.Logging\Core.Blueprint.Logging.csproj", "{85B4BC7C-5800-40A9-8310-F4EB2C82AF39}" | ||||||
|  | EndProject | ||||||
|  | Global | ||||||
|  | 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||||
|  | 		Debug|Any CPU = Debug|Any CPU | ||||||
|  | 		Release|Any CPU = Release|Any CPU | ||||||
|  | 	EndGlobalSection | ||||||
|  | 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||||
|  | 		{0B4D475C-6A41-443C-8FB4-21C759EDCE63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||||
|  | 		{0B4D475C-6A41-443C-8FB4-21C759EDCE63}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
|  | 		{0B4D475C-6A41-443C-8FB4-21C759EDCE63}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
|  | 		{0B4D475C-6A41-443C-8FB4-21C759EDCE63}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
|  | 		{27A8E3E1-D613-4D5B-8105-485699409F1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||||
|  | 		{27A8E3E1-D613-4D5B-8105-485699409F1E}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
|  | 		{27A8E3E1-D613-4D5B-8105-485699409F1E}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
|  | 		{27A8E3E1-D613-4D5B-8105-485699409F1E}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
|  | 		{11F2AA11-FB98-4A33-AEE4-CD49588D2FE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||||
|  | 		{11F2AA11-FB98-4A33-AEE4-CD49588D2FE1}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
|  | 		{11F2AA11-FB98-4A33-AEE4-CD49588D2FE1}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
|  | 		{11F2AA11-FB98-4A33-AEE4-CD49588D2FE1}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
|  | 		{636E4520-79F9-46C8-990D-08F2D24A151C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||||
|  | 		{636E4520-79F9-46C8-990D-08F2D24A151C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
|  | 		{636E4520-79F9-46C8-990D-08F2D24A151C}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
|  | 		{636E4520-79F9-46C8-990D-08F2D24A151C}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
|  | 		{A9CC126A-C187-49EF-9784-0F9F5B8ABDB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||||
|  | 		{A9CC126A-C187-49EF-9784-0F9F5B8ABDB1}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
|  | 		{A9CC126A-C187-49EF-9784-0F9F5B8ABDB1}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
|  | 		{A9CC126A-C187-49EF-9784-0F9F5B8ABDB1}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
|  | 		{85B4BC7C-5800-40A9-8310-F4EB2C82AF39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||||
|  | 		{85B4BC7C-5800-40A9-8310-F4EB2C82AF39}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||||
|  | 		{85B4BC7C-5800-40A9-8310-F4EB2C82AF39}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||||
|  | 		{85B4BC7C-5800-40A9-8310-F4EB2C82AF39}.Release|Any CPU.Build.0 = Release|Any CPU | ||||||
|  | 	EndGlobalSection | ||||||
|  | 	GlobalSection(SolutionProperties) = preSolution | ||||||
|  | 		HideSolutionNode = FALSE | ||||||
|  | 	EndGlobalSection | ||||||
|  | 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||||
|  | 		SolutionGuid = {60FDC812-CC26-4C4A-BCA0-90603A77E99D} | ||||||
|  | 	EndGlobalSection | ||||||
|  | EndGlobal | ||||||
							
								
								
									
										9
									
								
								Core.Blueprint.KeyVault/Adapters/KeyVaultRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Core.Blueprint.KeyVault/Adapters/KeyVaultRequest.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  |  | ||||||
|  | namespace Core.Blueprint.KeyVault | ||||||
|  | { | ||||||
|  |     public sealed class KeyVaultRequest | ||||||
|  |     { | ||||||
|  |         public required string Name { get; set; } | ||||||
|  |         public required string Value { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								Core.Blueprint.KeyVault/Adapters/KeyVaultResponse.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Core.Blueprint.KeyVault/Adapters/KeyVaultResponse.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.KeyVault | ||||||
|  | { | ||||||
|  |     public sealed class KeyVaultResponse | ||||||
|  |     { | ||||||
|  |         public string Name { get; set; } = null!; | ||||||
|  |         public string Value { get; set; } = null!; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								Core.Blueprint.KeyVault/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								Core.Blueprint.KeyVault/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | using Azure.Identity; | ||||||
|  | using Azure.Security.KeyVault.Secrets; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.KeyVault.Configuration | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Registers the SecretClient for Azure Key Vault as a singleton service. | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="services">The IServiceCollection to add the services to.</param> | ||||||
|  |     /// <param name="configuration">The application's configuration.</param> | ||||||
|  |     /// <returns>The updated IServiceCollection.</returns> | ||||||
|  |     /// <exception cref="ArgumentNullException">Thrown when the KeyVault URI is missing in the configuration.</exception> | ||||||
|  |     public static class RegisterBlueprint | ||||||
|  |     { | ||||||
|  |         public static IServiceCollection AddKeyVault(this IServiceCollection services, IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |             var keyVaultUriString = configuration["ConnectionStrings:KeyVaultDAL"]; | ||||||
|  |  | ||||||
|  |             if (string.IsNullOrEmpty(keyVaultUriString)) | ||||||
|  |             { | ||||||
|  |                 throw new ArgumentNullException("ConnectionStrings:KeyVault", "KeyVault URI is missing in the configuration."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var keyVaultUri = new Uri(keyVaultUriString); | ||||||
|  |  | ||||||
|  |             // Register SecretClient as a singleton | ||||||
|  |             services.AddSingleton(_ => new SecretClient(keyVaultUri, new DefaultAzureCredential())); | ||||||
|  |  | ||||||
|  |             services.AddSingleton<IKeyVaultProvider, KeyVaultProvider>(); | ||||||
|  |             return services; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								Core.Blueprint.KeyVault/Contracts/IKeyVaultProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Core.Blueprint.KeyVault/Contracts/IKeyVaultProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  |  | ||||||
|  | namespace Core.Blueprint.KeyVault | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Interface for managing secrets in Azure Key Vault. | ||||||
|  |     /// </summary> | ||||||
|  |     public interface IKeyVaultProvider | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a new secret in Azure Key Vault. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="keyVaultRequest">The request containing the name and value of the secret.</param> | ||||||
|  |         /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|  |         /// <returns>A <see cref="KeyVaultResponse"/> containing the details of the created secret.</returns> | ||||||
|  |         ValueTask<KeyVaultResponse> CreateSecretAsync(KeyVaultRequest keyVaultRequest, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a secret from Azure Key Vault if it exists. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="secretName">The name of the secret to delete.</param> | ||||||
|  |         /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// A <see cref="Tuple"/> containing a status message and a boolean indicating whether the secret was successfully deleted. | ||||||
|  |         /// </returns> | ||||||
|  |         ValueTask<Tuple<string, bool>> DeleteSecretAsync(string secretName, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves a secret from Azure Key Vault. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="secretName">The name of the secret to retrieve.</param> | ||||||
|  |         /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// A <see cref="Tuple"/> containing the <see cref="KeyVaultResponse"/> with secret details  | ||||||
|  |         /// and an optional error message if the secret was not found. | ||||||
|  |         /// </returns> | ||||||
|  |         ValueTask<Tuple<KeyVaultResponse, string?>> GetSecretAsync(string secretName, CancellationToken cancellationToken); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Updates an existing secret in Azure Key Vault. If the secret does not exist, an error is returned. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="newSecret">The updated secret information.</param> | ||||||
|  |         /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// A <see cref="Tuple"/> containing the updated <see cref="KeyVaultResponse"/> and an optional error message if the secret was not found. | ||||||
|  |         /// </returns> | ||||||
|  |         ValueTask<Tuple<KeyVaultResponse, string>> UpdateSecretAsync(KeyVaultRequest newSecret, CancellationToken cancellationToken); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								Core.Blueprint.KeyVault/Core.Blueprint.KeyVault.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Core.Blueprint.KeyVault/Core.Blueprint.KeyVault.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  |  | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <TargetFramework>net8.0</TargetFramework> | ||||||
|  |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|  |     <Nullable>enable</Nullable> | ||||||
|  |   </PropertyGroup> | ||||||
|  |  | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Azure.Identity" Version="1.13.1" /> | ||||||
|  |     <PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> | ||||||
|  |   </ItemGroup> | ||||||
|  |  | ||||||
|  | </Project> | ||||||
							
								
								
									
										93
									
								
								Core.Blueprint.KeyVault/Provider/KeyVaultProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								Core.Blueprint.KeyVault/Provider/KeyVaultProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | using Azure; | ||||||
|  | using Azure.Security.KeyVault.Secrets; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.KeyVault | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Provides operations for managing secrets in Azure Key Vault. | ||||||
|  |     /// </summary> | ||||||
|  |     public sealed class KeyVaultProvider(SecretClient keyVaultProvider): IKeyVaultProvider | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a new secret in Azure Key Vault. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="keyVaultRequest">The request containing the name and value of the secret.</param> | ||||||
|  |         /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|  |         /// <returns>A <see cref="KeyVaultResponse"/> containing the details of the created secret.</returns> | ||||||
|  |         public async ValueTask<KeyVaultResponse> CreateSecretAsync(KeyVaultRequest keyVaultRequest, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             KeyVaultResponse _response = new(); | ||||||
|  |             KeyVaultSecret azureResponse = await keyVaultProvider.SetSecretAsync(new KeyVaultSecret(keyVaultRequest.Name, keyVaultRequest.Value), cancellationToken); | ||||||
|  |  | ||||||
|  |             _response.Value = azureResponse.Value; | ||||||
|  |             _response.Name = azureResponse.Name; | ||||||
|  |  | ||||||
|  |             return _response; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a secret from Azure Key Vault if it exists. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="secretName">The name of the secret to delete.</param> | ||||||
|  |         /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// A <see cref="Tuple"/> containing a status message and a boolean indicating whether the secret was successfully deleted. | ||||||
|  |         /// </returns> | ||||||
|  |         public async ValueTask<Tuple<string, bool>> DeleteSecretAsync(string secretName, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             var existingSecret = await this.GetSecretAsync(secretName, cancellationToken); | ||||||
|  |             if (existingSecret != null) | ||||||
|  |             { | ||||||
|  |                 await keyVaultProvider.StartDeleteSecretAsync(secretName, cancellationToken); | ||||||
|  |                 return new("Key Deleted", true); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return new("Key Not Found", false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves a secret from Azure Key Vault. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="secretName">The name of the secret to retrieve.</param> | ||||||
|  |         /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// A <see cref="Tuple"/> containing the <see cref="KeyVaultResponse"/> with secret details  | ||||||
|  |         /// and an optional error message if the secret was not found. | ||||||
|  |         /// </returns> | ||||||
|  |         public async ValueTask<Tuple<KeyVaultResponse, string?>> GetSecretAsync(string secretName, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             KeyVaultSecret azureResponse = await keyVaultProvider.GetSecretAsync(secretName, cancellationToken: cancellationToken); | ||||||
|  |  | ||||||
|  |             if (azureResponse == null) | ||||||
|  |             { | ||||||
|  |                 return new(new KeyVaultResponse(), "Key Not Found"); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return new(new KeyVaultResponse { Name = secretName, Value = azureResponse.Value }, string.Empty); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Updates an existing secret in Azure Key Vault. If the secret does not exist, an error is returned. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="newSecret">The updated secret information.</param> | ||||||
|  |         /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// A <see cref="Tuple"/> containing the updated <see cref="KeyVaultResponse"/> and an optional error message if the secret was not found. | ||||||
|  |         /// </returns> | ||||||
|  |         public async ValueTask<Tuple<KeyVaultResponse, string>> UpdateSecretAsync(KeyVaultRequest newSecret, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             KeyVaultResponse _response = new(); | ||||||
|  |             var existingSecret = await this.GetSecretAsync(newSecret.Name, cancellationToken); | ||||||
|  |             if (existingSecret == null) | ||||||
|  |             { | ||||||
|  |                 return new(new KeyVaultResponse(), "Key Not Found"); | ||||||
|  |             } | ||||||
|  |             KeyVaultSecret azureResponse = await keyVaultProvider.SetSecretAsync(new KeyVaultSecret(newSecret.Name, newSecret.Value), cancellationToken); | ||||||
|  |  | ||||||
|  |             _response.Value = azureResponse.Value; | ||||||
|  |             _response.Name = azureResponse.Name; | ||||||
|  |  | ||||||
|  |             return new(new KeyVaultResponse { Name = newSecret.Name, Value = azureResponse.Value }, string.Empty); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								Core.Blueprint.Logging/Adapters/ErrorDetails.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Core.Blueprint.Logging/Adapters/ErrorDetails.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="ErrorDetailsDto.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The service error details transfer object. | ||||||
|  |     /// </summary> | ||||||
|  |     public class ErrorDetails | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the service error code. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>healthy</example> | ||||||
|  |         [DisplayName(DisplayNames.ErrorCode)] | ||||||
|  |         [JsonPropertyName(DisplayNames.ErrorCode)] | ||||||
|  |         public string? ErrorCode { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the service error message. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>This is an example message.</example> | ||||||
|  |         [DisplayName(DisplayNames.Message)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Message)] | ||||||
|  |         public string? Message { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the service target. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>healthy</example> | ||||||
|  |         [DisplayName(DisplayNames.Target)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Target)] | ||||||
|  |         public string? Target { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										45
									
								
								Core.Blueprint.Logging/Adapters/HttpError.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Core.Blueprint.Logging/Adapters/HttpError.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="HttpErrorDto.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The service HTTP error data transfer object. | ||||||
|  |     /// </summary> | ||||||
|  |     public class HttpError | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the error. | ||||||
|  |         /// </summary> | ||||||
|  |         [DisplayName(DisplayNames.Error)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Error)] | ||||||
|  |         public ErrorDetails Error { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a new instance of <see cref="HttpError{TMessage}"/> | ||||||
|  |         /// with custom parameters. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="message">The HTTP error message.</param> | ||||||
|  |         /// <param name="errorCode">The HTTP error code.</param> | ||||||
|  |         /// <param name="target">The HTTP error target.</param> | ||||||
|  |         public HttpError( | ||||||
|  |             string? message, | ||||||
|  |             string? errorCode, | ||||||
|  |             string? target) | ||||||
|  |         { | ||||||
|  |             Error = new ErrorDetails | ||||||
|  |             { | ||||||
|  |                 ErrorCode = errorCode, | ||||||
|  |                 Message = message, | ||||||
|  |                 Target = target, | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										41
									
								
								Core.Blueprint.Logging/Adapters/HttpException.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Core.Blueprint.Logging/Adapters/HttpException.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="HttpException.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The service HTTP exception. | ||||||
|  |     /// Extends the <see cref="Exception"/> class. | ||||||
|  |     /// </summary> | ||||||
|  |     public class HttpException : Exception | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the exception error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public string? ErrorCode { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the exception status code. | ||||||
|  |         /// </summary> | ||||||
|  |         public int StatusCode { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a new instance of <see cref="HttpException"/>. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="statusCode">The exception status code.</param> | ||||||
|  |         /// <param name="errorCode">The exception error code.</param> | ||||||
|  |         /// <param name="message">The exception message.</param> | ||||||
|  |         public HttpException( | ||||||
|  |             int statusCode, | ||||||
|  |             string errorCode, | ||||||
|  |             string message) | ||||||
|  |             : base(message) | ||||||
|  |         { | ||||||
|  |             ErrorCode = errorCode; | ||||||
|  |             StatusCode = statusCode; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										120
									
								
								Core.Blueprint.Logging/Adapters/LogDetail.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								Core.Blueprint.Logging/Adapters/LogDetail.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="LogDetail.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The service logger detail object. | ||||||
|  |     /// </summary> | ||||||
|  |     /// <typeparam name="TMessage">The generic message type.</typeparam> | ||||||
|  |     public class LogDetail<TMessage> | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the log severity. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>info</example> | ||||||
|  |         [DisplayName(DisplayNames.Severity)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Severity)] | ||||||
|  |         public LogSeverity Severity { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the timestamp. | ||||||
|  |         /// </summary> | ||||||
|  |         [DisplayName(DisplayNames.Timestamp)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Timestamp)] | ||||||
|  |         public DateTime Timestamp { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the environment. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>Development</example> | ||||||
|  |         [DisplayName(DisplayNames.Environment)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Environment)] | ||||||
|  |         public string? Environment { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the target. | ||||||
|  |         /// </summary> | ||||||
|  |         [DisplayName(DisplayNames.Target)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Target)] | ||||||
|  |         public LogTarget? Target { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the x-forwarded-for header. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>localhost</example> | ||||||
|  |         [DisplayName(DisplayNames.XForwardedFor)] | ||||||
|  |         [JsonPropertyName(DisplayNames.XForwardedFor)] | ||||||
|  |         public string? XForwardedFor { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the service identifier.  | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example><see cref="Guid.NewGuid()"/></example> | ||||||
|  |         [DisplayName(DisplayNames.ServiceId)] | ||||||
|  |         [JsonPropertyName(DisplayNames.ServiceId)] | ||||||
|  |         public string? ServiceId { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the request identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example><see cref="Guid.NewGuid()"/></example> | ||||||
|  |         [DisplayName(DisplayNames.RequestId)] | ||||||
|  |         [JsonPropertyName(DisplayNames.RequestId)] | ||||||
|  |         public string? RequestId { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the keyVaultProvider identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example><see cref="Guid.NewGuid()"/></example> | ||||||
|  |         [DisplayName(DisplayNames.ClientId)] | ||||||
|  |         [JsonPropertyName(DisplayNames.ClientId)] | ||||||
|  |         public string? ClientId { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the keyVaultProvider identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>keyVaultProviderRequest</example> | ||||||
|  |         [DisplayName(DisplayNames.Operation)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Operation)] | ||||||
|  |         public LogOperation Operation { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the user name. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>keyVaultProviderRequest</example> | ||||||
|  |         [DisplayName(DisplayNames.User)] | ||||||
|  |         [JsonPropertyName(DisplayNames.User)] | ||||||
|  |         public string? User { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets user's email. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>keyVaultProviderRequest</example> | ||||||
|  |         [DisplayName(DisplayNames.Email)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Email)] | ||||||
|  |         public string? Email { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the user identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>keyVaultProviderRequest</example> | ||||||
|  |         [DisplayName(DisplayNames.UserId)] | ||||||
|  |         [JsonPropertyName(DisplayNames.UserId)] | ||||||
|  |         public string? UserId { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the message. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>A custom log message.</example> | ||||||
|  |         [DisplayName(DisplayNames.Message)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Message)] | ||||||
|  |         public TMessage? Message { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								Core.Blueprint.Logging/Adapters/LogOperation.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								Core.Blueprint.Logging/Adapters/LogOperation.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="LogOperation.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using System.Runtime.Serialization; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Represents all possible values for log operation. | ||||||
|  |     /// </summary> | ||||||
|  |     [DataContract] | ||||||
|  |     public enum LogOperation | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The keyVaultProvider request log operation type. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.ClientRequest)] | ||||||
|  |         [JsonPropertyName(DisplayNames.ClientRequest)] | ||||||
|  |         ClientRequest = 0, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The keyVaultProvider response log operation type. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.ClientResponse)] | ||||||
|  |         ClientResponse = 1, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The external request log operation type. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.ExternalRequest)] | ||||||
|  |         ExternalRequest = 2, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The external response log operation type. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.ExternalResponse)] | ||||||
|  |         ExternalResponse = 3, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The error log operation type. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.Error)] | ||||||
|  |         Error = 4, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The info log operation type. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.Info)] | ||||||
|  |         Info = 5, | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								Core.Blueprint.Logging/Adapters/LogSeverity.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Core.Blueprint.Logging/Adapters/LogSeverity.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="LogSeverity.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using System.Runtime.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Represents all possible values for log severity. | ||||||
|  |     /// </summary> | ||||||
|  |     [DataContract] | ||||||
|  |     public enum LogSeverity | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The information severity level. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.Information)] | ||||||
|  |         Info = 0, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The warning severity level. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.Warning)] | ||||||
|  |         Warn = 1, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The error severity level. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.Error)] | ||||||
|  |         Error = 2, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The fatal severity level. | ||||||
|  |         /// </summary> | ||||||
|  |         [EnumMember(Value = DisplayNames.Fatal)] | ||||||
|  |         Fatal = 3, | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								Core.Blueprint.Logging/Adapters/LogTarget.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Core.Blueprint.Logging/Adapters/LogTarget.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="LogTarget.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The service logger target object. | ||||||
|  |     /// </summary> | ||||||
|  |     public class LogTarget | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the log target method. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>GET</example> | ||||||
|  |         [DisplayName(DisplayNames.Method)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Method)] | ||||||
|  |         public string? Method { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the log target host. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>GET</example> | ||||||
|  |         [DisplayName(DisplayNames.Host)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Host)] | ||||||
|  |         public string? Host { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the log target route. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <example>GET</example> | ||||||
|  |         [DisplayName(DisplayNames.Route)] | ||||||
|  |         [JsonPropertyName(DisplayNames.Route)] | ||||||
|  |         public string? Route { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								Core.Blueprint.Logging/Adapters/ServiceSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Core.Blueprint.Logging/Adapters/ServiceSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="ServiceSettings.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The service settings. | ||||||
|  |     /// </summary> | ||||||
|  |     public class ServiceSettings | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the service identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         public string? ApplicationName { get; set; } | ||||||
|  |         public string? LayerName { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										70
									
								
								Core.Blueprint.Logging/Configuration/Registerblueprint.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								Core.Blueprint.Logging/Configuration/Registerblueprint.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | using Microsoft.AspNetCore.Builder; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  | using Serilog; | ||||||
|  | using Serilog.Events; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging.Configuration | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Provides extension methods for configuring logging in the application. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class Registerblueprint | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Registers logging services in the application. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param> | ||||||
|  |         /// <param name="builder">The <see cref="WebApplicationBuilder"/> for accessing configuration and application setup.</param> | ||||||
|  |         /// <returns>The updated <see cref="IServiceCollection"/>.</returns> | ||||||
|  |         public static IServiceCollection AddLogs(this IServiceCollection services, WebApplicationBuilder builder) | ||||||
|  |         { | ||||||
|  |             Log.Logger = new LoggerConfiguration() | ||||||
|  |                .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information) | ||||||
|  |                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) | ||||||
|  |                .CreateLogger(); | ||||||
|  |  | ||||||
|  |             builder.Host.UseSerilog(Log.Logger); | ||||||
|  |  | ||||||
|  |             services.AddScoped<ILoggerProvider, LoggerProvider>(); | ||||||
|  |  | ||||||
|  |             return services; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Configures middleware for logging and error handling in the application. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="app">The <see cref="IApplicationBuilder"/> used to configure the middleware pipeline.</param> | ||||||
|  |         /// <param name="serviceSettings">The service settings required by the middleware.</param> | ||||||
|  |         public static void UseLogging(this IApplicationBuilder app, IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |             var serviceSettings = new ServiceSettings(); | ||||||
|  |             configuration.GetSection(nameof(ServiceSettings)).Bind(serviceSettings); | ||||||
|  |  | ||||||
|  |             app.UseCustomHttpLogging(serviceSettings); | ||||||
|  |             app.UseHttpExceptionHandler(serviceSettings); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Adds middleware to handle HTTP exceptions globally. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param> | ||||||
|  |         /// <param name="settings">The settings used by the exception handler middleware.</param> | ||||||
|  |         /// <returns>The updated <see cref="IApplicationBuilder"/>.</returns> | ||||||
|  |         public static IApplicationBuilder UseHttpExceptionHandler(this IApplicationBuilder builder, ServiceSettings settings) | ||||||
|  |         { | ||||||
|  |             return builder.UseMiddleware<HttpErrorMiddleware>(settings); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Adds custom HTTP logging middleware to the application pipeline. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param> | ||||||
|  |         /// <param name="settings">The settings used by the logging middleware.</param> | ||||||
|  |         /// <returns>The updated <see cref="IApplicationBuilder"/>.</returns> | ||||||
|  |         public static IApplicationBuilder UseCustomHttpLogging(this IApplicationBuilder builder, ServiceSettings settings) | ||||||
|  |         { | ||||||
|  |             return builder.UseMiddleware<HttpLoggingMiddleware>(settings); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								Core.Blueprint.Logging/Constants/Claims.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Core.Blueprint.Logging/Constants/Claims.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="Claims.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Constants for claims used in JWT tokens. | ||||||
|  |     /// </summary> | ||||||
|  |     public class Claims | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Claim name for user's name. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Name = "name"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Claim name for user's name. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Email = "email"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Claim name for user's ID. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Id = "id"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Claim name for user's role ID. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Rol = "rol"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Claim name for user's companies. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Companies = "companies"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Claim name for user's projects. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Projects = "projects"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Claim name for user's surveys. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Surveys = "surveys"; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										397
									
								
								Core.Blueprint.Logging/Constants/DisplayNames.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								Core.Blueprint.Logging/Constants/DisplayNames.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,397 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="DisplayNames.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Constants of the display names for this service. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class DisplayNames | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The active patameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Active = "active"; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The keyVaultProvider identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ClientId = "keyVaultProviderId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The keyVaultProvider request parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ClientRequest = "keyVaultProviderRequest"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The keyVaultProvider response parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ClientResponse = "keyVaultProviderResponse"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The creation date. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string CreationDate = "creationDate"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The content parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Content = "content"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The delete parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Delete = "delete"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The description parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Description = "description"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The detail parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Detail = "detail"; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The environment parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Environment = "environment"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The error log severity level parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Error = "error"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The error code parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ErrorCode = "errorCode"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The external request parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ExternalRequest = "externalRequest"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The external response parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ExternalResponse = "externalResponse"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The fatal log severity level parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Fatal = "fatal"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The host parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Host = "host"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Id = "id"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The inactive parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Inactive = "inactive"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The info log severity level parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Info = "info"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The information log severity level parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Information = "information"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The media parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Media = "media"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The media type parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string MediaType = "mediaType"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The media use type parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string MediaUseType = "mediaUseType"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Th message parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Message = "message"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The method parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Method = "method"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The monday parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Monday = "monday"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The MXN parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string MXN = "MXN"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The name parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Name = "name"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The next page parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string NextPage = "nextPage"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The nick name parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string NickName = "nickName"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The note parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Note = "note"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The not so affordable parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string NotSoAffordable = "notSoAffordable"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The object status parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ObjectStatus = "objectStatus"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The opening time parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string OpeningTime = "openingTime"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The operation days parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string OperationDays = "operationDays"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The page parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Page = "page"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The page count parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PageCount = "pageCount"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The page metadata parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PageMetadata = "pageMetadata"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The page size parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PageSize = "pageSize"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The parent identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ParentId = "ParentId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The pet ticket price parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PetTicketPrice = "petTicketPrice"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The place parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Place = "place"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The place type parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PlaceType = "placeType"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The previous page parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PreviousPage = "previousPage"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The provider identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ProviderId = "providerId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The provider type identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ProviderTypeId = "providerTypeId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The request identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string RequestId = "requestId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The RNT identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string RntId = "rntId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The route parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Route = "route"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The operation parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Operation = "operation"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The other ticket price parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string OtherTicketPrice = "otherTicketPrice"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The path parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Path = "path"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The saturday parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Saturday = "saturday"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The secondary parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Secondary = "secondary"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The service health parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ServiceHealth = "serviceHealth"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The service identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ServiceId = "serviceId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The severity parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Severity = "severity"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The state identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string StateId = "stateId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The region identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string StateProvinceRegionId = "regionId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The sunday parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Sunday = "sunday"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The tax identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string TaxId = "taxId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The target parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Target = "target"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The thursday parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Thursday = "thursday"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The timestamp parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Timestamp = "timestamp"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The total items parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string TotalItems = "totalItems"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the transaction identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string TransactionId = "transactionId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The tuesday parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Tuesday = "tuesday"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The URI parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Uri = "uri"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The update parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Update = "update"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The x-forwarded-for header parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string XForwardedFor = "xForwardedFor"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The x-forwarded-for header parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string XForwardedForHeader = "X-Forwarded-For"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The final currency identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string CurrencyId = "currencyId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The user identifier parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string UserId = "userId"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The user  parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string User = "user"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The warning log severity level. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Warning = "warning"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The email  parameter. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Email = "email"; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								Core.Blueprint.Logging/Constants/EnvironmentVariables.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Core.Blueprint.Logging/Constants/EnvironmentVariables.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="EnvironmentVariables.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Constants of the environment variables for this service. | ||||||
|  |     /// </summary>   | ||||||
|  |     public static class EnvironmentVariables | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The stage environment vriable. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Stage = "ASPNETCORE_ENVIRONMENT"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										79
									
								
								Core.Blueprint.Logging/Constants/ErrorCodes.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								Core.Blueprint.Logging/Constants/ErrorCodes.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="ErrorCodes.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Constants for the error codes. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class ErrorCodes | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The generic entities not found error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string EntitiesNotFound = "{0}EntitiesNotFound"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The entity already exsits error message. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string EntityAlreadyExists = "{0}EntityAlreadyExists"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The generic entity not found error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string EntityNotFound = "{0}EntityNotFound"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The generic not supported error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string EntityNotSupported = "{0}NotSupported"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The internal server error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string InternalServerError = "InternalServerError"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The invalid parameters in mapper error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string InvalidParametersMapper = "InvalidParametersMapper"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The page size invalid value error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PageSizeInvalidValue = "PageSizeInvalidValue"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The page ot of range error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PageOutOfRange = "PageOutOfRange"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The property does not match the regular expresion error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PropertyDoesNotMatchRegex = "{0}PropertyDoesNotMatchRegex"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The property is required error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PropertyIsRequired = "{0}PropertyIsRequired"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The property length invalid error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PropertyLengthInvalid = "{0}PropertyLengthInvalid"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The property must be in range error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string PropertyMustBeInRange = "{0}PropertyMustBeInRange"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The route not found error code. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string RouteNotFound = "RouteNotFound"; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								Core.Blueprint.Logging/Constants/Headers.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Core.Blueprint.Logging/Constants/Headers.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     public static class Headers | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The authorization header. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Authorization = "Authorization"; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										148
									
								
								Core.Blueprint.Logging/Constants/MimeTypes.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								Core.Blueprint.Logging/Constants/MimeTypes.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="MimeTypes.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using System.Globalization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Constants for the mime types. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class MimeTypes | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The service application/json mime type. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ApplicationJson = "application/json"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The application/pdf mime type. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ApplicationPdf = "application/pdf"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The end index. | ||||||
|  |         /// </summary> | ||||||
|  |         public const int EndIndex = 5; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The JPEG extension. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ExtensionGif = "gif"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The JPEG extension. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ExtensionJpeg = "jpeg"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The PNG extension. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ExtensionPng = "png"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The SVG extension. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ExtensionSvg = "svg"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The image/gif mime type. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ImageGif = "image/gif"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The image/jpeg mime type. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ImageJpeg = "image/jpeg"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The image/png mime type. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ImagePng = "image/png"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The image/svg+xml mime type. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ImageSvg = "image/svg+xml"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The identifier GIF. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string IdentifierGif = "R0LGO"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The identifier PNG. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string IdentifierJpeg = "/9J/4"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The identifier PDF. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string IdentifierPdf = "JVBER"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The identifier PNG. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string IdentifierPng = "IVBOR"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The identifier SVG. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string IdentifierSvg = "PHN2Z"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The parameter name. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string ParameterName = "MimeType"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The start index. | ||||||
|  |         /// </summary> | ||||||
|  |         public const int StartIndex = 0; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The mime type dictionary. | ||||||
|  |         /// </summary> | ||||||
|  |         public static readonly Dictionary<string, string> Dictionary = new Dictionary<string, string> | ||||||
|  |         { | ||||||
|  |             { IdentifierJpeg, ImageJpeg }, | ||||||
|  |             { IdentifierPng, ImagePng }, | ||||||
|  |             { IdentifierGif, ImageGif }, | ||||||
|  |             { IdentifierSvg, ImageSvg }, | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The mime type dictionary. | ||||||
|  |         /// </summary> | ||||||
|  |         public static readonly Dictionary<string, string> DictionaryExtension = new Dictionary<string, string> | ||||||
|  |         { | ||||||
|  |             { IdentifierJpeg, ExtensionJpeg }, | ||||||
|  |             { IdentifierPng, ExtensionPng }, | ||||||
|  |             { IdentifierGif, ExtensionGif }, | ||||||
|  |             { IdentifierSvg, ExtensionSvg }, | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets the mime type. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="content">The cpntent with mime type identifier, substring 0, 5 from content.</param> | ||||||
|  |         /// <returns>A <see cref="string"/> representing the value.</returns> | ||||||
|  |         public static string GetMimeType(this string content) | ||||||
|  |         { | ||||||
|  |             return Dictionary.FirstOrDefault(_ => _.Key == content[..EndIndex].ToUpper(CultureInfo.InvariantCulture)).Value; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets the extension. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="content">The mime type identifier, substring 0, 5 from content.</param> | ||||||
|  |         /// <returns>A <see cref="string"/> representing the value.</returns> | ||||||
|  |         public static string GetExtension(this string content) | ||||||
|  |         { | ||||||
|  |             return DictionaryExtension.FirstOrDefault(_ => _.Key == content[..EndIndex].ToUpper(CultureInfo.InvariantCulture)).Value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								Core.Blueprint.Logging/Constants/Responses.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Core.Blueprint.Logging/Constants/Responses.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="Responses.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Constants of the responses for this service. | ||||||
|  |     /// </summary>   | ||||||
|  |     public static class Responses | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The health response. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string HealthyService = "healthy"; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The route does not exist response. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string RouteDoesNotExist = "The specified route '{0}' does not exist for method '{1}' in this service."; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The target response. | ||||||
|  |         /// </summary> | ||||||
|  |         public const string Target = "{0}|{1}://{2}{3}"; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								Core.Blueprint.Logging/Contracts/ILoggerProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Core.Blueprint.Logging/Contracts/ILoggerProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     public interface ILoggerProvider | ||||||
|  |     { | ||||||
|  |         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); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								Core.Blueprint.Logging/Core.Blueprint.Logging.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Core.Blueprint.Logging/Core.Blueprint.Logging.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  | 	 | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <TargetFramework>net8.0</TargetFramework> | ||||||
|  |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|  |     <Nullable>enable</Nullable> | ||||||
|  |   </PropertyGroup> | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> | ||||||
|  | 		<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" /> | ||||||
|  | 		<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" /> | ||||||
|  | 		<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" /> | ||||||
|  | 		<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  | </Project> | ||||||
							
								
								
									
										92
									
								
								Core.Blueprint.Logging/Middleware/HttpErrorMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								Core.Blueprint.Logging/Middleware/HttpErrorMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="HttpErrorMiddleware.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Http; | ||||||
|  | using Serilog; | ||||||
|  | using System.Text.Json; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Handles HTTP logging. | ||||||
|  |     /// </summary> | ||||||
|  |     public class HttpErrorMiddleware | ||||||
|  |     { | ||||||
|  |         private readonly ILogger logger; | ||||||
|  |         private readonly RequestDelegate requestProcess; | ||||||
|  |         public readonly ServiceSettings settings; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a new instrance of <see cref="HttpErrorMiddleware"/>. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="logger">The logger representig an instance of <see cref="ILogger"/>.</param> | ||||||
|  |         /// <param name="requestProcess">The request delegate process.</param> | ||||||
|  |         public HttpErrorMiddleware(ILogger logger, RequestDelegate requestProcess, ServiceSettings settings) | ||||||
|  |         { | ||||||
|  |             this.logger = logger; | ||||||
|  |             this.requestProcess = requestProcess; | ||||||
|  |             this.settings = settings; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Invoke method. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         public async Task Invoke(HttpContext context) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 await requestProcess(context).ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |             catch (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); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Handles error responses. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="message">The error message.</param> | ||||||
|  |         /// <param name="errorCode">The error code.</param> | ||||||
|  |         /// <param name="statusCode">The HTTP status code.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         private async Task HandleErrorResponse(HttpContext context, string? message, string? errorCode, int statusCode) | ||||||
|  |         { | ||||||
|  |             var errorMessage = new HttpError( | ||||||
|  |                 message, | ||||||
|  |                 errorCode, | ||||||
|  |                 string.Format( | ||||||
|  |                     Responses.Target, | ||||||
|  |                     context.Request.Method, | ||||||
|  |                     context.Request.Scheme, | ||||||
|  |                     context.Request.Host.Host, | ||||||
|  |                     context.Request.Path)); | ||||||
|  |  | ||||||
|  |             logger.LogError<HttpError>(context, errorMessage, $"{settings.ApplicationName}-{settings.LayerName}"); | ||||||
|  |  | ||||||
|  |             context.Response.ContentType = MimeTypes.ApplicationJson; | ||||||
|  |             context.Response.StatusCode = statusCode; | ||||||
|  |  | ||||||
|  |             await context.Response.WriteAsync(JsonSerializer.Serialize(errorMessage)).ConfigureAwait(false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										237
									
								
								Core.Blueprint.Logging/Middleware/HttpLogger.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								Core.Blueprint.Logging/Middleware/HttpLogger.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,237 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="HttpLogger.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Http; | ||||||
|  | using Microsoft.IO; | ||||||
|  | using Serilog; | ||||||
|  | using System.IdentityModel.Tokens.Jwt; | ||||||
|  | using System.Text.Json; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Handles all logging scenarios. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class HttpLogger | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The JSON serializer options for logging methods. | ||||||
|  |         /// </summary> | ||||||
|  |         public static JsonSerializerOptions serializerOptions = new JsonSerializerOptions | ||||||
|  |         { | ||||||
|  |             DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, | ||||||
|  |             Converters = { | ||||||
|  |                 new JsonStringEnumConverter( JsonNamingPolicy.CamelCase), | ||||||
|  |             }, | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs an error message. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="TMessage">The generic message parameter.</typeparam> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="message">The message.</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         public static void LogError<TMessage>(this ILogger logger, HttpContext context, TMessage message, string? serviceId) | ||||||
|  |         { | ||||||
|  |             var logMessage = CreateErrorLog(context, message, serviceId); | ||||||
|  |             logger.Error(logMessage); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs an information message. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="TMessage">The generic message parameter.</typeparam> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="message">The message.</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         public static void LogInfo<TMessage>(this ILogger logger, HttpContext context, TMessage message, string? serviceId) | ||||||
|  |         { | ||||||
|  |             var logMessage = CreateInfoLog(context, message, serviceId); | ||||||
|  |             logger.Information(logMessage); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs an incoming HTTP request. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="logger">The logger.</param> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="recyclableMemoryStreamManager">The recyclable mmory stream manager.</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         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; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs an outcome HTTP response. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="logger">The logger.</param> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="recyclableMemoryStreamManager">The recyclable mmory stream manager.</param> | ||||||
|  |         /// <param name="requestProcess">The request delegate process.</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         ///  | ||||||
|  |         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); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates an error log. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="TMessage">The generic message.</typeparam> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="message">The error message.</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         /// <returns>A <see cref="string"/> representig the error log.</returns> | ||||||
|  |         private static string CreateErrorLog<TMessage>(HttpContext context, TMessage message, string? serviceId) | ||||||
|  |             => CreateLog(context, LogSeverity.Error, LogOperation.Error, message, serviceId); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates an info log. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="TMessage">The generic message.</typeparam> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="message">The info message.</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         /// <returns>A <see cref="string"/> representig the info log.</returns> | ||||||
|  |         private static string CreateInfoLog<TMessage>(HttpContext context, TMessage message, string? serviceId) | ||||||
|  |             => CreateLog(context, LogSeverity.Info, LogOperation.Info, message, serviceId); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a request log. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="requestBody">The request body.</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         /// <returns>A <see cref="string"/> representig the request log.</returns> | ||||||
|  |         private static string CreateRequestLog(HttpContext context, string? requestBody, string? serviceId) | ||||||
|  |             => CreateLog(context, LogSeverity.Info, LogOperation.ClientRequest, requestBody, serviceId); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a response log. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="responseBody">The response body.</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         /// <returns>A <see cref="string"/> representig the response log.</returns> | ||||||
|  |         private static string CreateResponseLog(HttpContext context, string? responseBody, string? serviceId) | ||||||
|  |             => CreateLog(context, LogSeverity.Info, LogOperation.ClientResponse, responseBody, serviceId); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a generic log. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <param name="severity">The log severity.</param> | ||||||
|  |         /// <param name="operation">The log operation.</param> | ||||||
|  |         /// <param name="message">The log message</param> | ||||||
|  |         /// <param name="serviceId">The service identifier.</param> | ||||||
|  |         /// <returns>A <see cref="string"/> representing a generic log.</returns> | ||||||
|  |         private static string CreateLog<TMessage>( | ||||||
|  |             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<TMessage> | ||||||
|  |             { | ||||||
|  |                 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", ""); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Reads the stream. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="stream">The stream to be read.</param> | ||||||
|  |         /// <returns>A <see cref="string?"/> representig the request body.</returns> | ||||||
|  |         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; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										69
									
								
								Core.Blueprint.Logging/Middleware/HttpLoggingMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								Core.Blueprint.Logging/Middleware/HttpLoggingMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | // *********************************************************************** | ||||||
|  | // <copyright file="HttpLoggingMiddleware.cs"> | ||||||
|  | //     Heath | ||||||
|  | // </copyright> | ||||||
|  | // *********************************************************************** | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Http; | ||||||
|  | using Microsoft.IO; | ||||||
|  | using Serilog; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Handles HTTP logging. | ||||||
|  |     /// </summary> | ||||||
|  |     public class HttpLoggingMiddleware | ||||||
|  |     { | ||||||
|  |         private readonly ILogger logger; | ||||||
|  |         private readonly RequestDelegate requestProcess; | ||||||
|  |         private readonly ServiceSettings settings; | ||||||
|  |         private readonly RecyclableMemoryStreamManager recyclableMemoryStreamManager; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates a new instrance of <see cref="HttpLoggingMiddleware"/>. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="requestProcess">The request delegate process.</param> | ||||||
|  |         /// <param name="logger">The logger representig an instance of <see cref="ILogger"/>.</param> | ||||||
|  |         /// <param name="settings">The service settings.</param> | ||||||
|  |         public HttpLoggingMiddleware(RequestDelegate requestProcess, ILogger logger, ServiceSettings settings) | ||||||
|  |         { | ||||||
|  |             this.logger = logger; | ||||||
|  |             this.requestProcess = requestProcess; | ||||||
|  |             this.settings = settings; | ||||||
|  |             recyclableMemoryStreamManager = new RecyclableMemoryStreamManager(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Invoke method. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         public async Task Invoke(HttpContext context) | ||||||
|  |         { | ||||||
|  |             await LogRequest(context); | ||||||
|  |             await LogResponse(context); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs an incoming HTTP request. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         private async Task LogRequest(HttpContext context) | ||||||
|  |         { | ||||||
|  |             await logger.LogRequest(context, recyclableMemoryStreamManager, $"{settings.ApplicationName}-{settings.LayerName}"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs an outcome HTTP response. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The HTTP context.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         private async Task LogResponse(HttpContext context) | ||||||
|  |         { | ||||||
|  |             await logger.LogResponse(context, recyclableMemoryStreamManager, requestProcess, $"{settings.ApplicationName}-{settings.LayerName}"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										89
									
								
								Core.Blueprint.Logging/Provider/LoggerProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								Core.Blueprint.Logging/Provider/LoggerProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | |||||||
|  | namespace Core.Blueprint.Logging | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Provides logging functionalities using Serilog. | ||||||
|  |     /// </summary> | ||||||
|  |     public class LoggerProvider : ILoggerProvider | ||||||
|  |     { | ||||||
|  |         private readonly Serilog.ILogger logger; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes a new instance of the <see cref="LoggerProvider"/> class. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="logger">The Serilog logger instance.</param> | ||||||
|  |         public LoggerProvider(Serilog.ILogger logger) | ||||||
|  |         { | ||||||
|  |             this.logger = logger; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs an informational message for a specific service. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="service">The name of the service.</param> | ||||||
|  |         /// <param name="args">Additional arguments to include in the log.</param> | ||||||
|  |         public void LogInformation(string service, params object[] args) | ||||||
|  |         { | ||||||
|  |             logger.Information("Starting operation in {service} service", service, args); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs a message indicating the start of an operation in a specific service. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="service">The name of the service.</param> | ||||||
|  |         /// <param name="args">Additional parameters associated with the operation.</param> | ||||||
|  |         public void LogOperationStarted(string service, params object[] args) | ||||||
|  |         { | ||||||
|  |             logger.Information("Starting operation in {Service} service with parameters: {@Args}", service, args); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs a message indicating the completion of an operation in a specific service. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="service">The name of the service.</param> | ||||||
|  |         /// <param name="args">Additional parameters associated with the operation.</param> | ||||||
|  |         public void LogOperationFinished(string service, params object[] args) | ||||||
|  |         { | ||||||
|  |             logger.Information("Finishing operation in {Service} service with parameters: {@Args}", service, args); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs a general informational message. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="message">The message to log.</param> | ||||||
|  |         public void LogInformation(string message) | ||||||
|  |         { | ||||||
|  |             logger.Information(message); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs a warning message with additional context. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="message">The warning message to log.</param> | ||||||
|  |         /// <param name="args">Additional arguments to include in the log.</param> | ||||||
|  |         public void LogWarning(string message, params object[] args) | ||||||
|  |         { | ||||||
|  |             logger.Warning(message, args); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs an error that occurred in a specific service. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="service">The name of the service.</param> | ||||||
|  |         /// <param name="args">Additional details about the error.</param> | ||||||
|  |         public void LogError(string service, params object[] args) | ||||||
|  |         { | ||||||
|  |             logger.Error("An error occurred in `{service}` Exception: {@Args}", service, args); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Logs a critical error with an exception, message, and additional context. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="exception">The exception associated with the critical error.</param> | ||||||
|  |         /// <param name="message">The critical error message.</param> | ||||||
|  |         /// <param name="args">Additional arguments to include in the log.</param> | ||||||
|  |         public void LogCritical(Exception exception, string message, params object[] args) | ||||||
|  |         { | ||||||
|  |             logger.Fatal(exception, message, args); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								Core.Blueprint.Mongo/Attributes/CollectionAttributeName.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Core.Blueprint.Mongo/Attributes/CollectionAttributeName.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The <see cref="CollectionAttributeName"/> attribute is used to specify the name of a MongoDB collection  | ||||||
|  |     /// that a class should be mapped to. This attribute can be applied to classes that represent MongoDB entities. | ||||||
|  |     /// </summary> | ||||||
|  |     [AttributeUsage(AttributeTargets.Class)]  // This attribute can only be applied to classes. | ||||||
|  |     public class CollectionAttributeName : Attribute | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the name of the MongoDB collection that the class is mapped to. | ||||||
|  |         /// </summary> | ||||||
|  |         public string Name { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes a new instance of the <see cref="CollectionAttributeName"/> class with the specified collection name. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="name">The name of the MongoDB collection that the class should be mapped to.</param> | ||||||
|  |         public CollectionAttributeName(string name) | ||||||
|  |         { | ||||||
|  |             Name = name ?? throw new ArgumentNullException(nameof(name), "Collection name cannot be null."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,121 @@ | |||||||
|  | using Azure.Core; | ||||||
|  | using Azure.Identity; | ||||||
|  | using MongoDB.Driver.Authentication.Oidc; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Mongo.Configuration | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The <see cref="HeathIdentityProvider"/> class is responsible for acquiring an OpenID Connect (OIDC)  | ||||||
|  |     /// access token for MongoDB authentication using Azure Identity and Managed Identity credentials. | ||||||
|  |     /// </summary> | ||||||
|  |     public class HeathIdentityProvider : IOidcCallback | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// The audience (resource identifier) for which the OIDC token is being requested. | ||||||
|  |         /// </summary> | ||||||
|  |         private readonly string _audience; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// The environment in which the application is running (e.g., Development, Production). | ||||||
|  |         /// </summary> | ||||||
|  |         private readonly string _environment; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes a new instance of the <see cref="HeathIdentityProvider"/> class with the specified audience. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="audience">The audience (resource identifier) for which the OIDC token is being requested.</param> | ||||||
|  |         public HeathIdentityProvider(string audience) | ||||||
|  |         { | ||||||
|  |             _audience = audience; | ||||||
|  |             _environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Synchronously retrieves the OIDC access token to authenticate to MongoDB. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="parameters">The callback parameters provided for the OIDC request.</param> | ||||||
|  |         /// <param name="cancellationToken">A token to cancel the operation.</param> | ||||||
|  |         /// <returns>An OIDC access token to authenticate to MongoDB.</returns> | ||||||
|  |         /// <exception cref="Exception">Thrown if an error occurs during the token acquisition process.</exception> | ||||||
|  |         public OidcAccessToken GetOidcAccessToken(OidcCallbackParameters parameters, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 AccessToken token; | ||||||
|  |  | ||||||
|  |                 TokenRequestContext tokenRequestContext = | ||||||
|  |                     new TokenRequestContext( | ||||||
|  |                         new[] { _audience } | ||||||
|  |                     ); | ||||||
|  |  | ||||||
|  |                 if (_environment == "Local") | ||||||
|  |                 { | ||||||
|  |                     token = | ||||||
|  |                     new ChainedTokenCredential( | ||||||
|  |                         new ManagedIdentityCredential(), | ||||||
|  |                         new VisualStudioCredential(), | ||||||
|  |                         new VisualStudioCodeCredential(), | ||||||
|  |                         new SharedTokenCacheCredential() | ||||||
|  |                     ) | ||||||
|  |                     .GetToken(tokenRequestContext); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     token = | ||||||
|  |                     new ManagedIdentityCredential() | ||||||
|  |                     .GetToken(tokenRequestContext); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 return new OidcAccessToken(token.Token, expiresIn: null); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 throw new Exception($"An error occurred while trying to get the OIDC token to connect to the database, ERROR: {ex.Message}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously retrieves the OIDC access token to authenticate to MongoDB. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="parameters">The callback parameters provided for the OIDC request.</param> | ||||||
|  |         /// <param name="cancellationToken">A token to cancel the operation.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation, with an OIDC access token as the result.</returns> | ||||||
|  |         /// <exception cref="Exception">Thrown if an error occurs during the token acquisition process.</exception> | ||||||
|  |         public async Task<OidcAccessToken> GetOidcAccessTokenAsync(OidcCallbackParameters parameters, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 TokenRequestContext tokenRequestContext = | ||||||
|  |                     new TokenRequestContext( | ||||||
|  |                         new[] { _audience } | ||||||
|  |                     ); | ||||||
|  |  | ||||||
|  |                 AccessToken token; | ||||||
|  |  | ||||||
|  |                 if (_environment == "Local") | ||||||
|  |                 { | ||||||
|  |                     token = await new ChainedTokenCredential( | ||||||
|  |                         new ManagedIdentityCredential(), | ||||||
|  |                         new VisualStudioCredential(), | ||||||
|  |                         new VisualStudioCodeCredential(), | ||||||
|  |                         new SharedTokenCacheCredential() | ||||||
|  |                     ) | ||||||
|  |                     .GetTokenAsync(tokenRequestContext, cancellationToken) | ||||||
|  |                     .ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     token = await new ManagedIdentityCredential() | ||||||
|  |                         .GetTokenAsync(tokenRequestContext, cancellationToken) | ||||||
|  |                         .ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 return new OidcAccessToken(token.Token, expiresIn: null); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 throw new Exception($"An error occurred while trying to get the OIDC token to connect to the database, ERROR: {ex.Message}"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								Core.Blueprint.Mongo/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								Core.Blueprint.Mongo/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | using Core.Blueprint.Mongo; | ||||||
|  | using Core.Blueprint.Mongo.Configuration; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  | using Microsoft.Extensions.Options; | ||||||
|  | using MongoDB.Driver; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.DAL.Mongo.Configuration | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The <see cref="RegisterBlueprint"/> class contains extension methods for registering the MongoDB context and configuration settings  | ||||||
|  |     /// to the <see cref="IServiceCollection"/> in the dependency injection container. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class RegisterBlueprint | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Adds the MongoDB layer services to the <see cref="IServiceCollection"/>. | ||||||
|  |         /// Registers the MongoDB context and configuration settings for MongoDB connection, database name, and audience. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="services">The <see cref="IServiceCollection"/> to which the services will be added.</param> | ||||||
|  |         /// <param name="configuration">The <see cref="IConfiguration"/> used to load MongoDB settings.</param> | ||||||
|  |         /// <returns>The updated <see cref="IServiceCollection"/> with MongoDB services registered.</returns> | ||||||
|  |         public static IServiceCollection AddMongoLayer(this IServiceCollection services, IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |             var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty; | ||||||
|  |  | ||||||
|  |             services.AddSingleton<IMongoContext, MongoContext>(); | ||||||
|  |  | ||||||
|  |             var ConnectionString = configuration.GetSection("ConnectionStrings:MongoDB").Value ?? string.Empty; | ||||||
|  |             var Databasename = configuration.GetSection("MongoDb:DatabaseName").Value ?? string.Empty; | ||||||
|  |             var Audience = (environment == "Local") | ||||||
|  |                 ? configuration.GetSection("MongoDb:LocalAudience").Value | ||||||
|  |                 : configuration.GetSection("MongoDb:Audience").Value; | ||||||
|  |  | ||||||
|  |             if (string.IsNullOrEmpty(ConnectionString) || string.IsNullOrEmpty(Databasename) || string.IsNullOrEmpty(Audience)) | ||||||
|  |                 throw new InvalidOperationException("Mongo connection is not configured correctly."); | ||||||
|  |  | ||||||
|  |             services.Configure<MongoDbSettings>(options => | ||||||
|  |             { | ||||||
|  |                 options.ConnectionString = ConnectionString; | ||||||
|  |                 options.Databasename = Databasename; | ||||||
|  |                 options.Audience = Audience; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             services.AddSingleton<IMongoClient>(serviceProvider => | ||||||
|  |             { | ||||||
|  |                 var settings = serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value; | ||||||
|  |                 var mongoClientSettings = MongoClientSettings.FromConnectionString(settings.ConnectionString); | ||||||
|  |                 mongoClientSettings.Credential = MongoCredential.CreateOidcCredential(new HeathIdentityProvider(settings.Audience)); | ||||||
|  |                 return new MongoClient(mongoClientSettings); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             services.AddSingleton<IMongoDatabase>(serviceProvider => | ||||||
|  |             { | ||||||
|  |                 var settings = serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value; | ||||||
|  |                 var client = serviceProvider.GetRequiredService<IMongoClient>(); | ||||||
|  |                 return client.GetDatabase(settings.Databasename); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             services.AddSingleton<IMongoDbSettings>(serviceProvider => serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value); | ||||||
|  |  | ||||||
|  |             return services; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								Core.Blueprint.Mongo/Context/MongoContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								Core.Blueprint.Mongo/Context/MongoContext.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The <see cref="MongoContext"/> class represents the MongoDB context that contains the connection information,  | ||||||
|  |     /// including the connection string, database name, and audience. | ||||||
|  |     /// It implements the <see cref="IMongoContext"/> interface to provide methods for accessing these values. | ||||||
|  |     /// </summary> | ||||||
|  |     public sealed class MongoContext : IMongoContext | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the connection string used to connect to the MongoDB instance. | ||||||
|  |         /// </summary> | ||||||
|  |         public string ConnectionString { get; set; } = string.Empty; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the name of the MongoDB database. | ||||||
|  |         /// </summary> | ||||||
|  |         public string Databasename { get; set; } = string.Empty; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the audience (resource identifier) used for MongoDB authentication. | ||||||
|  |         /// </summary> | ||||||
|  |         public string Audience { get; set; } = string.Empty; | ||||||
|  |  | ||||||
|  |         private readonly IConfiguration configuration; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes a new instance of the <see cref="MongoContext"/> class using the provided <see cref="IConfiguration"/>. | ||||||
|  |         /// The configuration is used to retrieve MongoDB connection settings. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="configuration">The configuration used to retrieve the MongoDB connection settings.</param> | ||||||
|  |         public MongoContext(IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |             this.configuration = configuration; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the MongoDB connection string from the configuration. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>The MongoDB connection string, or an empty string if not found.</returns> | ||||||
|  |         public string GetConnectionString() | ||||||
|  |         { | ||||||
|  |             return configuration.GetConnectionString("MongoDb:ConnectionString")?.ToString() ?? string.Empty; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the MongoDB database name from the configuration. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>The MongoDB database name, or an empty string if not found.</returns> | ||||||
|  |         public string GetDatabasename() | ||||||
|  |         { | ||||||
|  |             return configuration.GetSection("MongoDb:DatabaseName").Value ?? string.Empty; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the MongoDB audience (resource identifier) from the configuration. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>The MongoDB audience, or an empty string if not found.</returns> | ||||||
|  |         public string GetAudience() | ||||||
|  |         { | ||||||
|  |             return configuration.GetSection("MongoDb:Audience").Value ?? string.Empty; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										25
									
								
								Core.Blueprint.Mongo/Context/MongoDbSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Core.Blueprint.Mongo/Context/MongoDbSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Represents the MongoDB configuration settings, including the connection string,  | ||||||
|  |     /// database name, and audience, used for connecting and authenticating to a MongoDB instance. | ||||||
|  |     /// Implements the <see cref="IMongoDbSettings"/> interface to provide a strongly typed configuration. | ||||||
|  |     /// </summary> | ||||||
|  |     public class MongoDbSettings : IMongoDbSettings | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the connection string used to connect to the MongoDB instance. | ||||||
|  |         /// </summary> | ||||||
|  |         public string ConnectionString { get; set; } = string.Empty; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the name of the MongoDB database to connect to. | ||||||
|  |         /// </summary> | ||||||
|  |         public string Databasename { get; set; } = string.Empty; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the audience (resource identifier) used for MongoDB authentication. | ||||||
|  |         /// </summary> | ||||||
|  |         public string Audience { get; set; } = string.Empty; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										152
									
								
								Core.Blueprint.Mongo/Contracts/ICollectionRepository.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								Core.Blueprint.Mongo/Contracts/ICollectionRepository.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | |||||||
|  | using MongoDB.Driver; | ||||||
|  | using System.Linq.Expressions; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Interface for performing CRUD operations and queries on MongoDB collections. | ||||||
|  |     /// The <typeparamref name="TDocument"/> represents the type of documents in the collection, | ||||||
|  |     /// which must implement the <see cref="IDocument"/> interface. | ||||||
|  |     /// </summary> | ||||||
|  |     /// <typeparam name="TDocument">The type of document in the MongoDB collection, must implement <see cref="IDocument"/>.</typeparam> | ||||||
|  |     public interface ICollectionsRepository<TDocument> where TDocument : IDocument | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves all documents from the collection as an enumerable queryable result. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A <see cref="ValueTask"/> containing an <see cref="IEnumerable{TDocument}"/> representing the collection's documents.</returns> | ||||||
|  |         ValueTask<IEnumerable<TDocument>> AsQueryable(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Filters the documents in the collection by the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">An expression used to filter the documents based on the provided condition.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation, with a result of an <see cref="IEnumerable{TDocument}"/> of filtered documents.</returns> | ||||||
|  |         Task<IEnumerable<TDocument>> FilterBy( | ||||||
|  |             Expression<Func<TDocument, bool>> filterExpression); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Filters the documents in the collection by the provided filter expression and projects them to a different type. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="TProjected">The type to project the documents into.</typeparam> | ||||||
|  |         /// <param name="filterExpression">An expression used to filter the documents.</param> | ||||||
|  |         /// <param name="projectionExpression">An expression used to project the filtered documents into the <typeparamref name="TProjected"/> type.</param> | ||||||
|  |         /// <returns>An <see cref="IEnumerable{TProjected}"/> representing the projected documents.</returns> | ||||||
|  |         IEnumerable<TProjected> FilterBy<TProjected>( | ||||||
|  |             Expression<Func<TDocument, bool>> filterExpression, | ||||||
|  |             Expression<Func<TDocument, TProjected>> projectionExpression); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Filters documents in the collection based on the provided MongoDB filter definition. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterDefinition">A filter definition for MongoDB query.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation. The task result contains a list of documents that match the filter.</returns> | ||||||
|  |         Task<IEnumerable<TDocument>> FilterByMongoFilterAsync(FilterDefinition<TDocument> filterDefinition); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Finds a single document by the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">An expression used to filter the documents.</param> | ||||||
|  |         /// <returns>The first matching <see cref="TDocument"/> or null if no match is found.</returns> | ||||||
|  |         TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously finds a single document by the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">An expression used to filter the documents.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation, with the matching <see cref="TDocument"/> or null.</returns> | ||||||
|  |         Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Finds a document by its identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The identifier of the document.</param> | ||||||
|  |         /// <returns>The document with the provided identifier or null if not found.</returns> | ||||||
|  |         TDocument FindById(string id); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously finds a document by its identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The identifier of the document.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation, with the matching <see cref="TDocument"/> or null.</returns> | ||||||
|  |         Task<TDocument> FindByIdAsync(string id); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Inserts a single document into the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="document">The document to insert.</param> | ||||||
|  |         void InsertOne(TDocument document); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously inserts a single document into the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="document">The document to insert.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         Task InsertOneAsync(TDocument document); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Inserts multiple documents into the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="documents">The collection of documents to insert.</param> | ||||||
|  |         void InsertMany(ICollection<TDocument> documents); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously inserts multiple documents into the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="documents">The collection of documents to insert.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         Task InsertManyAsync(ICollection<TDocument> documents); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Replaces an existing document with a new one. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="document">The document to replace the existing one.</param> | ||||||
|  |         void ReplaceOne(TDocument document); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously replaces an existing document with a new one. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="document">The document to replace the existing one.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         Task ReplaceOneAsync(TDocument document); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a single document by the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">An expression used to filter the documents to delete.</param> | ||||||
|  |         void DeleteOne(Expression<Func<TDocument, bool>> filterExpression); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously deletes a single document by the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">An expression used to filter the documents to delete.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         Task<TDocument> DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a single document by its identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The identifier of the document to delete.</param> | ||||||
|  |         void DeleteById(string id); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously deletes a single document by its identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The identifier of the document to delete.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         Task DeleteByIdAsync(string id); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes multiple documents that match the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">An expression used to filter the documents to delete.</param> | ||||||
|  |         void DeleteMany(Expression<Func<TDocument, bool>> filterExpression); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously deletes multiple documents that match the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">An expression used to filter the documents to delete.</param> | ||||||
|  |         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||||
|  |         Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								Core.Blueprint.Mongo/Contracts/IDocument.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Core.Blueprint.Mongo/Contracts/IDocument.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | using Core.Blueprint.Mongo; | ||||||
|  |  | ||||||
|  | public interface IDocument | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the MongoDB ObjectId for the document. | ||||||
|  |     /// </summary> | ||||||
|  |     string _Id { get; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets a unique identifier for the document, represented as a string (GUID). | ||||||
|  |     /// </summary> | ||||||
|  |     string Id { get; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the timestamp of when the document was created. | ||||||
|  |     /// </summary> | ||||||
|  |     DateTime CreatedAt { get; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the user or system who created the document. | ||||||
|  |     /// </summary> | ||||||
|  |     string? CreatedBy { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the timestamp of when the document was last updated. | ||||||
|  |     /// </summary> | ||||||
|  |     DateTime? UpdatedAt { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the user or system who last updated the document. | ||||||
|  |     /// </summary> | ||||||
|  |     string? UpdatedBy { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the status of the document. | ||||||
|  |     /// </summary> | ||||||
|  |     StatusEnum? Status { get; set; } | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								Core.Blueprint.Mongo/Contracts/IMongoContext.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Core.Blueprint.Mongo/Contracts/IMongoContext.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Represents the context for interacting with MongoDB, providing access to connection-related information, | ||||||
|  |     /// such as the connection string, database name, and audience for authentication. | ||||||
|  |     /// </summary> | ||||||
|  |     public interface IMongoContext | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets the connection string used to connect to the MongoDB instance. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A string representing the MongoDB connection string.</returns> | ||||||
|  |         string GetConnectionString(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets the name of the MongoDB database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A string representing the MongoDB database name.</returns> | ||||||
|  |         string GetDatabasename(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets the audience (resource identifier) used for MongoDB authentication. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A string representing the MongoDB audience (typically the resource identifier for authentication).</returns> | ||||||
|  |         string GetAudience(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the MongoDB connection string used to connect to the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <value>A string representing the MongoDB connection string.</value> | ||||||
|  |         string ConnectionString { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the name of the MongoDB database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <value>A string representing the MongoDB database name.</value> | ||||||
|  |         string Databasename { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the audience (resource identifier) for MongoDB authentication. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <value>A string representing the MongoDB audience (resource identifier for authentication).</value> | ||||||
|  |         string Audience { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								Core.Blueprint.Mongo/Contracts/IMongoDbSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								Core.Blueprint.Mongo/Contracts/IMongoDbSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Represents the settings required to connect to a MongoDB instance, including the connection string,  | ||||||
|  |     /// database name, and audience used for authentication. | ||||||
|  |     /// </summary> | ||||||
|  |     public interface IMongoDbSettings | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the connection string used to connect to the MongoDB instance. | ||||||
|  |         /// The connection string includes details such as server address, port,  | ||||||
|  |         /// authentication credentials, and any additional options needed for connection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <value>A string representing the MongoDB connection string.</value> | ||||||
|  |         string ConnectionString { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the name of the MongoDB database to connect to. | ||||||
|  |         /// This value specifies which database to use within the MongoDB instance. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <value>A string representing the name of the MongoDB database.</value> | ||||||
|  |         string Databasename { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the audience (resource identifier) for MongoDB authentication. | ||||||
|  |         /// This is typically used in token-based authentication schemes (e.g., OAuth or OpenID Connect), | ||||||
|  |         /// to specify the intended recipient of an access token. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <value>A string representing the MongoDB audience (resource identifier for authentication).</value> | ||||||
|  |         string Audience { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								Core.Blueprint.Mongo/Core.Blueprint.Mongo.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Core.Blueprint.Mongo/Core.Blueprint.Mongo.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  |  | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <TargetFramework>net8.0</TargetFramework> | ||||||
|  |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|  |     <Nullable>enable</Nullable> | ||||||
|  |   </PropertyGroup> | ||||||
|  |  | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Azure.Core" Version="1.44.1" /> | ||||||
|  |     <PackageReference Include="Azure.Identity" Version="1.13.1" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Options" Version="2.1.0" /> | ||||||
|  |     <PackageReference Include="MongoDB.Bson" Version="3.0.0" /> | ||||||
|  |     <PackageReference Include="MongoDB.Driver" Version="3.0.0" /> | ||||||
|  |   </ItemGroup> | ||||||
|  |  | ||||||
|  | </Project> | ||||||
							
								
								
									
										81
									
								
								Core.Blueprint.Mongo/Entities/Document.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								Core.Blueprint.Mongo/Entities/Document.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | using Core.Blueprint.Mongo; | ||||||
|  | using MongoDB.Bson; | ||||||
|  | using MongoDB.Bson.Serialization.Attributes; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | public class Document : IDocument | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the MongoDB ObjectId for the document.  | ||||||
|  |     /// This property is automatically generated if not provided.  | ||||||
|  |     /// It is used as the primary key for the document in MongoDB. | ||||||
|  |     /// </summary> | ||||||
|  |     [BsonId] | ||||||
|  |     [BsonElement("_id")] | ||||||
|  |     [BsonRepresentation(BsonType.ObjectId)] | ||||||
|  |     [JsonPropertyName("_id")] | ||||||
|  |     public string _Id { get; init; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets a unique identifier for the document, represented as a string (GUID). | ||||||
|  |     /// This value is automatically generated if not provided and can be used as a secondary key. | ||||||
|  |     /// </summary> | ||||||
|  |     [BsonElement("id")] | ||||||
|  |     [BsonRepresentation(BsonType.String)] | ||||||
|  |     [JsonPropertyName("id")] | ||||||
|  |     public string Id { get; init; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the timestamp of when the document was created.  | ||||||
|  |     /// This value is automatically set to the current UTC time when the document is created. | ||||||
|  |     /// </summary> | ||||||
|  |     [BsonElement("createdAt")] | ||||||
|  |     [BsonRepresentation(BsonType.DateTime)] | ||||||
|  |     [JsonPropertyName("createdAt")] | ||||||
|  |     public DateTime CreatedAt { get; init; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the user or system who created the document. | ||||||
|  |     /// This field can be used for audit purposes. | ||||||
|  |     /// </summary> | ||||||
|  |     [BsonElement("createdBy")] | ||||||
|  |     [BsonRepresentation(BsonType.String)] | ||||||
|  |     [JsonPropertyName("createdBy")] | ||||||
|  |     public string? CreatedBy { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the timestamp of when the document was last updated. | ||||||
|  |     /// This value is nullable and will be set to null if the document has never been updated. | ||||||
|  |     /// </summary> | ||||||
|  |     [BsonElement("updatedAt")] | ||||||
|  |     [BsonRepresentation(BsonType.DateTime)] | ||||||
|  |     [JsonPropertyName("updatedAt")] | ||||||
|  |     public DateTime? UpdatedAt { get; set; } = null; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the user or system who last updated the document. | ||||||
|  |     /// This field can be used for audit purposes. | ||||||
|  |     /// </summary> | ||||||
|  |     [BsonElement("updatedBy")] | ||||||
|  |     [BsonRepresentation(BsonType.String)] | ||||||
|  |     [JsonPropertyName("updatedBy")] | ||||||
|  |     public string? UpdatedBy { get; set; } = null; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Gets or sets the status of the document.  | ||||||
|  |     /// The status is represented by an enum and defaults to <see cref="StatusEnum.Active"/>. | ||||||
|  |     /// </summary> | ||||||
|  |     [BsonElement("status")] | ||||||
|  |     [BsonRepresentation(BsonType.String)] | ||||||
|  |     [JsonPropertyName("status")] | ||||||
|  |     [JsonConverter(typeof(JsonStringEnumConverter))] | ||||||
|  |     public StatusEnum? Status { get; set; } | ||||||
|  |  | ||||||
|  |     public Document() | ||||||
|  |     { | ||||||
|  |         _Id = ObjectId.GenerateNewId().ToString(); | ||||||
|  |         Id = Guid.NewGuid().ToString(); | ||||||
|  |         CreatedAt = DateTime.UtcNow; | ||||||
|  |         Status = StatusEnum.Active; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								Core.Blueprint.Mongo/Entities/StatusEnum.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Core.Blueprint.Mongo/Entities/StatusEnum.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Represents the status of a document or entity. This enum is used to track the state of a document  | ||||||
|  |     /// within the system. The `JsonStringEnumConverter` ensures that the enum values are serialized as strings  | ||||||
|  |     /// in JSON format, rather than as numeric values. | ||||||
|  |     /// </summary> | ||||||
|  |     [JsonConverter(typeof(JsonStringEnumConverter))] | ||||||
|  |     public enum StatusEnum | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Represents an active document or entity. | ||||||
|  |         /// </summary> | ||||||
|  |         Active = 0, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Represents an inactive document or entity. | ||||||
|  |         /// </summary> | ||||||
|  |         Inactive = 1, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Represents a deleted document or entity. | ||||||
|  |         /// </summary> | ||||||
|  |         Deleted = 2 | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								Core.Blueprint.Mongo/Provider/MongoProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								Core.Blueprint.Mongo/Provider/MongoProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | using MongoDB.Driver; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Provides the MongoDB provider and database connection using the specified configuration settings. | ||||||
|  |     /// This class manages the connection to MongoDB and ensures that the correct credentials are used for authentication. | ||||||
|  |     /// </summary> | ||||||
|  |     public class MongoProvider | ||||||
|  |     { | ||||||
|  |         private readonly IMongoDatabase _database; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes a new instance of the <see cref="MongoProvider"/> class. | ||||||
|  |         /// This constructor sets up the MongoDB provider using the connection string, audience, and other settings provided. | ||||||
|  |         /// It also configures authentication using OpenID Connect (OIDC) credentials. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="mongoDbSettings">The MongoDB settings required for connecting to the database.</param> | ||||||
|  |         public MongoProvider(IMongoDatabase database) | ||||||
|  |         { | ||||||
|  |             _database = database ?? throw new ArgumentNullException(nameof(database)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets the initialized MongoDB database. If the database is not initialized, an exception is thrown. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <exception cref="InvalidOperationException">Thrown when the database connection is not initialized.</exception> | ||||||
|  |         protected IMongoDatabase Database | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  |                 if (_database == null) | ||||||
|  |                 { | ||||||
|  |                     throw new InvalidOperationException("MongoDB connection is not initialized."); | ||||||
|  |                 } | ||||||
|  |                 return _database; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										252
									
								
								Core.Blueprint.Mongo/Repositories/CollectionRepository.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								Core.Blueprint.Mongo/Repositories/CollectionRepository.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,252 @@ | |||||||
|  | using MongoDB.Bson; | ||||||
|  | using MongoDB.Driver; | ||||||
|  | using MongoDB.Driver.Linq; | ||||||
|  | using System.Linq.Expressions; | ||||||
|  | using System.Reflection; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Mongo | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Provides methods for interacting with a MongoDB collection for a specific document type. | ||||||
|  |     /// Inherits from <see cref="MongoProvider"/> and implements <see cref="ICollectionsRepository{TDocument}"/>. | ||||||
|  |     /// This class encapsulates common database operations such as querying, inserting, updating, and deleting documents. | ||||||
|  |     /// </summary> | ||||||
|  |     /// <typeparam name="TDocument">The type of document stored in the collection, which must implement <see cref="IDocument"/>.</typeparam> | ||||||
|  |     public class CollectionRepository<TDocument>(IMongoDatabase database) : ICollectionsRepository<TDocument> where TDocument : IDocument | ||||||
|  |     { | ||||||
|  |         private IMongoCollection<TDocument> _collection; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes the MongoDB collection based on the <see cref="CollectionAttributeName"/> attribute | ||||||
|  |         /// applied to the <typeparamref name="TDocument"/> type. Throws an exception if the attribute is not present. | ||||||
|  |         /// </summary> | ||||||
|  |         public void CollectionInitialization() | ||||||
|  |         { | ||||||
|  |             var collectionAttribute = typeof(TDocument).GetCustomAttribute<CollectionAttributeName>(); | ||||||
|  |             if (collectionAttribute == null) | ||||||
|  |             { | ||||||
|  |                 throw new InvalidOperationException($"The class {typeof(TDocument).Name} is missing the CollectionAttributeName attribute."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             string collectionName = collectionAttribute.Name; | ||||||
|  |  | ||||||
|  |             _collection = database.GetCollection<TDocument>(collectionName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Returns all documents from the collection as an enumerable list. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation. The task result contains an enumerable list of documents.</returns> | ||||||
|  |         public virtual async ValueTask<IEnumerable<TDocument>> AsQueryable() | ||||||
|  |         { | ||||||
|  |             return await _collection.AsQueryable().ToListAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Filters documents in the collection based on the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">A lambda expression that defines the filter criteria for the documents.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation. The task result contains a list of documents that match the filter.</returns> | ||||||
|  |         public virtual async Task<IEnumerable<TDocument>> FilterBy( | ||||||
|  |             Expression<Func<TDocument, bool>> filterExpression) | ||||||
|  |         { | ||||||
|  |             var objectResult = await _collection.FindAsync(filterExpression).ConfigureAwait(false); | ||||||
|  |             return objectResult.ToList(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Filters documents in the collection based on the provided filter and projection expressions. | ||||||
|  |         /// Projects the filtered documents into a new type. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="TProjected">The type to project the documents into.</typeparam> | ||||||
|  |         /// <param name="filterExpression">A lambda expression that defines the filter criteria for the documents.</param> | ||||||
|  |         /// <param name="projectionExpression">A lambda expression that defines how the documents should be projected.</param> | ||||||
|  |         /// <returns>An enumerable collection of projected documents.</returns> | ||||||
|  |         public virtual IEnumerable<TProjected> FilterBy<TProjected>( | ||||||
|  |             Expression<Func<TDocument, bool>> filterExpression, | ||||||
|  |             Expression<Func<TDocument, TProjected>> projectionExpression) | ||||||
|  |         { | ||||||
|  |             return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Finds a single document that matches the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">A lambda expression that defines the filter criteria for the document.</param> | ||||||
|  |         /// <returns>The document that matches the filter, or <c>null</c> if no document is found.</returns> | ||||||
|  |         public virtual TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression) | ||||||
|  |         { | ||||||
|  |             return _collection.Find(filterExpression).FirstOrDefault(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Filters documents in the collection based on the provided MongoDB filter definition. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterDefinition">A filter definition for MongoDB query.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation. The task result contains a list of documents that match the filter.</returns> | ||||||
|  |         public virtual async Task<IEnumerable<TDocument>> FilterByMongoFilterAsync(FilterDefinition<TDocument> filterDefinition) | ||||||
|  |         { | ||||||
|  |             var objectResult = await _collection.Find(filterDefinition).ToListAsync().ConfigureAwait(false); | ||||||
|  |             return objectResult; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously finds a single document that matches the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">A lambda expression that defines the filter criteria for the document.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation. The task result contains the document that matches the filter, or <c>null</c> if no document is found.</returns> | ||||||
|  |         public virtual Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression) | ||||||
|  |         { | ||||||
|  |             return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync()); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Finds a document by its unique identifier (ID). | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The unique identifier of the document.</param> | ||||||
|  |         /// <returns>The document that matches the specified ID, or <c>null</c> if no document is found.</returns> | ||||||
|  |         public virtual TDocument FindById(string id) | ||||||
|  |         { | ||||||
|  |             var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, id); | ||||||
|  |             return _collection.Find(filter).SingleOrDefault(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously finds a document by its unique identifier (ID). | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The unique identifier of the document.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation. The task result contains the document that matches the specified ID, or <c>null</c> if no document is found.</returns> | ||||||
|  |         public virtual Task<TDocument> FindByIdAsync(string id) | ||||||
|  |         { | ||||||
|  |             return Task.Run(() => | ||||||
|  |             { | ||||||
|  |                 var objectId = new ObjectId(id); | ||||||
|  |                 var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, id); | ||||||
|  |                 return _collection.Find(filter).SingleOrDefaultAsync(); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Inserts a single document into the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="document">The document to insert.</param> | ||||||
|  |         public virtual void InsertOne(TDocument document) | ||||||
|  |         { | ||||||
|  |             _collection.InsertOne(document); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously inserts a single document into the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="document">The document to insert.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation.</returns> | ||||||
|  |         public virtual Task InsertOneAsync(TDocument document) | ||||||
|  |         { | ||||||
|  |             return Task.Run(() => _collection.InsertOneAsync(document)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Inserts multiple documents into the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="documents">The collection of documents to insert.</param> | ||||||
|  |         public void InsertMany(ICollection<TDocument> documents) | ||||||
|  |         { | ||||||
|  |             _collection.InsertMany(documents); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously inserts multiple documents into the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="documents">The collection of documents to insert.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation.</returns> | ||||||
|  |         public virtual async Task InsertManyAsync(ICollection<TDocument> documents) | ||||||
|  |         { | ||||||
|  |             await _collection.InsertManyAsync(documents); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Replaces an existing document in the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="document">The document with the updated data.</param> | ||||||
|  |         public void ReplaceOne(TDocument document) | ||||||
|  |         { | ||||||
|  |             var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, document._Id); | ||||||
|  |             _collection.FindOneAndReplace(filter, document); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously replaces an existing document in the collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="document">The document with the updated data.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation.</returns> | ||||||
|  |         public virtual async Task ReplaceOneAsync(TDocument document) | ||||||
|  |         { | ||||||
|  |             var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, document._Id); | ||||||
|  |             await _collection.FindOneAndReplaceAsync(filter, document); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a single document from the collection based on the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">A lambda expression that defines the filter criteria for the document to delete.</param> | ||||||
|  |         public void DeleteOne(Expression<Func<TDocument, bool>> filterExpression) | ||||||
|  |         { | ||||||
|  |             _collection.FindOneAndDelete(filterExpression); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously deletes a single document from the collection based on the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">A lambda expression that defines the filter criteria for the document to delete.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation.</returns> | ||||||
|  |         public async Task<TDocument> DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression) | ||||||
|  |         { | ||||||
|  |             return await _collection.FindOneAndDeleteAsync(filterExpression); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a single document from the collection based on its unique identifier (ID). | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The unique identifier of the document to delete.</param> | ||||||
|  |         public void DeleteById(string id) | ||||||
|  |         { | ||||||
|  |             var objectId = new ObjectId(id); | ||||||
|  |             var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, id); | ||||||
|  |             _collection.FindOneAndDelete(filter); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously deletes a single document from the collection based on its unique identifier (ID). | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The unique identifier of the document to delete.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation.</returns> | ||||||
|  |         public Task DeleteByIdAsync(string id) | ||||||
|  |         { | ||||||
|  |             return Task.Run(() => | ||||||
|  |             { | ||||||
|  |                 var objectId = new ObjectId(id); | ||||||
|  |                 var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, id); | ||||||
|  |                 _collection.FindOneAndDeleteAsync(filter); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes multiple documents from the collection based on the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">A lambda expression that defines the filter criteria for the documents to delete.</param> | ||||||
|  |         public void DeleteMany(Expression<Func<TDocument, bool>> filterExpression) | ||||||
|  |         { | ||||||
|  |             _collection.DeleteMany(filterExpression); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Asynchronously deletes multiple documents from the collection based on the provided filter expression. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="filterExpression">A lambda expression that defines the filter criteria for the documents to delete.</param> | ||||||
|  |         /// <returns>A task that represents the asynchronous operation.</returns> | ||||||
|  |         public Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression) | ||||||
|  |         { | ||||||
|  |             return Task.Run(() => _collection.DeleteManyAsync(filterExpression)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								Core.Blueprint.Redis/Adapters/CacheSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								Core.Blueprint.Redis/Adapters/CacheSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Redis | ||||||
|  | { | ||||||
|  |     public interface ICacheSettings | ||||||
|  |     { | ||||||
|  |         int DefaultCacheDurationInMinutes { get; set; } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// Represents the settings for Redis caching. | ||||||
|  |     /// </summary> | ||||||
|  |     public class CacheSettings: ICacheSettings | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the default cache duration in minutes. | ||||||
|  |         /// </summary> | ||||||
|  |         public int DefaultCacheDurationInMinutes { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								Core.Blueprint.Redis/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Core.Blueprint.Redis/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  | using Microsoft.Extensions.Logging; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Redis.Configuration | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Provides extension methods for registering Redis-related services in the DI container. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class RegisterBlueprint | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Adds Redis caching services to the service collection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="services">The service collection to register the services into.</param> | ||||||
|  |         /// <param name="configuration">The application configuration object.</param> | ||||||
|  |         /// <returns>The updated service collection.</returns> | ||||||
|  |         public static IServiceCollection AddRedis(this IServiceCollection services, IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |             // Retrieve the Redis connection string from the configuration. | ||||||
|  |             // Get Redis configuration section | ||||||
|  |             var redisConnectionString = configuration.GetSection("ConnectionStrings:Redis").Value; | ||||||
|  |             if (string.IsNullOrEmpty(redisConnectionString)) | ||||||
|  |             { | ||||||
|  |                 throw new InvalidOperationException("Redis connection is not configured."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // Register RedisCacheProvider | ||||||
|  |             services.AddSingleton<IRedisCacheProvider>(provider => | ||||||
|  |                 new RedisCacheProvider(redisConnectionString, provider.GetRequiredService<ILogger<RedisCacheProvider>>())); | ||||||
|  |  | ||||||
|  |             // Get CacheSettings and register with the ICacheSettings interface | ||||||
|  |             var cacheSettings = configuration.GetSection("CacheSettings").Get<CacheSettings>(); | ||||||
|  |             if (cacheSettings == null) | ||||||
|  |             { | ||||||
|  |                 throw new InvalidOperationException("Redis CacheSettings section is not configured."); | ||||||
|  |             } | ||||||
|  |             services.AddSingleton<ICacheSettings>(cacheSettings); | ||||||
|  |  | ||||||
|  |             return services; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								Core.Blueprint.Redis/Contracts/IRedisCacheProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Core.Blueprint.Redis/Contracts/IRedisCacheProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | namespace Core.Blueprint.Redis | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Interface for managing Redis cache operations. | ||||||
|  |     /// </summary> | ||||||
|  |     public interface IRedisCacheProvider | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves a cache item by its key. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="T">The type of the cached item.</typeparam> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <returns>The cached item, or default if not found.</returns> | ||||||
|  |         ValueTask<TEntity> GetAsync<TEntity>(string key); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Sets a cache item with the specified key and value. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="T">The type of the item to cache.</typeparam> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <param name="value">The item to cache.</param> | ||||||
|  |         /// <param name="expiry">The optional expiration time for the cache item.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         ValueTask SetAsync<TEntity>(string key, TEntity value, TimeSpan? expiry = null); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Removes a cache item by its key. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         ValueTask RemoveAsync(string key); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Checks if a cache item exists for the specified key. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <returns>True if the cache item exists; otherwise, false.</returns> | ||||||
|  |         ValueTask<bool> ExistsAsync(string key); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Refreshes the expiration time of a cache item if it exists. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <param name="expiry">The new expiration time for the cache item.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         ValueTask RefreshAsync(string key, TimeSpan? expiry = null); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										18
									
								
								Core.Blueprint.Redis/Core.Blueprint.Redis.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Core.Blueprint.Redis/Core.Blueprint.Redis.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  |  | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <TargetFramework>net8.0</TargetFramework> | ||||||
|  |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|  |     <Nullable>enable</Nullable> | ||||||
|  |   </PropertyGroup> | ||||||
|  |  | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Microsoft.Azure.StackExchangeRedis" Version="3.2.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="StackExchange.Redis" Version="2.8.22" /> | ||||||
|  |   </ItemGroup> | ||||||
|  |  | ||||||
|  | </Project> | ||||||
							
								
								
									
										63
									
								
								Core.Blueprint.Redis/Helpers/RedisCacheKeyHelper.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								Core.Blueprint.Redis/Helpers/RedisCacheKeyHelper.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Text.RegularExpressions; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Redis.Helpers | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Helper class for generating consistent and normalized cache keys. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class CacheKeyHelper | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Generates a cache key based on the instance, method name, and parameters. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="instance">The instance of the class.</param> | ||||||
|  |         /// <param name="methodName">The method name related to the cache key.</param> | ||||||
|  |         /// <param name="parameters">The parameters used to generate the key.</param> | ||||||
|  |         /// <returns>A normalized cache key string.</returns> | ||||||
|  |         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(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Normalizes a parameter value for use in a cache key. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="param">The parameter to normalize.</param> | ||||||
|  |         /// <returns>A normalized string representation of the parameter.</returns> | ||||||
|  |         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. | ||||||
|  |             return Regex.Replace(paramString, @"[^a-zA-Z0-9]", "_"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										171
									
								
								Core.Blueprint.Redis/RedisCacheProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								Core.Blueprint.Redis/RedisCacheProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | |||||||
|  | using Azure.Identity; | ||||||
|  | using Microsoft.Extensions.Logging; | ||||||
|  | using StackExchange.Redis; | ||||||
|  | using System.Text.Json; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Redis | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Redis cache provider for managing cache operations. | ||||||
|  |     /// </summary> | ||||||
|  |     public sealed class RedisCacheProvider : IRedisCacheProvider | ||||||
|  |     { | ||||||
|  |         private IDatabase _cacheDatabase = null!; | ||||||
|  |         private readonly ILogger<RedisCacheProvider> _logger; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes a new instance of the <see cref="RedisCacheProvider"/> class. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="connectionString">The Redis connection string.</param> | ||||||
|  |         /// <param name="logger">The logger instance for logging operations.</param> | ||||||
|  |         /// <exception cref="ArgumentNullException">Thrown when connection string is null or empty.</exception> | ||||||
|  |         public RedisCacheProvider(string connectionString, ILogger<RedisCacheProvider> logger) | ||||||
|  |         { | ||||||
|  |             if (string.IsNullOrWhiteSpace(connectionString)) | ||||||
|  |                 throw new ArgumentNullException(nameof(connectionString), "Redis connection string cannot be null or empty."); | ||||||
|  |  | ||||||
|  |             _logger = logger; | ||||||
|  |             _cacheDatabase = InitializeRedisAsync(connectionString).GetAwaiter().GetResult(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes and establishes a connection to Redis using the provided connection string. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="connectionString">The Redis connection string.</param> | ||||||
|  |         /// <returns>An <see cref="IDatabase"/> instance representing the Redis cache database.</returns> | ||||||
|  |         /// <exception cref="Exception">Thrown when the connection to Redis fails.</exce | ||||||
|  |         async Task<IDatabase> InitializeRedisAsync(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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves a cache item by its key. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="T">The type of the cached item.</typeparam> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <returns>The cached item of type <typeparamref name="T"/>, or default if not found.</returns> | ||||||
|  |         public async ValueTask<TEntity> GetAsync<TEntity>(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<TEntity>(value); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 _logger.LogError(ex, $"Error getting cache item with key {key}"); | ||||||
|  |                 throw; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         /// <summary> | ||||||
|  |         /// Sets a cache item with the specified key and value. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <typeparam name="T">The type of the item to cache.</typeparam> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <param name="value">The item to cache.</param> | ||||||
|  |         /// <param name="expiry">The optional expiration time for the cache item.</param> | ||||||
|  |         public async ValueTask SetAsync<TEntity>(string key, TEntity 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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Removes a cache item by its key. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         public async ValueTask 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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Checks if a cache item exists for the specified key. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <returns>True if the cache item exists; otherwise, false.</returns> | ||||||
|  |         public async ValueTask<bool> 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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Refreshes the expiration time of a cache item if it exists. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="key">The cache key.</param> | ||||||
|  |         /// <param name="expiry">The new expiration time for the cache item.</param> | ||||||
|  |         public async ValueTask 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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								Core.Blueprint.SQLServer/Adapters/BaseSQLAdapter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								Core.Blueprint.SQLServer/Adapters/BaseSQLAdapter.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | using System.ComponentModel.DataAnnotations; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.SQLServer.Entities | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Represents the base class for SQL Server entities, providing common properties for auditing and state management. | ||||||
|  |     /// </summary> | ||||||
|  |     public abstract class BaseSQLAdapter : IBaseSQLAdapter | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the identifier for the entity. | ||||||
|  |         /// </summary> | ||||||
|  |         [Key] | ||||||
|  |         [JsonPropertyName("id")] | ||||||
|  |         public int Id { get; init; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the unique identifier for the entity. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("guid")] | ||||||
|  |         public string Guid { get; init; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the timestamp when the entity was created. | ||||||
|  |         /// Default value is the current UTC time at the moment of instantiation. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("createdAt")] | ||||||
|  |         public DateTime? CreatedAt { get; init; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the identifier of the user or system that created the entity. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("createdBy")] | ||||||
|  |         public string? CreatedBy { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the timestamp when the entity was last updated. | ||||||
|  |         /// Null if the entity has not been updated. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("updatedAt")] | ||||||
|  |         public DateTime? UpdatedAt { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the identifier of the user or system that last updated the entity. | ||||||
|  |         /// Null if the entity has not been updated. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("updatedBy")] | ||||||
|  |         public string? UpdatedBy { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the status of the entity, indicating whether it is active, inactive, or in another state. | ||||||
|  |         /// Default value is <see cref="StatusEnum.Active"/>. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("status")] | ||||||
|  |         [JsonConverter(typeof(JsonStringEnumConverter))] | ||||||
|  |         public StatusEnum Status { get; set; } | ||||||
|  |  | ||||||
|  |         protected BaseSQLAdapter() | ||||||
|  |         { | ||||||
|  |             Guid = System.Guid.NewGuid().ToString(); | ||||||
|  |             CreatedAt = DateTime.UtcNow; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								Core.Blueprint.SQLServer/Adapters/StatusEnum.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Core.Blueprint.SQLServer/Adapters/StatusEnum.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.SQLServer.Entities | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Defines the possible statuses for entities in the system. | ||||||
|  |     /// Used to track the state of an entity, such as whether it is active, inactive, or deleted. | ||||||
|  |     /// </summary> | ||||||
|  |     [JsonConverter(typeof(JsonStringEnumConverter))] | ||||||
|  |     public enum StatusEnum | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Indicates that the entity is currently active and operational. | ||||||
|  |         /// </summary> | ||||||
|  |         Active = 0, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Indicates that the entity is currently inactive but still exists in the system. | ||||||
|  |         /// Typically used for temporary deactivation or soft-offline states. | ||||||
|  |         /// </summary> | ||||||
|  |         Inactive = 1, | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Indicates that the entity has been deleted and is no longer accessible. | ||||||
|  |         /// Often used in soft-delete scenarios where the entity is retained for archival or audit purposes. | ||||||
|  |         /// </summary> | ||||||
|  |         Deleted = 2 | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										33
									
								
								Core.Blueprint.SQLServer/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Core.Blueprint.SQLServer/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | using Azure.Identity; | ||||||
|  | using Core.Blueprint.DAL.SQLServer; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.SQLServer.Configuration | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Provides extension methods for configuring SQL Server. | ||||||
|  |     /// </summary> | ||||||
|  |     public static class RegisterBlueprint | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Configures SQL Server services, including the database context and generic repository, for dependency injection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="services">The service collection to which the SQL Server services will be added.</param> | ||||||
|  |         /// <param name="configuration">The application configuration object for accessing settings such as connection strings.</param> | ||||||
|  |         /// <returns>An updated <see cref="IServiceCollection"/> with SQL Server services registered.</returns> | ||||||
|  |         public static IServiceCollection AddSQLServer(this IServiceCollection services, IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |             var chainedCredentials = new ChainedTokenCredential( | ||||||
|  |                     new ManagedIdentityCredential(), | ||||||
|  |                     new SharedTokenCacheCredential(), | ||||||
|  |                     new VisualStudioCredential(), | ||||||
|  |                     new VisualStudioCodeCredential() | ||||||
|  |                 ); | ||||||
|  |  | ||||||
|  |             services.AddScoped(typeof(IEntityRepository<,>), typeof(EntityRepository<,>)); | ||||||
|  |  | ||||||
|  |             return services; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										57
									
								
								Core.Blueprint.SQLServer/Contracts/IBaseSQLAdapter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								Core.Blueprint.SQLServer/Contracts/IBaseSQLAdapter.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | using Core.Blueprint.SQLServer.Entities; | ||||||
|  | using System.ComponentModel.DataAnnotations; | ||||||
|  | using System.Text.Json.Serialization; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.SQLServer | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Defines the interface for SQL Server entities, providing common properties for auditing and state management. | ||||||
|  |     /// </summary> | ||||||
|  |     public interface IBaseSQLAdapter | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the identifier for the entity. | ||||||
|  |         /// </summary> | ||||||
|  |         [Key] | ||||||
|  |         [JsonPropertyName("id")] | ||||||
|  |         int Id { get; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the GUID for the entity. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("guid")] | ||||||
|  |         string Guid { get; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the timestamp when the entity was created. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("createdAt")] | ||||||
|  |         DateTime? CreatedAt { get; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the identifier of the user or system that created the entity. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("createdBy")] | ||||||
|  |         string? CreatedBy { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the timestamp when the entity was last updated. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("updatedAt")] | ||||||
|  |         DateTime? UpdatedAt { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the identifier of the user or system that last updated the entity. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("updatedBy")] | ||||||
|  |         string? UpdatedBy { get; set; } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets or sets the status of the entity, indicating whether it is active, inactive, or in another state. | ||||||
|  |         /// </summary> | ||||||
|  |         [JsonPropertyName("status")] | ||||||
|  |         [JsonConverter(typeof(JsonStringEnumConverter))] | ||||||
|  |         StatusEnum Status { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										108
									
								
								Core.Blueprint.SQLServer/Contracts/IEntityRepository.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								Core.Blueprint.SQLServer/Contracts/IEntityRepository.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  | using System.Linq.Expressions; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.DAL.SQLServer | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Defines the contract for a generic repository to manage entities in a SQL Server database. | ||||||
|  |     /// </summary> | ||||||
|  |     /// <typeparam name="TEntity">The type of the entity managed by the repository. Must be a class.</typeparam> | ||||||
|  |     /// <typeparam name="TContext">The type of the database context used by the repository. Must inherit from <see cref="DbContext"/>.</typeparam> | ||||||
|  |     public interface IEntityRepository<TEntity, TContext> | ||||||
|  |         where TEntity : class | ||||||
|  |         where TContext : DbContext | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves all entities of type <typeparamref name="T"/> from the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with a collection of entities as the result.</returns> | ||||||
|  |         Task<IEnumerable<TEntity>> GetAllAsync(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves all entities of type <typeparamref name="T"/> from the database that match a specified condition. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="predicate">An expression to filter the entities.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with a collection of matching entities as the result.</returns> | ||||||
|  |         Task<IEnumerable<TEntity>> GetByConditionAsync(Expression<Func<TEntity, bool>> predicate); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves a single entity of type <typeparamref name="T"/> by its identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The identifier of the entity to retrieve.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with the entity as the result, or null if not found.</returns> | ||||||
|  |         Task<TEntity?> GetByIdAsync(int id); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the first entity of type <typeparamref name="T"/> that matches a specified condition, or null if no match is found. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="predicate">An expression to filter the entities.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with the matching entity as the result, or null if none match.</returns> | ||||||
|  |         Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Adds a new entity of type <typeparamref name="T"/> to the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entity">The entity to add.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         Task AddAsync(TEntity entity); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Adds multiple entities of type <typeparamref name="T"/> to the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entities">The collection of entities to add.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         Task AddRangeAsync(IEnumerable<TEntity> entities); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Updates an existing entity of type <typeparamref name="T"/> in the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entity">The entity to update.</param> | ||||||
|  |         /// <returns>The updated entity.</returns> | ||||||
|  |         TEntity Update(TEntity entity); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Updates multiple entities of type <typeparamref name="T"/> in the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entities">The collection of entities to update.</param> | ||||||
|  |         void UpdateRange(IEnumerable<TEntity> entities); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes an entity of type <typeparamref name="T"/> from the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entity">The entity to delete.</param> | ||||||
|  |         void Delete(TEntity entity); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes multiple entities of type <typeparamref name="T"/> from the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entities">The collection of entities to delete.</param> | ||||||
|  |         void DeleteRange(IEnumerable<TEntity> entities); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Determines whether any entities of type <typeparamref name="T"/> exist in the database that match a specified condition. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="predicate">An expression to filter the entities.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with a boolean result indicating whether any match exists.</returns> | ||||||
|  |         Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Executes a raw SQL query and maps the result to entities of type <typeparamref name="T"/>. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="sql">The raw SQL query to execute.</param> | ||||||
|  |         /// <param name="parameters">Optional parameters for the SQL query.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with a collection of entities as the result.</returns> | ||||||
|  |         Task<IEnumerable<TEntity>> ExecuteRawSqlAsync(string sql, params object[] parameters); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Counts the total number of entities of type <typeparamref name="T"/> in the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with the count as the result.</returns> | ||||||
|  |         Task<int> CountAsync(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Saves all pending changes to the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         Task SaveAsync(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								Core.Blueprint.SQLServer/Core.Blueprint.SQLServer.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Core.Blueprint.SQLServer/Core.Blueprint.SQLServer.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  |  | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <TargetFramework>net8.0</TargetFramework> | ||||||
|  |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|  |     <Nullable>enable</Nullable> | ||||||
|  |   </PropertyGroup> | ||||||
|  |  | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Azure.Identity" Version="1.13.1" /> | ||||||
|  |     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" /> | ||||||
|  |   </ItemGroup> | ||||||
|  |  | ||||||
|  | </Project> | ||||||
							
								
								
									
										182
									
								
								Core.Blueprint.SQLServer/Repositories/EntityRepository.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								Core.Blueprint.SQLServer/Repositories/EntityRepository.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | |||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  | using System.Linq.Expressions; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.DAL.SQLServer | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// The <see cref="EntityRepository{TEntity, TContext}"/> class provides a comprehensive generic repository  | ||||||
|  |     /// for managing entities using Entity Framework Core with SQL Server as the underlying database. | ||||||
|  |     /// Designed as a package for consumption by external applications. | ||||||
|  |     /// </summary> | ||||||
|  |     /// <typeparam name="TEntity">The entity type managed by the repository. Must be a class.</typeparam> | ||||||
|  |     /// <typeparam name="TContext">The database context type. Must inherit from <see cref="DbContext"/>.</typeparam> | ||||||
|  |     public class EntityRepository<TEntity, TContext> : IEntityRepository<TEntity, TContext> | ||||||
|  |         where TEntity : class | ||||||
|  |         where TContext : DbContext | ||||||
|  |     { | ||||||
|  |         private readonly TContext _context; | ||||||
|  |         private readonly DbSet<TEntity> _dbSet; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Initializes a new instance of the <see cref="EntityRepository{TEntity, TContext}"/> class with a specified database context. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="context">The <see cref="TContext"/> for database operations.</param> | ||||||
|  |         public EntityRepository(TContext context) | ||||||
|  |         { | ||||||
|  |             _context = context; | ||||||
|  |             _dbSet = _context.Set<TEntity>(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves all entities of type <typeparamref name="TEntity"/> from the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with a list of entities as the result.</returns> | ||||||
|  |         public async Task<IEnumerable<TEntity>> GetAllAsync() | ||||||
|  |         { | ||||||
|  |             return await _dbSet.ToListAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves all entities of type <typeparamref name="TEntity"/> from the database that match a specified filter. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="predicate">An expression to filter entities.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with a list of filtered entities as the result.</returns> | ||||||
|  |         public async Task<IEnumerable<TEntity>> GetByConditionAsync(Expression<Func<TEntity, bool>> predicate) | ||||||
|  |         { | ||||||
|  |             return await _dbSet.Where(predicate).ToListAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves a single entity of type <typeparamref name="TEntity"/> by its identifier. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">The identifier of the entity.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with the entity as the result, or null if not found.</returns> | ||||||
|  |         public async Task<TEntity?> GetByIdAsync(int id) | ||||||
|  |         { | ||||||
|  |             var existingEntity = await _dbSet.FindAsync(id); | ||||||
|  |  | ||||||
|  |             if (existingEntity != null) | ||||||
|  |             { | ||||||
|  |                 _context.Entry(existingEntity).State = EntityState.Detached; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return existingEntity; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Adds a new entity to the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entity">The entity to add.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         public async Task AddAsync(TEntity entity) | ||||||
|  |         { | ||||||
|  |             await _dbSet.AddAsync(entity); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Adds multiple entities to the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entities">The collection of entities to add.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         public async Task AddRangeAsync(IEnumerable<TEntity> entities) | ||||||
|  |         { | ||||||
|  |             await _dbSet.AddRangeAsync(entities); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Updates an existing entity in the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entity">The entity to update.</param> | ||||||
|  |         /// <returns>The updated entity.</returns> | ||||||
|  |         public TEntity Update(TEntity entity) | ||||||
|  |         { | ||||||
|  |             _dbSet.Attach(entity); | ||||||
|  |             _context.Entry(entity).State = EntityState.Modified; | ||||||
|  |             return entity; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Updates multiple entities in the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entities">The collection of entities to update.</param> | ||||||
|  |         public void UpdateRange(IEnumerable<TEntity> entities) | ||||||
|  |         { | ||||||
|  |             foreach (var entity in entities) | ||||||
|  |             { | ||||||
|  |                 _dbSet.Attach(entity); | ||||||
|  |                 _context.Entry(entity).State = EntityState.Modified; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes an entity from the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entity">The entity to delete.</param> | ||||||
|  |         public void Delete(TEntity entity) | ||||||
|  |         { | ||||||
|  |             if (_context.Entry(entity).State == EntityState.Detached) | ||||||
|  |             { | ||||||
|  |                 _dbSet.Attach(entity); | ||||||
|  |             } | ||||||
|  |             _dbSet.Remove(entity); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes multiple entities from the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="entities">The collection of entities to delete.</param> | ||||||
|  |         public void DeleteRange(IEnumerable<TEntity> entities) | ||||||
|  |         { | ||||||
|  |             _dbSet.RemoveRange(entities); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the first entity matching the specified condition or null if no match is found. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="predicate">An expression to filter entities.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with the matched entity as the result.</returns> | ||||||
|  |         public async Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate) | ||||||
|  |         { | ||||||
|  |             return await _dbSet.FirstOrDefaultAsync(predicate); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Determines if any entities exist that match the specified condition. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="predicate">An expression to filter entities.</param> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with a boolean result indicating existence.</returns> | ||||||
|  |         public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate) | ||||||
|  |         { | ||||||
|  |             return await _dbSet.AnyAsync(predicate); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Saves all pending changes to the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A task representing the asynchronous operation.</returns> | ||||||
|  |         public async Task SaveAsync() | ||||||
|  |         { | ||||||
|  |             await _context.SaveChangesAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Executes a raw SQL query and maps the result to the specified entity type. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="sql">The raw SQL query.</param> | ||||||
|  |         /// <param name="parameters">Optional parameters for the query.</param> | ||||||
|  |         /// <returns>An <see cref="IEnumerable{TEntity}"/> representing the result set.</returns> | ||||||
|  |         public async Task<IEnumerable<TEntity>> ExecuteRawSqlAsync(string sql, params object[] parameters) | ||||||
|  |         { | ||||||
|  |             return await _dbSet.FromSqlRaw(sql, parameters).ToListAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Counts the total number of entities in the database. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A task representing the asynchronous operation, with the count as the result.</returns> | ||||||
|  |         public async Task<int> CountAsync() | ||||||
|  |         { | ||||||
|  |             return await _dbSet.CountAsync(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								Core.Blueprint.Storage/Adapters/BlobAddDto.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Core.Blueprint.Storage/Adapters/BlobAddDto.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | namespace Core.Blueprint.Storage | ||||||
|  | { | ||||||
|  |     public class BlobAddDto | ||||||
|  |     { | ||||||
|  |         public string? FileName { get; set; } | ||||||
|  |         public byte[] FileContent { get; set; } = null!; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								Core.Blueprint.Storage/Adapters/BlobDownloadAdapter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Core.Blueprint.Storage/Adapters/BlobDownloadAdapter.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Storage.Adapters | ||||||
|  | { | ||||||
|  |     class BlobDownloadAdapter | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | namespace Core.Blueprint.Storage.Adapters | ||||||
|  | { | ||||||
|  |     public class BlobDownloadUriAdapter | ||||||
|  |     { | ||||||
|  |         public Uri Uri { get; set; } = null!; | ||||||
|  |         public string Name { get; set; } = null!; | ||||||
|  |         public string? Status { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								Core.Blueprint.Storage/Adapters/BlobFileAdapter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Core.Blueprint.Storage/Adapters/BlobFileAdapter.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | namespace Core.Blueprint.Storage | ||||||
|  | { | ||||||
|  |     public class BlobFileAdapter | ||||||
|  |     { | ||||||
|  |         public string? Uri { get; set; } | ||||||
|  |         public string Name { get; set; } = null!; | ||||||
|  |         public string? DateUpload { get; set; } | ||||||
|  |         public string? ContentType { get; set; } | ||||||
|  |         public long? Size { get; set; } | ||||||
|  |         public string? Status { get; set; } | ||||||
|  |         public string? ShortDate { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								Core.Blueprint.Storage/Adapters/BlobStorageAdapter.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Core.Blueprint.Storage/Adapters/BlobStorageAdapter.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Storage | ||||||
|  | { | ||||||
|  |     public class BlobStorageAdapter | ||||||
|  |     { | ||||||
|  |         public string FileName { get; set; } = string.Empty; | ||||||
|  |         public string Content { get; set; } = string.Empty; | ||||||
|  |         public string DownloadUrl { get; set; } = string.Empty; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								Core.Blueprint.Storage/Adapters/BlobStorageFolder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Core.Blueprint.Storage/Adapters/BlobStorageFolder.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Storage.Adapters | ||||||
|  | { | ||||||
|  |     public record BlobStorageFolder | ||||||
|  |     { | ||||||
|  |         public string Name { get; set; } | ||||||
|  |         public List<BlobStorageFolder> SubFolders { get; set; } = []; | ||||||
|  |         public List<BlobStorageFilesAdapter> Files { get; set; } = []; | ||||||
|  |     } | ||||||
|  |     public record BlobStorageFilesAdapter(string Content, string Name, string ContentType, string DownloadUrl); | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								Core.Blueprint.Storage/Adapters/Trie/TrieNode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								Core.Blueprint.Storage/Adapters/Trie/TrieNode.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Storage | ||||||
|  | { | ||||||
|  |     public class TrieNode | ||||||
|  |     { | ||||||
|  |         public Dictionary<char, TrieNode> Children { get; private set; } | ||||||
|  |         public bool IsEndOfWord { get; set; } | ||||||
|  |  | ||||||
|  |         public TrieNode() | ||||||
|  |         { | ||||||
|  |             Children = []; | ||||||
|  |             IsEndOfWord = false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     public class Trie | ||||||
|  |     { | ||||||
|  |         private readonly TrieNode _root; | ||||||
|  |  | ||||||
|  |         public Trie() | ||||||
|  |         { | ||||||
|  |             _root = new TrieNode(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void Insert(string word) | ||||||
|  |         { | ||||||
|  |             var node = _root; | ||||||
|  |             foreach (var ch in word) | ||||||
|  |             { | ||||||
|  |                 if (!node.Children.ContainsKey(ch)) | ||||||
|  |                 { | ||||||
|  |                     node.Children[ch] = new TrieNode(); | ||||||
|  |                 } | ||||||
|  |                 node = node.Children[ch]; | ||||||
|  |             } | ||||||
|  |             node.IsEndOfWord = true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public List<string> SearchByPrefix(string? prefix) | ||||||
|  |         { | ||||||
|  |             var results = new List<string>(); | ||||||
|  |             var node = _root; | ||||||
|  |             foreach (var ch in prefix) | ||||||
|  |             { | ||||||
|  |                 if (!node.Children.ContainsKey(ch)) | ||||||
|  |                 { | ||||||
|  |                     return results; | ||||||
|  |                 } | ||||||
|  |                 node = node.Children[ch]; | ||||||
|  |             } | ||||||
|  |             SearchByPrefixHelper(node, prefix, results); | ||||||
|  |             return results; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void SearchByPrefixHelper(TrieNode node, string currentPrefix, List<string> results) | ||||||
|  |         { | ||||||
|  |             if (node.IsEndOfWord) | ||||||
|  |             { | ||||||
|  |                 results.Add(currentPrefix); | ||||||
|  |             } | ||||||
|  |             foreach (var kvp in node.Children) | ||||||
|  |             { | ||||||
|  |                 SearchByPrefixHelper(kvp.Value, currentPrefix + kvp.Key, results); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | using Azure.Identity; | ||||||
|  | using Core.Blueprint.Storage.Contracts; | ||||||
|  | using Core.Blueprint.Storage.Provider; | ||||||
|  | using Microsoft.Extensions.Azure; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Storage.Configuration | ||||||
|  | { | ||||||
|  |     public static class RegisterBlueprint | ||||||
|  |     { | ||||||
|  |         public static IServiceCollection AddBlobStorage(this IServiceCollection services, IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             var blobConnection = configuration.GetConnectionString("BlobStorage"); | ||||||
|  |  | ||||||
|  |             if (blobConnection == null || string.IsNullOrWhiteSpace(blobConnection)) | ||||||
|  |             { | ||||||
|  |                 throw new ArgumentException("The BlobStorage configuration section is missing or empty."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var chainedCredentials = new ChainedTokenCredential( | ||||||
|  |                     new ManagedIdentityCredential(), | ||||||
|  |                     new SharedTokenCacheCredential(), | ||||||
|  |                     new VisualStudioCredential(), | ||||||
|  |                     new VisualStudioCodeCredential() | ||||||
|  |                 ); | ||||||
|  |             services.AddAzureClients(cfg => | ||||||
|  |             { | ||||||
|  |                 cfg.AddBlobServiceClient(new Uri(blobConnection)).WithCredential(chainedCredentials); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             services.AddScoped<IBlobStorageProvider, BlobStorageProvider>(); | ||||||
|  |  | ||||||
|  |             return services; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										181
									
								
								Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | |||||||
|  | using Azure; | ||||||
|  | using Azure.Storage.Blobs; | ||||||
|  | using Azure.Storage.Blobs.Models; | ||||||
|  | using Azure.Storage.Sas; | ||||||
|  | using Core.Blueprint.Storage.Adapters; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Storage.Contracts | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Defines a contract for managing blobs and containers in Azure Blob Storage. | ||||||
|  |     /// </summary> | ||||||
|  |     public interface IBlobStorageProvider | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates the blob container if it does not exist. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A <see cref="Response{T}"/> containing the container information.</returns> | ||||||
|  |         Task<Response<BlobContainerInfo>> CreateIfNotExistsAsync(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes the blob container if it exists. | ||||||
|  |         /// </summary> | ||||||
|  |         Task DeleteIfExistsAsync(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets properties of the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <returns>A <see cref="Response{T}"/> containing container properties.</returns> | ||||||
|  |         Task<Response<BlobContainerProperties>> GetPropertiesAsync(); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Sets metadata for the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="metadata">The metadata to set for the container.</param> | ||||||
|  |         Task SetMetadataAsync(IDictionary<string, string> metadata); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Uploads a blob to the container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="blobName">The name of the blob.</param> | ||||||
|  |         /// <param name="content">The content to upload.</param> | ||||||
|  |         /// <returns>A <see cref="Response{T}"/> containing blob content information.</returns> | ||||||
|  |         Task<Response<BlobContentInfo>> UploadBlobAsync(string blobName, Stream content); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Downloads a blob from the container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="blobName">The name of the blob.</param> | ||||||
|  |         /// <returns>A <see cref="Response{T}"/> containing blob download information.</returns> | ||||||
|  |         /// <exception cref="FileNotFoundException">Thrown if the blob does not exist.</exception> | ||||||
|  |         Task<Response<BlobDownloadInfo>> DownloadBlobAsync(string blobName); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a blob from the container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="blobName">The name of the blob.</param> | ||||||
|  |         Task<bool> DeleteBlobAsync(string blobName); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Lists all blobs in the container with an optional prefix. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="prefix">The prefix to filter blobs.</param> | ||||||
|  |         /// <returns>A collection of <see cref="BlobItem"/>.</returns> | ||||||
|  |         Task<IEnumerable<BlobItem>> ListBlobItemAsync(string? prefix = null); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the account information for the associated Blob Service Client. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="cancellation"> | ||||||
|  |         /// A <see cref="CancellationToken"/> that can be used to cancel the operation. | ||||||
|  |         /// </param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// A task that represents the asynchronous operation. The task result contains the | ||||||
|  |         /// <see cref="AccountInfo"/> object, which provides details about the account, such as the SKU | ||||||
|  |         /// and account kind. | ||||||
|  |         Task<AccountInfo> GetAccountInfoAsync(CancellationToken cancellation); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets a blob client for a specific blob. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="blobName">The name of the blob.</param> | ||||||
|  |         /// <returns>A <see cref="BlobClient"/> for the blob.</returns> | ||||||
|  |         BlobClient GetBlobClient(string blobName); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Lists blobs hierarchically using a delimiter. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="prefix">The prefix to filter blobs.</param> | ||||||
|  |         /// <param name="delimiter">The delimiter to use for hierarchy.</param> | ||||||
|  |         /// <returns>A collection of <see cref="BlobHierarchyItem"/>.</returns> | ||||||
|  |         Task<IEnumerable<BlobHierarchyItem>> ListBlobsByHierarchyAsync(string? prefix = null, string delimiter = "/"); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Generates a SAS token for the container with specified permissions. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="permissions">The permissions to assign to the SAS token.</param> | ||||||
|  |         /// <param name="expiresOn">The expiration time for the SAS token.</param> | ||||||
|  |         /// <returns>A <see cref="Uri"/> containing the SAS token.</returns> | ||||||
|  |         /// <exception cref="InvalidOperationException">Thrown if SAS URI generation is not supported.</exception> | ||||||
|  |         Uri GenerateContainerSasUri(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Acquires a lease on the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="proposedId">The optional proposed lease ID.</param> | ||||||
|  |         /// <param name="duration">The optional lease duration.</param> | ||||||
|  |         /// <returns>A <see cref="Response{T}"/> containing lease information.</returns> | ||||||
|  |         Task<Response<BlobLease>> AcquireLeaseAsync(string? proposedId = null, TimeSpan? duration = null); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Releases a lease on the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="leaseId">The lease ID to release.</param> | ||||||
|  |         Task ReleaseLeaseAsync(string leaseId); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Sets access policies for the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="accessType">The type of public access to allow.</param> | ||||||
|  |         /// <param name="identifiers">The optional list of signed identifiers for access policy.</param> | ||||||
|  |         Task SetAccessPolicyAsync(PublicAccessType accessType, IEnumerable<BlobSignedIdentifier>? identifiers = null); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Lists blobs in the container with an optional prefix. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="prefix">The prefix to filter blobs.</param> | ||||||
|  |         /// <returns>A collection of <see cref="BlobFileAdapter"/>.</returns> | ||||||
|  |         Task<IEnumerable<BlobFileAdapter>> ListBlobsAsync(string? prefix = null); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Uploads a blob to the container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="newBlob">The blob to upload.</param> | ||||||
|  |         /// <returns>A <see cref="BlobFileAdapter"/> representing the uploaded blob.</returns> | ||||||
|  |         Task<BlobFileAdapter> UploadBlobAsync(BlobAddDto newBlob); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a blob from the container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="fileName">The name of the blob to delete.</param> | ||||||
|  |         /// <returns>A <see cref="BlobFileAdapter"/> representing the deleted blob, or null if the blob was not found.</returns> | ||||||
|  |         Task<BlobFileAdapter?> DeleteBlobsAsync(string fileName); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Downloads a blob's content. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="blobName">The name of the blob.</param> | ||||||
|  |         /// <returns>A <see cref="BlobDownloadInfo"/> representing the downloaded blob.</returns> | ||||||
|  |         /// <exception cref="FileNotFoundException">Thrown if the blob does not exist.</exception> | ||||||
|  |         Task<BlobDownloadInfo> DownloadBlobsAsync(string blobName); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Generates a secure download URI for a specified blob in the storage container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="blobName">The name of the blob for which the download URI is being generated.</param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// An instance of <see cref="BlobDownloadUriAdapter"/> containing the generated URI, blob name, and status. | ||||||
|  |         /// </returns> | ||||||
|  |         /// <remarks> | ||||||
|  |         /// The generated URI includes a Shared Access Signature (SAS) token, which allows secure, time-limited access to the blob. | ||||||
|  |         /// The SAS token grants read-only access to the blob for a duration of 5 minutes starting from the current time. | ||||||
|  |         /// </remarks> | ||||||
|  |         /// <exception cref="ArgumentNullException">Thrown if <paramref name="blobName"/> is null or empty.</exception> | ||||||
|  |         /// <exception cref="StorageException">Thrown if there is an issue communicating with the Azure Blob service.</exception> | ||||||
|  |         BlobDownloadUriAdapter GenerateBlobDownloadUri(string blobName); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the hierarchical folder structure. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="prefix">The prefix to start the hierarchy retrieval.</param> | ||||||
|  |         /// <returns>A list of <see cref="BlobStorageFolder"/> representing the folder structure.</returns> | ||||||
|  |         Task<List<BlobStorageFolder>> GetFolderHierarchyAsync(string prefix); | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Lists neighboring folders based on a prefix. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="prefix">The prefix to search for neighboring folders.</param> | ||||||
|  |         /// <returns>A dictionary grouping folder names by their prefix.</returns> | ||||||
|  |         Task<Dictionary<string, List<string>>> ListNeighborFoldersAsync(string? prefix); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										17
									
								
								Core.Blueprint.Storage/Core.Blueprint.Storage.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Core.Blueprint.Storage/Core.Blueprint.Storage.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  |  | ||||||
|  |   <PropertyGroup> | ||||||
|  |     <TargetFramework>net8.0</TargetFramework> | ||||||
|  |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|  |     <Nullable>enable</Nullable> | ||||||
|  |   </PropertyGroup> | ||||||
|  |  | ||||||
|  |   <ItemGroup> | ||||||
|  |     <PackageReference Include="Azure.Identity" Version="1.13.1" /> | ||||||
|  |     <PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Azure" Version="1.9.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> | ||||||
|  |   </ItemGroup> | ||||||
|  |  | ||||||
|  | </Project> | ||||||
							
								
								
									
										372
									
								
								Core.Blueprint.Storage/Provider/BlobStorageProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										372
									
								
								Core.Blueprint.Storage/Provider/BlobStorageProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,372 @@ | |||||||
|  | using Azure; | ||||||
|  | using Azure.Storage.Blobs; | ||||||
|  | using Azure.Storage.Blobs.Models; | ||||||
|  | using Azure.Storage.Blobs.Specialized; | ||||||
|  | using Azure.Storage.Sas; | ||||||
|  | using Core.Blueprint.Storage.Adapters; | ||||||
|  | using Core.Blueprint.Storage.Contracts; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.Storage.Provider | ||||||
|  | { | ||||||
|  |     public sealed class BlobStorageProvider : IBlobStorageProvider | ||||||
|  |     { | ||||||
|  |         private readonly BlobServiceClient _blobServiceClient; | ||||||
|  |         private readonly BlobContainerClient _blobContainerClient; | ||||||
|  |         private readonly string _containerName; | ||||||
|  |         private readonly Trie _trie = new Trie(); | ||||||
|  |  | ||||||
|  |         public BlobStorageProvider(BlobServiceClient blobServiceClient, IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |             _blobServiceClient = blobServiceClient; | ||||||
|  |             _containerName = configuration.GetSection("BlobStorage:ContainerName").Value ?? ""; | ||||||
|  |  | ||||||
|  |             if (string.IsNullOrEmpty(_containerName)) | ||||||
|  |                 throw new ArgumentException("Blob container cannot be null or empty."); | ||||||
|  |  | ||||||
|  |             _blobContainerClient = blobServiceClient.GetBlobContainerClient(_containerName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Creates the blob container if it does not exist. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<Response<BlobContainerInfo>> CreateIfNotExistsAsync() | ||||||
|  |         { | ||||||
|  |             return await _blobContainerClient.CreateIfNotExistsAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes the blob container if it exists. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task DeleteIfExistsAsync() | ||||||
|  |         { | ||||||
|  |             await _blobContainerClient.DeleteIfExistsAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets properties of the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<Response<BlobContainerProperties>> GetPropertiesAsync() | ||||||
|  |         { | ||||||
|  |             return await _blobContainerClient.GetPropertiesAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Sets metadata for the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task SetMetadataAsync(IDictionary<string, string> metadata) | ||||||
|  |         { | ||||||
|  |             await _blobContainerClient.SetMetadataAsync(metadata); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Uploads a blob to the container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<Response<BlobContentInfo>> UploadBlobAsync(string blobName, Stream content) | ||||||
|  |         { | ||||||
|  |             var blobClient = _blobContainerClient.GetBlobClient(blobName); | ||||||
|  |             return await blobClient.UploadAsync(content, overwrite: true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Downloads a blob from the container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<Response<BlobDownloadInfo>> DownloadBlobAsync(string blobName) | ||||||
|  |         { | ||||||
|  |             var blobClient = _blobContainerClient.GetBlobClient(blobName); | ||||||
|  |             return await blobClient.DownloadAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a blob from the container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<bool> DeleteBlobAsync(string blobName) | ||||||
|  |         { | ||||||
|  |             var blobClient = _blobContainerClient.GetBlobClient(blobName); | ||||||
|  |             return await blobClient.DeleteIfExistsAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Lists all blobs in the container with an optional prefix. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<IEnumerable<BlobItem>> ListBlobItemAsync(string? prefix = null) | ||||||
|  |         { | ||||||
|  |             var blobs = new List<BlobItem>(); | ||||||
|  |  | ||||||
|  |             await foreach (var blobItem in _blobContainerClient.GetBlobsAsync(prefix: prefix)) | ||||||
|  |             { | ||||||
|  |                 blobs.Add(blobItem); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return blobs; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the account information for the associated Blob Service Client. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="cancellation"> | ||||||
|  |         /// A <see cref="CancellationToken"/> that can be used to cancel the operation. | ||||||
|  |         /// </param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// A task that represents the asynchronous operation. The task result contains the | ||||||
|  |         /// <see cref="AccountInfo"/> object, which provides details about the account, such as the SKU | ||||||
|  |         /// and account kind. | ||||||
|  |         public async Task<AccountInfo> GetAccountInfoAsync(CancellationToken cancellation) | ||||||
|  |         { | ||||||
|  |             return await _blobServiceClient.GetAccountInfoAsync(cancellation); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Gets a blob client for a specific blob. | ||||||
|  |         /// </summary> | ||||||
|  |         public BlobClient GetBlobClient(string blobName) | ||||||
|  |         { | ||||||
|  |             return _blobContainerClient.GetBlobClient(blobName); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Lists blobs hierarchically using a delimiter. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<IEnumerable<BlobHierarchyItem>> ListBlobsByHierarchyAsync(string? prefix = null, string delimiter = "/") | ||||||
|  |         { | ||||||
|  |             var blobs = new List<BlobHierarchyItem>(); | ||||||
|  |  | ||||||
|  |             await foreach (var blobHierarchyItem in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: delimiter)) | ||||||
|  |             { | ||||||
|  |                 blobs.Add(blobHierarchyItem); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return blobs; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Generates a SAS token for the container with specified permissions. | ||||||
|  |         /// </summary> | ||||||
|  |         public Uri GenerateContainerSasUri(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn) | ||||||
|  |         { | ||||||
|  |             if (!_blobContainerClient.CanGenerateSasUri) | ||||||
|  |             { | ||||||
|  |                 throw new InvalidOperationException("Cannot generate SAS URI. Ensure the client is authorized with account key credentials."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var sasBuilder = new BlobSasBuilder | ||||||
|  |             { | ||||||
|  |                 BlobContainerName = _blobContainerClient.Name, | ||||||
|  |                 Resource = "c", // c for container | ||||||
|  |                 ExpiresOn = expiresOn | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             sasBuilder.SetPermissions(permissions); | ||||||
|  |             return _blobContainerClient.GenerateSasUri(sasBuilder); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Acquires a lease on the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<Response<BlobLease>> AcquireLeaseAsync(string? proposedId = null, TimeSpan? duration = null) | ||||||
|  |         { | ||||||
|  |             return await _blobContainerClient.GetBlobLeaseClient(proposedId).AcquireAsync(duration ?? TimeSpan.FromSeconds(60)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Releases a lease on the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task ReleaseLeaseAsync(string leaseId) | ||||||
|  |         { | ||||||
|  |             await _blobContainerClient.GetBlobLeaseClient(leaseId).ReleaseAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Sets access policies for the blob container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task SetAccessPolicyAsync(PublicAccessType accessType, IEnumerable<BlobSignedIdentifier>? identifiers = null) | ||||||
|  |         { | ||||||
|  |             await _blobContainerClient.SetAccessPolicyAsync(accessType, identifiers); | ||||||
|  |         } | ||||||
|  |         /// <summary> | ||||||
|  |         /// Lists blobs in the container with an optional prefix. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<IEnumerable<BlobFileAdapter>> ListBlobsAsync(string? prefix = null) | ||||||
|  |         { | ||||||
|  |             var blobs = new List<BlobFileAdapter>(); | ||||||
|  |  | ||||||
|  |             await foreach (BlobItem blob in _blobContainerClient.GetBlobsAsync(prefix: prefix)) | ||||||
|  |             { | ||||||
|  |                 blobs.Add(new BlobFileAdapter | ||||||
|  |                 { | ||||||
|  |                     Name = blob.Name, | ||||||
|  |                     Uri = $"{_blobContainerClient.Uri}/{blob.Name}", | ||||||
|  |                     ContentType = blob.Properties.ContentType, | ||||||
|  |                     Size = blob.Properties.ContentLength, | ||||||
|  |                     DateUpload = blob.Properties.LastModified?.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"), | ||||||
|  |                     ShortDate = blob.Properties.LastModified?.UtcDateTime.ToString("MM-dd-yyyy"), | ||||||
|  |                     Status = "Available" | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return blobs; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Uploads a blob to the container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<BlobFileAdapter> UploadBlobAsync(BlobAddDto newBlob) | ||||||
|  |         { | ||||||
|  |             var blobClient = _blobContainerClient.GetBlobClient(newBlob.FileName); | ||||||
|  |  | ||||||
|  |             using var stream = new MemoryStream(newBlob.FileContent); | ||||||
|  |             await blobClient.UploadAsync(stream, overwrite: true); | ||||||
|  |  | ||||||
|  |             var properties = await blobClient.GetPropertiesAsync(); | ||||||
|  |  | ||||||
|  |             return new BlobFileAdapter | ||||||
|  |             { | ||||||
|  |                 Name = newBlob.FileName ?? "", | ||||||
|  |                 Uri = blobClient.Uri.ToString(), | ||||||
|  |                 ContentType = properties.Value.ContentType, | ||||||
|  |                 Size = properties.Value.ContentLength, | ||||||
|  |                 DateUpload = properties.Value.LastModified.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"), | ||||||
|  |                 ShortDate = properties.Value.LastModified.UtcDateTime.ToString("MM-dd-yyyy"), | ||||||
|  |                 Status = "Uploaded" | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Deletes a blob from the container. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<BlobFileAdapter?> DeleteBlobsAsync(string fileName) | ||||||
|  |         { | ||||||
|  |             var blobClient = _blobContainerClient.GetBlobClient(fileName); | ||||||
|  |  | ||||||
|  |             if (await blobClient.ExistsAsync()) | ||||||
|  |             { | ||||||
|  |                 var properties = await blobClient.GetPropertiesAsync(); | ||||||
|  |                 var _response = await blobClient.DeleteIfExistsAsync(); | ||||||
|  |  | ||||||
|  |                 return new BlobFileAdapter | ||||||
|  |                 { | ||||||
|  |                     Name = fileName, | ||||||
|  |                     Uri = blobClient.Uri.ToString(), | ||||||
|  |                     ContentType = properties.Value.ContentType, | ||||||
|  |                     Size = properties.Value.ContentLength, | ||||||
|  |                     DateUpload = properties.Value.LastModified.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"), | ||||||
|  |                     ShortDate = properties.Value.LastModified.UtcDateTime.ToString("MM-dd-yyyy"), | ||||||
|  |                     Status = _response ? "Deleted" : "Failed to delete" | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Downloads a blob's content. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<BlobDownloadInfo> DownloadBlobsAsync(string blobName) | ||||||
|  |         { | ||||||
|  |             var blobClient = _blobContainerClient.GetBlobClient(blobName); | ||||||
|  |  | ||||||
|  |             if (!await blobClient.ExistsAsync()) | ||||||
|  |             { | ||||||
|  |                 throw new FileNotFoundException($"Blob '{blobName}' does not exist in the container '{_containerName}'."); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return await blobClient.DownloadAsync(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Generates a secure download URI for a specified blob in the storage container. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="blobName">The name of the blob for which the download URI is being generated.</param> | ||||||
|  |         /// <returns> | ||||||
|  |         /// An instance of <see cref="BlobDownloadUriAdapter"/> containing the generated URI, blob name, and status. | ||||||
|  |         /// </returns> | ||||||
|  |         /// <remarks> | ||||||
|  |         /// The generated URI includes a Shared Access Signature (SAS) token, which allows secure, time-limited access to the blob. | ||||||
|  |         /// The SAS token grants read-only access to the blob for a duration of 5 minutes starting from the current time. | ||||||
|  |         /// </remarks> | ||||||
|  |         /// <exception cref="ArgumentNullException">Thrown if <paramref name="blobName"/> is null or empty.</exception> | ||||||
|  |         /// <exception cref="StorageException">Thrown if there is an issue communicating with the Azure Blob service.</exception> | ||||||
|  |         public BlobDownloadUriAdapter GenerateBlobDownloadUri(string blobName) | ||||||
|  |         { | ||||||
|  |             var delegationKey = _blobServiceClient.GetUserDelegationKey(DateTimeOffset.UtcNow, | ||||||
|  |                                                                     DateTimeOffset.UtcNow.AddHours(2)); | ||||||
|  |  | ||||||
|  |             var blob = _blobContainerClient.GetBlobClient(blobName); | ||||||
|  |  | ||||||
|  |             var sasBuilder = new BlobSasBuilder() | ||||||
|  |             { | ||||||
|  |                 BlobContainerName = blob.BlobContainerName, | ||||||
|  |                 BlobName = blob.Name, | ||||||
|  |                 Resource = "b", | ||||||
|  |                 StartsOn = DateTimeOffset.UtcNow, | ||||||
|  |                 ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5), | ||||||
|  |             }; | ||||||
|  |             sasBuilder.SetPermissions(BlobAccountSasPermissions.Read); | ||||||
|  |             sasBuilder.Protocol = SasProtocol.Https; | ||||||
|  |  | ||||||
|  |             var blobUriBuilder = new BlobUriBuilder(blob.Uri) | ||||||
|  |             { | ||||||
|  |                 Sas = sasBuilder.ToSasQueryParameters(delegationKey, _blobServiceClient.AccountName) | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             return new BlobDownloadUriAdapter | ||||||
|  |             { | ||||||
|  |                 Uri = blobUriBuilder.ToUri(), | ||||||
|  |                 Name = blob.Name, | ||||||
|  |                 Status = "Available" | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         /// Retrieves the hierarchical folder structure. | ||||||
|  |         /// </summary> | ||||||
|  |         public async Task<List<BlobStorageFolder>> GetFolderHierarchyAsync(string prefix) | ||||||
|  |         { | ||||||
|  |             var rootFolder = new BlobStorageFolder { Name = prefix }; | ||||||
|  |             await PopulateFolderAsync(rootFolder, prefix); | ||||||
|  |             return new List<BlobStorageFolder> { rootFolder }; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private async Task PopulateFolderAsync(BlobStorageFolder folder, string? prefix) | ||||||
|  |         { | ||||||
|  |             await foreach (var blobHierarchy in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: "/")) | ||||||
|  |             { | ||||||
|  |                 if (blobHierarchy.IsPrefix) | ||||||
|  |                 { | ||||||
|  |                     var subFolder = new BlobStorageFolder { Name = blobHierarchy.Prefix.TrimEnd('/') }; | ||||||
|  |                     folder.SubFolders.Add(subFolder); | ||||||
|  |                     await PopulateFolderAsync(subFolder, blobHierarchy.Prefix); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     folder.Files.Add(new BlobStorageFilesAdapter(Content: blobHierarchy.Prefix,  //Fix | ||||||
|  |                                                                 Name: blobHierarchy.Blob.Name, | ||||||
|  |                                                                 ContentType: "", | ||||||
|  |                                                                 DownloadUrl: $"{_blobContainerClient.Uri}/{blobHierarchy.Blob.Name}")); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         public async Task<Dictionary<string, List<string>>> ListNeighborFoldersAsync(string? prefix) | ||||||
|  |         { | ||||||
|  |             await ListFoldersInTrieAsync(prefix); | ||||||
|  |  | ||||||
|  |             var groupedFolders = _trie.SearchByPrefix(prefix) | ||||||
|  |                 .OrderBy(folder => folder) | ||||||
|  |                 .GroupBy(folder => folder.Substring(0, 1)) | ||||||
|  |                 .ToDictionary(group => group.Key, group => group.ToList()); | ||||||
|  |  | ||||||
|  |             return groupedFolders; | ||||||
|  |         } | ||||||
|  |         private async Task ListFoldersInTrieAsync(string? prefix) | ||||||
|  |         { | ||||||
|  |             await foreach (var blobHierarchy in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: "/")) | ||||||
|  |             { | ||||||
|  |                 if (blobHierarchy.IsPrefix) | ||||||
|  |                 { | ||||||
|  |                     var folderName = blobHierarchy.Prefix.TrimEnd('/').Split('/').Last(); | ||||||
|  |                     _trie.Insert(folderName); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Sergio Matias Urquin
					Sergio Matias Urquin