diff --git a/Core.BluePrint.Packages.sln b/Core.BluePrint.Packages.sln
new file mode 100644
index 0000000..7a136ec
--- /dev/null
+++ b/Core.BluePrint.Packages.sln
@@ -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
diff --git a/Core.Blueprint.KeyVault/Adapters/KeyVaultRequest.cs b/Core.Blueprint.KeyVault/Adapters/KeyVaultRequest.cs
new file mode 100644
index 0000000..4d0a72f
--- /dev/null
+++ b/Core.Blueprint.KeyVault/Adapters/KeyVaultRequest.cs
@@ -0,0 +1,9 @@
+
+namespace Core.Blueprint.KeyVault
+{
+ public sealed class KeyVaultRequest
+ {
+ public required string Name { get; set; }
+ public required string Value { get; set; }
+ }
+}
diff --git a/Core.Blueprint.KeyVault/Adapters/KeyVaultResponse.cs b/Core.Blueprint.KeyVault/Adapters/KeyVaultResponse.cs
new file mode 100644
index 0000000..2811656
--- /dev/null
+++ b/Core.Blueprint.KeyVault/Adapters/KeyVaultResponse.cs
@@ -0,0 +1,10 @@
+
+
+namespace Core.Blueprint.KeyVault
+{
+ public sealed class KeyVaultResponse
+ {
+ public string Name { get; set; } = null!;
+ public string Value { get; set; } = null!;
+ }
+}
diff --git a/Core.Blueprint.KeyVault/Configuration/RegisterBlueprint.cs b/Core.Blueprint.KeyVault/Configuration/RegisterBlueprint.cs
new file mode 100644
index 0000000..ea094e9
--- /dev/null
+++ b/Core.Blueprint.KeyVault/Configuration/RegisterBlueprint.cs
@@ -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
+{
+ ///
+ /// Registers the SecretClient for Azure Key Vault as a singleton service.
+ ///
+ /// The IServiceCollection to add the services to.
+ /// The application's configuration.
+ /// The updated IServiceCollection.
+ /// Thrown when the KeyVault URI is missing in the configuration.
+ 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();
+ return services;
+ }
+ }
+}
diff --git a/Core.Blueprint.KeyVault/Contracts/IKeyVaultProvider.cs b/Core.Blueprint.KeyVault/Contracts/IKeyVaultProvider.cs
new file mode 100644
index 0000000..b0025c6
--- /dev/null
+++ b/Core.Blueprint.KeyVault/Contracts/IKeyVaultProvider.cs
@@ -0,0 +1,48 @@
+
+namespace Core.Blueprint.KeyVault
+{
+ ///
+ /// Interface for managing secrets in Azure Key Vault.
+ ///
+ public interface IKeyVaultProvider
+ {
+ ///
+ /// Creates a new secret in Azure Key Vault.
+ ///
+ /// The request containing the name and value of the secret.
+ /// The cancellation token to cancel the operation.
+ /// A containing the details of the created secret.
+ ValueTask CreateSecretAsync(KeyVaultRequest keyVaultRequest, CancellationToken cancellationToken);
+
+ ///
+ /// Deletes a secret from Azure Key Vault if it exists.
+ ///
+ /// The name of the secret to delete.
+ /// The cancellation token to cancel the operation.
+ ///
+ /// A containing a status message and a boolean indicating whether the secret was successfully deleted.
+ ///
+ ValueTask> DeleteSecretAsync(string secretName, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves a secret from Azure Key Vault.
+ ///
+ /// The name of the secret to retrieve.
+ /// The cancellation token to cancel the operation.
+ ///
+ /// A containing the with secret details
+ /// and an optional error message if the secret was not found.
+ ///
+ ValueTask> GetSecretAsync(string secretName, CancellationToken cancellationToken);
+
+ ///
+ /// Updates an existing secret in Azure Key Vault. If the secret does not exist, an error is returned.
+ ///
+ /// The updated secret information.
+ /// The cancellation token to cancel the operation.
+ ///
+ /// A containing the updated and an optional error message if the secret was not found.
+ ///
+ ValueTask> UpdateSecretAsync(KeyVaultRequest newSecret, CancellationToken cancellationToken);
+ }
+}
diff --git a/Core.Blueprint.KeyVault/Core.Blueprint.KeyVault.csproj b/Core.Blueprint.KeyVault/Core.Blueprint.KeyVault.csproj
new file mode 100644
index 0000000..84d4a87
--- /dev/null
+++ b/Core.Blueprint.KeyVault/Core.Blueprint.KeyVault.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
diff --git a/Core.Blueprint.KeyVault/Provider/KeyVaultProvider.cs b/Core.Blueprint.KeyVault/Provider/KeyVaultProvider.cs
new file mode 100644
index 0000000..707fb5c
--- /dev/null
+++ b/Core.Blueprint.KeyVault/Provider/KeyVaultProvider.cs
@@ -0,0 +1,93 @@
+using Azure;
+using Azure.Security.KeyVault.Secrets;
+
+namespace Core.Blueprint.KeyVault
+{
+ ///
+ /// Provides operations for managing secrets in Azure Key Vault.
+ ///
+ public sealed class KeyVaultProvider(SecretClient keyVaultProvider): IKeyVaultProvider
+ {
+ ///
+ /// Creates a new secret in Azure Key Vault.
+ ///
+ /// The request containing the name and value of the secret.
+ /// The cancellation token to cancel the operation.
+ /// A containing the details of the created secret.
+ public async ValueTask 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;
+ }
+
+ ///
+ /// Deletes a secret from Azure Key Vault if it exists.
+ ///
+ /// The name of the secret to delete.
+ /// The cancellation token to cancel the operation.
+ ///
+ /// A containing a status message and a boolean indicating whether the secret was successfully deleted.
+ ///
+ public async ValueTask> 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);
+ }
+
+ ///
+ /// Retrieves a secret from Azure Key Vault.
+ ///
+ /// The name of the secret to retrieve.
+ /// The cancellation token to cancel the operation.
+ ///
+ /// A containing the with secret details
+ /// and an optional error message if the secret was not found.
+ ///
+ public async ValueTask> 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);
+ }
+
+ ///
+ /// Updates an existing secret in Azure Key Vault. If the secret does not exist, an error is returned.
+ ///
+ /// The updated secret information.
+ /// The cancellation token to cancel the operation.
+ ///
+ /// A containing the updated and an optional error message if the secret was not found.
+ ///
+ public async ValueTask> 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);
+ }
+ }
+}
diff --git a/Core.Blueprint.Logging/Adapters/ErrorDetails.cs b/Core.Blueprint.Logging/Adapters/ErrorDetails.cs
new file mode 100644
index 0000000..77ebbdf
--- /dev/null
+++ b/Core.Blueprint.Logging/Adapters/ErrorDetails.cs
@@ -0,0 +1,43 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using System.ComponentModel;
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// The service error details transfer object.
+ ///
+ public class ErrorDetails
+ {
+ ///
+ /// Gets or sets the service error code.
+ ///
+ /// healthy
+ [DisplayName(DisplayNames.ErrorCode)]
+ [JsonPropertyName(DisplayNames.ErrorCode)]
+ public string? ErrorCode { get; set; }
+
+ ///
+ /// Gets or sets the service error message.
+ ///
+ /// This is an example message.
+ [DisplayName(DisplayNames.Message)]
+ [JsonPropertyName(DisplayNames.Message)]
+ public string? Message { get; set; }
+
+ ///
+ /// Gets or sets the service target.
+ ///
+ /// healthy
+ [DisplayName(DisplayNames.Target)]
+ [JsonPropertyName(DisplayNames.Target)]
+ public string? Target { get; set; }
+ }
+}
+
+
diff --git a/Core.Blueprint.Logging/Adapters/HttpError.cs b/Core.Blueprint.Logging/Adapters/HttpError.cs
new file mode 100644
index 0000000..21eea87
--- /dev/null
+++ b/Core.Blueprint.Logging/Adapters/HttpError.cs
@@ -0,0 +1,45 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using System.ComponentModel;
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// The service HTTP error data transfer object.
+ ///
+ public class HttpError
+ {
+ ///
+ /// Gets or sets the error.
+ ///
+ [DisplayName(DisplayNames.Error)]
+ [JsonPropertyName(DisplayNames.Error)]
+ public ErrorDetails Error { get; set; }
+
+ ///
+ /// Creates a new instance of
+ /// with custom parameters.
+ ///
+ /// The HTTP error message.
+ /// The HTTP error code.
+ /// The HTTP error target.
+ public HttpError(
+ string? message,
+ string? errorCode,
+ string? target)
+ {
+ Error = new ErrorDetails
+ {
+ ErrorCode = errorCode,
+ Message = message,
+ Target = target,
+ };
+ }
+ }
+}
+
diff --git a/Core.Blueprint.Logging/Adapters/HttpException.cs b/Core.Blueprint.Logging/Adapters/HttpException.cs
new file mode 100644
index 0000000..a6cc11d
--- /dev/null
+++ b/Core.Blueprint.Logging/Adapters/HttpException.cs
@@ -0,0 +1,41 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// The service HTTP exception.
+ /// Extends the class.
+ ///
+ public class HttpException : Exception
+ {
+ ///
+ /// Gets or sets the exception error code.
+ ///
+ public string? ErrorCode { get; set; }
+
+ ///
+ /// Gets or sets the exception status code.
+ ///
+ public int StatusCode { get; set; }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The exception status code.
+ /// The exception error code.
+ /// The exception message.
+ public HttpException(
+ int statusCode,
+ string errorCode,
+ string message)
+ : base(message)
+ {
+ ErrorCode = errorCode;
+ StatusCode = statusCode;
+ }
+ }
+}
diff --git a/Core.Blueprint.Logging/Adapters/LogDetail.cs b/Core.Blueprint.Logging/Adapters/LogDetail.cs
new file mode 100644
index 0000000..b73477f
--- /dev/null
+++ b/Core.Blueprint.Logging/Adapters/LogDetail.cs
@@ -0,0 +1,120 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using System.ComponentModel;
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// The service logger detail object.
+ ///
+ /// The generic message type.
+ public class LogDetail
+ {
+ ///
+ /// Gets or sets the log severity.
+ ///
+ /// info
+ [DisplayName(DisplayNames.Severity)]
+ [JsonPropertyName(DisplayNames.Severity)]
+ public LogSeverity Severity { get; set; }
+
+ ///
+ /// Gets or sets the timestamp.
+ ///
+ [DisplayName(DisplayNames.Timestamp)]
+ [JsonPropertyName(DisplayNames.Timestamp)]
+ public DateTime Timestamp { get; set; }
+
+ ///
+ /// Gets or sets the environment.
+ ///
+ /// Development
+ [DisplayName(DisplayNames.Environment)]
+ [JsonPropertyName(DisplayNames.Environment)]
+ public string? Environment { get; set; }
+
+ ///
+ /// Gets or sets the target.
+ ///
+ [DisplayName(DisplayNames.Target)]
+ [JsonPropertyName(DisplayNames.Target)]
+ public LogTarget? Target { get; set; }
+
+ ///
+ /// Gets or sets the x-forwarded-for header.
+ ///
+ /// localhost
+ [DisplayName(DisplayNames.XForwardedFor)]
+ [JsonPropertyName(DisplayNames.XForwardedFor)]
+ public string? XForwardedFor { get; set; }
+
+ ///
+ /// Gets or sets the service identifier.
+ ///
+ ///
+ [DisplayName(DisplayNames.ServiceId)]
+ [JsonPropertyName(DisplayNames.ServiceId)]
+ public string? ServiceId { get; set; }
+
+ ///
+ /// Gets or sets the request identifier.
+ ///
+ ///
+ [DisplayName(DisplayNames.RequestId)]
+ [JsonPropertyName(DisplayNames.RequestId)]
+ public string? RequestId { get; set; }
+
+ ///
+ /// Gets or sets the keyVaultProvider identifier.
+ ///
+ ///
+ [DisplayName(DisplayNames.ClientId)]
+ [JsonPropertyName(DisplayNames.ClientId)]
+ public string? ClientId { get; set; }
+
+ ///
+ /// Gets or sets the keyVaultProvider identifier.
+ ///
+ /// keyVaultProviderRequest
+ [DisplayName(DisplayNames.Operation)]
+ [JsonPropertyName(DisplayNames.Operation)]
+ public LogOperation Operation { get; set; }
+
+ ///
+ /// Gets or sets the user name.
+ ///
+ /// keyVaultProviderRequest
+ [DisplayName(DisplayNames.User)]
+ [JsonPropertyName(DisplayNames.User)]
+ public string? User { get; set; }
+
+ ///
+ /// Gets or sets user's email.
+ ///
+ /// keyVaultProviderRequest
+ [DisplayName(DisplayNames.Email)]
+ [JsonPropertyName(DisplayNames.Email)]
+ public string? Email { get; set; }
+
+ ///
+ /// Gets or sets the user identifier.
+ ///
+ /// keyVaultProviderRequest
+ [DisplayName(DisplayNames.UserId)]
+ [JsonPropertyName(DisplayNames.UserId)]
+ public string? UserId { get; set; }
+
+ ///
+ /// Gets or sets the message.
+ ///
+ /// A custom log message.
+ [DisplayName(DisplayNames.Message)]
+ [JsonPropertyName(DisplayNames.Message)]
+ public TMessage? Message { get; set; }
+ }
+}
diff --git a/Core.Blueprint.Logging/Adapters/LogOperation.cs b/Core.Blueprint.Logging/Adapters/LogOperation.cs
new file mode 100644
index 0000000..33259b7
--- /dev/null
+++ b/Core.Blueprint.Logging/Adapters/LogOperation.cs
@@ -0,0 +1,55 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Represents all possible values for log operation.
+ ///
+ [DataContract]
+ public enum LogOperation
+ {
+ ///
+ /// The keyVaultProvider request log operation type.
+ ///
+ [EnumMember(Value = DisplayNames.ClientRequest)]
+ [JsonPropertyName(DisplayNames.ClientRequest)]
+ ClientRequest = 0,
+
+ ///
+ /// The keyVaultProvider response log operation type.
+ ///
+ [EnumMember(Value = DisplayNames.ClientResponse)]
+ ClientResponse = 1,
+
+ ///
+ /// The external request log operation type.
+ ///
+ [EnumMember(Value = DisplayNames.ExternalRequest)]
+ ExternalRequest = 2,
+
+ ///
+ /// The external response log operation type.
+ ///
+ [EnumMember(Value = DisplayNames.ExternalResponse)]
+ ExternalResponse = 3,
+
+ ///
+ /// The error log operation type.
+ ///
+ [EnumMember(Value = DisplayNames.Error)]
+ Error = 4,
+
+ ///
+ /// The info log operation type.
+ ///
+ [EnumMember(Value = DisplayNames.Info)]
+ Info = 5,
+ }
+}
diff --git a/Core.Blueprint.Logging/Adapters/LogSeverity.cs b/Core.Blueprint.Logging/Adapters/LogSeverity.cs
new file mode 100644
index 0000000..fac3b13
--- /dev/null
+++ b/Core.Blueprint.Logging/Adapters/LogSeverity.cs
@@ -0,0 +1,41 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using System.Runtime.Serialization;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Represents all possible values for log severity.
+ ///
+ [DataContract]
+ public enum LogSeverity
+ {
+ ///
+ /// The information severity level.
+ ///
+ [EnumMember(Value = DisplayNames.Information)]
+ Info = 0,
+
+ ///
+ /// The warning severity level.
+ ///
+ [EnumMember(Value = DisplayNames.Warning)]
+ Warn = 1,
+
+ ///
+ /// The error severity level.
+ ///
+ [EnumMember(Value = DisplayNames.Error)]
+ Error = 2,
+
+ ///
+ /// The fatal severity level.
+ ///
+ [EnumMember(Value = DisplayNames.Fatal)]
+ Fatal = 3,
+ }
+}
diff --git a/Core.Blueprint.Logging/Adapters/LogTarget.cs b/Core.Blueprint.Logging/Adapters/LogTarget.cs
new file mode 100644
index 0000000..7149856
--- /dev/null
+++ b/Core.Blueprint.Logging/Adapters/LogTarget.cs
@@ -0,0 +1,41 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using System.ComponentModel;
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// The service logger target object.
+ ///
+ public class LogTarget
+ {
+ ///
+ /// Gets or sets the log target method.
+ ///
+ /// GET
+ [DisplayName(DisplayNames.Method)]
+ [JsonPropertyName(DisplayNames.Method)]
+ public string? Method { get; set; }
+
+ ///
+ /// Gets or sets the log target host.
+ ///
+ /// GET
+ [DisplayName(DisplayNames.Host)]
+ [JsonPropertyName(DisplayNames.Host)]
+ public string? Host { get; set; }
+
+ ///
+ /// Gets or sets the log target route.
+ ///
+ /// GET
+ [DisplayName(DisplayNames.Route)]
+ [JsonPropertyName(DisplayNames.Route)]
+ public string? Route { get; set; }
+ }
+}
diff --git a/Core.Blueprint.Logging/Adapters/ServiceSettings.cs b/Core.Blueprint.Logging/Adapters/ServiceSettings.cs
new file mode 100644
index 0000000..b7f3c21
--- /dev/null
+++ b/Core.Blueprint.Logging/Adapters/ServiceSettings.cs
@@ -0,0 +1,20 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// The service settings.
+ ///
+ public class ServiceSettings
+ {
+ ///
+ /// Gets or sets the service identifier.
+ ///
+ public string? ApplicationName { get; set; }
+ public string? LayerName { get; set; }
+ }
+}
diff --git a/Core.Blueprint.Logging/Configuration/Registerblueprint.cs b/Core.Blueprint.Logging/Configuration/Registerblueprint.cs
new file mode 100644
index 0000000..418cd79
--- /dev/null
+++ b/Core.Blueprint.Logging/Configuration/Registerblueprint.cs
@@ -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
+{
+ ///
+ /// Provides extension methods for configuring logging in the application.
+ ///
+ public static class Registerblueprint
+ {
+ ///
+ /// Registers logging services in the application.
+ ///
+ /// The to add the services to.
+ /// The for accessing configuration and application setup.
+ /// The updated .
+ 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();
+
+ return services;
+ }
+
+ ///
+ /// Configures middleware for logging and error handling in the application.
+ ///
+ /// The used to configure the middleware pipeline.
+ /// The service settings required by the middleware.
+ 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);
+ }
+
+ ///
+ /// Adds middleware to handle HTTP exceptions globally.
+ ///
+ /// The to add the middleware to.
+ /// The settings used by the exception handler middleware.
+ /// The updated .
+ public static IApplicationBuilder UseHttpExceptionHandler(this IApplicationBuilder builder, ServiceSettings settings)
+ {
+ return builder.UseMiddleware(settings);
+ }
+
+ ///
+ /// Adds custom HTTP logging middleware to the application pipeline.
+ ///
+ /// The to add the middleware to.
+ /// The settings used by the logging middleware.
+ /// The updated .
+ public static IApplicationBuilder UseCustomHttpLogging(this IApplicationBuilder builder, ServiceSettings settings)
+ {
+ return builder.UseMiddleware(settings);
+ }
+ }
+}
diff --git a/Core.Blueprint.Logging/Constants/Claims.cs b/Core.Blueprint.Logging/Constants/Claims.cs
new file mode 100644
index 0000000..26443b1
--- /dev/null
+++ b/Core.Blueprint.Logging/Constants/Claims.cs
@@ -0,0 +1,48 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Constants for claims used in JWT tokens.
+ ///
+ public class Claims
+ {
+ ///
+ /// Claim name for user's name.
+ ///
+ public const string Name = "name";
+
+ ///
+ /// Claim name for user's name.
+ ///
+ public const string Email = "email";
+
+ ///
+ /// Claim name for user's ID.
+ ///
+ public const string Id = "id";
+
+ ///
+ /// Claim name for user's role ID.
+ ///
+ public const string Rol = "rol";
+
+ ///
+ /// Claim name for user's companies.
+ ///
+ public const string Companies = "companies";
+
+ ///
+ /// Claim name for user's projects.
+ ///
+ public const string Projects = "projects";
+
+ ///
+ /// Claim name for user's surveys.
+ ///
+ public const string Surveys = "surveys";
+ }
+}
diff --git a/Core.Blueprint.Logging/Constants/DisplayNames.cs b/Core.Blueprint.Logging/Constants/DisplayNames.cs
new file mode 100644
index 0000000..3fd50f3
--- /dev/null
+++ b/Core.Blueprint.Logging/Constants/DisplayNames.cs
@@ -0,0 +1,397 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Constants of the display names for this service.
+ ///
+ public static class DisplayNames
+ {
+ ///
+ /// The active patameter.
+ ///
+ public const string Active = "active";
+
+
+ ///
+ /// The keyVaultProvider identifier parameter.
+ ///
+ public const string ClientId = "keyVaultProviderId";
+
+ ///
+ /// The keyVaultProvider request parameter.
+ ///
+ public const string ClientRequest = "keyVaultProviderRequest";
+
+ ///
+ /// The keyVaultProvider response parameter.
+ ///
+ public const string ClientResponse = "keyVaultProviderResponse";
+
+ ///
+ /// The creation date.
+ ///
+ public const string CreationDate = "creationDate";
+
+ ///
+ /// The content parameter.
+ ///
+ public const string Content = "content";
+
+ ///
+ /// The delete parameter.
+ ///
+ public const string Delete = "delete";
+
+ ///
+ /// The description parameter.
+ ///
+ public const string Description = "description";
+
+ ///
+ /// The detail parameter.
+ ///
+ public const string Detail = "detail";
+
+
+ ///
+ /// The environment parameter.
+ ///
+ public const string Environment = "environment";
+
+ ///
+ /// The error log severity level parameter.
+ ///
+ public const string Error = "error";
+
+ ///
+ /// The error code parameter.
+ ///
+ public const string ErrorCode = "errorCode";
+
+ ///
+ /// The external request parameter.
+ ///
+ public const string ExternalRequest = "externalRequest";
+
+ ///
+ /// The external response parameter.
+ ///
+ public const string ExternalResponse = "externalResponse";
+
+ ///
+ /// The fatal log severity level parameter.
+ ///
+ public const string Fatal = "fatal";
+
+ ///
+ /// The host parameter.
+ ///
+ public const string Host = "host";
+
+ ///
+ /// The identifier parameter.
+ ///
+ public const string Id = "id";
+
+ ///
+ /// The inactive parameter.
+ ///
+ public const string Inactive = "inactive";
+
+ ///
+ /// The info log severity level parameter.
+ ///
+ public const string Info = "info";
+
+ ///
+ /// The information log severity level parameter.
+ ///
+ public const string Information = "information";
+
+ ///
+ /// The media parameter.
+ ///
+ public const string Media = "media";
+
+ ///
+ /// The media type parameter.
+ ///
+ public const string MediaType = "mediaType";
+
+ ///
+ /// The media use type parameter.
+ ///
+ public const string MediaUseType = "mediaUseType";
+
+ ///
+ /// Th message parameter.
+ ///
+ public const string Message = "message";
+
+ ///
+ /// The method parameter.
+ ///
+ public const string Method = "method";
+
+ ///
+ /// The monday parameter.
+ ///
+ public const string Monday = "monday";
+
+ ///
+ /// The MXN parameter.
+ ///
+ public const string MXN = "MXN";
+
+ ///
+ /// The name parameter.
+ ///
+ public const string Name = "name";
+
+ ///
+ /// The next page parameter.
+ ///
+ public const string NextPage = "nextPage";
+
+ ///
+ /// The nick name parameter.
+ ///
+ public const string NickName = "nickName";
+
+ ///
+ /// The note parameter.
+ ///
+ public const string Note = "note";
+
+ ///
+ /// The not so affordable parameter.
+ ///
+ public const string NotSoAffordable = "notSoAffordable";
+
+ ///
+ /// The object status parameter.
+ ///
+ public const string ObjectStatus = "objectStatus";
+
+ ///
+ /// The opening time parameter.
+ ///
+ public const string OpeningTime = "openingTime";
+
+ ///
+ /// The operation days parameter.
+ ///
+ public const string OperationDays = "operationDays";
+
+ ///
+ /// The page parameter.
+ ///
+ public const string Page = "page";
+
+ ///
+ /// The page count parameter.
+ ///
+ public const string PageCount = "pageCount";
+
+ ///
+ /// The page metadata parameter.
+ ///
+ public const string PageMetadata = "pageMetadata";
+
+ ///
+ /// The page size parameter.
+ ///
+ public const string PageSize = "pageSize";
+
+ ///
+ /// The parent identifier parameter.
+ ///
+ public const string ParentId = "ParentId";
+
+ ///
+ /// The pet ticket price parameter.
+ ///
+ public const string PetTicketPrice = "petTicketPrice";
+
+ ///
+ /// The place parameter.
+ ///
+ public const string Place = "place";
+
+ ///
+ /// The place type parameter.
+ ///
+ public const string PlaceType = "placeType";
+
+ ///
+ /// The previous page parameter.
+ ///
+ public const string PreviousPage = "previousPage";
+
+ ///
+ /// The provider identifier parameter.
+ ///
+ public const string ProviderId = "providerId";
+
+ ///
+ /// The provider type identifier parameter.
+ ///
+ public const string ProviderTypeId = "providerTypeId";
+
+ ///
+ /// The request identifier parameter.
+ ///
+ public const string RequestId = "requestId";
+
+ ///
+ /// The RNT identifier parameter.
+ ///
+ public const string RntId = "rntId";
+
+ ///
+ /// The route parameter.
+ ///
+ public const string Route = "route";
+
+ ///
+ /// The operation parameter.
+ ///
+ public const string Operation = "operation";
+
+ ///
+ /// The other ticket price parameter.
+ ///
+ public const string OtherTicketPrice = "otherTicketPrice";
+
+ ///
+ /// The path parameter.
+ ///
+ public const string Path = "path";
+
+ ///
+ /// The saturday parameter.
+ ///
+ public const string Saturday = "saturday";
+
+ ///
+ /// The secondary parameter.
+ ///
+ public const string Secondary = "secondary";
+
+ ///
+ /// The service health parameter.
+ ///
+ public const string ServiceHealth = "serviceHealth";
+
+ ///
+ /// The service identifier parameter.
+ ///
+ public const string ServiceId = "serviceId";
+
+ ///
+ /// The severity parameter.
+ ///
+ public const string Severity = "severity";
+
+ ///
+ /// The state identifier parameter.
+ ///
+ public const string StateId = "stateId";
+
+ ///
+ /// The region identifier parameter.
+ ///
+ public const string StateProvinceRegionId = "regionId";
+
+ ///
+ /// The sunday parameter.
+ ///
+ public const string Sunday = "sunday";
+
+ ///
+ /// The tax identifier parameter.
+ ///
+ public const string TaxId = "taxId";
+
+ ///
+ /// The target parameter.
+ ///
+ public const string Target = "target";
+
+ ///
+ /// The thursday parameter.
+ ///
+ public const string Thursday = "thursday";
+
+ ///
+ /// The timestamp parameter.
+ ///
+ public const string Timestamp = "timestamp";
+
+ ///
+ /// The total items parameter.
+ ///
+ public const string TotalItems = "totalItems";
+
+ ///
+ /// Gets or sets the transaction identifier parameter.
+ ///
+ public const string TransactionId = "transactionId";
+
+ ///
+ /// The tuesday parameter.
+ ///
+ public const string Tuesday = "tuesday";
+
+ ///
+ /// The URI parameter.
+ ///
+ public const string Uri = "uri";
+
+ ///
+ /// The update parameter.
+ ///
+ public const string Update = "update";
+
+ ///
+ /// The x-forwarded-for header parameter.
+ ///
+ public const string XForwardedFor = "xForwardedFor";
+
+ ///
+ /// The x-forwarded-for header parameter.
+ ///
+ public const string XForwardedForHeader = "X-Forwarded-For";
+
+ ///
+ /// The final currency identifier parameter.
+ ///
+ public const string CurrencyId = "currencyId";
+
+ ///
+ /// The user identifier parameter.
+ ///
+ public const string UserId = "userId";
+
+ ///
+ /// The user parameter.
+ ///
+ public const string User = "user";
+
+ ///
+ /// The warning log severity level.
+ ///
+ public const string Warning = "warning";
+
+ ///
+ /// The email parameter.
+ ///
+ public const string Email = "email";
+
+ }
+}
diff --git a/Core.Blueprint.Logging/Constants/EnvironmentVariables.cs b/Core.Blueprint.Logging/Constants/EnvironmentVariables.cs
new file mode 100644
index 0000000..936e14a
--- /dev/null
+++ b/Core.Blueprint.Logging/Constants/EnvironmentVariables.cs
@@ -0,0 +1,21 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Constants of the environment variables for this service.
+ ///
+ public static class EnvironmentVariables
+ {
+ ///
+ /// The stage environment vriable.
+ ///
+ public const string Stage = "ASPNETCORE_ENVIRONMENT";
+ }
+}
+
+
diff --git a/Core.Blueprint.Logging/Constants/ErrorCodes.cs b/Core.Blueprint.Logging/Constants/ErrorCodes.cs
new file mode 100644
index 0000000..2811fbc
--- /dev/null
+++ b/Core.Blueprint.Logging/Constants/ErrorCodes.cs
@@ -0,0 +1,79 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Constants for the error codes.
+ ///
+ public static class ErrorCodes
+ {
+ ///
+ /// The generic entities not found error code.
+ ///
+ public const string EntitiesNotFound = "{0}EntitiesNotFound";
+
+ ///
+ /// The entity already exsits error message.
+ ///
+ public const string EntityAlreadyExists = "{0}EntityAlreadyExists";
+
+ ///
+ /// The generic entity not found error code.
+ ///
+ public const string EntityNotFound = "{0}EntityNotFound";
+
+ ///
+ /// The generic not supported error code.
+ ///
+ public const string EntityNotSupported = "{0}NotSupported";
+
+ ///
+ /// The internal server error code.
+ ///
+ public const string InternalServerError = "InternalServerError";
+
+ ///
+ /// The invalid parameters in mapper error code.
+ ///
+ public const string InvalidParametersMapper = "InvalidParametersMapper";
+
+ ///
+ /// The page size invalid value error code.
+ ///
+ public const string PageSizeInvalidValue = "PageSizeInvalidValue";
+
+ ///
+ /// The page ot of range error code.
+ ///
+ public const string PageOutOfRange = "PageOutOfRange";
+
+ ///
+ /// The property does not match the regular expresion error code.
+ ///
+ public const string PropertyDoesNotMatchRegex = "{0}PropertyDoesNotMatchRegex";
+
+ ///
+ /// The property is required error code.
+ ///
+ public const string PropertyIsRequired = "{0}PropertyIsRequired";
+
+ ///
+ /// The property length invalid error code.
+ ///
+ public const string PropertyLengthInvalid = "{0}PropertyLengthInvalid";
+
+ ///
+ /// The property must be in range error code.
+ ///
+ public const string PropertyMustBeInRange = "{0}PropertyMustBeInRange";
+
+ ///
+ /// The route not found error code.
+ ///
+ public const string RouteNotFound = "RouteNotFound";
+ }
+}
diff --git a/Core.Blueprint.Logging/Constants/Headers.cs b/Core.Blueprint.Logging/Constants/Headers.cs
new file mode 100644
index 0000000..05c6a2f
--- /dev/null
+++ b/Core.Blueprint.Logging/Constants/Headers.cs
@@ -0,0 +1,10 @@
+namespace Core.Blueprint.Logging
+{
+ public static class Headers
+ {
+ ///
+ /// The authorization header.
+ ///
+ public const string Authorization = "Authorization";
+ }
+}
diff --git a/Core.Blueprint.Logging/Constants/MimeTypes.cs b/Core.Blueprint.Logging/Constants/MimeTypes.cs
new file mode 100644
index 0000000..515211f
--- /dev/null
+++ b/Core.Blueprint.Logging/Constants/MimeTypes.cs
@@ -0,0 +1,148 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using System.Globalization;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Constants for the mime types.
+ ///
+ public static class MimeTypes
+ {
+ ///
+ /// The service application/json mime type.
+ ///
+ public const string ApplicationJson = "application/json";
+
+ ///
+ /// The application/pdf mime type.
+ ///
+ public const string ApplicationPdf = "application/pdf";
+
+ ///
+ /// The end index.
+ ///
+ public const int EndIndex = 5;
+
+ ///
+ /// The JPEG extension.
+ ///
+ public const string ExtensionGif = "gif";
+
+ ///
+ /// The JPEG extension.
+ ///
+ public const string ExtensionJpeg = "jpeg";
+
+ ///
+ /// The PNG extension.
+ ///
+ public const string ExtensionPng = "png";
+
+ ///
+ /// The SVG extension.
+ ///
+ public const string ExtensionSvg = "svg";
+
+ ///
+ /// The image/gif mime type.
+ ///
+ public const string ImageGif = "image/gif";
+
+ ///
+ /// The image/jpeg mime type.
+ ///
+ public const string ImageJpeg = "image/jpeg";
+
+ ///
+ /// The image/png mime type.
+ ///
+ public const string ImagePng = "image/png";
+
+ ///
+ /// The image/svg+xml mime type.
+ ///
+ public const string ImageSvg = "image/svg+xml";
+
+ ///
+ /// The identifier GIF.
+ ///
+ public const string IdentifierGif = "R0LGO";
+
+ ///
+ /// The identifier PNG.
+ ///
+ public const string IdentifierJpeg = "/9J/4";
+
+ ///
+ /// The identifier PDF.
+ ///
+ public const string IdentifierPdf = "JVBER";
+
+ ///
+ /// The identifier PNG.
+ ///
+ public const string IdentifierPng = "IVBOR";
+
+ ///
+ /// The identifier SVG.
+ ///
+ public const string IdentifierSvg = "PHN2Z";
+
+ ///
+ /// The parameter name.
+ ///
+ public const string ParameterName = "MimeType";
+
+ ///
+ /// The start index.
+ ///
+ public const int StartIndex = 0;
+
+ ///
+ /// The mime type dictionary.
+ ///
+ public static readonly Dictionary Dictionary = new Dictionary
+ {
+ { IdentifierJpeg, ImageJpeg },
+ { IdentifierPng, ImagePng },
+ { IdentifierGif, ImageGif },
+ { IdentifierSvg, ImageSvg },
+ };
+
+ ///
+ /// The mime type dictionary.
+ ///
+ public static readonly Dictionary DictionaryExtension = new Dictionary
+ {
+ { IdentifierJpeg, ExtensionJpeg },
+ { IdentifierPng, ExtensionPng },
+ { IdentifierGif, ExtensionGif },
+ { IdentifierSvg, ExtensionSvg },
+ };
+
+ ///
+ /// Gets the mime type.
+ ///
+ /// The cpntent with mime type identifier, substring 0, 5 from content.
+ /// A representing the value.
+ public static string GetMimeType(this string content)
+ {
+ return Dictionary.FirstOrDefault(_ => _.Key == content[..EndIndex].ToUpper(CultureInfo.InvariantCulture)).Value;
+ }
+
+ ///
+ /// Gets the extension.
+ ///
+ /// The mime type identifier, substring 0, 5 from content.
+ /// A representing the value.
+ public static string GetExtension(this string content)
+ {
+ return DictionaryExtension.FirstOrDefault(_ => _.Key == content[..EndIndex].ToUpper(CultureInfo.InvariantCulture)).Value;
+ }
+ }
+}
diff --git a/Core.Blueprint.Logging/Constants/Responses.cs b/Core.Blueprint.Logging/Constants/Responses.cs
new file mode 100644
index 0000000..ab1c8c2
--- /dev/null
+++ b/Core.Blueprint.Logging/Constants/Responses.cs
@@ -0,0 +1,29 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Constants of the responses for this service.
+ ///
+ public static class Responses
+ {
+ ///
+ /// The health response.
+ ///
+ public const string HealthyService = "healthy";
+
+ ///
+ /// The route does not exist response.
+ ///
+ public const string RouteDoesNotExist = "The specified route '{0}' does not exist for method '{1}' in this service.";
+
+ ///
+ /// The target response.
+ ///
+ public const string Target = "{0}|{1}://{2}{3}";
+ }
+}
diff --git a/Core.Blueprint.Logging/Contracts/ILoggerProvider.cs b/Core.Blueprint.Logging/Contracts/ILoggerProvider.cs
new file mode 100644
index 0000000..95b427e
--- /dev/null
+++ b/Core.Blueprint.Logging/Contracts/ILoggerProvider.cs
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/Core.Blueprint.Logging/Core.Blueprint.Logging.csproj b/Core.Blueprint.Logging/Core.Blueprint.Logging.csproj
new file mode 100644
index 0000000..5570fb2
--- /dev/null
+++ b/Core.Blueprint.Logging/Core.Blueprint.Logging.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core.Blueprint.Logging/Middleware/HttpErrorMiddleware.cs b/Core.Blueprint.Logging/Middleware/HttpErrorMiddleware.cs
new file mode 100644
index 0000000..2894e93
--- /dev/null
+++ b/Core.Blueprint.Logging/Middleware/HttpErrorMiddleware.cs
@@ -0,0 +1,92 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using Microsoft.AspNetCore.Http;
+using Serilog;
+using System.Text.Json;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Handles HTTP logging.
+ ///
+ public class HttpErrorMiddleware
+ {
+ private readonly ILogger logger;
+ private readonly RequestDelegate requestProcess;
+ public readonly ServiceSettings settings;
+
+ ///
+ /// Creates a new instrance of .
+ ///
+ /// The logger representig an instance of .
+ /// The request delegate process.
+ public HttpErrorMiddleware(ILogger logger, RequestDelegate requestProcess, ServiceSettings settings)
+ {
+ this.logger = logger;
+ this.requestProcess = requestProcess;
+ this.settings = settings;
+ }
+
+ ///
+ /// Invoke method.
+ ///
+ /// The HTTP context.
+ /// A representing the asynchronous operation.
+ public async Task Invoke(HttpContext context)
+ {
+ try
+ {
+ await 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);
+ }
+ }
+
+ ///
+ /// Handles error responses.
+ ///
+ /// The HTTP context.
+ /// The error message.
+ /// The error code.
+ /// The HTTP status code.
+ /// A representing the asynchronous operation.
+ private async Task HandleErrorResponse(HttpContext context, string? message, string? errorCode, int statusCode)
+ {
+ var errorMessage = new HttpError(
+ message,
+ errorCode,
+ string.Format(
+ Responses.Target,
+ context.Request.Method,
+ context.Request.Scheme,
+ context.Request.Host.Host,
+ context.Request.Path));
+
+ logger.LogError(context, errorMessage, $"{settings.ApplicationName}-{settings.LayerName}");
+
+ context.Response.ContentType = MimeTypes.ApplicationJson;
+ context.Response.StatusCode = statusCode;
+
+ await context.Response.WriteAsync(JsonSerializer.Serialize(errorMessage)).ConfigureAwait(false);
+ }
+ }
+}
+
diff --git a/Core.Blueprint.Logging/Middleware/HttpLogger.cs b/Core.Blueprint.Logging/Middleware/HttpLogger.cs
new file mode 100644
index 0000000..e994839
--- /dev/null
+++ b/Core.Blueprint.Logging/Middleware/HttpLogger.cs
@@ -0,0 +1,237 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+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
+{
+ ///
+ /// Handles all logging scenarios.
+ ///
+ public static class HttpLogger
+ {
+ ///
+ /// The JSON serializer options for logging methods.
+ ///
+ public static JsonSerializerOptions serializerOptions = new JsonSerializerOptions
+ {
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ Converters = {
+ new JsonStringEnumConverter( JsonNamingPolicy.CamelCase),
+ },
+ };
+
+ ///
+ /// Logs an error message.
+ ///
+ /// The generic message parameter.
+ /// The HTTP context.
+ /// The message.
+ /// The service identifier.
+ public static void LogError(this ILogger logger, HttpContext context, TMessage message, string? serviceId)
+ {
+ var logMessage = CreateErrorLog(context, message, serviceId);
+ logger.Error(logMessage);
+ }
+
+ ///
+ /// Logs an information message.
+ ///
+ /// The generic message parameter.
+ /// The HTTP context.
+ /// The message.
+ /// The service identifier.
+ public static void LogInfo(this ILogger logger, HttpContext context, TMessage message, string? serviceId)
+ {
+ var logMessage = CreateInfoLog(context, message, serviceId);
+ logger.Information(logMessage);
+ }
+
+ ///
+ /// Logs an incoming HTTP request.
+ ///
+ /// The logger.
+ /// The HTTP context.
+ /// The recyclable mmory stream manager.
+ /// The service identifier.
+ /// A representing the asynchronous operation.
+ public static async Task LogRequest(
+ this ILogger logger,
+ HttpContext context,
+ RecyclableMemoryStreamManager recyclableMemoryStreamManager,
+ string? serviceId)
+ {
+ context.Request.EnableBuffering();
+ await using var requestStream = recyclableMemoryStreamManager.GetStream();
+ await context.Request.Body.CopyToAsync(requestStream);
+
+ var logMessage = CreateRequestLog(
+ context,
+ ReadStream(requestStream),
+ serviceId);
+ logger.Information(logMessage);
+
+ context.Request.Body.Position = 0;
+ }
+
+ ///
+ /// Logs an outcome HTTP response.
+ ///
+ /// The logger.
+ /// The HTTP context.
+ /// The recyclable mmory stream manager.
+ /// The request delegate process.
+ /// The service identifier.
+ /// A representing the asynchronous operation.
+ ///
+ public static async Task LogResponse(
+ this ILogger logger,
+ HttpContext context,
+ RecyclableMemoryStreamManager recyclableMemoryStreamManager,
+ RequestDelegate requestProcess,
+ string? serviceId)
+ {
+ var originalBodyStream = context.Response.Body;
+ await using var responseBody = recyclableMemoryStreamManager.GetStream();
+ context.Response.Body = responseBody;
+
+ await requestProcess(context);
+
+ context.Response.Body.Seek(0, SeekOrigin.Begin);
+ var text = await new StreamReader(context.Response.Body).ReadToEndAsync();
+ context.Response.Body.Seek(0, SeekOrigin.Begin);
+
+ var logMessage = CreateResponseLog(context, text, serviceId);
+ logger.Information(logMessage);
+
+ await responseBody.CopyToAsync(originalBodyStream);
+ }
+
+ ///
+ /// Creates an error log.
+ ///
+ /// The generic message.
+ /// The HTTP context.
+ /// The error message.
+ /// The service identifier.
+ /// A representig the error log.
+ private static string CreateErrorLog(HttpContext context, TMessage message, string? serviceId)
+ => CreateLog(context, LogSeverity.Error, LogOperation.Error, message, serviceId);
+
+ ///
+ /// Creates an info log.
+ ///
+ /// The generic message.
+ /// The HTTP context.
+ /// The info message.
+ /// The service identifier.
+ /// A representig the info log.
+ private static string CreateInfoLog(HttpContext context, TMessage message, string? serviceId)
+ => CreateLog(context, LogSeverity.Info, LogOperation.Info, message, serviceId);
+
+ ///
+ /// Creates a request log.
+ ///
+ /// The HTTP context.
+ /// The request body.
+ /// The service identifier.
+ /// A representig the request log.
+ private static string CreateRequestLog(HttpContext context, string? requestBody, string? serviceId)
+ => CreateLog(context, LogSeverity.Info, LogOperation.ClientRequest, requestBody, serviceId);
+
+ ///
+ /// Creates a response log.
+ ///
+ /// The HTTP context.
+ /// The response body.
+ /// The service identifier.
+ /// A representig the response log.
+ private static string CreateResponseLog(HttpContext context, string? responseBody, string? serviceId)
+ => CreateLog(context, LogSeverity.Info, LogOperation.ClientResponse, responseBody, serviceId);
+
+ ///
+ /// Creates a generic log.
+ ///
+ /// The HTTP context.
+ /// The log severity.
+ /// The log operation.
+ /// The log message
+ /// The service identifier.
+ /// A representing a generic log.
+ private static string CreateLog(
+ HttpContext context,
+ LogSeverity severity,
+ LogOperation operation,
+ TMessage message,
+ string? serviceId)
+ {
+ var tokenHeader = context.Request.Headers[Headers.Authorization].FirstOrDefault()?.Split(" ").Last();
+ var tokenHandler = new JwtSecurityTokenHandler();
+ var token = tokenHeader is not null ? tokenHandler.ReadJwtToken(tokenHeader) : null;
+
+ var log = new LogDetail
+ {
+ Severity = severity,
+ Target = new LogTarget
+ {
+ Method = context.Request.Method,
+ Host = context.Request.Host.Host,
+ Route = context.Request.Path,
+ },
+ Email = token?.Claims.FirstOrDefault(c => c.Type == Claims.Email)?.Value,
+ User = token?.Claims.FirstOrDefault(c => c.Type == Claims.Name)?.Value,
+ UserId = token?.Claims.FirstOrDefault(c => c.Type == Claims.Id)?.Value,
+ Environment = Environment.GetEnvironmentVariable(EnvironmentVariables.Stage),
+ Operation = operation,
+ RequestId = context.Request.Headers[DisplayNames.RequestId],
+ ServiceId = serviceId,
+ XForwardedFor = context.Request.Headers[DisplayNames.XForwardedForHeader],
+ Timestamp = DateTime.Now,
+ Message = message,
+ };
+
+ var serializedLog = JsonSerializer.Serialize(log, serializerOptions);
+
+ return serializedLog
+ .Replace("\\u0022", "\"")
+ .Replace("\"{", "{")
+ .Replace("}\"", "}")
+ .Replace("\\u0027", "'")
+ .Replace("\\\u0027", "'")
+ .Replace("\n", "");
+ }
+
+ ///
+ /// Reads the stream.
+ ///
+ /// The stream to be read.
+ /// A representig the request body.
+ private static string? ReadStream(Stream stream)
+ {
+ const int readChunkBufferLength = 4096;
+ stream.Seek(0, SeekOrigin.Begin);
+ using var textWriter = new StringWriter();
+ using var reader = new StreamReader(stream);
+ var readChunk = new char[readChunkBufferLength];
+ int readChunkLength;
+ do
+ {
+ readChunkLength = reader.ReadBlock(readChunk, 0, readChunkBufferLength);
+ textWriter.Write(readChunk, 0, readChunkLength);
+ } while (readChunkLength > 0);
+
+ var stringItem = textWriter.ToString();
+
+ return stringItem != string.Empty ? stringItem : null;
+ }
+ }
+}
+
diff --git a/Core.Blueprint.Logging/Middleware/HttpLoggingMiddleware.cs b/Core.Blueprint.Logging/Middleware/HttpLoggingMiddleware.cs
new file mode 100644
index 0000000..b0e4c9f
--- /dev/null
+++ b/Core.Blueprint.Logging/Middleware/HttpLoggingMiddleware.cs
@@ -0,0 +1,69 @@
+// ***********************************************************************
+//
+// Heath
+//
+// ***********************************************************************
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.IO;
+using Serilog;
+
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Handles HTTP logging.
+ ///
+ public class HttpLoggingMiddleware
+ {
+ private readonly ILogger logger;
+ private readonly RequestDelegate requestProcess;
+ private readonly ServiceSettings settings;
+ private readonly RecyclableMemoryStreamManager recyclableMemoryStreamManager;
+
+ ///
+ /// Creates a new instrance of .
+ ///
+ /// The request delegate process.
+ /// The logger representig an instance of .
+ /// The service settings.
+ public HttpLoggingMiddleware(RequestDelegate requestProcess, ILogger logger, ServiceSettings settings)
+ {
+ this.logger = logger;
+ this.requestProcess = requestProcess;
+ this.settings = settings;
+ recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
+ }
+
+ ///
+ /// Invoke method.
+ ///
+ /// The HTTP context.
+ ///
+ public async Task Invoke(HttpContext context)
+ {
+ await LogRequest(context);
+ await LogResponse(context);
+ }
+
+ ///
+ /// Logs an incoming HTTP request.
+ ///
+ /// The HTTP context.
+ /// A representing the asynchronous operation.
+ private async Task LogRequest(HttpContext context)
+ {
+ await logger.LogRequest(context, recyclableMemoryStreamManager, $"{settings.ApplicationName}-{settings.LayerName}");
+ }
+
+ ///
+ /// Logs an outcome HTTP response.
+ ///
+ /// The HTTP context.
+ /// A representing the asynchronous operation.
+ private async Task LogResponse(HttpContext context)
+ {
+ await logger.LogResponse(context, recyclableMemoryStreamManager, requestProcess, $"{settings.ApplicationName}-{settings.LayerName}");
+ }
+ }
+}
+
diff --git a/Core.Blueprint.Logging/Provider/LoggerProvider.cs b/Core.Blueprint.Logging/Provider/LoggerProvider.cs
new file mode 100644
index 0000000..7716839
--- /dev/null
+++ b/Core.Blueprint.Logging/Provider/LoggerProvider.cs
@@ -0,0 +1,89 @@
+namespace Core.Blueprint.Logging
+{
+ ///
+ /// Provides logging functionalities using Serilog.
+ ///
+ public class LoggerProvider : ILoggerProvider
+ {
+ private readonly Serilog.ILogger logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Serilog logger instance.
+ public LoggerProvider(Serilog.ILogger logger)
+ {
+ this.logger = logger;
+ }
+
+ ///
+ /// Logs an informational message for a specific service.
+ ///
+ /// The name of the service.
+ /// Additional arguments to include in the log.
+ public void LogInformation(string service, params object[] args)
+ {
+ logger.Information("Starting operation in {service} service", service, args);
+ }
+
+ ///
+ /// Logs a message indicating the start of an operation in a specific service.
+ ///
+ /// The name of the service.
+ /// Additional parameters associated with the operation.
+ public void LogOperationStarted(string service, params object[] args)
+ {
+ logger.Information("Starting operation in {Service} service with parameters: {@Args}", service, args);
+ }
+
+ ///
+ /// Logs a message indicating the completion of an operation in a specific service.
+ ///
+ /// The name of the service.
+ /// Additional parameters associated with the operation.
+ public void LogOperationFinished(string service, params object[] args)
+ {
+ logger.Information("Finishing operation in {Service} service with parameters: {@Args}", service, args);
+ }
+
+ ///
+ /// Logs a general informational message.
+ ///
+ /// The message to log.
+ public void LogInformation(string message)
+ {
+ logger.Information(message);
+ }
+
+ ///
+ /// Logs a warning message with additional context.
+ ///
+ /// The warning message to log.
+ /// Additional arguments to include in the log.
+ public void LogWarning(string message, params object[] args)
+ {
+ logger.Warning(message, args);
+ }
+
+ ///
+ /// Logs an error that occurred in a specific service.
+ ///
+ /// The name of the service.
+ /// Additional details about the error.
+ public void LogError(string service, params object[] args)
+ {
+ logger.Error("An error occurred in `{service}` Exception: {@Args}", service, args);
+ }
+
+ ///
+ /// Logs a critical error with an exception, message, and additional context.
+ ///
+ /// The exception associated with the critical error.
+ /// The critical error message.
+ /// Additional arguments to include in the log.
+ public void LogCritical(Exception exception, string message, params object[] args)
+ {
+ logger.Fatal(exception, message, args);
+ }
+ }
+}
diff --git a/Core.Blueprint.Mongo/Attributes/CollectionAttributeName.cs b/Core.Blueprint.Mongo/Attributes/CollectionAttributeName.cs
new file mode 100644
index 0000000..a70d174
--- /dev/null
+++ b/Core.Blueprint.Mongo/Attributes/CollectionAttributeName.cs
@@ -0,0 +1,24 @@
+namespace Core.Blueprint.Mongo
+{
+ ///
+ /// The 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.
+ ///
+ [AttributeUsage(AttributeTargets.Class)] // This attribute can only be applied to classes.
+ public class CollectionAttributeName : Attribute
+ {
+ ///
+ /// Gets or sets the name of the MongoDB collection that the class is mapped to.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Initializes a new instance of the class with the specified collection name.
+ ///
+ /// The name of the MongoDB collection that the class should be mapped to.
+ public CollectionAttributeName(string name)
+ {
+ Name = name ?? throw new ArgumentNullException(nameof(name), "Collection name cannot be null.");
+ }
+ }
+}
diff --git a/Core.Blueprint.Mongo/Configuration/IdentityProvider/HeathIdentityProvider.cs b/Core.Blueprint.Mongo/Configuration/IdentityProvider/HeathIdentityProvider.cs
new file mode 100644
index 0000000..1b72949
--- /dev/null
+++ b/Core.Blueprint.Mongo/Configuration/IdentityProvider/HeathIdentityProvider.cs
@@ -0,0 +1,121 @@
+using Azure.Core;
+using Azure.Identity;
+using MongoDB.Driver.Authentication.Oidc;
+
+namespace Core.Blueprint.Mongo.Configuration
+{
+ ///
+ /// The class is responsible for acquiring an OpenID Connect (OIDC)
+ /// access token for MongoDB authentication using Azure Identity and Managed Identity credentials.
+ ///
+ public class HeathIdentityProvider : IOidcCallback
+ {
+ ///
+ /// The audience (resource identifier) for which the OIDC token is being requested.
+ ///
+ private readonly string _audience;
+
+ ///
+ /// The environment in which the application is running (e.g., Development, Production).
+ ///
+ private readonly string _environment;
+
+ ///
+ /// Initializes a new instance of the class with the specified audience.
+ ///
+ /// The audience (resource identifier) for which the OIDC token is being requested.
+ public HeathIdentityProvider(string audience)
+ {
+ _audience = audience;
+ _environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty;
+ }
+
+ ///
+ /// Synchronously retrieves the OIDC access token to authenticate to MongoDB.
+ ///
+ /// The callback parameters provided for the OIDC request.
+ /// A token to cancel the operation.
+ /// An OIDC access token to authenticate to MongoDB.
+ /// Thrown if an error occurs during the token acquisition process.
+ 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}");
+ }
+ }
+
+ ///
+ /// Asynchronously retrieves the OIDC access token to authenticate to MongoDB.
+ ///
+ /// The callback parameters provided for the OIDC request.
+ /// A token to cancel the operation.
+ /// A task that represents the asynchronous operation, with an OIDC access token as the result.
+ /// Thrown if an error occurs during the token acquisition process.
+ public async Task 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}");
+ }
+ }
+ }
+}
diff --git a/Core.Blueprint.Mongo/Configuration/RegisterBlueprint.cs b/Core.Blueprint.Mongo/Configuration/RegisterBlueprint.cs
new file mode 100644
index 0000000..bd78c3b
--- /dev/null
+++ b/Core.Blueprint.Mongo/Configuration/RegisterBlueprint.cs
@@ -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
+{
+ ///
+ /// The class contains extension methods for registering the MongoDB context and configuration settings
+ /// to the in the dependency injection container.
+ ///
+ public static class RegisterBlueprint
+ {
+ ///
+ /// Adds the MongoDB layer services to the .
+ /// Registers the MongoDB context and configuration settings for MongoDB connection, database name, and audience.
+ ///
+ /// The to which the services will be added.
+ /// The used to load MongoDB settings.
+ /// The updated with MongoDB services registered.
+ public static IServiceCollection AddMongoLayer(this IServiceCollection services, IConfiguration configuration)
+ {
+ var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty;
+
+ services.AddSingleton();
+
+ 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(options =>
+ {
+ options.ConnectionString = ConnectionString;
+ options.Databasename = Databasename;
+ options.Audience = Audience;
+ });
+
+ services.AddSingleton(serviceProvider =>
+ {
+ var settings = serviceProvider.GetRequiredService>().Value;
+ var mongoClientSettings = MongoClientSettings.FromConnectionString(settings.ConnectionString);
+ mongoClientSettings.Credential = MongoCredential.CreateOidcCredential(new HeathIdentityProvider(settings.Audience));
+ return new MongoClient(mongoClientSettings);
+ });
+
+ services.AddSingleton(serviceProvider =>
+ {
+ var settings = serviceProvider.GetRequiredService>().Value;
+ var client = serviceProvider.GetRequiredService();
+ return client.GetDatabase(settings.Databasename);
+ });
+
+ services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService>().Value);
+
+ return services;
+ }
+ }
+}
diff --git a/Core.Blueprint.Mongo/Context/MongoContext.cs b/Core.Blueprint.Mongo/Context/MongoContext.cs
new file mode 100644
index 0000000..8561bbd
--- /dev/null
+++ b/Core.Blueprint.Mongo/Context/MongoContext.cs
@@ -0,0 +1,66 @@
+using Microsoft.Extensions.Configuration;
+
+namespace Core.Blueprint.Mongo
+{
+ ///
+ /// The class represents the MongoDB context that contains the connection information,
+ /// including the connection string, database name, and audience.
+ /// It implements the interface to provide methods for accessing these values.
+ ///
+ public sealed class MongoContext : IMongoContext
+ {
+ ///
+ /// Gets or sets the connection string used to connect to the MongoDB instance.
+ ///
+ public string ConnectionString { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the name of the MongoDB database.
+ ///
+ public string Databasename { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the audience (resource identifier) used for MongoDB authentication.
+ ///
+ public string Audience { get; set; } = string.Empty;
+
+ private readonly IConfiguration configuration;
+
+ ///
+ /// Initializes a new instance of the class using the provided .
+ /// The configuration is used to retrieve MongoDB connection settings.
+ ///
+ /// The configuration used to retrieve the MongoDB connection settings.
+ public MongoContext(IConfiguration configuration)
+ {
+ this.configuration = configuration;
+ }
+
+ ///
+ /// Retrieves the MongoDB connection string from the configuration.
+ ///
+ /// The MongoDB connection string, or an empty string if not found.
+ public string GetConnectionString()
+ {
+ return configuration.GetConnectionString("MongoDb:ConnectionString")?.ToString() ?? string.Empty;
+ }
+
+ ///
+ /// Retrieves the MongoDB database name from the configuration.
+ ///
+ /// The MongoDB database name, or an empty string if not found.
+ public string GetDatabasename()
+ {
+ return configuration.GetSection("MongoDb:DatabaseName").Value ?? string.Empty;
+ }
+
+ ///
+ /// Retrieves the MongoDB audience (resource identifier) from the configuration.
+ ///
+ /// The MongoDB audience, or an empty string if not found.
+ public string GetAudience()
+ {
+ return configuration.GetSection("MongoDb:Audience").Value ?? string.Empty;
+ }
+ }
+}
diff --git a/Core.Blueprint.Mongo/Context/MongoDbSettings.cs b/Core.Blueprint.Mongo/Context/MongoDbSettings.cs
new file mode 100644
index 0000000..2f803cb
--- /dev/null
+++ b/Core.Blueprint.Mongo/Context/MongoDbSettings.cs
@@ -0,0 +1,25 @@
+namespace Core.Blueprint.Mongo
+{
+ ///
+ /// Represents the MongoDB configuration settings, including the connection string,
+ /// database name, and audience, used for connecting and authenticating to a MongoDB instance.
+ /// Implements the interface to provide a strongly typed configuration.
+ ///
+ public class MongoDbSettings : IMongoDbSettings
+ {
+ ///
+ /// Gets or sets the connection string used to connect to the MongoDB instance.
+ ///
+ public string ConnectionString { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the name of the MongoDB database to connect to.
+ ///
+ public string Databasename { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the audience (resource identifier) used for MongoDB authentication.
+ ///
+ public string Audience { get; set; } = string.Empty;
+ }
+}
diff --git a/Core.Blueprint.Mongo/Contracts/ICollectionRepository.cs b/Core.Blueprint.Mongo/Contracts/ICollectionRepository.cs
new file mode 100644
index 0000000..644452d
--- /dev/null
+++ b/Core.Blueprint.Mongo/Contracts/ICollectionRepository.cs
@@ -0,0 +1,152 @@
+using MongoDB.Driver;
+using System.Linq.Expressions;
+
+namespace Core.Blueprint.Mongo
+{
+ ///
+ /// Interface for performing CRUD operations and queries on MongoDB collections.
+ /// The represents the type of documents in the collection,
+ /// which must implement the interface.
+ ///
+ /// The type of document in the MongoDB collection, must implement .
+ public interface ICollectionsRepository where TDocument : IDocument
+ {
+ ///
+ /// Retrieves all documents from the collection as an enumerable queryable result.
+ ///
+ /// A containing an representing the collection's documents.
+ ValueTask> AsQueryable();
+
+ ///
+ /// Filters the documents in the collection by the provided filter expression.
+ ///
+ /// An expression used to filter the documents based on the provided condition.
+ /// A representing the asynchronous operation, with a result of an of filtered documents.
+ Task> FilterBy(
+ Expression> filterExpression);
+
+ ///
+ /// Filters the documents in the collection by the provided filter expression and projects them to a different type.
+ ///
+ /// The type to project the documents into.
+ /// An expression used to filter the documents.
+ /// An expression used to project the filtered documents into the type.
+ /// An representing the projected documents.
+ IEnumerable FilterBy(
+ Expression> filterExpression,
+ Expression> projectionExpression);
+
+ ///
+ /// Filters documents in the collection based on the provided MongoDB filter definition.
+ ///
+ /// A filter definition for MongoDB query.
+ /// A task that represents the asynchronous operation. The task result contains a list of documents that match the filter.
+ Task> FilterByMongoFilterAsync(FilterDefinition filterDefinition);
+
+ ///
+ /// Finds a single document by the provided filter expression.
+ ///
+ /// An expression used to filter the documents.
+ /// The first matching or null if no match is found.
+ TDocument FindOne(Expression> filterExpression);
+
+ ///
+ /// Asynchronously finds a single document by the provided filter expression.
+ ///
+ /// An expression used to filter the documents.
+ /// A representing the asynchronous operation, with the matching or null.
+ Task FindOneAsync(Expression> filterExpression);
+
+ ///
+ /// Finds a document by its identifier.
+ ///
+ /// The identifier of the document.
+ /// The document with the provided identifier or null if not found.
+ TDocument FindById(string id);
+
+ ///
+ /// Asynchronously finds a document by its identifier.
+ ///
+ /// The identifier of the document.
+ /// A representing the asynchronous operation, with the matching or null.
+ Task FindByIdAsync(string id);
+
+ ///
+ /// Inserts a single document into the collection.
+ ///
+ /// The document to insert.
+ void InsertOne(TDocument document);
+
+ ///
+ /// Asynchronously inserts a single document into the collection.
+ ///
+ /// The document to insert.
+ /// A representing the asynchronous operation.
+ Task InsertOneAsync(TDocument document);
+
+ ///
+ /// Inserts multiple documents into the collection.
+ ///
+ /// The collection of documents to insert.
+ void InsertMany(ICollection documents);
+
+ ///
+ /// Asynchronously inserts multiple documents into the collection.
+ ///
+ /// The collection of documents to insert.
+ /// A representing the asynchronous operation.
+ Task InsertManyAsync(ICollection documents);
+
+ ///
+ /// Replaces an existing document with a new one.
+ ///
+ /// The document to replace the existing one.
+ void ReplaceOne(TDocument document);
+
+ ///
+ /// Asynchronously replaces an existing document with a new one.
+ ///
+ /// The document to replace the existing one.
+ /// A representing the asynchronous operation.
+ Task ReplaceOneAsync(TDocument document);
+
+ ///
+ /// Deletes a single document by the provided filter expression.
+ ///
+ /// An expression used to filter the documents to delete.
+ void DeleteOne(Expression> filterExpression);
+
+ ///
+ /// Asynchronously deletes a single document by the provided filter expression.
+ ///
+ /// An expression used to filter the documents to delete.
+ /// A representing the asynchronous operation.
+ Task DeleteOneAsync(Expression> filterExpression);
+
+ ///
+ /// Deletes a single document by its identifier.
+ ///
+ /// The identifier of the document to delete.
+ void DeleteById(string id);
+
+ ///
+ /// Asynchronously deletes a single document by its identifier.
+ ///
+ /// The identifier of the document to delete.
+ /// A representing the asynchronous operation.
+ Task DeleteByIdAsync(string id);
+
+ ///
+ /// Deletes multiple documents that match the provided filter expression.
+ ///
+ /// An expression used to filter the documents to delete.
+ void DeleteMany(Expression> filterExpression);
+
+ ///
+ /// Asynchronously deletes multiple documents that match the provided filter expression.
+ ///
+ /// An expression used to filter the documents to delete.
+ /// A representing the asynchronous operation.
+ Task DeleteManyAsync(Expression> filterExpression);
+ }
+}
diff --git a/Core.Blueprint.Mongo/Contracts/IDocument.cs b/Core.Blueprint.Mongo/Contracts/IDocument.cs
new file mode 100644
index 0000000..ae0d4d1
--- /dev/null
+++ b/Core.Blueprint.Mongo/Contracts/IDocument.cs
@@ -0,0 +1,39 @@
+using Core.Blueprint.Mongo;
+
+public interface IDocument
+{
+ ///
+ /// Gets or sets the MongoDB ObjectId for the document.
+ ///
+ string _Id { get; }
+
+ ///
+ /// Gets or sets a unique identifier for the document, represented as a string (GUID).
+ ///
+ string Id { get; }
+
+ ///
+ /// Gets or sets the timestamp of when the document was created.
+ ///
+ DateTime CreatedAt { get; }
+
+ ///
+ /// Gets or sets the user or system who created the document.
+ ///
+ string? CreatedBy { get; set; }
+
+ ///
+ /// Gets or sets the timestamp of when the document was last updated.
+ ///
+ DateTime? UpdatedAt { get; set; }
+
+ ///
+ /// Gets or sets the user or system who last updated the document.
+ ///
+ string? UpdatedBy { get; set; }
+
+ ///
+ /// Gets or sets the status of the document.
+ ///
+ StatusEnum? Status { get; set; }
+}
\ No newline at end of file
diff --git a/Core.Blueprint.Mongo/Contracts/IMongoContext.cs b/Core.Blueprint.Mongo/Contracts/IMongoContext.cs
new file mode 100644
index 0000000..410e446
--- /dev/null
+++ b/Core.Blueprint.Mongo/Contracts/IMongoContext.cs
@@ -0,0 +1,45 @@
+namespace Core.Blueprint.Mongo
+{
+ ///
+ /// Represents the context for interacting with MongoDB, providing access to connection-related information,
+ /// such as the connection string, database name, and audience for authentication.
+ ///
+ public interface IMongoContext
+ {
+ ///
+ /// Gets the connection string used to connect to the MongoDB instance.
+ ///
+ /// A string representing the MongoDB connection string.
+ string GetConnectionString();
+
+ ///
+ /// Gets the name of the MongoDB database.
+ ///
+ /// A string representing the MongoDB database name.
+ string GetDatabasename();
+
+ ///
+ /// Gets the audience (resource identifier) used for MongoDB authentication.
+ ///
+ /// A string representing the MongoDB audience (typically the resource identifier for authentication).
+ string GetAudience();
+
+ ///
+ /// Gets or sets the MongoDB connection string used to connect to the database.
+ ///
+ /// A string representing the MongoDB connection string.
+ string ConnectionString { get; set; }
+
+ ///
+ /// Gets or sets the name of the MongoDB database.
+ ///
+ /// A string representing the MongoDB database name.
+ string Databasename { get; set; }
+
+ ///
+ /// Gets or sets the audience (resource identifier) for MongoDB authentication.
+ ///
+ /// A string representing the MongoDB audience (resource identifier for authentication).
+ string Audience { get; set; }
+ }
+}
diff --git a/Core.Blueprint.Mongo/Contracts/IMongoDbSettings.cs b/Core.Blueprint.Mongo/Contracts/IMongoDbSettings.cs
new file mode 100644
index 0000000..fff23cc
--- /dev/null
+++ b/Core.Blueprint.Mongo/Contracts/IMongoDbSettings.cs
@@ -0,0 +1,32 @@
+namespace Core.Blueprint.Mongo
+{
+ ///
+ /// Represents the settings required to connect to a MongoDB instance, including the connection string,
+ /// database name, and audience used for authentication.
+ ///
+ public interface IMongoDbSettings
+ {
+ ///
+ /// 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.
+ ///
+ /// A string representing the MongoDB connection string.
+ string ConnectionString { get; set; }
+
+ ///
+ /// Gets or sets the name of the MongoDB database to connect to.
+ /// This value specifies which database to use within the MongoDB instance.
+ ///
+ /// A string representing the name of the MongoDB database.
+ string Databasename { get; set; }
+
+ ///
+ /// 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.
+ ///
+ /// A string representing the MongoDB audience (resource identifier for authentication).
+ string Audience { get; set; }
+ }
+}
diff --git a/Core.Blueprint.Mongo/Core.Blueprint.Mongo.csproj b/Core.Blueprint.Mongo/Core.Blueprint.Mongo.csproj
new file mode 100644
index 0000000..e75ebb1
--- /dev/null
+++ b/Core.Blueprint.Mongo/Core.Blueprint.Mongo.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core.Blueprint.Mongo/Entities/Document.cs b/Core.Blueprint.Mongo/Entities/Document.cs
new file mode 100644
index 0000000..3d164f0
--- /dev/null
+++ b/Core.Blueprint.Mongo/Entities/Document.cs
@@ -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
+{
+ ///
+ /// 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.
+ ///
+ [BsonId]
+ [BsonElement("_id")]
+ [BsonRepresentation(BsonType.ObjectId)]
+ [JsonPropertyName("_id")]
+ public string _Id { get; init; }
+
+ ///
+ /// 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.
+ ///
+ [BsonElement("id")]
+ [BsonRepresentation(BsonType.String)]
+ [JsonPropertyName("id")]
+ public string Id { get; init; }
+
+ ///
+ /// 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.
+ ///
+ [BsonElement("createdAt")]
+ [BsonRepresentation(BsonType.DateTime)]
+ [JsonPropertyName("createdAt")]
+ public DateTime CreatedAt { get; init; }
+
+ ///
+ /// Gets or sets the user or system who created the document.
+ /// This field can be used for audit purposes.
+ ///
+ [BsonElement("createdBy")]
+ [BsonRepresentation(BsonType.String)]
+ [JsonPropertyName("createdBy")]
+ public string? CreatedBy { get; set; }
+
+ ///
+ /// 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.
+ ///
+ [BsonElement("updatedAt")]
+ [BsonRepresentation(BsonType.DateTime)]
+ [JsonPropertyName("updatedAt")]
+ public DateTime? UpdatedAt { get; set; } = null;
+
+ ///
+ /// Gets or sets the user or system who last updated the document.
+ /// This field can be used for audit purposes.
+ ///
+ [BsonElement("updatedBy")]
+ [BsonRepresentation(BsonType.String)]
+ [JsonPropertyName("updatedBy")]
+ public string? UpdatedBy { get; set; } = null;
+
+ ///
+ /// Gets or sets the status of the document.
+ /// The status is represented by an enum and defaults to .
+ ///
+ [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;
+ }
+}
\ No newline at end of file
diff --git a/Core.Blueprint.Mongo/Entities/StatusEnum.cs b/Core.Blueprint.Mongo/Entities/StatusEnum.cs
new file mode 100644
index 0000000..f66938c
--- /dev/null
+++ b/Core.Blueprint.Mongo/Entities/StatusEnum.cs
@@ -0,0 +1,28 @@
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.Mongo
+{
+ ///
+ /// 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.
+ ///
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum StatusEnum
+ {
+ ///
+ /// Represents an active document or entity.
+ ///
+ Active = 0,
+
+ ///
+ /// Represents an inactive document or entity.
+ ///
+ Inactive = 1,
+
+ ///
+ /// Represents a deleted document or entity.
+ ///
+ Deleted = 2
+ }
+}
\ No newline at end of file
diff --git a/Core.Blueprint.Mongo/Provider/MongoProvider.cs b/Core.Blueprint.Mongo/Provider/MongoProvider.cs
new file mode 100644
index 0000000..1f1f764
--- /dev/null
+++ b/Core.Blueprint.Mongo/Provider/MongoProvider.cs
@@ -0,0 +1,40 @@
+using MongoDB.Driver;
+
+namespace Core.Blueprint.Mongo
+{
+ ///
+ /// 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.
+ ///
+ public class MongoProvider
+ {
+ private readonly IMongoDatabase _database;
+
+ ///
+ /// Initializes a new instance of the 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.
+ ///
+ /// The MongoDB settings required for connecting to the database.
+ public MongoProvider(IMongoDatabase database)
+ {
+ _database = database ?? throw new ArgumentNullException(nameof(database));
+ }
+
+ ///
+ /// Gets the initialized MongoDB database. If the database is not initialized, an exception is thrown.
+ ///
+ /// Thrown when the database connection is not initialized.
+ protected IMongoDatabase Database
+ {
+ get
+ {
+ if (_database == null)
+ {
+ throw new InvalidOperationException("MongoDB connection is not initialized.");
+ }
+ return _database;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Core.Blueprint.Mongo/Repositories/CollectionRepository.cs b/Core.Blueprint.Mongo/Repositories/CollectionRepository.cs
new file mode 100644
index 0000000..9214251
--- /dev/null
+++ b/Core.Blueprint.Mongo/Repositories/CollectionRepository.cs
@@ -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
+{
+ ///
+ /// Provides methods for interacting with a MongoDB collection for a specific document type.
+ /// Inherits from and implements .
+ /// This class encapsulates common database operations such as querying, inserting, updating, and deleting documents.
+ ///
+ /// The type of document stored in the collection, which must implement .
+ public class CollectionRepository(IMongoDatabase database) : ICollectionsRepository where TDocument : IDocument
+ {
+ private IMongoCollection _collection;
+
+ ///
+ /// Initializes the MongoDB collection based on the attribute
+ /// applied to the type. Throws an exception if the attribute is not present.
+ ///
+ public void CollectionInitialization()
+ {
+ var collectionAttribute = typeof(TDocument).GetCustomAttribute();
+ if (collectionAttribute == null)
+ {
+ throw new InvalidOperationException($"The class {typeof(TDocument).Name} is missing the CollectionAttributeName attribute.");
+ }
+
+ string collectionName = collectionAttribute.Name;
+
+ _collection = database.GetCollection(collectionName);
+ }
+
+ ///
+ /// Returns all documents from the collection as an enumerable list.
+ ///
+ /// A task that represents the asynchronous operation. The task result contains an enumerable list of documents.
+ public virtual async ValueTask> AsQueryable()
+ {
+ return await _collection.AsQueryable().ToListAsync();
+ }
+
+ ///
+ /// Filters documents in the collection based on the provided filter expression.
+ ///
+ /// A lambda expression that defines the filter criteria for the documents.
+ /// A task that represents the asynchronous operation. The task result contains a list of documents that match the filter.
+ public virtual async Task> FilterBy(
+ Expression> filterExpression)
+ {
+ var objectResult = await _collection.FindAsync(filterExpression).ConfigureAwait(false);
+ return objectResult.ToList();
+ }
+
+ ///
+ /// Filters documents in the collection based on the provided filter and projection expressions.
+ /// Projects the filtered documents into a new type.
+ ///
+ /// The type to project the documents into.
+ /// A lambda expression that defines the filter criteria for the documents.
+ /// A lambda expression that defines how the documents should be projected.
+ /// An enumerable collection of projected documents.
+ public virtual IEnumerable FilterBy(
+ Expression> filterExpression,
+ Expression> projectionExpression)
+ {
+ return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable();
+ }
+
+ ///
+ /// Finds a single document that matches the provided filter expression.
+ ///
+ /// A lambda expression that defines the filter criteria for the document.
+ /// The document that matches the filter, or null if no document is found.
+ public virtual TDocument FindOne(Expression> filterExpression)
+ {
+ return _collection.Find(filterExpression).FirstOrDefault();
+ }
+
+ ///
+ /// Filters documents in the collection based on the provided MongoDB filter definition.
+ ///
+ /// A filter definition for MongoDB query.
+ /// A task that represents the asynchronous operation. The task result contains a list of documents that match the filter.
+ public virtual async Task> FilterByMongoFilterAsync(FilterDefinition filterDefinition)
+ {
+ var objectResult = await _collection.Find(filterDefinition).ToListAsync().ConfigureAwait(false);
+ return objectResult;
+ }
+
+ ///
+ /// Asynchronously finds a single document that matches the provided filter expression.
+ ///
+ /// A lambda expression that defines the filter criteria for the document.
+ /// A task that represents the asynchronous operation. The task result contains the document that matches the filter, or null if no document is found.
+ public virtual Task FindOneAsync(Expression> filterExpression)
+ {
+ return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync());
+ }
+
+ ///
+ /// Finds a document by its unique identifier (ID).
+ ///
+ /// The unique identifier of the document.
+ /// The document that matches the specified ID, or null if no document is found.
+ public virtual TDocument FindById(string id)
+ {
+ var filter = Builders.Filter.Eq(doc => doc._Id, id);
+ return _collection.Find(filter).SingleOrDefault();
+ }
+
+ ///
+ /// Asynchronously finds a document by its unique identifier (ID).
+ ///
+ /// The unique identifier of the document.
+ /// A task that represents the asynchronous operation. The task result contains the document that matches the specified ID, or null if no document is found.
+ public virtual Task FindByIdAsync(string id)
+ {
+ return Task.Run(() =>
+ {
+ var objectId = new ObjectId(id);
+ var filter = Builders.Filter.Eq(doc => doc._Id, id);
+ return _collection.Find(filter).SingleOrDefaultAsync();
+ });
+ }
+
+ ///
+ /// Inserts a single document into the collection.
+ ///
+ /// The document to insert.
+ public virtual void InsertOne(TDocument document)
+ {
+ _collection.InsertOne(document);
+ }
+
+ ///
+ /// Asynchronously inserts a single document into the collection.
+ ///
+ /// The document to insert.
+ /// A task that represents the asynchronous operation.
+ public virtual Task InsertOneAsync(TDocument document)
+ {
+ return Task.Run(() => _collection.InsertOneAsync(document));
+ }
+
+ ///
+ /// Inserts multiple documents into the collection.
+ ///
+ /// The collection of documents to insert.
+ public void InsertMany(ICollection documents)
+ {
+ _collection.InsertMany(documents);
+ }
+
+ ///
+ /// Asynchronously inserts multiple documents into the collection.
+ ///
+ /// The collection of documents to insert.
+ /// A task that represents the asynchronous operation.
+ public virtual async Task InsertManyAsync(ICollection documents)
+ {
+ await _collection.InsertManyAsync(documents);
+ }
+
+ ///
+ /// Replaces an existing document in the collection.
+ ///
+ /// The document with the updated data.
+ public void ReplaceOne(TDocument document)
+ {
+ var filter = Builders.Filter.Eq(doc => doc._Id, document._Id);
+ _collection.FindOneAndReplace(filter, document);
+ }
+
+ ///
+ /// Asynchronously replaces an existing document in the collection.
+ ///
+ /// The document with the updated data.
+ /// A task that represents the asynchronous operation.
+ public virtual async Task ReplaceOneAsync(TDocument document)
+ {
+ var filter = Builders.Filter.Eq(doc => doc._Id, document._Id);
+ await _collection.FindOneAndReplaceAsync(filter, document);
+ }
+
+ ///
+ /// Deletes a single document from the collection based on the provided filter expression.
+ ///
+ /// A lambda expression that defines the filter criteria for the document to delete.
+ public void DeleteOne(Expression> filterExpression)
+ {
+ _collection.FindOneAndDelete(filterExpression);
+ }
+
+ ///
+ /// Asynchronously deletes a single document from the collection based on the provided filter expression.
+ ///
+ /// A lambda expression that defines the filter criteria for the document to delete.
+ /// A task that represents the asynchronous operation.
+ public async Task DeleteOneAsync(Expression> filterExpression)
+ {
+ return await _collection.FindOneAndDeleteAsync(filterExpression);
+ }
+
+ ///
+ /// Deletes a single document from the collection based on its unique identifier (ID).
+ ///
+ /// The unique identifier of the document to delete.
+ public void DeleteById(string id)
+ {
+ var objectId = new ObjectId(id);
+ var filter = Builders.Filter.Eq(doc => doc._Id, id);
+ _collection.FindOneAndDelete(filter);
+ }
+
+ ///
+ /// Asynchronously deletes a single document from the collection based on its unique identifier (ID).
+ ///
+ /// The unique identifier of the document to delete.
+ /// A task that represents the asynchronous operation.
+ public Task DeleteByIdAsync(string id)
+ {
+ return Task.Run(() =>
+ {
+ var objectId = new ObjectId(id);
+ var filter = Builders.Filter.Eq(doc => doc._Id, id);
+ _collection.FindOneAndDeleteAsync(filter);
+ });
+ }
+
+ ///
+ /// Deletes multiple documents from the collection based on the provided filter expression.
+ ///
+ /// A lambda expression that defines the filter criteria for the documents to delete.
+ public void DeleteMany(Expression> filterExpression)
+ {
+ _collection.DeleteMany(filterExpression);
+ }
+
+ ///
+ /// Asynchronously deletes multiple documents from the collection based on the provided filter expression.
+ ///
+ /// A lambda expression that defines the filter criteria for the documents to delete.
+ /// A task that represents the asynchronous operation.
+ public Task DeleteManyAsync(Expression> filterExpression)
+ {
+ return Task.Run(() => _collection.DeleteManyAsync(filterExpression));
+ }
+ }
+}
diff --git a/Core.Blueprint.Redis/Adapters/CacheSettings.cs b/Core.Blueprint.Redis/Adapters/CacheSettings.cs
new file mode 100644
index 0000000..0d357b4
--- /dev/null
+++ b/Core.Blueprint.Redis/Adapters/CacheSettings.cs
@@ -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; }
+ }
+ ///
+ /// Represents the settings for Redis caching.
+ ///
+ public class CacheSettings: ICacheSettings
+ {
+ ///
+ /// Gets or sets the default cache duration in minutes.
+ ///
+ public int DefaultCacheDurationInMinutes { get; set; }
+ }
+}
diff --git a/Core.Blueprint.Redis/Configuration/RegisterBlueprint.cs b/Core.Blueprint.Redis/Configuration/RegisterBlueprint.cs
new file mode 100644
index 0000000..3aeb596
--- /dev/null
+++ b/Core.Blueprint.Redis/Configuration/RegisterBlueprint.cs
@@ -0,0 +1,43 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Core.Blueprint.Redis.Configuration
+{
+ ///
+ /// Provides extension methods for registering Redis-related services in the DI container.
+ ///
+ public static class RegisterBlueprint
+ {
+ ///
+ /// Adds Redis caching services to the service collection.
+ ///
+ /// The service collection to register the services into.
+ /// The application configuration object.
+ /// The updated service collection.
+ 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(provider =>
+ new RedisCacheProvider(redisConnectionString, provider.GetRequiredService>()));
+
+ // Get CacheSettings and register with the ICacheSettings interface
+ var cacheSettings = configuration.GetSection("CacheSettings").Get();
+ if (cacheSettings == null)
+ {
+ throw new InvalidOperationException("Redis CacheSettings section is not configured.");
+ }
+ services.AddSingleton(cacheSettings);
+
+ return services;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Core.Blueprint.Redis/Contracts/IRedisCacheProvider.cs b/Core.Blueprint.Redis/Contracts/IRedisCacheProvider.cs
new file mode 100644
index 0000000..f9a7b5e
--- /dev/null
+++ b/Core.Blueprint.Redis/Contracts/IRedisCacheProvider.cs
@@ -0,0 +1,48 @@
+namespace Core.Blueprint.Redis
+{
+ ///
+ /// Interface for managing Redis cache operations.
+ ///
+ public interface IRedisCacheProvider
+ {
+ ///
+ /// Retrieves a cache item by its key.
+ ///
+ /// The type of the cached item.
+ /// The cache key.
+ /// The cached item, or default if not found.
+ ValueTask GetAsync(string key);
+
+ ///
+ /// Sets a cache item with the specified key and value.
+ ///
+ /// The type of the item to cache.
+ /// The cache key.
+ /// The item to cache.
+ /// The optional expiration time for the cache item.
+ /// A task representing the asynchronous operation.
+ ValueTask SetAsync(string key, TEntity value, TimeSpan? expiry = null);
+
+ ///
+ /// Removes a cache item by its key.
+ ///
+ /// The cache key.
+ /// A task representing the asynchronous operation.
+ ValueTask RemoveAsync(string key);
+
+ ///
+ /// Checks if a cache item exists for the specified key.
+ ///
+ /// The cache key.
+ /// True if the cache item exists; otherwise, false.
+ ValueTask ExistsAsync(string key);
+
+ ///
+ /// Refreshes the expiration time of a cache item if it exists.
+ ///
+ /// The cache key.
+ /// The new expiration time for the cache item.
+ /// A task representing the asynchronous operation.
+ ValueTask RefreshAsync(string key, TimeSpan? expiry = null);
+ }
+}
diff --git a/Core.Blueprint.Redis/Core.Blueprint.Redis.csproj b/Core.Blueprint.Redis/Core.Blueprint.Redis.csproj
new file mode 100644
index 0000000..e1322ba
--- /dev/null
+++ b/Core.Blueprint.Redis/Core.Blueprint.Redis.csproj
@@ -0,0 +1,18 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core.Blueprint.Redis/Helpers/RedisCacheKeyHelper.cs b/Core.Blueprint.Redis/Helpers/RedisCacheKeyHelper.cs
new file mode 100644
index 0000000..f7f53cb
--- /dev/null
+++ b/Core.Blueprint.Redis/Helpers/RedisCacheKeyHelper.cs
@@ -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
+{
+ ///
+ /// Helper class for generating consistent and normalized cache keys.
+ ///
+ public static class CacheKeyHelper
+ {
+ ///
+ /// Generates a cache key based on the instance, method name, and parameters.
+ ///
+ /// The instance of the class.
+ /// The method name related to the cache key.
+ /// The parameters used to generate the key.
+ /// A normalized cache key string.
+ 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();
+ }
+
+ ///
+ /// Normalizes a parameter value for use in a cache key.
+ ///
+ /// The parameter to normalize.
+ /// A normalized string representation of the parameter.
+ 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]", "_");
+ }
+ }
+}
diff --git a/Core.Blueprint.Redis/RedisCacheProvider.cs b/Core.Blueprint.Redis/RedisCacheProvider.cs
new file mode 100644
index 0000000..525e310
--- /dev/null
+++ b/Core.Blueprint.Redis/RedisCacheProvider.cs
@@ -0,0 +1,171 @@
+using Azure.Identity;
+using Microsoft.Extensions.Logging;
+using StackExchange.Redis;
+using System.Text.Json;
+
+namespace Core.Blueprint.Redis
+{
+ ///
+ /// Redis cache provider for managing cache operations.
+ ///
+ public sealed class RedisCacheProvider : IRedisCacheProvider
+ {
+ private IDatabase _cacheDatabase = null!;
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Redis connection string.
+ /// The logger instance for logging operations.
+ /// Thrown when connection string is null or empty.
+ public RedisCacheProvider(string connectionString, ILogger 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();
+ }
+
+ ///
+ /// Initializes and establishes a connection to Redis using the provided connection string.
+ ///
+ /// The Redis connection string.
+ /// An instance representing the Redis cache database.
+ /// Thrown when the connection to Redis fails. 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;
+ }
+ }
+
+ ///
+ /// Retrieves a cache item by its key.
+ ///
+ /// The type of the cached item.
+ /// The cache key.
+ /// The cached item of type , or default if not found.
+ public async ValueTask GetAsync(string key)
+ {
+ try
+ {
+ var value = await _cacheDatabase.StringGetAsync(key);
+ if (value.IsNullOrEmpty)
+ {
+ _logger.LogInformation($"Cache miss for key: {key}");
+ return default;
+ }
+
+ _logger.LogInformation($"Cache hit for key: {key}");
+ return JsonSerializer.Deserialize(value);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, $"Error getting cache item with key {key}");
+ throw;
+ }
+ }
+ ///
+ /// Sets a cache item with the specified key and value.
+ ///
+ /// The type of the item to cache.
+ /// The cache key.
+ /// The item to cache.
+ /// The optional expiration time for the cache item.
+ public async ValueTask SetAsync(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;
+ }
+ }
+
+ ///
+ /// Removes a cache item by its key.
+ ///
+ /// The cache key.
+ 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;
+ }
+ }
+
+ ///
+ /// Checks if a cache item exists for the specified key.
+ ///
+ /// The cache key.
+ /// True if the cache item exists; otherwise, false.
+ public async ValueTask 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;
+ }
+ }
+
+ ///
+ /// Refreshes the expiration time of a cache item if it exists.
+ ///
+ /// The cache key.
+ /// The new expiration time for the cache item.
+ 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;
+ }
+ }
+ }
+}
diff --git a/Core.Blueprint.SQLServer/Adapters/BaseSQLAdapter.cs b/Core.Blueprint.SQLServer/Adapters/BaseSQLAdapter.cs
new file mode 100644
index 0000000..9f1e438
--- /dev/null
+++ b/Core.Blueprint.SQLServer/Adapters/BaseSQLAdapter.cs
@@ -0,0 +1,65 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.SQLServer.Entities
+{
+ ///
+ /// Represents the base class for SQL Server entities, providing common properties for auditing and state management.
+ ///
+ public abstract class BaseSQLAdapter : IBaseSQLAdapter
+ {
+ ///
+ /// Gets or sets the identifier for the entity.
+ ///
+ [Key]
+ [JsonPropertyName("id")]
+ public int Id { get; init; }
+
+ ///
+ /// Gets or sets the unique identifier for the entity.
+ ///
+ [JsonPropertyName("guid")]
+ public string Guid { get; init; }
+
+ ///
+ /// Gets or sets the timestamp when the entity was created.
+ /// Default value is the current UTC time at the moment of instantiation.
+ ///
+ [JsonPropertyName("createdAt")]
+ public DateTime? CreatedAt { get; init; }
+
+ ///
+ /// Gets or sets the identifier of the user or system that created the entity.
+ ///
+ [JsonPropertyName("createdBy")]
+ public string? CreatedBy { get; set; }
+
+ ///
+ /// Gets or sets the timestamp when the entity was last updated.
+ /// Null if the entity has not been updated.
+ ///
+ [JsonPropertyName("updatedAt")]
+ public DateTime? UpdatedAt { get; set; }
+
+ ///
+ /// Gets or sets the identifier of the user or system that last updated the entity.
+ /// Null if the entity has not been updated.
+ ///
+ [JsonPropertyName("updatedBy")]
+ public string? UpdatedBy { get; set; }
+
+ ///
+ /// Gets or sets the status of the entity, indicating whether it is active, inactive, or in another state.
+ /// Default value is .
+ ///
+ [JsonPropertyName("status")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public StatusEnum Status { get; set; }
+
+ protected BaseSQLAdapter()
+ {
+ Guid = System.Guid.NewGuid().ToString();
+ CreatedAt = DateTime.UtcNow;
+ }
+ }
+}
diff --git a/Core.Blueprint.SQLServer/Adapters/StatusEnum.cs b/Core.Blueprint.SQLServer/Adapters/StatusEnum.cs
new file mode 100644
index 0000000..a7a72c8
--- /dev/null
+++ b/Core.Blueprint.SQLServer/Adapters/StatusEnum.cs
@@ -0,0 +1,29 @@
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.SQLServer.Entities
+{
+ ///
+ /// 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.
+ ///
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ public enum StatusEnum
+ {
+ ///
+ /// Indicates that the entity is currently active and operational.
+ ///
+ Active = 0,
+
+ ///
+ /// Indicates that the entity is currently inactive but still exists in the system.
+ /// Typically used for temporary deactivation or soft-offline states.
+ ///
+ Inactive = 1,
+
+ ///
+ /// 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.
+ ///
+ Deleted = 2
+ }
+}
diff --git a/Core.Blueprint.SQLServer/Configuration/RegisterBlueprint.cs b/Core.Blueprint.SQLServer/Configuration/RegisterBlueprint.cs
new file mode 100644
index 0000000..76d6af6
--- /dev/null
+++ b/Core.Blueprint.SQLServer/Configuration/RegisterBlueprint.cs
@@ -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
+{
+ ///
+ /// Provides extension methods for configuring SQL Server.
+ ///
+ public static class RegisterBlueprint
+ {
+ ///
+ /// Configures SQL Server services, including the database context and generic repository, for dependency injection.
+ ///
+ /// The service collection to which the SQL Server services will be added.
+ /// The application configuration object for accessing settings such as connection strings.
+ /// An updated with SQL Server services registered.
+ 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;
+ }
+ }
+}
diff --git a/Core.Blueprint.SQLServer/Contracts/IBaseSQLAdapter.cs b/Core.Blueprint.SQLServer/Contracts/IBaseSQLAdapter.cs
new file mode 100644
index 0000000..3b37b6a
--- /dev/null
+++ b/Core.Blueprint.SQLServer/Contracts/IBaseSQLAdapter.cs
@@ -0,0 +1,57 @@
+using Core.Blueprint.SQLServer.Entities;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+
+namespace Core.Blueprint.SQLServer
+{
+ ///
+ /// Defines the interface for SQL Server entities, providing common properties for auditing and state management.
+ ///
+ public interface IBaseSQLAdapter
+ {
+
+ ///
+ /// Gets or sets the identifier for the entity.
+ ///
+ [Key]
+ [JsonPropertyName("id")]
+ int Id { get; }
+
+ ///
+ /// Gets or sets the GUID for the entity.
+ ///
+ [JsonPropertyName("guid")]
+ string Guid { get; }
+
+ ///
+ /// Gets or sets the timestamp when the entity was created.
+ ///
+ [JsonPropertyName("createdAt")]
+ DateTime? CreatedAt { get; }
+
+ ///
+ /// Gets or sets the identifier of the user or system that created the entity.
+ ///
+ [JsonPropertyName("createdBy")]
+ string? CreatedBy { get; set; }
+
+ ///
+ /// Gets or sets the timestamp when the entity was last updated.
+ ///
+ [JsonPropertyName("updatedAt")]
+ DateTime? UpdatedAt { get; set; }
+
+ ///
+ /// Gets or sets the identifier of the user or system that last updated the entity.
+ ///
+ [JsonPropertyName("updatedBy")]
+ string? UpdatedBy { get; set; }
+
+ ///
+ /// Gets or sets the status of the entity, indicating whether it is active, inactive, or in another state.
+ ///
+ [JsonPropertyName("status")]
+ [JsonConverter(typeof(JsonStringEnumConverter))]
+ StatusEnum Status { get; set; }
+ }
+}
diff --git a/Core.Blueprint.SQLServer/Contracts/IEntityRepository.cs b/Core.Blueprint.SQLServer/Contracts/IEntityRepository.cs
new file mode 100644
index 0000000..865c1fc
--- /dev/null
+++ b/Core.Blueprint.SQLServer/Contracts/IEntityRepository.cs
@@ -0,0 +1,108 @@
+using Microsoft.EntityFrameworkCore;
+using System.Linq.Expressions;
+
+namespace Core.Blueprint.DAL.SQLServer
+{
+ ///
+ /// Defines the contract for a generic repository to manage entities in a SQL Server database.
+ ///
+ /// The type of the entity managed by the repository. Must be a class.
+ /// The type of the database context used by the repository. Must inherit from .
+ public interface IEntityRepository
+ where TEntity : class
+ where TContext : DbContext
+ {
+ ///
+ /// Retrieves all entities of type from the database.
+ ///
+ /// A task representing the asynchronous operation, with a collection of entities as the result.
+ Task> GetAllAsync();
+
+ ///
+ /// Retrieves all entities of type from the database that match a specified condition.
+ ///
+ /// An expression to filter the entities.
+ /// A task representing the asynchronous operation, with a collection of matching entities as the result.
+ Task> GetByConditionAsync(Expression> predicate);
+
+ ///
+ /// Retrieves a single entity of type by its identifier.
+ ///
+ /// The identifier of the entity to retrieve.
+ /// A task representing the asynchronous operation, with the entity as the result, or null if not found.
+ Task GetByIdAsync(int id);
+
+ ///
+ /// Retrieves the first entity of type that matches a specified condition, or null if no match is found.
+ ///
+ /// An expression to filter the entities.
+ /// A task representing the asynchronous operation, with the matching entity as the result, or null if none match.
+ Task FirstOrDefaultAsync(Expression> predicate);
+
+ ///
+ /// Adds a new entity of type to the database.
+ ///
+ /// The entity to add.
+ /// A task representing the asynchronous operation.
+ Task AddAsync(TEntity entity);
+
+ ///
+ /// Adds multiple entities of type to the database.
+ ///
+ /// The collection of entities to add.
+ /// A task representing the asynchronous operation.
+ Task AddRangeAsync(IEnumerable entities);
+
+ ///
+ /// Updates an existing entity of type in the database.
+ ///
+ /// The entity to update.
+ /// The updated entity.
+ TEntity Update(TEntity entity);
+
+ ///
+ /// Updates multiple entities of type in the database.
+ ///
+ /// The collection of entities to update.
+ void UpdateRange(IEnumerable entities);
+
+ ///
+ /// Deletes an entity of type from the database.
+ ///
+ /// The entity to delete.
+ void Delete(TEntity entity);
+
+ ///
+ /// Deletes multiple entities of type from the database.
+ ///
+ /// The collection of entities to delete.
+ void DeleteRange(IEnumerable entities);
+
+ ///
+ /// Determines whether any entities of type exist in the database that match a specified condition.
+ ///
+ /// An expression to filter the entities.
+ /// A task representing the asynchronous operation, with a boolean result indicating whether any match exists.
+ Task AnyAsync(Expression> predicate);
+
+ ///
+ /// Executes a raw SQL query and maps the result to entities of type .
+ ///
+ /// The raw SQL query to execute.
+ /// Optional parameters for the SQL query.
+ /// A task representing the asynchronous operation, with a collection of entities as the result.
+ Task> ExecuteRawSqlAsync(string sql, params object[] parameters);
+
+ ///
+ /// Counts the total number of entities of type in the database.
+ ///
+ /// A task representing the asynchronous operation, with the count as the result.
+ Task CountAsync();
+
+ ///
+ /// Saves all pending changes to the database.
+ ///
+ /// A task representing the asynchronous operation.
+ Task SaveAsync();
+ }
+}
diff --git a/Core.Blueprint.SQLServer/Core.Blueprint.SQLServer.csproj b/Core.Blueprint.SQLServer/Core.Blueprint.SQLServer.csproj
new file mode 100644
index 0000000..7dc396d
--- /dev/null
+++ b/Core.Blueprint.SQLServer/Core.Blueprint.SQLServer.csproj
@@ -0,0 +1,16 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
diff --git a/Core.Blueprint.SQLServer/Repositories/EntityRepository.cs b/Core.Blueprint.SQLServer/Repositories/EntityRepository.cs
new file mode 100644
index 0000000..f87f62b
--- /dev/null
+++ b/Core.Blueprint.SQLServer/Repositories/EntityRepository.cs
@@ -0,0 +1,182 @@
+using Microsoft.EntityFrameworkCore;
+using System.Linq.Expressions;
+
+namespace Core.Blueprint.DAL.SQLServer
+{
+ ///
+ /// The 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.
+ ///
+ /// The entity type managed by the repository. Must be a class.
+ /// The database context type. Must inherit from .
+ public class EntityRepository : IEntityRepository
+ where TEntity : class
+ where TContext : DbContext
+ {
+ private readonly TContext _context;
+ private readonly DbSet _dbSet;
+
+ ///
+ /// Initializes a new instance of the class with a specified database context.
+ ///
+ /// The for database operations.
+ public EntityRepository(TContext context)
+ {
+ _context = context;
+ _dbSet = _context.Set();
+ }
+
+ ///
+ /// Retrieves all entities of type from the database.
+ ///
+ /// A task representing the asynchronous operation, with a list of entities as the result.
+ public async Task> GetAllAsync()
+ {
+ return await _dbSet.ToListAsync();
+ }
+
+ ///
+ /// Retrieves all entities of type from the database that match a specified filter.
+ ///
+ /// An expression to filter entities.
+ /// A task representing the asynchronous operation, with a list of filtered entities as the result.
+ public async Task> GetByConditionAsync(Expression> predicate)
+ {
+ return await _dbSet.Where(predicate).ToListAsync();
+ }
+
+ ///
+ /// Retrieves a single entity of type by its identifier.
+ ///
+ /// The identifier of the entity.
+ /// A task representing the asynchronous operation, with the entity as the result, or null if not found.
+ public async Task GetByIdAsync(int id)
+ {
+ var existingEntity = await _dbSet.FindAsync(id);
+
+ if (existingEntity != null)
+ {
+ _context.Entry(existingEntity).State = EntityState.Detached;
+ }
+
+ return existingEntity;
+ }
+
+ ///
+ /// Adds a new entity to the database.
+ ///
+ /// The entity to add.
+ /// A task representing the asynchronous operation.
+ public async Task AddAsync(TEntity entity)
+ {
+ await _dbSet.AddAsync(entity);
+ }
+
+ ///
+ /// Adds multiple entities to the database.
+ ///
+ /// The collection of entities to add.
+ /// A task representing the asynchronous operation.
+ public async Task AddRangeAsync(IEnumerable entities)
+ {
+ await _dbSet.AddRangeAsync(entities);
+ }
+
+ ///
+ /// Updates an existing entity in the database.
+ ///
+ /// The entity to update.
+ /// The updated entity.
+ public TEntity Update(TEntity entity)
+ {
+ _dbSet.Attach(entity);
+ _context.Entry(entity).State = EntityState.Modified;
+ return entity;
+ }
+
+ ///
+ /// Updates multiple entities in the database.
+ ///
+ /// The collection of entities to update.
+ public void UpdateRange(IEnumerable entities)
+ {
+ foreach (var entity in entities)
+ {
+ _dbSet.Attach(entity);
+ _context.Entry(entity).State = EntityState.Modified;
+ }
+ }
+
+ ///
+ /// Deletes an entity from the database.
+ ///
+ /// The entity to delete.
+ public void Delete(TEntity entity)
+ {
+ if (_context.Entry(entity).State == EntityState.Detached)
+ {
+ _dbSet.Attach(entity);
+ }
+ _dbSet.Remove(entity);
+ }
+
+ ///
+ /// Deletes multiple entities from the database.
+ ///
+ /// The collection of entities to delete.
+ public void DeleteRange(IEnumerable entities)
+ {
+ _dbSet.RemoveRange(entities);
+ }
+
+ ///
+ /// Retrieves the first entity matching the specified condition or null if no match is found.
+ ///
+ /// An expression to filter entities.
+ /// A task representing the asynchronous operation, with the matched entity as the result.
+ public async Task FirstOrDefaultAsync(Expression> predicate)
+ {
+ return await _dbSet.FirstOrDefaultAsync(predicate);
+ }
+
+ ///
+ /// Determines if any entities exist that match the specified condition.
+ ///
+ /// An expression to filter entities.
+ /// A task representing the asynchronous operation, with a boolean result indicating existence.
+ public async Task AnyAsync(Expression> predicate)
+ {
+ return await _dbSet.AnyAsync(predicate);
+ }
+
+ ///
+ /// Saves all pending changes to the database.
+ ///
+ /// A task representing the asynchronous operation.
+ public async Task SaveAsync()
+ {
+ await _context.SaveChangesAsync();
+ }
+
+ ///
+ /// Executes a raw SQL query and maps the result to the specified entity type.
+ ///
+ /// The raw SQL query.
+ /// Optional parameters for the query.
+ /// An representing the result set.
+ public async Task> ExecuteRawSqlAsync(string sql, params object[] parameters)
+ {
+ return await _dbSet.FromSqlRaw(sql, parameters).ToListAsync();
+ }
+
+ ///
+ /// Counts the total number of entities in the database.
+ ///
+ /// A task representing the asynchronous operation, with the count as the result.
+ public async Task CountAsync()
+ {
+ return await _dbSet.CountAsync();
+ }
+ }
+}
diff --git a/Core.Blueprint.Storage/Adapters/BlobAddDto.cs b/Core.Blueprint.Storage/Adapters/BlobAddDto.cs
new file mode 100644
index 0000000..2040feb
--- /dev/null
+++ b/Core.Blueprint.Storage/Adapters/BlobAddDto.cs
@@ -0,0 +1,8 @@
+namespace Core.Blueprint.Storage
+{
+ public class BlobAddDto
+ {
+ public string? FileName { get; set; }
+ public byte[] FileContent { get; set; } = null!;
+ }
+}
diff --git a/Core.Blueprint.Storage/Adapters/BlobDownloadAdapter.cs b/Core.Blueprint.Storage/Adapters/BlobDownloadAdapter.cs
new file mode 100644
index 0000000..0fbed6c
--- /dev/null
+++ b/Core.Blueprint.Storage/Adapters/BlobDownloadAdapter.cs
@@ -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
+ {
+ }
+}
diff --git a/Core.Blueprint.Storage/Adapters/BlobDownloadUriAdapter.cs b/Core.Blueprint.Storage/Adapters/BlobDownloadUriAdapter.cs
new file mode 100644
index 0000000..f7e0716
--- /dev/null
+++ b/Core.Blueprint.Storage/Adapters/BlobDownloadUriAdapter.cs
@@ -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; }
+ }
+}
diff --git a/Core.Blueprint.Storage/Adapters/BlobFileAdapter.cs b/Core.Blueprint.Storage/Adapters/BlobFileAdapter.cs
new file mode 100644
index 0000000..3c08dab
--- /dev/null
+++ b/Core.Blueprint.Storage/Adapters/BlobFileAdapter.cs
@@ -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; }
+ }
+}
diff --git a/Core.Blueprint.Storage/Adapters/BlobStorageAdapter.cs b/Core.Blueprint.Storage/Adapters/BlobStorageAdapter.cs
new file mode 100644
index 0000000..be398da
--- /dev/null
+++ b/Core.Blueprint.Storage/Adapters/BlobStorageAdapter.cs
@@ -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;
+ }
+}
diff --git a/Core.Blueprint.Storage/Adapters/BlobStorageFolder.cs b/Core.Blueprint.Storage/Adapters/BlobStorageFolder.cs
new file mode 100644
index 0000000..a04262f
--- /dev/null
+++ b/Core.Blueprint.Storage/Adapters/BlobStorageFolder.cs
@@ -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 SubFolders { get; set; } = [];
+ public List Files { get; set; } = [];
+ }
+ public record BlobStorageFilesAdapter(string Content, string Name, string ContentType, string DownloadUrl);
+}
diff --git a/Core.Blueprint.Storage/Adapters/Trie/TrieNode.cs b/Core.Blueprint.Storage/Adapters/Trie/TrieNode.cs
new file mode 100644
index 0000000..09c1498
--- /dev/null
+++ b/Core.Blueprint.Storage/Adapters/Trie/TrieNode.cs
@@ -0,0 +1,66 @@
+
+namespace Core.Blueprint.Storage
+{
+ public class TrieNode
+ {
+ public Dictionary 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 SearchByPrefix(string? prefix)
+ {
+ var results = new List();
+ 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 results)
+ {
+ if (node.IsEndOfWord)
+ {
+ results.Add(currentPrefix);
+ }
+ foreach (var kvp in node.Children)
+ {
+ SearchByPrefixHelper(kvp.Value, currentPrefix + kvp.Key, results);
+ }
+ }
+ }
+}
diff --git a/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs b/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs
new file mode 100644
index 0000000..9a98bec
--- /dev/null
+++ b/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs
@@ -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();
+
+ return services;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs b/Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs
new file mode 100644
index 0000000..f47a099
--- /dev/null
+++ b/Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs
@@ -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
+{
+ ///
+ /// Defines a contract for managing blobs and containers in Azure Blob Storage.
+ ///
+ public interface IBlobStorageProvider
+ {
+ ///
+ /// Creates the blob container if it does not exist.
+ ///
+ /// A containing the container information.
+ Task> CreateIfNotExistsAsync();
+
+ ///
+ /// Deletes the blob container if it exists.
+ ///
+ Task DeleteIfExistsAsync();
+
+ ///
+ /// Gets properties of the blob container.
+ ///
+ /// A containing container properties.
+ Task> GetPropertiesAsync();
+
+ ///
+ /// Sets metadata for the blob container.
+ ///
+ /// The metadata to set for the container.
+ Task SetMetadataAsync(IDictionary metadata);
+
+ ///
+ /// Uploads a blob to the container.
+ ///
+ /// The name of the blob.
+ /// The content to upload.
+ /// A containing blob content information.
+ Task> UploadBlobAsync(string blobName, Stream content);
+
+ ///
+ /// Downloads a blob from the container.
+ ///
+ /// The name of the blob.
+ /// A containing blob download information.
+ /// Thrown if the blob does not exist.
+ Task> DownloadBlobAsync(string blobName);
+
+ ///
+ /// Deletes a blob from the container.
+ ///
+ /// The name of the blob.
+ Task DeleteBlobAsync(string blobName);
+
+ ///
+ /// Lists all blobs in the container with an optional prefix.
+ ///
+ /// The prefix to filter blobs.
+ /// A collection of .
+ Task> ListBlobItemAsync(string? prefix = null);
+
+ ///
+ /// Retrieves the account information for the associated Blob Service Client.
+ ///
+ ///
+ /// A that can be used to cancel the operation.
+ ///
+ ///
+ /// A task that represents the asynchronous operation. The task result contains the
+ /// object, which provides details about the account, such as the SKU
+ /// and account kind.
+ Task GetAccountInfoAsync(CancellationToken cancellation);
+
+ ///
+ /// Gets a blob client for a specific blob.
+ ///
+ /// The name of the blob.
+ /// A for the blob.
+ BlobClient GetBlobClient(string blobName);
+
+ ///
+ /// Lists blobs hierarchically using a delimiter.
+ ///
+ /// The prefix to filter blobs.
+ /// The delimiter to use for hierarchy.
+ /// A collection of .
+ Task> ListBlobsByHierarchyAsync(string? prefix = null, string delimiter = "/");
+
+ ///
+ /// Generates a SAS token for the container with specified permissions.
+ ///
+ /// The permissions to assign to the SAS token.
+ /// The expiration time for the SAS token.
+ /// A containing the SAS token.
+ /// Thrown if SAS URI generation is not supported.
+ Uri GenerateContainerSasUri(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn);
+
+ ///
+ /// Acquires a lease on the blob container.
+ ///
+ /// The optional proposed lease ID.
+ /// The optional lease duration.
+ /// A containing lease information.
+ Task> AcquireLeaseAsync(string? proposedId = null, TimeSpan? duration = null);
+
+ ///
+ /// Releases a lease on the blob container.
+ ///
+ /// The lease ID to release.
+ Task ReleaseLeaseAsync(string leaseId);
+
+ ///
+ /// Sets access policies for the blob container.
+ ///
+ /// The type of public access to allow.
+ /// The optional list of signed identifiers for access policy.
+ Task SetAccessPolicyAsync(PublicAccessType accessType, IEnumerable? identifiers = null);
+
+ ///
+ /// Lists blobs in the container with an optional prefix.
+ ///
+ /// The prefix to filter blobs.
+ /// A collection of .
+ Task> ListBlobsAsync(string? prefix = null);
+
+ ///
+ /// Uploads a blob to the container.
+ ///
+ /// The blob to upload.
+ /// A representing the uploaded blob.
+ Task UploadBlobAsync(BlobAddDto newBlob);
+
+ ///
+ /// Deletes a blob from the container.
+ ///
+ /// The name of the blob to delete.
+ /// A representing the deleted blob, or null if the blob was not found.
+ Task DeleteBlobsAsync(string fileName);
+
+ ///
+ /// Downloads a blob's content.
+ ///
+ /// The name of the blob.
+ /// A representing the downloaded blob.
+ /// Thrown if the blob does not exist.
+ Task DownloadBlobsAsync(string blobName);
+
+ ///
+ /// Generates a secure download URI for a specified blob in the storage container.
+ ///
+ /// The name of the blob for which the download URI is being generated.
+ ///
+ /// An instance of containing the generated URI, blob name, and status.
+ ///
+ ///
+ /// 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.
+ ///
+ /// Thrown if is null or empty.
+ /// Thrown if there is an issue communicating with the Azure Blob service.
+ BlobDownloadUriAdapter GenerateBlobDownloadUri(string blobName);
+
+ ///
+ /// Retrieves the hierarchical folder structure.
+ ///
+ /// The prefix to start the hierarchy retrieval.
+ /// A list of representing the folder structure.
+ Task> GetFolderHierarchyAsync(string prefix);
+
+ ///
+ /// Lists neighboring folders based on a prefix.
+ ///
+ /// The prefix to search for neighboring folders.
+ /// A dictionary grouping folder names by their prefix.
+ Task>> ListNeighborFoldersAsync(string? prefix);
+ }
+}
\ No newline at end of file
diff --git a/Core.Blueprint.Storage/Core.Blueprint.Storage.csproj b/Core.Blueprint.Storage/Core.Blueprint.Storage.csproj
new file mode 100644
index 0000000..e892e7d
--- /dev/null
+++ b/Core.Blueprint.Storage/Core.Blueprint.Storage.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core.Blueprint.Storage/Provider/BlobStorageProvider.cs b/Core.Blueprint.Storage/Provider/BlobStorageProvider.cs
new file mode 100644
index 0000000..5069a42
--- /dev/null
+++ b/Core.Blueprint.Storage/Provider/BlobStorageProvider.cs
@@ -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);
+ }
+
+ ///
+ /// Creates the blob container if it does not exist.
+ ///
+ public async Task> CreateIfNotExistsAsync()
+ {
+ return await _blobContainerClient.CreateIfNotExistsAsync();
+ }
+
+ ///
+ /// Deletes the blob container if it exists.
+ ///
+ public async Task DeleteIfExistsAsync()
+ {
+ await _blobContainerClient.DeleteIfExistsAsync();
+ }
+
+ ///
+ /// Gets properties of the blob container.
+ ///
+ public async Task> GetPropertiesAsync()
+ {
+ return await _blobContainerClient.GetPropertiesAsync();
+ }
+
+ ///
+ /// Sets metadata for the blob container.
+ ///
+ public async Task SetMetadataAsync(IDictionary metadata)
+ {
+ await _blobContainerClient.SetMetadataAsync(metadata);
+ }
+
+ ///
+ /// Uploads a blob to the container.
+ ///
+ public async Task> UploadBlobAsync(string blobName, Stream content)
+ {
+ var blobClient = _blobContainerClient.GetBlobClient(blobName);
+ return await blobClient.UploadAsync(content, overwrite: true);
+ }
+
+ ///
+ /// Downloads a blob from the container.
+ ///
+ public async Task> DownloadBlobAsync(string blobName)
+ {
+ var blobClient = _blobContainerClient.GetBlobClient(blobName);
+ return await blobClient.DownloadAsync();
+ }
+
+ ///
+ /// Deletes a blob from the container.
+ ///
+ public async Task DeleteBlobAsync(string blobName)
+ {
+ var blobClient = _blobContainerClient.GetBlobClient(blobName);
+ return await blobClient.DeleteIfExistsAsync();
+ }
+
+ ///
+ /// Lists all blobs in the container with an optional prefix.
+ ///
+ public async Task> ListBlobItemAsync(string? prefix = null)
+ {
+ var blobs = new List();
+
+ await foreach (var blobItem in _blobContainerClient.GetBlobsAsync(prefix: prefix))
+ {
+ blobs.Add(blobItem);
+ }
+
+ return blobs;
+ }
+
+ ///
+ /// Retrieves the account information for the associated Blob Service Client.
+ ///
+ ///
+ /// A that can be used to cancel the operation.
+ ///
+ ///
+ /// A task that represents the asynchronous operation. The task result contains the
+ /// object, which provides details about the account, such as the SKU
+ /// and account kind.
+ public async Task GetAccountInfoAsync(CancellationToken cancellation)
+ {
+ return await _blobServiceClient.GetAccountInfoAsync(cancellation);
+ }
+
+ ///
+ /// Gets a blob client for a specific blob.
+ ///
+ public BlobClient GetBlobClient(string blobName)
+ {
+ return _blobContainerClient.GetBlobClient(blobName);
+ }
+
+ ///
+ /// Lists blobs hierarchically using a delimiter.
+ ///
+ public async Task> ListBlobsByHierarchyAsync(string? prefix = null, string delimiter = "/")
+ {
+ var blobs = new List();
+
+ await foreach (var blobHierarchyItem in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: delimiter))
+ {
+ blobs.Add(blobHierarchyItem);
+ }
+
+ return blobs;
+ }
+
+ ///
+ /// Generates a SAS token for the container with specified permissions.
+ ///
+ 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);
+ }
+
+ ///
+ /// Acquires a lease on the blob container.
+ ///
+ public async Task> AcquireLeaseAsync(string? proposedId = null, TimeSpan? duration = null)
+ {
+ return await _blobContainerClient.GetBlobLeaseClient(proposedId).AcquireAsync(duration ?? TimeSpan.FromSeconds(60));
+ }
+
+ ///
+ /// Releases a lease on the blob container.
+ ///
+ public async Task ReleaseLeaseAsync(string leaseId)
+ {
+ await _blobContainerClient.GetBlobLeaseClient(leaseId).ReleaseAsync();
+ }
+
+ ///
+ /// Sets access policies for the blob container.
+ ///
+ public async Task SetAccessPolicyAsync(PublicAccessType accessType, IEnumerable? identifiers = null)
+ {
+ await _blobContainerClient.SetAccessPolicyAsync(accessType, identifiers);
+ }
+ ///
+ /// Lists blobs in the container with an optional prefix.
+ ///
+ public async Task> ListBlobsAsync(string? prefix = null)
+ {
+ var blobs = new List();
+
+ 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;
+ }
+
+ ///
+ /// Uploads a blob to the container.
+ ///
+ public async Task 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"
+ };
+ }
+
+ ///
+ /// Deletes a blob from the container.
+ ///
+ public async Task 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;
+ }
+
+ ///
+ /// Downloads a blob's content.
+ ///
+ public async Task 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();
+ }
+
+ ///
+ /// Generates a secure download URI for a specified blob in the storage container.
+ ///
+ /// The name of the blob for which the download URI is being generated.
+ ///
+ /// An instance of containing the generated URI, blob name, and status.
+ ///
+ ///
+ /// 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.
+ ///
+ /// Thrown if is null or empty.
+ /// Thrown if there is an issue communicating with the Azure Blob service.
+ 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"
+ };
+ }
+
+ ///
+ /// Retrieves the hierarchical folder structure.
+ ///
+ public async Task> GetFolderHierarchyAsync(string prefix)
+ {
+ var rootFolder = new BlobStorageFolder { Name = prefix };
+ await PopulateFolderAsync(rootFolder, prefix);
+ return new List { 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>> 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);
+ }
+ }
+ }
+ }
+}