From eda79010ce27352b28964f896e741004ffa3714a Mon Sep 17 00:00:00 2001 From: SergioMatias94 Date: Sun, 8 Jun 2025 18:20:34 -0600 Subject: [PATCH 1/3] Implement azurite --- .../Configuration/RegisterBlueprint.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs b/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs index 9a98bec..ee4997f 100644 --- a/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs +++ b/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs @@ -11,23 +11,31 @@ namespace Core.Blueprint.Storage.Configuration { public static IServiceCollection AddBlobStorage(this IServiceCollection services, IConfiguration configuration) { - var blobConnection = configuration.GetConnectionString("BlobStorage"); - if (blobConnection == null || string.IsNullOrWhiteSpace(blobConnection)) - { + if (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() - ); + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty; + services.AddAzureClients(cfg => { - cfg.AddBlobServiceClient(new Uri(blobConnection)).WithCredential(chainedCredentials); + if (environment == "Local") + { + cfg.AddBlobServiceClient(configuration.GetConnectionString("BlobStorage")); + } + else + { + var chainedCredentials = new ChainedTokenCredential( + new ManagedIdentityCredential(), + new SharedTokenCacheCredential(), + new VisualStudioCredential(), + new VisualStudioCodeCredential() + ); + + cfg.AddBlobServiceClient(new Uri(blobConnection)) + .WithCredential(chainedCredentials); + } }); services.AddScoped(); -- 2.49.1 From 626105cf0c8ad94119bcb5667656ea7062ca82f8 Mon Sep 17 00:00:00 2001 From: SergioMatias94 Date: Mon, 9 Jun 2025 00:39:20 -0600 Subject: [PATCH 2/3] Implement azurite --- .../Configuration/RegisterBlueprint.cs | 6 ++ .../Contracts/IBlobStorageProvider.cs | 2 +- .../Provider/BlobStorageProvider.cs | 72 +++++++++++++++++-- 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs b/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs index ee4997f..3dcf087 100644 --- a/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs +++ b/Core.Blueprint.Storage/Configuration/RegisterBlueprint.cs @@ -22,6 +22,12 @@ namespace Core.Blueprint.Storage.Configuration { if (environment == "Local") { + var accountKey = configuration.GetSection("BlobStorage:AccountKey").Value; + var accountName = configuration.GetSection("BlobStorage:AccountName").Value; + + if(string.IsNullOrEmpty(accountKey) && string.IsNullOrEmpty(accountName)) + throw new ArgumentException("The BlobStorage configuration section is missing or empty."); + cfg.AddBlobServiceClient(configuration.GetConnectionString("BlobStorage")); } else diff --git a/Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs b/Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs index f47a099..62fee8a 100644 --- a/Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs +++ b/Core.Blueprint.Storage/Contracts/IBlobStorageProvider.cs @@ -162,7 +162,7 @@ namespace Core.Blueprint.Storage.Contracts /// /// Thrown if is null or empty. /// Thrown if there is an issue communicating with the Azure Blob service. - BlobDownloadUriAdapter GenerateBlobDownloadUri(string blobName); + ValueTask GenerateBlobDownloadUri(string blobName); /// /// Retrieves the hierarchical folder structure. diff --git a/Core.Blueprint.Storage/Provider/BlobStorageProvider.cs b/Core.Blueprint.Storage/Provider/BlobStorageProvider.cs index 5069a42..ffc753a 100644 --- a/Core.Blueprint.Storage/Provider/BlobStorageProvider.cs +++ b/Core.Blueprint.Storage/Provider/BlobStorageProvider.cs @@ -1,4 +1,5 @@ using Azure; +using Azure.Storage; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Azure.Storage.Blobs.Specialized; @@ -6,6 +7,7 @@ using Azure.Storage.Sas; using Core.Blueprint.Storage.Adapters; using Core.Blueprint.Storage.Contracts; using Microsoft.Extensions.Configuration; +using System.Threading.Tasks; namespace Core.Blueprint.Storage.Provider { @@ -15,10 +17,12 @@ namespace Core.Blueprint.Storage.Provider private readonly BlobContainerClient _blobContainerClient; private readonly string _containerName; private readonly Trie _trie = new Trie(); + private readonly IConfiguration _configuration; public BlobStorageProvider(BlobServiceClient blobServiceClient, IConfiguration configuration) { _blobServiceClient = blobServiceClient; + _configuration = configuration; _containerName = configuration.GetSection("BlobStorage:ContainerName").Value ?? ""; if (string.IsNullOrEmpty(_containerName)) @@ -278,7 +282,8 @@ namespace Core.Blueprint.Storage.Provider /// /// The name of the blob for which the download URI is being generated. /// - /// An instance of containing the generated URI, blob name, and status. + /// An instance of containing the generated URI, blob name, and status, + /// or null if the blob does not exist. /// /// /// The generated URI includes a Shared Access Signature (SAS) token, which allows secure, time-limited access to the blob. @@ -286,22 +291,36 @@ namespace Core.Blueprint.Storage.Provider /// /// Thrown if is null or empty. /// Thrown if there is an issue communicating with the Azure Blob service. - public BlobDownloadUriAdapter GenerateBlobDownloadUri(string blobName) + public async ValueTask GenerateBlobDownloadUri(string blobName) { - var delegationKey = _blobServiceClient.GetUserDelegationKey(DateTimeOffset.UtcNow, - DateTimeOffset.UtcNow.AddHours(2)); + if (string.IsNullOrWhiteSpace(blobName)) + throw new ArgumentNullException(nameof(blobName), "Blob name cannot be null or empty."); var blob = _blobContainerClient.GetBlobClient(blobName); - var sasBuilder = new BlobSasBuilder() + if (!await blob.ExistsAsync()) + return null; + + var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty; + + if (environment == "Local") + { + return GenerateDownloadUri(blob); + } + + var delegationKey = await _blobServiceClient.GetUserDelegationKeyAsync( + DateTimeOffset.UtcNow, + DateTimeOffset.UtcNow.AddHours(2)); + + var sasBuilder = new BlobSasBuilder { BlobContainerName = blob.BlobContainerName, BlobName = blob.Name, Resource = "b", StartsOn = DateTimeOffset.UtcNow, - ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5), + ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5) }; - sasBuilder.SetPermissions(BlobAccountSasPermissions.Read); + sasBuilder.SetPermissions(BlobSasPermissions.Read); sasBuilder.Protocol = SasProtocol.Https; var blobUriBuilder = new BlobUriBuilder(blob.Uri) @@ -317,6 +336,45 @@ namespace Core.Blueprint.Storage.Provider }; } + + /// + /// Generates a download URI for a blob using a Shared Access Signature in local (Azurite) environment. + /// + /// The blob client for which the URI is being generated. + /// An instance of containing the SAS URI and metadata. + private BlobDownloadUriAdapter GenerateDownloadUri(BlobClient blob) + { + var sasBuilder = new BlobSasBuilder + { + BlobContainerName = blob.BlobContainerName, + BlobName = blob.Name, + Resource = "b", + StartsOn = DateTimeOffset.UtcNow, + ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5) + }; + sasBuilder.SetPermissions(BlobSasPermissions.Read); + sasBuilder.Protocol = SasProtocol.HttpsAndHttp; + + var accountName = _configuration["BlobStorage:AccountName"]; + var accountKey = _configuration["BlobStorage:AccountKey"]; + + var storageCredentials = new StorageSharedKeyCredential(accountName, accountKey); + var sasToken = sasBuilder.ToSasQueryParameters(storageCredentials); + + var blobUriBuilder = new BlobUriBuilder(blob.Uri) + { + Sas = sasToken + }; + + return new BlobDownloadUriAdapter + { + Uri = blobUriBuilder.ToUri(), + Name = blob.Name, + Status = "Available" + }; + } + + /// /// Retrieves the hierarchical folder structure. /// -- 2.49.1 From 5935e877046103be5d13f335e12f59ea0172e98a Mon Sep 17 00:00:00 2001 From: Oscar Morales Date: Tue, 17 Jun 2025 15:12:10 -0600 Subject: [PATCH 3/3] Adapt to create packages --- Core.Blueprint.Mongo/nuget.config | 12 ++++++++++++ Core.Blueprint.Redis/Core.Blueprint.Caching.csproj | 1 + 2 files changed, 13 insertions(+) create mode 100644 Core.Blueprint.Mongo/nuget.config diff --git a/Core.Blueprint.Mongo/nuget.config b/Core.Blueprint.Mongo/nuget.config new file mode 100644 index 0000000..0604bd0 --- /dev/null +++ b/Core.Blueprint.Mongo/nuget.config @@ -0,0 +1,12 @@ + + + + + + + + oscarmmtz + 544831e1ceaf52958e02c5de4d23cbde9e7a860a + + + diff --git a/Core.Blueprint.Redis/Core.Blueprint.Caching.csproj b/Core.Blueprint.Redis/Core.Blueprint.Caching.csproj index 2b15ab8..8109d2c 100644 --- a/Core.Blueprint.Redis/Core.Blueprint.Caching.csproj +++ b/Core.Blueprint.Redis/Core.Blueprint.Caching.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + Core.Blueprint.Redis -- 2.49.1