From aebab308da7d8dd46f78430ed2339d548c1b64c0 Mon Sep 17 00:00:00 2001 From: Sergio Barriel Date: Fri, 7 Mar 2025 09:44:21 +0100 Subject: [PATCH 1/2] merged "enumerate" method and updated README.md --- README.md | 100 ++++++++++-------- .../Should/BaseShould.cs | 3 - .../Download/DownloadBlobReferenceShould.cs | 41 ++++++- .../Download/DownloadBlobStreamShould.cs | 1 + .../DownloadBlobUriVariationsShould.cs | 1 + ...erateShould.cs => EnumerateBlobsShould.cs} | 59 ++++++----- .../AzureStorageWrapper.cs | 45 +------- .../AzureStorageWrapper.csproj | 2 +- .../Commands/EnumerateAllBlobs.cs | 15 --- .../IAzureStorageWrapper.cs | 8 +- .../{Commands => Queries}/DownloadBlob.cs | 2 +- .../DownloadBlobReference.cs | 2 +- .../{Commands => Queries}/EnumerateBlobs.cs | 10 +- .../{Commands => Queries}/GetSasUri.cs | 2 +- 14 files changed, 143 insertions(+), 148 deletions(-) rename src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Enumerate/{EnumerateShould.cs => EnumerateBlobsShould.cs} (61%) delete mode 100644 src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateAllBlobs.cs rename src/AzureStorageWrapper/AzureStorageWrapper/{Commands => Queries}/DownloadBlob.cs (93%) rename src/AzureStorageWrapper/AzureStorageWrapper/{Commands => Queries}/DownloadBlobReference.cs (95%) rename src/AzureStorageWrapper/AzureStorageWrapper/{Commands => Queries}/EnumerateBlobs.cs (54%) rename src/AzureStorageWrapper/AzureStorageWrapper/{Commands => Queries}/GetSasUri.cs (95%) diff --git a/README.md b/README.md index 350cc11..21d20a6 100644 --- a/README.md +++ b/README.md @@ -18,23 +18,22 @@ To add **AzureStorageWrapper** to dependencies container, just use the method `AddAzureStorageWrapper(...)` ```csharp -public void ConfigureServices(IServiceCollection serviceCollection) +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddAzureStorageWrapper(configuration => { - serviceCollection.AddAzureStorageWrapper(configuration => - { - configuration.ConnectionString = "azure-storage-connection-string" - configuration.MaxSasUriExpiration = 600; - configuration.DefaultSasUriExpiration = 300; - configuration.CreateContainerIfNotExists = true; - }); -} + configuration.ConnectionString = "azure-storage-connection-string"; + configuration.MaxSasUriExpiration = 600; + configuration.DefaultSasUriExpiration = 300; + configuration.CreateContainerIfNotExists = true; +}); ``` These are the *main* properties: -- **ConnectionString**: The connection string of your Azure Storage Account. You can export by following [this document](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal#view-account-access-keys) -- **MaxSasUriExpiration**: You can set a maximum duration value for the Shared Access Signature (SAS) of an Azure Storage file to prevent someone from attempting to generate a token with a longer expiration time. Expressed in seconds. -- **DefaultSasUriExpiration**: You can download a file using AzureStorageWrapper without specifying the `ExpiresIn` property. By doing so, this value will be automatically set. Expressed in seconds -- **CreateContainerIfNotExists**: When uploading a file to Azure Storage, you need to specify the container, which may not exist and can be created automatically. You can set it to `true` or `false` based on your requirements. Please consider this property if you have automated your infrastructure with any Infrastructure as Code (IaC) mechanism because it affects the state of your infrastructure. +- **ConnectionString**: The connection string of your Azure Storage Account. You can export it by following [this documentation](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal#view-account-access-keys) +- **MaxSasUriExpiration**: You can set a maximum duration value for the Shared Access Signature (SAS) of an Azure Storage file to prevent someone from attempting to generate a token with a longer expiration time. The duration is expressed in seconds. +- **DefaultSasUriExpiration**: You can download a file using AzureStorageWrapper without specifying the `ExpiresIn` property. By doing so, this value will be automatically set. The duration is xpressed in seconds +- **CreateContainerIfNotExists**: When uploading a file to Azure Storage, you need to specify the container, which may not exist and can be created automatically. You can set it to `true` or `false` based on your requirements. Consider this property if you have automated your infrastructure with an Infrastructure as Code (IaC) mechanism because it affects the state of your infrastructure. Then you can inject `IAzureStorageWrapper` into your services through constructor: @@ -54,11 +53,13 @@ public class MyService There are **3 options** to upload blobs, all the ways follow the same pattern: -- You need to specify the *file name* and *extension*. -- You need to specify the *container* where you want to store the file. -- You can add additional *metadata* with relevant information. +- You need to specify the `Name` and `Extension`. +- You need to specify the `Container` where you want to store the file. +- You can add additional `Metadata` with relevant information. + +The file will be placed in `Base64`, `Bytes` or `Stream` property. -The file will be placed in *Base64*, *Bytes* or *Stream* property. +Optionally you can specify the `UseVirtualFolder` property to save the file in a virtual folder. By default, it is set to `true`. We delve deeper into this point later. ### Base64 @@ -113,7 +114,7 @@ var response = await _azureStorageWrapper.UploadBlobAsync(command); ## Response after upload blobs -Regardless of the chosen upload mechanism, you will always receive this response after upload a file. +Regardless of the chosen upload mechanism, you will always receive a `BlobReference` object after uploading a file. ```csharp public class BlobReference @@ -145,15 +146,21 @@ In example, if you upload the file `hello.md` file to container `files` you will } ``` -It is your responsibility to save the reference (URI property) of the file you have uploaded to Azure Storage somewhere, as you will need it for later downloads. +> It is your responsibility to save the reference (URI property) of the file you have uploaded to Azure Storage somewhere, as you will need it for later downloads. ## Virtual Folders -The upload commands have a property called `UseVirtualFolder` which by default has a value of `true` but you can set it to `false` if you wish. +By default, files are stored in the desired container using **virtual folders**, allowing you to upload files with the same name without risking name collisions. -**Be careful.** If you make that change, the files will NOT be saved in virtual directories, and file names may collide, causing files to be overwritten. +For example, a virtual folder with a unique identifier is automatically created between the container name `files` and the file name `hello.md`, resulting in a URI like this: -> In this case, you must be responsible for establishing your own mechanism to generate unique file names. +`https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md` + +However, you can customize the `UseVirtualFolder` property, which by default has a value of `true` but you can set it to `false` if you wish. + +⚠️ When `UseVirtualFolder` is set to `false`, files will **NOT** be stored in virtual directories. This change may lead to file name collisions, causing files to be **overwritten**. + +In this scenario, you must implement your own mechanism to generate unique file names. ```csharp var base64 = "SGVsbG8g8J+Zgg=="; @@ -162,7 +169,7 @@ var command = new UploadBase64() { Base64 = base64, Container = "files", - Name = "hello", + Name = $"{Guid.NewGuid()}", Extension = "md", UseVirtualFolder = false // be careful! }; @@ -172,16 +179,16 @@ var response = await _azureStorageWrapper.UploadBlobAsync(command); ## Download blob references -To download a blob reference, you need specify the *Uri*. +To download a blob reference, you need to specify the `Uri`, which you should have stored in your system in some way ```csharp -var command = new DownloadBlobReference() +var query = new DownloadBlobReference() { Uri = "https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md" ExpiresIn = 60, }; -var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); +var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(query); ``` The response when *downloading* file reference resembles the response when *uploading* files: @@ -202,7 +209,7 @@ The response when *downloading* file reference resembles the response when *uplo ## Delete blobs -You can delete a blob by specifying the *Uri*. +You can delete a blob by specifying the `Uri`. ```csharp var command = new DeleteBlob() @@ -215,53 +222,58 @@ await _azureStorageWrapper.DeleteBlobAsync(command); ## Enumerate blobs -You can list all blobs in a container by using the method `EnumerateAllBlobsAsync` or paginate the results by using `EnumerateBlobsAsync` method. The second one requires the `Size` property to be set. +You can list all blobs in a container by using the method `EnumerateBlobsAsync`. -In both cases, the response `BlobReferenceCollection` will contain a collection of `BlobReference` elements. +The response `BlobReferenceCollection` will contain a collection of `BlobReference` elements. ### Without pagination +You should only run this query if you are certain that your container stores a small number of blobs. + ```csharp -var command = new EnumerateAllBlobs() +var query = new EnumerateBlobs() { - Container = "files" + Container = "files", + Paginate = false }; -var response = await _azureStorageWrapper.EnumerateAllBlobsAsync(command); - - +var response = await _azureStorageWrapper.EnumerateAllBlobsAsync(query); ``` + ### With pagination ```csharp -var command = new EnumerateBlobs() +var query = new EnumerateBlobs() { - Container = "files" + Container = "files", + Paginate = true. Size = 10, }; -var response = await _azureStorageWrapper.EnumerateBlobsAsync(command); +var response = await _azureStorageWrapper.EnumerateBlobsAsync(query); ``` Then you can request additional pages by using the `ContinuationToken` property in the next request. ```csharp -var firstCommand = new EnumerateBlobs() +var firstQuery = new EnumerateBlobs() { - Container = "files" + Container = "files", + Paginate = true, Size = 10, }; -var firstResponse = await _azureStorageWrapper.EnumerateBlobsAsync(firstCommand); +var firstResponse = await _azureStorageWrapper.EnumerateBlobsAsync(firstQuery); -var secondCommand = new EnumerateBlobs() +var secondQuery = new EnumerateBlobs() { - Container = "files" + Container = "files", + Paginate = true, Size = 10, ContinuationToken = firstResponse.ContinuationToken }; -var secondResponse = await _azureStorageWrapper.EnumerateBlobsAsync(secondCommand); +var secondResponse = await _azureStorageWrapper.EnumerateBlobsAsync(secondQuery); ``` # Contributors / Collaborators @@ -278,4 +290,4 @@ If you like the project, you can consider making a donation at [ko-fi.com](https # Support -You can contact me via Twitter [@sergiobarriel](https://twitter.com/sergiobarriel), or if you have an [issue](https://github.com/sergiobarriel/AzureStorageWrapper/issues), you can open one 🙂 +You can contact me via Bluesky [@sergiobarriel.bsky.social](https://bsky.app/profile/sergiobarriel.bsky.social) or Twitter [@sergiobarriel](https://twitter.com/sergiobarriel), or if you have an [issue](https://github.com/sergiobarriel/AzureStorageWrapper/issues), you can open one 🙂 diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/BaseShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/BaseShould.cs index 296c16b..8170a46 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/BaseShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/BaseShould.cs @@ -2,9 +2,6 @@ { public class BaseShould { - internal const string ContainerWhereUploadFiles = "files"; - internal const string ContainerWhereUploadFilesAndEnumerateThem = "files-to-enumerate"; - protected static async Task PingAsync(string uri) { using var httpClient = new HttpClient(); diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobReferenceShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobReferenceShould.cs index 68dfaa1..612031d 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobReferenceShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobReferenceShould.cs @@ -1,5 +1,6 @@ using AzureStorageWrapper.Commands; using AzureStorageWrapper.Exceptions; +using AzureStorageWrapper.Queries; using AzureStorageWrapper.Tests.Sources; using Xunit; @@ -14,6 +15,39 @@ public DownloadBlobReferenceShould(IAzureStorageWrapper azureStorageWrapper) _azureStorageWrapper = azureStorageWrapper; } + [Fact] + public async Task DownloadBlobReference_Should_ReturnReference() + { + // Arrange + + var base64 = "SGVsbG8g8J+Zgg=="; + + var uploadBlobCommand = new UploadBase64() + { + Base64 = base64, + Container = "files", + Name = "hello", + Extension = "md", + Metadata = new Dictionary() + {{"hello", "world"}} + }; + + var uploadBlobResponse = await _azureStorageWrapper.UploadBlobAsync(uploadBlobCommand); + + var downloadBlobReferenceCommand = new DownloadBlobReference() + { + Uri = uploadBlobResponse.Uri, + ExpiresIn = 360, + }; + + // Act + var blobReference = await _azureStorageWrapper.DownloadBlobReferenceAsync(downloadBlobReferenceCommand); + + // Assert + Assert.NotNull(blobReference); + Assert.True(await PingAsync(blobReference.SasUri)); + } + [Fact] public async Task DownloadBlobReference_WithManyDots_Should_ReturnReference() { @@ -76,8 +110,9 @@ public async Task DownloadBlobReference_WithExtensions_Should_ReturnReference() Assert.True(await PingAsync(blobReference.SasUri)); } - [Fact] - public async Task DownloadBlobReference_Should_ReturnReference() + [Theory] + [MemberData(nameof(InvalidExpiresIn))] + public async Task DownloadBlobReference_WithInvalidExpires_Should_DownloadBlobReferences(int expires) { // Arrange @@ -98,7 +133,7 @@ public async Task DownloadBlobReference_Should_ReturnReference() var downloadBlobReferenceCommand = new DownloadBlobReference() { Uri = uploadBlobResponse.Uri, - ExpiresIn = 360, + ExpiresIn = expires, }; // Act diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobStreamShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobStreamShould.cs index b6fbe1f..c8ebbc3 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobStreamShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobStreamShould.cs @@ -1,5 +1,6 @@ using AzureStorageWrapper.Commands; using AzureStorageWrapper.Exceptions; +using AzureStorageWrapper.Queries; using AzureStorageWrapper.Tests.Sources; using Xunit; diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobUriVariationsShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobUriVariationsShould.cs index 84afb03..77785fe 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobUriVariationsShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobUriVariationsShould.cs @@ -1,4 +1,5 @@ using AzureStorageWrapper.Commands; +using AzureStorageWrapper.Queries; using AzureStorageWrapper.Tests.Sources; using Xunit; diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Enumerate/EnumerateShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Enumerate/EnumerateBlobsShould.cs similarity index 61% rename from src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Enumerate/EnumerateShould.cs rename to src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Enumerate/EnumerateBlobsShould.cs index db3930c..039837f 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Enumerate/EnumerateShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Enumerate/EnumerateBlobsShould.cs @@ -1,40 +1,46 @@ using AzureStorageWrapper.Commands; +using AzureStorageWrapper.Queries; using Xunit; using Xunit.Abstractions; namespace AzureStorageWrapper.Tests.Should.Enumerate { - public class EnumerateShould : BaseShould + public class EnumerateBlobsShould : BaseShould { private readonly IAzureStorageWrapper _azureStorageWrapper; private readonly ITestOutputHelper _output; - public EnumerateShould(IAzureStorageWrapper azureStorageWrapper, ITestOutputHelper output) + public EnumerateBlobsShould(IAzureStorageWrapper azureStorageWrapper, ITestOutputHelper output) { _azureStorageWrapper = azureStorageWrapper; _output = output; } [Fact] - public async Task EnumerateBlobs_ShouldReturnAllBlobsFromAContainer() + public async Task EnumerateBlobs_WithoutPagination_Should_ReturnAllBlobsFromContainer() { var amount = 10; + var container = "without-pagination"; - await UploadFilesAsync(amount); + await UploadFilesAsync(amount, container); - var command = new EnumerateAllBlobs() + var query = new EnumerateBlobs() { - Container = ContainerWhereUploadFilesAndEnumerateThem + Container = container, + Paginate = false }; - var references = await _azureStorageWrapper.EnumerateAllBlobsAsync(command); + var references = await _azureStorageWrapper.EnumerateBlobsAsync(query); _output.WriteLine($"Enumerating {references.References.Count()} references"); Assert.True(references.References.Any()); - Assert.True(references.References.Count() == amount); + Assert.True(references.References.Count() >= amount); - await RemoveFilesAsync(references.References.Select(reference => reference.Uri).ToArray()); + foreach (var reference in references.References) + { + await _azureStorageWrapper.DeleteBlobAsync(new DeleteBlob() { Uri = reference.Uri }); + } } [Fact] @@ -42,12 +48,14 @@ public async Task EnumerateBlobs_WithContinuationToken_ShouldReturnBlobsPageByPa { var amount = 20; var size = 10; + var container = "pagination"; - await UploadFilesAsync(amount); + await UploadFilesAsync(amount, container); var firstIterationReferences = await _azureStorageWrapper.EnumerateBlobsAsync(new EnumerateBlobs() { - Container = ContainerWhereUploadFilesAndEnumerateThem, + Container = container, + Paginate = true, Size = size, }); @@ -57,7 +65,8 @@ public async Task EnumerateBlobs_WithContinuationToken_ShouldReturnBlobsPageByPa var secondIterationReferences = await _azureStorageWrapper.EnumerateBlobsAsync(new EnumerateBlobs() { - Container = ContainerWhereUploadFilesAndEnumerateThem, + Container = container, + Paginate = true, Size = size, ContinuationToken = firstIterationReferences.ContinuationToken }); @@ -66,18 +75,25 @@ public async Task EnumerateBlobs_WithContinuationToken_ShouldReturnBlobsPageByPa Assert.True(secondIterationReferences.References.Any()); - await RemoveFilesAsync(firstIterationReferences.References.Select(reference => reference.Uri).ToArray()); - await RemoveFilesAsync(secondIterationReferences.References.Select(reference => reference.Uri).ToArray()); + foreach (var reference in firstIterationReferences.References) + { + await _azureStorageWrapper.DeleteBlobAsync(new DeleteBlob() { Uri = reference.Uri }); + } + + foreach (var reference in secondIterationReferences.References) + { + await _azureStorageWrapper.DeleteBlobAsync(new DeleteBlob() { Uri = reference.Uri }); + } } - private async Task UploadFilesAsync(int amount) + private async Task UploadFilesAsync(int amount, string container) { for (var i = 0; i < amount; i++) { await _azureStorageWrapper.UploadBlobAsync(new UploadBase64() { Base64 = "SGVsbG8g8J+Zgg==", - Container = ContainerWhereUploadFilesAndEnumerateThem, + Container = container, Name = "hello", Extension = "md", Metadata = new Dictionary() @@ -85,16 +101,5 @@ await _azureStorageWrapper.UploadBlobAsync(new UploadBase64() }); } } - - private async Task RemoveFilesAsync(string[] uris) - { - foreach (var uri in uris) - { - await _azureStorageWrapper.DeleteBlobAsync(new DeleteBlob() - { - Uri = uri - }); - } - } } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs index 8b7fd7d..e2215e2 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs @@ -11,6 +11,7 @@ using Azure.Storage.Sas; using AzureStorageWrapper.Commands; using AzureStorageWrapper.Exceptions; +using AzureStorageWrapper.Queries; using AzureStorageWrapper.Responses; namespace AzureStorageWrapper @@ -149,48 +150,7 @@ public async Task EnumerateBlobsAsync(EnumerateBlobs co var segment = container .GetBlobsAsync() - .AsPages(command.ContinuationToken, command.Size); - - var enumerator = segment.GetAsyncEnumerator(); - - var references = new List(); - - while (await enumerator.MoveNextAsync()) - { - var page = enumerator.Current; - - foreach (var item in page.Values) - { - var blobReference = await DownloadBlobReferenceAsync(new DownloadBlobReference() - { - Uri = $"{container.Uri}/{item.Name}", - ExpiresIn = _configuration.DefaultSasUriExpiration - }); - - references.Add(blobReference); - } - - await enumerator.DisposeAsync(); - - return new BlobReferenceCollection() - { - References = references, - ContinuationToken = page.ContinuationToken - }; - } - - return new BlobReferenceCollection(); - } - - public async Task EnumerateAllBlobsAsync(EnumerateAllBlobs command) - { - command.Validate(); - - var container = new BlobContainerClient(_configuration.ConnectionString, command.Container); - - var segment = container - .GetBlobsAsync() - .AsPages(null, 10); + .AsPages(command.Paginate ? command.ContinuationToken : null, command.Paginate ? command.Size : (int?)null); var enumerator = segment.GetAsyncEnumerator(); @@ -223,6 +183,7 @@ public async Task EnumerateAllBlobsAsync(EnumerateAllBl return new BlobReferenceCollection(); } + private async Task GetSasUriAsync(GetSasUri command) { command.Validate(_configuration); diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.csproj b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.csproj index ff884df..6aeefe8 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.csproj +++ b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.csproj @@ -2,7 +2,7 @@ netstandard2.0;netstandard2.1 - 2.4.1 + 2.4.2 AzureStorageWrapper Sergio Barriel Wrapper for Azure Storage, designed to simplify the file upload process and provide links for downloading them. It also supports file deletion and enumeration. diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateAllBlobs.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateAllBlobs.cs deleted file mode 100644 index e759e14..0000000 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateAllBlobs.cs +++ /dev/null @@ -1,15 +0,0 @@ -using AzureStorageWrapper.Exceptions; - -namespace AzureStorageWrapper.Commands -{ - public class EnumerateAllBlobs - { - public string Container { get; set; } - - internal void Validate() - { - if (string.IsNullOrEmpty(Container)) - throw new AzureStorageWrapperException($"{nameof(Container)} is empty!"); - } - } -} \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/IAzureStorageWrapper.cs b/src/AzureStorageWrapper/AzureStorageWrapper/IAzureStorageWrapper.cs index 1f91b61..425c6d6 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/IAzureStorageWrapper.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/IAzureStorageWrapper.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using AzureStorageWrapper.Commands; +using AzureStorageWrapper.Queries; using AzureStorageWrapper.Responses; namespace AzureStorageWrapper @@ -40,12 +41,5 @@ public interface IAzureStorageWrapper /// /// Task EnumerateBlobsAsync(EnumerateBlobs command); - - /// - /// Enumerate all blobs inside an Azure Storage container - /// - /// - /// - Task EnumerateAllBlobsAsync(EnumerateAllBlobs command); } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/DownloadBlob.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Queries/DownloadBlob.cs similarity index 93% rename from src/AzureStorageWrapper/AzureStorageWrapper/Commands/DownloadBlob.cs rename to src/AzureStorageWrapper/AzureStorageWrapper/Queries/DownloadBlob.cs index 25adb19..4995520 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/DownloadBlob.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Queries/DownloadBlob.cs @@ -1,7 +1,7 @@ using System; using AzureStorageWrapper.Exceptions; -namespace AzureStorageWrapper.Commands +namespace AzureStorageWrapper.Queries { public class DownloadBlob { diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/DownloadBlobReference.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Queries/DownloadBlobReference.cs similarity index 95% rename from src/AzureStorageWrapper/AzureStorageWrapper/Commands/DownloadBlobReference.cs rename to src/AzureStorageWrapper/AzureStorageWrapper/Queries/DownloadBlobReference.cs index f0339a8..fd28e3b 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/DownloadBlobReference.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Queries/DownloadBlobReference.cs @@ -1,7 +1,7 @@ using System; using AzureStorageWrapper.Exceptions; -namespace AzureStorageWrapper.Commands +namespace AzureStorageWrapper.Queries { public class DownloadBlobReference { diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateBlobs.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Queries/EnumerateBlobs.cs similarity index 54% rename from src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateBlobs.cs rename to src/AzureStorageWrapper/AzureStorageWrapper/Queries/EnumerateBlobs.cs index f12477f..a0c803a 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateBlobs.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Queries/EnumerateBlobs.cs @@ -1,10 +1,11 @@ using AzureStorageWrapper.Exceptions; -namespace AzureStorageWrapper.Commands +namespace AzureStorageWrapper.Queries { public class EnumerateBlobs { public string Container { get; set; } + public bool Paginate { get; set; } public int Size { get; set; } public string ContinuationToken { get; set; } @@ -13,8 +14,11 @@ internal void Validate() if (string.IsNullOrEmpty(Container)) throw new AzureStorageWrapperException($"{nameof(Container)} is empty!"); - if (Size <= 0) - throw new AzureStorageWrapperException($"{nameof(Size)} should be greater than zero."); + if (Paginate && Size <= 0) + throw new AzureStorageWrapperException($"{nameof(Size)} should be greater than zero when {nameof(Paginate)} is true."); + + if(!Paginate && !string.IsNullOrEmpty(ContinuationToken)) + throw new AzureStorageWrapperException($"{nameof(ContinuationToken)} should be empty when {nameof(Paginate)} is false."); } } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/GetSasUri.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Queries/GetSasUri.cs similarity index 95% rename from src/AzureStorageWrapper/AzureStorageWrapper/Commands/GetSasUri.cs rename to src/AzureStorageWrapper/AzureStorageWrapper/Queries/GetSasUri.cs index d822aa7..7e0b2de 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/GetSasUri.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Queries/GetSasUri.cs @@ -1,7 +1,7 @@ using System; using AzureStorageWrapper.Exceptions; -namespace AzureStorageWrapper.Commands +namespace AzureStorageWrapper.Queries { internal class GetSasUri { From 0976fd516f0bd677e739eb37477cb81126912a82 Mon Sep 17 00:00:00 2001 From: Sergio Barriel Date: Fri, 7 Mar 2025 09:45:55 +0100 Subject: [PATCH 2/2] updated README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 21d20a6..6f175d5 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ For example, a virtual folder with a unique identifier is automatically created However, you can customize the `UseVirtualFolder` property, which by default has a value of `true` but you can set it to `false` if you wish. -⚠️ When `UseVirtualFolder` is set to `false`, files will **NOT** be stored in virtual directories. This change may lead to file name collisions, causing files to be **overwritten**. +> ⚠️ When `UseVirtualFolder` is set to `false`, files will **NOT** be stored in virtual directories. This change may lead to file name collisions, causing files to be **overwritten**. In this scenario, you must implement your own mechanism to generate unique file names.