diff --git a/.github/workflows/run-tests-and-deploy.yml b/.github/workflows/run-tests-and-deploy.yml index f524779..b522ca2 100644 --- a/.github/workflows/run-tests-and-deploy.yml +++ b/.github/workflows/run-tests-and-deploy.yml @@ -12,12 +12,18 @@ jobs: - name: '⚙️ Setup .NET Core' uses: actions/setup-dotnet@v1 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' - name: '🧱 dotnet build' run: 'dotnet build' working-directory: './src/AzureStorageWrapper/AzureStorageWrapper' - + + - name: '💾 azurite' + run: | + npm install -g azurite + mkdir -p azurite + azurite --location ./azurite --debug ./azurite/debug.log & + - name: '🚨 dotnet test' run: dotnet test --environment AZURE_STORAGE_CONNECTION_STRING="${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}" working-directory: './src/AzureStorageWrapper/AzureStorageWrapper.Tests' diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 249e83f..d596230 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -11,18 +11,27 @@ on: jobs: build: runs-on: ubuntu-latest + steps: - uses: actions/checkout@v3 - name: '⚙️ Setup .NET Core' uses: actions/setup-dotnet@v1 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' - name: '🧱 dotnet build' run: dotnet build working-directory: './src/AzureStorageWrapper/AzureStorageWrapper' - + + - name: '💾 azurite' + run: | + npm install -g azurite + mkdir -p azurite + azurite --location ./azurite --debug ./azurite/debug.log & + - name: '🚨 dotnet test' run: dotnet test --environment AZURE_STORAGE_CONNECTION_STRING="${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}" working-directory: './src/AzureStorageWrapper/AzureStorageWrapper.Tests' + + diff --git a/README.md b/README.md index 74baa1e..350cc11 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ The upload commands have a property called `UseVirtualFolder` which by default h **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. -In this case, you must be responsible for establishing your own mechanism to generate unique file names. +> In this case, you must be responsible for establishing your own mechanism to generate unique file names. ```csharp var base64 = "SGVsbG8g8J+Zgg=="; @@ -170,7 +170,7 @@ var command = new UploadBase64() var response = await _azureStorageWrapper.UploadBlobAsync(command); ``` -## Download blobs +## Download blob references To download a blob reference, you need specify the *Uri*. @@ -270,6 +270,7 @@ These individuals have contributed to the repository through suggestions, error - [ginodcs](https://github.com/ginodcs) - [christian-cell](https://github.com/christian-cell) +- [scabrera](https://github.com/scabrera) # Sponsor diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/AzureStorageWrapper.Tests.csproj b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/AzureStorageWrapper.Tests.csproj index 6f59064..7a60451 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/AzureStorageWrapper.Tests.csproj +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/AzureStorageWrapper.Tests.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 enable enable 4125ba48-a89c-4eba-989b-dab2cbd677c4 @@ -12,9 +12,8 @@ - - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/BaseShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/BaseShould.cs index 4199419..296c16b 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/BaseShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/BaseShould.cs @@ -15,13 +15,7 @@ protected static async Task PingAsync(string uri) return response.IsSuccessStatusCode; } - - - public static IEnumerable InvalidMetadata() => new List() - { - new object[] { new Dictionary() }, - new object[] { null }, - }; + public static IEnumerable InvalidExpiresIn() => new List() { @@ -36,7 +30,7 @@ protected static async Task PingAsync(string uri) /// order: container, fileName, fileExtension /// /// - public static IEnumerable InvalidFilePropertiesCombination() => new List() + public static IEnumerable WrongUploadBlobCommandProperties() => new List() { new object[] { "", "", "" }, new object[] { "files", "", "" }, diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobReferenceShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobReferenceShould.cs index 5f4b34c..68dfaa1 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobReferenceShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobReferenceShould.cs @@ -1,8 +1,8 @@ -using AzureStorageWrapper.Commands; +using AzureStorageWrapper.Commands; using AzureStorageWrapper.Exceptions; using AzureStorageWrapper.Tests.Sources; using Xunit; - + namespace AzureStorageWrapper.Tests.Should.Download { public class DownloadBlobReferenceShould : BaseShould @@ -17,49 +17,96 @@ public DownloadBlobReferenceShould(IAzureStorageWrapper azureStorageWrapper) [Fact] public async Task DownloadBlobReference_WithManyDots_Should_ReturnReference() { - var command = new DownloadBlobReference() + var base64 = "SGVsbG8g8J+Zgg=="; + + var uploadBlobCommand = new UploadBase64() { - Uri = Uris.ExistingFileWithManyDots, - ExpiresIn = 360, + Base64 = base64, + Container = "files", + Name = "hello.world.hello.world", + Extension = "md", + Metadata = new Dictionary() + {{"hello", "world"}} }; - var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); + var uploadBlobResponse = await _azureStorageWrapper.UploadBlobAsync(uploadBlobCommand); + + var downloadBlobReferenceCommand = new DownloadBlobReference() + { + Uri = uploadBlobResponse.Uri, + ExpiresIn = 360, + }; - Assert.NotNull(response); + // Act + var blobReference = await _azureStorageWrapper.DownloadBlobReferenceAsync(downloadBlobReferenceCommand); - Assert.True(await PingAsync(response.SasUri)); + // Assert + Assert.NotNull(blobReference); + Assert.True(await PingAsync(blobReference.SasUri)); } [Fact] public async Task DownloadBlobReference_WithExtensions_Should_ReturnReference() { - var command = new DownloadBlobReference() + var base64 = "SGVsbG8g8J+Zgg=="; + + var uploadBlobCommand = new UploadBase64() { - Uri = Uris.ExistingFileWithManyExtensions, - ExpiresIn = 360, + Base64 = base64, + Container = "files", + Name = "hello", + Extension = "md.md.md", + Metadata = new Dictionary() + {{"hello", "world"}} }; - var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); + var uploadBlobResponse = await _azureStorageWrapper.UploadBlobAsync(uploadBlobCommand); + + var downloadBlobReferenceCommand = new DownloadBlobReference() + { + Uri = uploadBlobResponse.Uri, + ExpiresIn = 360, + }; - Assert.NotNull(response); + // Act + var blobReference = await _azureStorageWrapper.DownloadBlobReferenceAsync(downloadBlobReferenceCommand); - Assert.True(await PingAsync(response.SasUri)); + // Assert + Assert.NotNull(blobReference); + Assert.True(await PingAsync(blobReference.SasUri)); } [Fact] public async Task DownloadBlobReference_Should_ReturnReference() { - var command = new DownloadBlobReference() + // Arrange + + var base64 = "SGVsbG8g8J+Zgg=="; + + var uploadBlobCommand = new UploadBase64() { - Uri = Uris.ExistingFile, - ExpiresIn = 360, + Base64 = base64, + Container = "files", + Name = "hello", + Extension = "md", + Metadata = new Dictionary() + {{"hello", "world"}} }; - var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); + var uploadBlobResponse = await _azureStorageWrapper.UploadBlobAsync(uploadBlobCommand); + + var downloadBlobReferenceCommand = new DownloadBlobReference() + { + Uri = uploadBlobResponse.Uri, + ExpiresIn = 360, + }; - Assert.NotNull(response); + // Act + var blobReference = await _azureStorageWrapper.DownloadBlobReferenceAsync(downloadBlobReferenceCommand); - Assert.True(await PingAsync(response.SasUri)); + // Assert + Assert.NotNull(blobReference); + Assert.True(await PingAsync(blobReference.SasUri)); } [Fact] @@ -84,7 +131,7 @@ public async Task DownloadBlobReference_WithUnExistingUri_Should_ThrowException( { var command = new DownloadBlobReference() { - Uri = Uris.UnExistingFile, + Uri = "", ExpiresIn = 360, }; @@ -94,39 +141,7 @@ await Assert.ThrowsAsync(async () => _ = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); }); } - - - [Theory] - [MemberData(nameof(InvalidExpiresIn))] - public async Task DownloadBlobReference_WithWrongExpiration_Should_ReturnReference(int expiresIn) - { - var command = new DownloadBlobReference() - { - Uri = Uris.ExistingFile, - ExpiresIn = expiresIn, - }; - - var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); - - Assert.NotNull(response); - - Assert.True(await PingAsync(response.SasUri)); - } - - [Fact] - public async Task DownloadBlobReference_WithHighExpiration_Should_ThrowException() - { - var command = new DownloadBlobReference() - { - Uri = Uris.ExistingFile, - ExpiresIn = int.MaxValue, - }; - - await Assert.ThrowsAsync(async () => - { - _ = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); - }); - } + } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobStreamShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobStreamShould.cs index d4b12b7..b6fbe1f 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobStreamShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobStreamShould.cs @@ -17,21 +17,40 @@ public DownloadBlobStreamShould(IAzureStorageWrapper azureStorageWrapper) [Fact] public async Task DownloadBlob_Should_ReturnBlob() { - var commandReference = new DownloadBlobReference() + // Arrange + + var base64 = "SGVsbG8g8J+Zgg=="; + + var uploadBlobCommand = new UploadBase64() { - Uri = Uris.ExistingFile, - ExpiresIn = 60 + Base64 = base64, + Container = "files", + Name = "hello world", + Extension = "md", + Metadata = new Dictionary() + {{"hello", "world"}} }; - var blobReference = await _azureStorageWrapper.DownloadBlobReferenceAsync(commandReference); + var uploadBlobResponse = await _azureStorageWrapper.UploadBlobAsync(uploadBlobCommand); + + var downloadBlobCommand = new DownloadBlobReference() + { + Uri = uploadBlobResponse.Uri, + ExpiresIn = 360, + }; + + var blobReference = await _azureStorageWrapper.DownloadBlobReferenceAsync(downloadBlobCommand); var command = new DownloadBlob() { Uri = blobReference.SasUri, }; + // Act + var response = await _azureStorageWrapper.DownloadBlobAsync(command); + // Assert Assert.NotNull(response); Assert.NotNull(response.Stream); @@ -39,26 +58,26 @@ public async Task DownloadBlob_Should_ReturnBlob() } - [Fact] - public async Task DownloadBlob_WithInvalidUri_Should_ReturnBlob() - { - var commandReference = new DownloadBlobReference() - { - Uri = Uris.ExistingFile, - ExpiresIn = 60 - }; - - var blobReference = await _azureStorageWrapper.DownloadBlobReferenceAsync(commandReference); - - var command = new DownloadBlob() - { - Uri = blobReference.Uri, - }; - - await Assert.ThrowsAsync(async () => - { - _ = await _azureStorageWrapper.DownloadBlobAsync(command); - }); - } + // [Fact] + // public async Task DownloadBlob_WithInvalidUri_Should_ReturnBlob() + // { + // var commandReference = new DownloadBlobReference() + // { + // Uri = Uris.ExistingFile, + // ExpiresIn = 60 + // }; + // + // var blobReference = await _azureStorageWrapper.DownloadBlobReferenceAsync(commandReference); + // + // var command = new DownloadBlob() + // { + // Uri = blobReference.Uri, + // }; + // + // await Assert.ThrowsAsync(async () => + // { + // _ = await _azureStorageWrapper.DownloadBlobAsync(command); + // }); + // } } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobUriVariationsShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobUriVariationsShould.cs index 6da9dc5..84afb03 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobUriVariationsShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Download/DownloadBlobUriVariationsShould.cs @@ -14,31 +14,73 @@ public DownloadBlobUriVariationsShould(IAzureStorageWrapper azureStorageWrapper) } + // [Fact] + // public async Task DownloadBlob_WithEncodedFileName_Should_ReturnFileReference() + // { + // // Arrange + // + // var base64 = "SGVsbG8g8J+Zgg=="; + // + // var uploadBlobCommand = new UploadBase64() + // { + // Base64 = base64, + // Container = "files", + // Name = "hello world", + // Extension = "md", + // Metadata = new Dictionary() + // {{"hello", "world"}} + // }; + // + // var uploadBlobResponse = await _azureStorageWrapper.UploadBlobAsync(uploadBlobCommand); + // + // var downloadBlobReferenceCommand = new DownloadBlobReference() + // { + // Uri = uploadBlobResponse.Uri, + // ExpiresIn = 360, + // }; + // + // var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); + // + // Assert.True(await PingAsync(response.SasUri)); + // } + [Fact] - public async Task DownloadBlob_WithEncodedFileName_Should_ReturnFileReference() + public async Task DownloadBlob_WithBlanksFileName_Should_ReturnFileReference() { - var command = new DownloadBlobReference() + // Arrange + + var base64 = "SGVsbG8g8J+Zgg=="; + + var uploadBlobCommand = new UploadBase64() { - Uri = Uris.ExistingFileWithUriEncoded, - ExpiresIn = 180 + Base64 = base64, + Container = "files", + Name = "hello world", + Extension = "md", + Metadata = new Dictionary() + {{"hello", "world"}} }; - var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); + var uploadBlobResponse = await _azureStorageWrapper.UploadBlobAsync(uploadBlobCommand); + + var downloadBlobReferenceCommand = new DownloadBlobReference() + { + Uri = uploadBlobResponse.Uri, + ExpiresIn = 360, + }; - Assert.True(await PingAsync(response.SasUri)); - } - - [Fact] - public async Task DownloadBlob_WithBlanksFileName_Should_ReturnFileReference() - { var command = new DownloadBlobReference() { - Uri = Uris.ExistingFileWithBlanks, + Uri = uploadBlobResponse.Uri, ExpiresIn = 180 }; + // Act + var response = await _azureStorageWrapper.DownloadBlobReferenceAsync(command); + // Assert + Assert.True(await PingAsync(response.SasUri)); } diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Services/UriShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Services/UriShould.cs deleted file mode 100644 index 2d7a5e5..0000000 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Services/UriShould.cs +++ /dev/null @@ -1,153 +0,0 @@ -using Xunit; - -namespace AzureStorageWrapper.Tests.Should -{ - public class UriShould : BaseShould - { - private readonly IUriService _uriService; - - private string UriWithVirtualFolder = $"https://accountname.blob.core.windows.net/container/virtualFolder/file.extension"; - private string UriWithMultipleVirtualFolder = $"https://accountname.blob.core.windows.net/container/multiple/virtual/folder/file.extension"; - private string UriWithoutVirtualFolder = $"https://accountname.blob.core.windows.net/container/file.extension"; - - private string UriWithTwoExtensions = $"https://accountname.blob.core.windows.net/container/file.extension.extension"; - private string UriWithVirtualFolderWithTwoExtensions = $"https://accountname.blob.core.windows.net/container/virtual/folder/file.extension.extension"; - private string UriWithManyExtensions = $"https://accountname.blob.core.windows.net/container/file.extension.extension.extension"; - private string UriWithVirtualFolderWithManyExtensions = $"https://accountname.blob.core.windows.net/container/virtual/folder/file.extension.extension.extension"; - - public UriShould() - { - _uriService = new UriService(); - } - - - [Fact] - public void GetHost_WithVirtualFolder_ShouldReturnHost() - { - var host = _uriService.GetHost(UriWithVirtualFolder); - - Assert.Equal("https://accountname.blob.core.windows.net", host); - } - - [Fact] - public void GetHost_WithoutVirtualFolder_ShouldReturnHost() - { - var host = _uriService.GetHost(UriWithoutVirtualFolder); - - Assert.Equal("https://accountname.blob.core.windows.net", host); - } - - [Fact] - public void GetHost_WithMultipleVirtualFolder_ShouldReturnHost() - { - var host = _uriService.GetHost(UriWithMultipleVirtualFolder); - - Assert.Equal("https://accountname.blob.core.windows.net", host); - } - - [Fact] - public void GetContainer_WithVirtualFolder_ShouldReturnContainer() - { - var container = _uriService.GetContainer(UriWithVirtualFolder); - - Assert.Equal("container", container); - } - - [Fact] - public void GetContainer_WithoutVirtualFolder_ShouldReturnContainer() - { - var container = _uriService.GetContainer(UriWithoutVirtualFolder); - - Assert.Equal("container", container); - } - - [Fact] - public void GetContainer_WithMultipleVirtualFolder_ShouldReturnContainer() - { - var container = _uriService.GetContainer(UriWithMultipleVirtualFolder); - - Assert.Equal("container", container); - } - - [Fact] - public void GetFileName_WithVirtualFolder_ShouldReturnFileName() - { - var fileName = _uriService.GetFileName(UriWithVirtualFolder); - - Assert.Equal("virtualFolder/file", fileName); - } - - - [Fact] - public void GetFileName_WithoutVirtualFolder_ShouldReturnFileName() - { - var fileName = _uriService.GetFileName(UriWithoutVirtualFolder); - - Assert.Equal("file", fileName); - } - - [Fact] - public void GetFileName_WithTwoExtension_ShouldReturnFileName() - { - var fileName = _uriService.GetFileName(UriWithTwoExtensions); - - Assert.Equal("file.extension", fileName); - } - - [Fact] - public void GetFileName_WithVirtualFolder_WithTwoExtension_ShouldReturnFileName() - { - var fileName = _uriService.GetFileName(UriWithVirtualFolderWithTwoExtensions); - - Assert.Equal("virtual/folder/file.extension", fileName); - } - - [Fact] - public void GetFileName_WithManyExtension_ShouldReturnFileName() - { - var fileName = _uriService.GetFileName(UriWithManyExtensions); - - Assert.Equal("file.extension.extension", fileName); - } - - [Fact] - public void GetFileName_WithVirtualFolder_WithManyExtension_ShouldReturnFileName() - { - var fileName = _uriService.GetFileName(UriWithVirtualFolderWithManyExtensions); - - Assert.Equal("virtual/folder/file.extension.extension", fileName); - } - - [Fact] - public void GetFileName_WithMultipleVirtualFolder_ShouldReturnFileName() - { - var fileName = _uriService.GetFileName(UriWithMultipleVirtualFolder); - - Assert.Equal("multiple/virtual/folder/file", fileName); - } - - [Fact] - public void GetFileExtension_WithVirtualFolder_ShouldReturnFileExtension() - { - var fileExtension = _uriService.GetFileExtension(UriWithVirtualFolder); - - Assert.Equal("extension", fileExtension); - } - - [Fact] - public void GetFileExtension_WithoutVirtualFolder_ShouldReturnFileExtension() - { - var fileExtension = _uriService.GetFileExtension(UriWithoutVirtualFolder); - - Assert.Equal("extension", fileExtension); - } - - [Fact] - public void GetFileExtension_WithMultipleVirtualFolder_ShouldReturnFileExtension() - { - var fileExtension = _uriService.GetFileExtension(UriWithMultipleVirtualFolder); - - Assert.Equal("extension", fileExtension); - } - } -} \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBlobWithNameVariations.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/DiacriticsShould.cs similarity index 58% rename from src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBlobWithNameVariations.cs rename to src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/DiacriticsShould.cs index e1dd163..e3255af 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBlobWithNameVariations.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/DiacriticsShould.cs @@ -3,36 +3,15 @@ namespace AzureStorageWrapper.Tests.Should.Upload { - public class UploadBlobWithNameVariations : BaseShould + public class DiacriticsShould : BaseShould { private readonly IAzureStorageWrapper _azureStorageWrapper; - public UploadBlobWithNameVariations(IAzureStorageWrapper azureStorageWrapper) + public DiacriticsShould(IAzureStorageWrapper azureStorageWrapper) { _azureStorageWrapper = azureStorageWrapper; } - - [Fact] - public async Task UploadBlob_WithBlankSpacesInName_Should_UploadBlob() - { - var base64 = "SGVsbG8g8J+Zgg=="; - - var command = new UploadBase64() - { - Base64 = base64, - Container = "files", - Name = "hello world", - Extension = "md", - Metadata = new Dictionary() - {{"hello", "world"}} - }; - - var response = await _azureStorageWrapper.UploadBlobAsync(command); - - Assert.NotNull(response); - - Assert.True(await PingAsync(response.SasUri)); - } + [Fact] public async Task UploadBlobWithDiacriticsInName_Should_UploadBlob() @@ -52,9 +31,7 @@ public async Task UploadBlobWithDiacriticsInName_Should_UploadBlob() var response = await _azureStorageWrapper.UploadBlobAsync(command); Assert.True(response.Name.IndexOfAny(new char[]{ 'á','é', 'í', 'ó', 'ú', 'ä', 'ë','ï', 'ö', 'ü', 'à', 'è', 'ì', 'ò', 'ù'}) > 0); // no diacritics - Assert.NotNull(response); - Assert.True(await PingAsync(response.SasUri)); } diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/MetadataShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/MetadataShould.cs index 81df8ed..ce63cfa 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/MetadataShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/MetadataShould.cs @@ -8,97 +8,97 @@ public class MetadataShould : BaseShould private readonly IAzureStorageWrapper _azureStorageWrapper; public MetadataShould(IAzureStorageWrapper azureStorageWrapper) - { - _azureStorageWrapper = azureStorageWrapper; - } - + { + _azureStorageWrapper = azureStorageWrapper; + } + [Fact] public async Task UploadBlob_WithMetadataDiacriticsKey_Should_UploadBlob() - { - var base64 = "SGVsbG8g8J+Zgg=="; - var diacriticsOne = "áéíóú"; - var diacriticsTwo = "áéíóúäëïöüàèìòù"; - - var command = new UploadBase64() { - Base64 = base64, - Container = "files", - Name = "hello", - Extension = "md", - Metadata = new Dictionary() + var base64 = "SGVsbG8g8J+Zgg=="; + var diacriticsOne = "áéíóú"; + var diacriticsTwo = "áéíóúäëïöüàèìòù"; + + var command = new UploadBase64() { - { diacriticsOne, "diacritics_one" }, - { diacriticsTwo, "diacritics_two" }, - }, - }; + Base64 = base64, + Container = "files", + Name = "hello", + Extension = "md", + Metadata = new Dictionary() + { + { diacriticsOne, "diacritics_one" }, + { diacriticsTwo, "diacritics_two" }, + }, + }; - var response = await _azureStorageWrapper.UploadBlobAsync(command); + var response = await _azureStorageWrapper.UploadBlobAsync(command); - Assert.NotNull(response); + Assert.NotNull(response); + + Assert.True(response.Metadata.TryGetValue("aeiou", out var _)); + Assert.True(response.Metadata.TryGetValue("aeiouaeiouaeiou", out var _)); + + Assert.True(await PingAsync(response.SasUri)); + } - Assert.True(response.Metadata.TryGetValue("aeiou", out var _)); - Assert.True(response.Metadata.TryGetValue("aeiouaeiouaeiou", out var _)); - - Assert.True(await PingAsync(response.SasUri)); - } - [Fact] public async Task UploadBlob_WithDiacriticsMetadataValue_Should_UploadBlob() - { - var base64 = "SGVsbG8g8J+Zgg=="; - var diacriticsOne = "áéíóú"; - var diacriticsTwo = "áéíóúäëïöüàèìòù"; - - var command = new UploadBase64() { - Base64 = base64, - Container = "files", - Name = "hello", - Extension = "md", - Metadata = new Dictionary() + var base64 = "SGVsbG8g8J+Zgg=="; + var diacriticsOne = "áéíóú"; + var diacriticsTwo = "áéíóúäëïöüàèìòù"; + + var command = new UploadBase64() { - { "diacritics_one", diacriticsOne }, - { "diacritics_two", diacriticsTwo }, - }, - }; + Base64 = base64, + Container = "files", + Name = "hello", + Extension = "md", + Metadata = new Dictionary() + { + { "diacritics_one", diacriticsOne }, + { "diacritics_two", diacriticsTwo }, + }, + }; - var response = await _azureStorageWrapper.UploadBlobAsync(command); + var response = await _azureStorageWrapper.UploadBlobAsync(command); + + Assert.NotNull(response); + + Assert.Equal("aeiou", response.Metadata["diacritics_one"]); + Assert.Equal("aeiouaeiouaeiou", response.Metadata["diacritics_two"]); + + Assert.True(await PingAsync(response.SasUri)); + } - Assert.NotNull(response); - - Assert.Equal("aeiou", response.Metadata["diacritics_one"]); - Assert.Equal("aeiouaeiouaeiou", response.Metadata["diacritics_two"]); - Assert.True(await PingAsync(response.SasUri)); - } - - [Fact] public async Task UploadBlob_WithBlankSpaceMetadataKey_Should_UploadBlob() - { - var base64 = "SGVsbG8g8J+Zgg=="; - - var command = new UploadBase64() { - Base64 = base64, - Container = "files", - Name = "hello", - Extension = "md", - Metadata = new Dictionary() + var base64 = "SGVsbG8g8J+Zgg=="; + + var command = new UploadBase64() { - { "xxx xxx", "xxx" }, - { "zzz zzz", "zzz" }, - }, - }; + Base64 = base64, + Container = "files", + Name = "hello", + Extension = "md", + Metadata = new Dictionary() + { + { "xxx xxx", "xxx" }, + { "zzz zzz", "zzz" }, + }, + }; - var response = await _azureStorageWrapper.UploadBlobAsync(command); + var response = await _azureStorageWrapper.UploadBlobAsync(command); - Assert.NotNull(response); - - Assert.True(response.Metadata.TryGetValue("xxx_xxx", out var _)); - Assert.True(response.Metadata.TryGetValue("zzz_zzz", out var _)); + Assert.NotNull(response); - Assert.True(await PingAsync(response.SasUri)); - } + Assert.True(response.Metadata.TryGetValue("xxx_xxx", out var _)); + Assert.True(response.Metadata.TryGetValue("zzz_zzz", out var _)); + + Assert.True(await PingAsync(response.SasUri)); + } } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBase64Should.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBase64Should.cs index ab52f9d..501fb18 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBase64Should.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBase64Should.cs @@ -13,9 +13,39 @@ public UploadBase64Should(IAzureStorageWrapper azureStorageWrapper) _azureStorageWrapper = azureStorageWrapper; } + [Fact] + public async Task UploadBase64_Should_UploadBlob() + { + // Arrange + + var base64 = "SGVsbG8g8J+Zgg=="; + + var command = new UploadBase64() + { + Base64 = base64, + Container = "files", + Name = "hello", + Extension = "md", + Metadata = new Dictionary() + {{"hello", "world"}} + }; + + // Act + + var response = await _azureStorageWrapper.UploadBlobAsync(command); + + // Assert + + Assert.NotNull(response); + Assert.True(await PingAsync(response.SasUri)); + } + + [Fact] public async Task UploadEmptyBase64_Should_ThrowException() { + // Arrange + var base64 = string.Empty; var command = new UploadBase64() @@ -28,6 +58,8 @@ public async Task UploadEmptyBase64_Should_ThrowException() {{"hello", "world"}} }; + // Act and Assert + await Assert.ThrowsAsync(async () => { _ = await _azureStorageWrapper.UploadBlobAsync(command); @@ -35,9 +67,11 @@ await Assert.ThrowsAsync(async () => } [Theory] - [MemberData(nameof(InvalidFilePropertiesCombination))] + [MemberData(nameof(WrongUploadBlobCommandProperties))] public async Task UploadBase64Blob_WithWrongFileProperties_Should_ThrowException(string container, string fileName, string fileExtension) { + // Arrange + var base64 = "SGVsbG8g8J+Zgg=="; var command = new UploadBase64() @@ -50,76 +84,65 @@ public async Task UploadBase64Blob_WithWrongFileProperties_Should_ThrowException {{"hello", "world"}} }; + // Act & Assert + await Assert.ThrowsAsync(async () => { _ = await _azureStorageWrapper.UploadBlobAsync(command); }); } - [Theory] - [MemberData(nameof(InvalidMetadata))] - public async Task UploadBase64Blob_WithWrongMetadata_Should_UploadBlob(Dictionary properties) - { - var base64 = "SGVsbG8g8J+Zgg=="; - - var command = new UploadBase64() - { - Base64 = base64, - Container = "files", - Name = "hello", - Extension = "md", - Metadata = properties - }; - - var response = await _azureStorageWrapper.UploadBlobAsync(command); - - Assert.NotNull(response); - - Assert.True(await PingAsync(response.SasUri)); - } - [Fact] - public async Task UploadBase64_Should_UploadBlob() + public async Task UploadBase64_WithMultipleDotsInName_Should_UploadBlob() { + // Arrange + var base64 = "SGVsbG8g8J+Zgg=="; var command = new UploadBase64() { Base64 = base64, Container = "files", - Name = "hello", + Name = "hello.hello.hello.hello", Extension = "md", Metadata = new Dictionary() {{"hello", "world"}} }; + // Act + var response = await _azureStorageWrapper.UploadBlobAsync(command); + // Assert + Assert.NotNull(response); - Assert.True(await PingAsync(response.SasUri)); } - [Fact] - public async Task UploadBase64_WithMultipleDotsInName_Should_UploadBlob() + public async Task UploadBlob_WithBlankSpacesInName_Should_UploadBlob() { + // Arrange + var base64 = "SGVsbG8g8J+Zgg=="; var command = new UploadBase64() { Base64 = base64, Container = "files", - Name = "hello.hello.hello.hello", + Name = "hello world", Extension = "md", Metadata = new Dictionary() {{"hello", "world"}} }; + // Act + var response = await _azureStorageWrapper.UploadBlobAsync(command); + // Assert + Assert.NotNull(response); - Assert.True(await PingAsync(response.SasUri)); } diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBytesShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBytesShould.cs index 4889c47..859a653 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBytesShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadBytesShould.cs @@ -13,9 +13,39 @@ public UploadBytesShould(IAzureStorageWrapper azureStorageWrapper) _azureStorageWrapper = azureStorageWrapper; } + + [Fact] + public async Task UploadBytesBlob_Should_UploadBlob() + { + // Arrange + + var bytes = Convert.FromBase64String("SGVsbG8g8J+Zgg=="); + + var command = new UploadBytes() + { + Bytes = bytes, + Container = "files", + Name = "hello", + Extension = "md", + Metadata = new Dictionary() + { { "hello", "world" } } + }; + + // Act + + var response = await _azureStorageWrapper.UploadBlobAsync(command); + + // Assert + + Assert.NotNull(response); + Assert.True(await PingAsync(response.SasUri)); + } + [Fact] public async Task UploadEmptyBytes_Should_ThrowException() { + // Arrange + var bytes = Convert.FromBase64String(string.Empty); var command = new UploadBytes() @@ -28,6 +58,8 @@ public async Task UploadEmptyBytes_Should_ThrowException() {{"hello", "world"}} }; + // Act & Assert + await Assert.ThrowsAsync(async () => { _ = await _azureStorageWrapper.UploadBlobAsync(command); @@ -35,9 +67,11 @@ await Assert.ThrowsAsync(async () => } [Theory] - [MemberData(nameof(InvalidFilePropertiesCombination))] + [MemberData(nameof(WrongUploadBlobCommandProperties))] public async Task UploadBytesBlob_WithWrongFileProperties_Should_ThrowException(string container, string fileName, string fileExtension) { + // Arrange + var bytes = Convert.FromBase64String("SGVsbG8g8J+Zgg=="); var command = new UploadBytes() @@ -50,52 +84,13 @@ public async Task UploadBytesBlob_WithWrongFileProperties_Should_ThrowException( {{"hello", "world"}} }; + // Act & Assert + await Assert.ThrowsAsync(async () => { _ = await _azureStorageWrapper.UploadBlobAsync(command); }); } - [Theory] - [MemberData(nameof(InvalidMetadata))] - public async Task UploadBytesBlob_WithWrongMetadata_Should_UploadBlob(Dictionary properties) - { - var bytes = Convert.FromBase64String("SGVsbG8g8J+Zgg=="); - - var command = new UploadBytes() - { - Bytes = bytes, - Container = "files", - Name = "hello", - Extension = "md", - Metadata = properties - }; - - var response = await _azureStorageWrapper.UploadBlobAsync(command); - - Assert.NotNull(response); - - Assert.True(await PingAsync(response.SasUri)); - } - - [Fact] - public async Task UploadBytesBlob_Should_UploadBlob() - { - var bytes = Convert.FromBase64String("SGVsbG8g8J+Zgg=="); - - var response = await _azureStorageWrapper.UploadBlobAsync(new UploadBytes() - { - Bytes = bytes, - Container = "files", - Name = "hello", - Extension = "md", - Metadata = new Dictionary() - {{"hello", "world"}} - }); - - Assert.NotNull(response); - - Assert.True(await PingAsync(response.SasUri)); - } } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadStreamShould.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadStreamShould.cs index a9e33d2..93d7cb6 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadStreamShould.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Should/Upload/UploadStreamShould.cs @@ -13,9 +13,38 @@ public UploadStreamShould(IAzureStorageWrapper azureStorageWrapper) _azureStorageWrapper = azureStorageWrapper; } + [Fact] + public async Task UploadStreamBlob_Should_UploadBlob() + { + // Arrange + + var stream = new MemoryStream(Convert.FromBase64String("SGVsbG8g8J+Zgg==")); + + var command = new UploadStream() + { + Stream = stream, + Container = "files", + Name = "hello", + Extension = "md", + Metadata = new Dictionary() + { { "hello", "world" } } + }; + + // Act + + var response = await _azureStorageWrapper.UploadBlobAsync(command); + + // Assert + + Assert.NotNull(response); + Assert.True(await PingAsync(response.SasUri)); + } + [Fact] public async Task UploadEmptyStream_ShouldThrowException() { + // Arrange + var stream = new MemoryStream(Convert.FromBase64String(string.Empty)); var command = new UploadStream() @@ -28,6 +57,8 @@ public async Task UploadEmptyStream_ShouldThrowException() {{"hello", "world"}} }; + // Act & Assert + await Assert.ThrowsAsync(async () => { _ = await _azureStorageWrapper.UploadBlobAsync(command); @@ -35,9 +66,11 @@ await Assert.ThrowsAsync(async () => } [Theory] - [MemberData(nameof(InvalidFilePropertiesCombination))] + [MemberData(nameof(WrongUploadBlobCommandProperties))] public async Task UploadStreamBlob_WithWrongFileProperties_Should_ThrowException(string container, string fileName, string fileExtension) { + // Arrange + var stream = new MemoryStream(Convert.FromBase64String("SGVsbG8g8J+Zgg==")); var command = new UploadStream() @@ -50,52 +83,15 @@ public async Task UploadStreamBlob_WithWrongFileProperties_Should_ThrowException {{"hello", "world"}} }; + // Act & Assert + await Assert.ThrowsAsync(async () => { _ = await _azureStorageWrapper.UploadBlobAsync(command); }); } + - [Theory] - [MemberData(nameof(InvalidMetadata))] - public async Task UploadStreamBlob_WithWrongMetadata_Should_UploadFile(Dictionary properties) - { - var stream = new MemoryStream(Convert.FromBase64String("SGVsbG8g8J+Zgg==")); - - var command = new UploadStream() - { - Stream = stream, - Container = "files", - Name = "hello", - Extension = "md", - Metadata = properties - }; - - var response = await _azureStorageWrapper.UploadBlobAsync(command); - - Assert.NotNull(response); - - Assert.True(await PingAsync(response.SasUri)); - } - - [Fact] - public async Task UploadStreamBlob_Should_UploadBlob() - { - var stream = new MemoryStream(Convert.FromBase64String("SGVsbG8g8J+Zgg==")); - - var response = await _azureStorageWrapper.UploadBlobAsync(new UploadStream() - { - Stream = stream, - Container = "files", - Name = "hello", - Extension = "md", - Metadata = new Dictionary() - {{"hello", "world"}} - }); - - Assert.NotNull(response); - - Assert.True(await PingAsync(response.SasUri)); - } + } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Sources/Uris.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Sources/Uris.cs deleted file mode 100644 index 1c37d04..0000000 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Sources/Uris.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace AzureStorageWrapper.Tests.Sources -{ - public static class Uris - { - public const string ExistingFile = "https://stgazstgwrapper001westeu.blob.core.windows.net/static/hello.md"; - - public const string ExistingFileWithManyDots = "https://stgazstgwrapper001westeu.blob.core.windows.net/static/hello.hello.hello.hello.md"; - - public const string ExistingFileWithManyExtensions = "https://stgazstgwrapper001westeu.blob.core.windows.net/static/hello.md.md"; - - public const string UnExistingFile = "https://stgazstgwrapper001westeu.blob.core.windows.net/static/unexisting-hello.md"; - - public const string ExistingFileWithBlanks = "https://stgazstgwrapper001westeu.blob.core.windows.net/static/hello hello.md"; - - public const string ExistingFileWithUriEncoded = "https://stgazstgwrapper001westeu.blob.core.windows.net/static/hello%20hello.md"; - - } -} \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Startup.cs b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Startup.cs index da53254..3c4c40b 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Startup.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper.Tests/Startup.cs @@ -9,35 +9,11 @@ public void ConfigureServices(IServiceCollection serviceCollection) { serviceCollection.AddAzureStorageWrapper(configuration => { - configuration.ConnectionString = GetConnectionString(); + configuration.ConnectionString = "UseDevelopmentStorage=true"; configuration.MaxSasUriExpiration = 360; configuration.DefaultSasUriExpiration = 360; configuration.CreateContainerIfNotExists = true; }); } - - private string GetConnectionString() - { - var fromEnvironmentVariable = GetConnectionStringFromEnvironmentVariable(); - - var fromUserSecrets = GetConnectionStringFromUserSecretsFile(); - - return !string.IsNullOrEmpty(fromEnvironmentVariable) - ? fromEnvironmentVariable - : fromUserSecrets; - } - - private string GetConnectionStringFromEnvironmentVariable() => Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING"); - - private string GetConnectionStringFromUserSecretsFile() - { - var configuration = new ConfigurationBuilder() - .AddUserSecrets() - .Build(); - - var connectionString = configuration["AZURE_STORAGE_CONNECTION_STRING"]; - - return connectionString; - } } } diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs index eafc44a..8b7fd7d 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Net.Http; using System.Text; @@ -14,10 +15,9 @@ namespace AzureStorageWrapper { - public class AzureStorageWrapper : IAzureStorageWrapper + public class AzureStorageWrapper : AzureStorageWrapperBase, IAzureStorageWrapper { private readonly AzureStorageWrapperConfiguration _configuration; - private readonly IUriService _uriService = new UriService(); public AzureStorageWrapper(AzureStorageWrapperConfiguration configuration) { @@ -27,31 +27,31 @@ public AzureStorageWrapper(AzureStorageWrapperConfiguration configuration) public async Task UploadBlobAsync(UploadBlob command) { command.Validate(); - - await CreateContainerIfNotExists(command.Container); - - var container = GetContainer(command.Container); + + var container = new BlobContainerClient(_configuration.ConnectionString, command.Container); - var blobClient = command.UseVirtualFolder - ? container.GetBlobClient($"{GetRandomId()}/{command.Name}.{command.Extension}") - : container.GetBlobClient($"{command.Name}.{command.Extension}"); - - await blobClient.UploadAsync(command.GetContent(), overwrite: true); - - if (command.Metadata is null) + if (!await container.ExistsAsync()) { - command.Metadata = new Dictionary(); + if (_configuration.CreateContainerIfNotExists) await container.CreateIfNotExistsAsync(); + + else throw new AzureStorageWrapperException($"container {command.Container} doesn't exists!"); } - command.Metadata["_timestamp"] = $"{DateTime.UtcNow}"; + var blobName = command.UseVirtualFolder + ? $"{GetRandomId()}/{command.Name}.{command.Extension}" + : $"{command.Name}.{command.Extension}"; + var blob = container.GetBlobClient(blobName); + + await blob.UploadAsync(command.GetContent(), overwrite: true); + var sanitizedDictionary = SanitizeDictionary(command.Metadata); - await blobClient.SetMetadataAsync(sanitizedDictionary); + await blob.SetMetadataAsync(sanitizedDictionary); var sasUri = await GetSasUriAsync(new GetSasUri() { - Uri = blobClient.Uri.AbsoluteUri, + Uri = blob.Uri.AbsoluteUri, ExpiresIn = _configuration.DefaultSasUriExpiration, }); @@ -60,39 +60,33 @@ public async Task UploadBlobAsync(UploadBlob command) Container = command.Container, Name = command.Name, Extension = command.Extension, - Uri = blobClient.Uri.AbsoluteUri, + Uri = blob.Uri.AbsoluteUri, SasUri = sasUri, Metadata = sanitizedDictionary, SasExpires = DateTime.UtcNow.AddSeconds(_configuration.DefaultSasUriExpiration) }; return blobReference; + } public async Task DownloadBlobReferenceAsync(DownloadBlobReference command) { command.Validate(_configuration); - var containerName = _uriService.GetContainer(command.Uri); - var fileName = GetFileName(command.Uri); - - var container = GetContainer(containerName); + var blob = new BlobClient(new Uri(command.Uri)); - if (! await container.ExistsAsync()) - throw new AzureStorageWrapperException($"container {containerName} doesn't exists!"); + var container = new BlobContainerClient(_configuration.ConnectionString, blob.BlobContainerName); - var blobClient = container.GetBlobClient($"{fileName.name}.{fileName.extension}"); - - if (! await blobClient.ExistsAsync()) - throw new AzureStorageWrapperException($"blob {fileName.name}.{fileName.extension} doesn't exists!"); + var blobClient = container.GetBlobClient(blob.Name); var blobProperties = await blobClient.GetPropertiesAsync(); - + return new BlobReference() { Container = blobClient.BlobContainerName, - Name = fileName.name, - Extension = fileName.extension, + Name = blobClient.Name, + Extension = Path.GetExtension(blobClient.Name), Uri = blobClient.Uri.AbsoluteUri, SasUri = await GetSasUriAsync(new GetSasUri() { @@ -105,23 +99,44 @@ public async Task DownloadBlobReferenceAsync(DownloadBlobReferenc Metadata = blobProperties.Value.Metadata, }; } - - public async Task DeleteBlobAsync(DeleteBlob command) + + public async Task DownloadBlobAsync(DownloadBlob command) { command.Validate(); - var containerName = _uriService.GetContainer(command.Uri); - var fileName = GetFileName(command.Uri); - - var container = GetContainer(containerName); + var sasUri = await GetSasUriAsync(new GetSasUri() + { + Uri = command.Uri, + ExpiresIn = _configuration.DefaultSasUriExpiration, + }); + + using (var httpClient = new HttpClient()) + { + var response = await httpClient.GetAsync(sasUri); - if (!await container.ExistsAsync()) - throw new AzureStorageWrapperException($"container {containerName} doesn't exists!"); + if (!response.IsSuccessStatusCode) + { + throw new AzureStorageWrapperException($"something went wrong when downloading blob {command.Uri}"); + } - var blobClient = container.GetBlobClient($"{fileName.name}.{fileName.extension}"); + var stream = await response.Content.ReadAsStreamAsync(); + + return new Blob() + { + Stream = stream + }; + } + } + + public async Task DeleteBlobAsync(DeleteBlob command) + { + command.Validate(); - if (!await blobClient.ExistsAsync()) - throw new AzureStorageWrapperException($"blob {fileName.name}.{fileName.extension} doesn't exists!"); + var blob = new BlobClient(new Uri(command.Uri)); + + var container = new BlobContainerClient(_configuration.ConnectionString, blob.BlobContainerName); + + var blobClient = container.GetBlobClient(blob.Name); await blobClient.DeleteIfExistsAsync(); } @@ -130,9 +145,12 @@ public async Task EnumerateBlobsAsync(EnumerateBlobs co { command.Validate(); - var container = GetContainer(command.Container); + var container = new BlobContainerClient(_configuration.ConnectionString, command.Container); - var segment = container.GetBlobsAsync().AsPages(command.ContinuationToken, command.Size); + var segment = container + .GetBlobsAsync() + .AsPages(command.ContinuationToken, command.Size); + var enumerator = segment.GetAsyncEnumerator(); var references = new List(); @@ -161,16 +179,19 @@ public async Task EnumerateBlobsAsync(EnumerateBlobs co }; } - return null; + return new BlobReferenceCollection(); } public async Task EnumerateAllBlobsAsync(EnumerateAllBlobs command) { command.Validate(); - var container = GetContainer(command.Container); + var container = new BlobContainerClient(_configuration.ConnectionString, command.Container); - var segment = container.GetBlobsAsync().AsPages(null, 10); + var segment = container + .GetBlobsAsync() + .AsPages(null, 10); + var enumerator = segment.GetAsyncEnumerator(); var references = new List(); @@ -199,62 +220,18 @@ public async Task EnumerateAllBlobsAsync(EnumerateAllBl }; } - return null; - } - - private BlobContainerClient GetContainer(string containerName) - { - return new BlobContainerClient(_configuration.ConnectionString, containerName); - } - - private async Task CreateContainerIfNotExists(string containerName) - { - var container = GetContainer(containerName); - - if (!await container.ExistsAsync()) - { - if (_configuration.CreateContainerIfNotExists) - await container.CreateIfNotExistsAsync(); - - else throw new AzureStorageWrapperException($"container {containerName} doesn't exists!"); - } + return new BlobReferenceCollection(); } - public async Task DownloadBlobAsync(DownloadBlob command) - { - command.Validate(); - - using (var httpClient = new HttpClient()) - { - var response = await httpClient.GetAsync(command.Uri); - - if (!response.IsSuccessStatusCode) - { - var fileName = _uriService.GetFileName(command.Uri); - var fileExtension = _uriService.GetFileExtension(command.Uri); - - throw new AzureStorageWrapperException( - $"something went wrong when downloading blob {fileName}.{fileExtension}: {response.ReasonPhrase}"); - } - - var stream = await response.Content.ReadAsStreamAsync(); - - return new Blob() - { - Stream = stream - }; - } - } - private async Task GetSasUriAsync(GetSasUri command) { command.Validate(_configuration); - var fileName = GetFileName(command.Uri); - - var container = GetContainer(_uriService.GetContainer(command.Uri)); - - var blobClient = container.GetBlobClient($"{fileName.name}.{fileName.extension}"); + var blob = new BlobClient(new Uri(command.Uri)); + + var container = new BlobContainerClient(_configuration.ConnectionString, blob.BlobContainerName); + + var blobClient = container.GetBlobClient(blob.Name); if (!await blobClient.ExistsAsync()) return null; @@ -263,16 +240,6 @@ private async Task GetSasUriAsync(GetSasUri command) return blobSasUri.AbsoluteUri; } - private (string name, string extension) GetFileName(string uri) - { - var unescapedUriRepresentation = Uri.UnescapeDataString(uri); - - var name = _uriService.GetFileName(unescapedUriRepresentation); - var extension = _uriService.GetFileExtension(unescapedUriRepresentation); - - return (name: name, extension: extension); - } - private static Dictionary SanitizeDictionary(Dictionary metadata) { return metadata.ToDictionary(item => SanitizeKey(item.Key), item => SanitizeValue(item.Value)); @@ -315,14 +282,6 @@ string RemoveDiacritics(string fileName) } } - /// - /// ~2 centuries of work are needed in order to have a 1% probability of at least one collision: https://alex7kom.github.io/nano-nanoid-cc - /// - private static string GetRandomId() - { - var guid = Guid.NewGuid().ToString("N"); - return guid.Substring(0, 15); - } } } diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.csproj b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.csproj index e468630..ff884df 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.csproj +++ b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapper.csproj @@ -2,7 +2,7 @@ netstandard2.0;netstandard2.1 - 2.4.0 + 2.4.1 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. @@ -34,8 +34,8 @@ - - + + diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapperBase.cs b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapperBase.cs new file mode 100644 index 0000000..cb7be35 --- /dev/null +++ b/src/AzureStorageWrapper/AzureStorageWrapper/AzureStorageWrapperBase.cs @@ -0,0 +1,17 @@ +using System; + +namespace AzureStorageWrapper +{ + public abstract class AzureStorageWrapperBase + { + /// + /// ~2 centuries of work are needed in order to have a 1% probability of at least one collision: https://alex7kom.github.io/nano-nanoid-cc + /// + internal string GetRandomId() + { + var guid = Guid.NewGuid().ToString("N"); + + return guid.Substring(0, 15); + } + } +} \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateAllBlobs.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateAllBlobs.cs index 4464255..e759e14 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateAllBlobs.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/EnumerateAllBlobs.cs @@ -10,7 +10,6 @@ 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/Commands/GetSasUri.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/GetSasUri.cs index 0e68c51..d822aa7 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/GetSasUri.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/GetSasUri.cs @@ -3,7 +3,7 @@ namespace AzureStorageWrapper.Commands { - public class GetSasUri + internal class GetSasUri { public string Uri { get; set; } public int ExpiresIn { get; set; } diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/UploadBase64.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/UploadBase64.cs index 8fa1be7..12b3fbc 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/UploadBase64.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/UploadBase64.cs @@ -13,7 +13,9 @@ public override Stream GetContent() { if (string.IsNullOrEmpty(Base64)) throw new AzureStorageWrapperException($"{nameof(Base64)} is empty"); - RemoveBase64Header(); + var regex = new Regex(@"^data:[^;]+;base64,"); + + Base64 = regex.Replace(Base64, string.Empty); try { @@ -25,17 +27,5 @@ public override Stream GetContent() throw new AzureStorageWrapperException("Invalid base64 string"); } } - - /// - /// Remove starting tags from base64 like: - /// data:image/png;base64,... - /// data:application/pdf;base64,... - /// - private void RemoveBase64Header() - { - var regex = new Regex(@"^data:[^;]+;base64,"); - - Base64 = regex.Replace(Base64, string.Empty); - } } } \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/UploadBlob.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/UploadBlob.cs index 4b37109..bd347ea 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Commands/UploadBlob.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Commands/UploadBlob.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using AzureStorageWrapper.Exceptions; @@ -8,7 +9,7 @@ public abstract class UploadBlob { protected UploadBlob() { - Metadata = new Dictionary(); + Metadata["_timestamp"] = $"{DateTime.UtcNow}"; UseVirtualFolder = true; } @@ -16,7 +17,7 @@ protected UploadBlob() public string Extension { get; set; } public string Container { get; set; } - public Dictionary Metadata { get; set; } + public Dictionary Metadata { get; set; } = new Dictionary(); /// /// If you set this property to 'false' the files will NOT be saved in virtual directories, and file names may collide, causing files to be overwritten diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/IUriService.cs b/src/AzureStorageWrapper/AzureStorageWrapper/IUriService.cs deleted file mode 100644 index b14e916..0000000 --- a/src/AzureStorageWrapper/AzureStorageWrapper/IUriService.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace AzureStorageWrapper -{ - public interface IUriService - { - /// - /// Returns the host of the URI - /// In example: https://accountName.blob.core.windows.net/container/virtualFolder/file.extension -> https://accountName.blob.core.windows.net - /// - /// - /// - string GetHost(string uri); - - /// - /// Returns the container of the file in the URI - /// In example: https://accountName.blob.core.windows.net/container/virtualFolder/file.extension -> container - /// - /// - /// - string GetContainer(string uri); - - /// - /// 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 - /// - /// - /// - string GetFileName(string uri); - - /// - /// Returns the file extension of the file in the URI - /// In example: https://accountName.blob.core.windows.net/container/virtualFolder/file.extension -> extension - /// - /// - /// - string GetFileExtension(string uri); - } -} \ No newline at end of file diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/Responses/BlobReferenceCollection.cs b/src/AzureStorageWrapper/AzureStorageWrapper/Responses/BlobReferenceCollection.cs index 85e1b3a..8d2b289 100644 --- a/src/AzureStorageWrapper/AzureStorageWrapper/Responses/BlobReferenceCollection.cs +++ b/src/AzureStorageWrapper/AzureStorageWrapper/Responses/BlobReferenceCollection.cs @@ -4,6 +4,11 @@ namespace AzureStorageWrapper.Responses { public class BlobReferenceCollection { + public BlobReferenceCollection() + { + References = new List(); + } + public IEnumerable References { get; set; } public string ContinuationToken { get; set; } } diff --git a/src/AzureStorageWrapper/AzureStorageWrapper/UriService.cs b/src/AzureStorageWrapper/AzureStorageWrapper/UriService.cs deleted file mode 100644 index a96b43b..0000000 --- a/src/AzureStorageWrapper/AzureStorageWrapper/UriService.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.IO; - -namespace AzureStorageWrapper -{ - public class UriService : IUriService - { - public UriService() { } - - public string GetHost(string uri) - { - var uriObject = new Uri(uri); - - return $"{uriObject.Scheme}://{uriObject.Authority}"; - } - - public string GetContainer(string uri) - { - var uriObject = new Uri(uri); - - return uriObject.Segments[1].TrimEnd('/'); - } - - public string GetFileName(string uri) - { - var uriObject = new Uri(uri); - - var host = GetHost(uri); - var container = GetContainer(uri); - var extension = GetFileExtension(uri); - - var remainder = uriObject.LocalPath - .Replace($"{host}", string.Empty) - .Replace($"/{container}/", string.Empty); - - var index = remainder.LastIndexOf($".{extension}", StringComparison.Ordinal); - - var fileName = remainder.Substring(0, index); - - return fileName; - } - - public string GetFileExtension(string uri) - { - var uriObject = new Uri(uri); - - return Path.GetExtension(uriObject.LocalPath).TrimStart('.'); - } - - } -} \ No newline at end of file