Skip to content

Commit

Permalink
Merge pull request #47 from sergiobarriel/feature/enumerate-blobs
Browse files Browse the repository at this point in the history
Added enumeration methods
  • Loading branch information
sergiobarriel committed Jun 25, 2024
2 parents 2374260 + 0393a07 commit d64a2f3
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 19 deletions.
65 changes: 64 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,75 @@ The response when *downloading* file reference resembles the response when *uplo
}
```

## Delete blobs

You can delete a blob by specifying the *Uri*.

```csharp
var command = new DeleteBlob()
{
Uri = "https://accountName.blob.core.windows.net/files/5a19306fc5014a4/hello.md"
};

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.

In both cases, the response `BlobReferenceCollection` will contain a collection of `BlobReference` elements.

### Without pagination

```csharp
var command = new EnumerateAllBlobs()
{
Container = "files"
};

var response = await _azureStorageWrapper.EnumerateAllBlobsAsync(command);


```
### With pagination

```csharp
var command = new EnumerateBlobs()
{
Container = "files"
Size = 10,
};

var response = await _azureStorageWrapper.EnumerateBlobsAsync(command);

```
Then you can request additional pages by using the `ContinuationToken` property in the next request.

```csharp
var firstCommand = new EnumerateBlobs()
{
Container = "files"
Size = 10,
};

var firstResponse = await _azureStorageWrapper.EnumerateBlobsAsync(firstCommand);

var secondCommand = new EnumerateBlobs()
{
Container = "files"
Size = 10,
ContinuationToken = firstResponse.ContinuationToken
};

var secondResponse = await _azureStorageWrapper.EnumerateBlobsAsync(secondCommand);
```

# Contributors / Collaborators

These individuals have contributed to the repository through suggestions, error corrections, or by opening issues. Thanks 😊

- [ginodcs](https://github.com/ginodcs)
- [joseantoniolopez](https://github.com/joseantoniolopez)

# Sponsor

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using AzureStorageWrapper.Commands;
using AzureStorageWrapper.Commands;
using AzureStorageWrapper.Exceptions;
using AzureStorageWrapper.Tests.Sources;
using Xunit;
Expand Down Expand Up @@ -129,4 +129,4 @@ await Assert.ThrowsAsync<AzureStorageWrapperException>(async () =>
}

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using AzureStorageWrapper.Commands;
using Xunit;
using Xunit.Abstractions;

namespace AzureStorageWrapper.Tests.Should.Enumerate
{
public class EnumerateShould : BaseShould
{
private readonly IAzureStorageWrapper _azureStorageWrapper;
private readonly ITestOutputHelper _output;

public EnumerateShould(IAzureStorageWrapper azureStorageWrapper, ITestOutputHelper output)
{
_azureStorageWrapper = azureStorageWrapper;
_output = output;
}

// [Fact]
// public async Task EnumerateBlobs_ShouldReturnAllBlobsFromAContainer()
// {
// var command = new EnumerateAllBlobs()
// {
// Container = "files"
// };
//
// var references = await _azureStorageWrapper.EnumerateAllBlobsAsync(command);
//
// _output.WriteLine($"Enumerating {references.References.Count()} references");
//
// Assert.True(references.References.Any());
//
// }

[Fact]
public async Task EnumerateBlobs_WithContinuationToken_ShouldReturnBlobsPageByPage()
{
var firstIterationReferences = await _azureStorageWrapper.EnumerateBlobsAsync(new EnumerateBlobs()
{
Container = "files",
Size = 10,
});

_output.WriteLine($"Enumerating {firstIterationReferences.References.Count()} references");

Assert.True(firstIterationReferences.References.Any());

var secondIterationReferences = await _azureStorageWrapper.EnumerateBlobsAsync(new EnumerateBlobs()
{
Container = "files",
Size = 10,
ContinuationToken = firstIterationReferences.ContinuationToken
});

_output.WriteLine($"Enumerating {secondIterationReferences.References.Count()} references");

Assert.True(secondIterationReferences.References.Any());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Xunit;
using Xunit;

namespace AzureStorageWrapper.Tests.Should
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
using AzureStorageWrapper.Commands;
using Xunit;

namespace AzureStorageWrapper.Tests.Should.Upload;

public class MetadataShould : BaseShould
namespace AzureStorageWrapper.Tests.Should.Upload
{
private readonly IAzureStorageWrapper _azureStorageWrapper;
public class MetadataShould : BaseShould
{
private readonly IAzureStorageWrapper _azureStorageWrapper;

public MetadataShould(IAzureStorageWrapper azureStorageWrapper)
public MetadataShould(IAzureStorageWrapper azureStorageWrapper)
{
_azureStorageWrapper = azureStorageWrapper;
}

[Fact]
public async Task UploadBlob_WithMetadataDiacriticsKey_Should_UploadBlob()
[Fact]
public async Task UploadBlob_WithMetadataDiacriticsKey_Should_UploadBlob()
{
var base64 = "SGVsbG8g8J+Zgg==";
var diacriticsOne = "áéíóú";
Expand Down Expand Up @@ -42,8 +42,8 @@ public async Task UploadBlob_WithMetadataDiacriticsKey_Should_UploadBlob()
Assert.True(await PingAsync(response.SasUri));
}

[Fact]
public async Task UploadBlob_WithDiacriticsMetadataValue_Should_UploadBlob()
[Fact]
public async Task UploadBlob_WithDiacriticsMetadataValue_Should_UploadBlob()
{
var base64 = "SGVsbG8g8J+Zgg==";
var diacriticsOne = "áéíóú";
Expand Down Expand Up @@ -73,8 +73,8 @@ public async Task UploadBlob_WithDiacriticsMetadataValue_Should_UploadBlob()
}


[Fact]
public async Task UploadBlob_WithBlankSpaceMetadataKey_Should_UploadBlob()
[Fact]
public async Task UploadBlob_WithBlankSpaceMetadataKey_Should_UploadBlob()
{
var base64 = "SGVsbG8g8J+Zgg==";

Expand All @@ -100,4 +100,5 @@ public async Task UploadBlob_WithBlankSpaceMetadataKey_Should_UploadBlob()

Assert.True(await PingAsync(response.SasUri));
}
}
}
60 changes: 60 additions & 0 deletions src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,66 @@ public async Task DeleteBlobAsync(DeleteBlob command)

await blobClient.DeleteIfExistsAsync();
}

public async Task<BlobReferenceCollection> EnumerateBlobsAsync(EnumerateBlobs command)
{
var container = GetContainer(command.Container);

var segment = container.GetBlobsAsync().AsPages(command.ContinuationToken, command.Size);

var references = new List<BlobReference>();

await foreach (var page in segment)
{
foreach (var item in page.Values)
{
var blobReference = await DownloadBlobReferenceAsync(new DownloadBlobReference()
{
Uri = $"{container.Uri}/{item.Name}",
ExpiresIn = _configuration.DefaultSasUriExpiration
});

references.Add(blobReference);
}

return new BlobReferenceCollection()
{
References = references,
ContinuationToken = page.ContinuationToken
};
}

return null;
}

public async Task<BlobReferenceCollection> EnumerateAllBlobsAsync(EnumerateAllBlobs command)
{
var container = GetContainer(command.Container);

var segment = container.GetBlobsAsync().AsPages(null, 10);

var references = new List<BlobReference>();

await foreach (var page in segment)
{
foreach (var item in page.Values)
{
var blobReference = await DownloadBlobReferenceAsync(new DownloadBlobReference()
{
Uri = $"{container.Uri}/{item.Name}",
ExpiresIn = _configuration.DefaultSasUriExpiration
});

references.Add(blobReference);
}
}

return new BlobReferenceCollection()
{
References = references,
ContinuationToken = null
};
}

private BlobContainerClient GetContainer(string containerName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<version>2.2.14</version>
<Title>AzureStorageWrapper</Title>
<Authors>Sergio Barriel</Authors>
<Description>Wrapper for Azure Storage, aimed at simplifying the file upload process and obtaining links for downloading them.</Description>
<Description>Wrapper for Azure Storage, designed to simplify the file upload process and provide links for downloading them. It also supports file deletion and enumeration.</Description>
<PackageProjectUrl>https://github.com/sergiobarriel/azure.storage.wrapper</PackageProjectUrl>
<RepositoryUrl>https://github.com/sergiobarriel/azure.storage.wrapper</RepositoryUrl>
<RepositoryType>git</RepositoryType>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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!");

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using AzureStorageWrapper.Exceptions;

namespace AzureStorageWrapper.Commands
{
public class EnumerateBlobs
{
public string Container { get; set; }
public int Size { get; set; }
public string ContinuationToken { get; set; }

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.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using AzureStorageWrapper.Commands;
using AzureStorageWrapper.Responses;

Expand Down Expand Up @@ -33,5 +34,18 @@ public interface IAzureStorageWrapper
/// <param name="command"></param>
/// <returns></returns>
Task DeleteBlobAsync(DeleteBlob command);

/// <summary>
/// Enumerate and paginate blobs inside an Azure Storage container
/// </summary>
/// <returns></returns>
Task<BlobReferenceCollection> EnumerateBlobsAsync(EnumerateBlobs command);

/// <summary>
/// Enumerate all blobs inside an Azure Storage container
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
Task<BlobReferenceCollection> EnumerateAllBlobsAsync(EnumerateAllBlobs command);
}
}
28 changes: 28 additions & 0 deletions src/AzureStorageWrapper/AzureStorageWrapper/IUriService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,37 @@
{
public interface IUriService
{
/// <summary>
/// Returns the host of the URI
/// In example: https://accountName.blob.core.windows.net/container/virtualFolder/file.extension -> https://accountName.blob.core.windows.net
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
string GetHost(string uri);

/// <summary>
/// Returns the container of the file in the URI
/// In example: https://accountName.blob.core.windows.net/container/virtualFolder/file.extension -> container
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
string GetContainer(string uri);

/// <summary>
/// Returns the file name of the file in the URI
/// In example: https://accountName.blob.core.windows.net/container/virtualFolder/file.extension -> virtualFolder/file
/// In example: https://accountName.blob.core.windows.net/container/multiple/virtual/folder/file.extension -> multiple/virtual/folder/file
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
string GetFileName(string uri);

/// <summary>
/// Returns the file extension of the file in the URI
/// In example: https://accountName.blob.core.windows.net/container/virtualFolder/file.extension -> extension
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
string GetFileExtension(string uri);
}
}
Loading

0 comments on commit d64a2f3

Please sign in to comment.