Skip to content

Commit 4ce140b

Browse files
committed
SSP-4836 fix ListBlobsInContainerAsync folderName prefix
it will now not return blobs from other folders that start with same prefix e.g. prefix "1" will not return blobs prefixed with "11" anymore # Conflicts: # src/Samhammer.AzureBlobStorage.Test/IntegrationTest.cs
1 parent bb246d6 commit 4ce140b

File tree

2 files changed

+82
-100
lines changed

2 files changed

+82
-100
lines changed

src/Samhammer.AzureBlobStorage.Test/IntegrationTest.cs

Lines changed: 72 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Samhammer.AzureBlobStorage.Test
1919
{
2020
public class IntegrationTest
2121
{
22-
// Create a storage account and configure the connection string here
22+
// Create a storage account and configure the connection string with valid credentials: DefaultEndpointsProtocol=https;AccountName=USERNAME;AccountKey=PASSWORD;EndpointSuffix=core.windows.net;
2323
// Warning: This test creates and deletes a container named test and testdefault
2424
private const string ConnectionString = "";
2525
private const string DefaultContainerName = "testdefault";
@@ -89,102 +89,77 @@ public async Task TestEntireProcess(string containerName)
8989

9090
containers.Should().Contain(i => i.Name == expectedContainerName);
9191

92-
// Upload a file
93-
await _service.UploadBlobAsync(testFileName, testFileContentType, testFileReadStream, containerName);
94-
95-
// Load file list and verify that the upload is there
96-
var files = await _service.ListBlobsInContainerAsync(containerName).ToListAsync();
97-
98-
files.Should().HaveCount(1);
99-
files.First().Should().BeEquivalentTo(
100-
new BlobInfoContract
101-
{
102-
Name = testFileName,
103-
AccessTier = "Hot",
104-
BlobType = "Block",
105-
ContentEncoding = string.Empty,
106-
ContentType = testFileContentType,
107-
DateCreated = DateTimeOffset.UtcNow,
108-
Size = 1021702,
109-
},
110-
_comparisonOptions);
111-
112-
// Load individual file and verify infos
113-
var file = await _service.GetBlobContentsAsync(testFileName, containerName);
114-
115-
file.Should().NotBeNull();
116-
file.Should().BeEquivalentTo(
117-
new BlobInfoContract
118-
{
119-
Name = testFileName,
120-
AccessTier = "Hot",
121-
BlobType = "Block",
122-
ContentEncoding = string.Empty,
123-
ContentType = testFileContentType,
124-
DateCreated = DateTimeOffset.UtcNow,
125-
Size = 1021702,
126-
},
127-
_comparisonOptions);
128-
129-
// Delete blob and verify it's gone
130-
await _service.DeleteBlobAsync(testFileName, containerName);
131-
var filesAfterDeletion = await _service.ListBlobsInContainerAsync(containerName).ToListAsync();
132-
133-
filesAfterDeletion.Count.Should().Be(0);
134-
135-
// Delete folder and verify only folder with prefix is gone, others kept
136-
testFileReadStream.Seek(0, SeekOrigin.Begin);
137-
await _service.UploadBlobAsync(testFileName, testFileContentType, testFileReadStream, containerName, "1");
138-
testFileReadStream.Seek(0, SeekOrigin.Begin);
139-
await _service.UploadBlobAsync(testFileName, testFileContentType, testFileReadStream, containerName, "10");
140-
testFileReadStream.Seek(0, SeekOrigin.Begin);
141-
await _service.UploadBlobAsync(testFileName, testFileContentType, testFileReadStream, containerName, "11");
142-
await _service.DeleteFolderAsync("1", containerName);
143-
144-
var filesAfterFolderDeletion = await _service.ListBlobsInContainerAsync(containerName).ToListAsync();
145-
146-
filesAfterFolderDeletion.Should().ContainEquivalentOf(
147-
new BlobInfoContract
148-
{
149-
Name = "10/" + testFileName,
150-
AccessTier = "Hot",
151-
BlobType = "Block",
152-
ContentEncoding = string.Empty,
153-
ContentType = testFileContentType,
154-
DateCreated = DateTimeOffset.UtcNow,
155-
Size = 1021702,
156-
},
157-
_comparisonOptions)
158-
.And.ContainEquivalentOf(
159-
new BlobInfoContract
160-
{
161-
Name = "11/" + testFileName,
162-
AccessTier = "Hot",
163-
BlobType = "Block",
164-
ContentEncoding = string.Empty,
165-
ContentType = testFileContentType,
166-
DateCreated = DateTimeOffset.UtcNow,
167-
Size = 1021702,
168-
},
169-
_comparisonOptions)
170-
.And.NotContainEquivalentOf(
171-
new BlobInfoContract
172-
{
173-
Name = "1/" + testFileName,
174-
AccessTier = "Hot",
175-
BlobType = "Block",
176-
ContentEncoding = string.Empty,
177-
ContentType = testFileContentType,
178-
DateCreated = DateTimeOffset.UtcNow,
179-
Size = 1021702,
180-
},
181-
_comparisonOptions);
182-
183-
// Delete container and verify it's gone
184-
await _service.DeleteContainerAsync(containerName);
185-
var containersAfterDeletion = await _service.GetContainersAsync().ToListAsync();
186-
187-
containersAfterDeletion.Should().NotContain(i => i.Name == expectedContainerName);
92+
try
93+
{
94+
// Upload a file
95+
await _service.UploadBlobAsync(testFileName, testFileContentType, testFileReadStream, containerName);
96+
97+
// Load file list and verify that the upload is there
98+
var files = await _service.ListBlobsInContainerAsync(containerName).ToListAsync();
99+
100+
files.Should().HaveCount(1);
101+
files.First().Should().BeEquivalentTo(GetBlobContract(testFileName), _comparisonOptions);
102+
103+
// Load individual file and verify infos
104+
var file = await _service.GetBlobContentsAsync(testFileName, containerName);
105+
106+
file.Should().NotBeNull();
107+
file.Should().BeEquivalentTo(GetBlobContract(testFileName), _comparisonOptions);
108+
109+
// Delete blob and verify it's gone
110+
await _service.DeleteBlobAsync(testFileName, containerName);
111+
var filesAfterDeletion = await _service.ListBlobsInContainerAsync(containerName).ToListAsync();
112+
113+
filesAfterDeletion.Count.Should().Be(0);
114+
115+
// Upload to multiple folders (starting with same character)
116+
testFileReadStream.Seek(0, SeekOrigin.Begin);
117+
await _service.UploadBlobAsync(testFileName, testFileContentType, testFileReadStream, containerName, "1");
118+
testFileReadStream.Seek(0, SeekOrigin.Begin);
119+
await _service.UploadBlobAsync(testFileName, testFileContentType, testFileReadStream, containerName, "10");
120+
testFileReadStream.Seek(0, SeekOrigin.Begin);
121+
await _service.UploadBlobAsync(testFileName, testFileContentType, testFileReadStream, containerName, "11");
122+
123+
// Access folder and verify only blobs from folder with prefix are returned
124+
var filesInFolder = await _service.ListBlobsInContainerAsync(containerName, "1").ToListAsync();
125+
126+
filesInFolder.Should()
127+
.ContainEquivalentOf(GetBlobContract("1/" + testFileName), _comparisonOptions)
128+
.And.NotContainEquivalentOf(GetBlobContract("10/" + testFileName), _comparisonOptions)
129+
.And.NotContainEquivalentOf(GetBlobContract("11/" + testFileName), _comparisonOptions);
130+
131+
// Delete folder and verify only folder with prefix is gone, others kept
132+
await _service.DeleteFolderAsync("1", containerName);
133+
134+
var filesAfterFolderDeletion = await _service.ListBlobsInContainerAsync(containerName).ToListAsync();
135+
136+
filesAfterFolderDeletion.Should()
137+
.ContainEquivalentOf(GetBlobContract("10/" + testFileName), _comparisonOptions)
138+
.And.ContainEquivalentOf(GetBlobContract("11/" + testFileName), _comparisonOptions)
139+
.And.NotContainEquivalentOf(GetBlobContract("1/" + testFileName), _comparisonOptions);
140+
}
141+
finally
142+
{
143+
// Delete container and verify it's gone
144+
await _service.DeleteContainerAsync(containerName);
145+
var containersAfterDeletion = await _service.GetContainersAsync().ToListAsync();
146+
147+
containersAfterDeletion.Should().NotContain(i => i.Name == expectedContainerName);
148+
}
149+
}
150+
151+
private BlobInfoContract GetBlobContract(string testFileName)
152+
{
153+
return new BlobInfoContract
154+
{
155+
Name = testFileName,
156+
AccessTier = "Hot",
157+
BlobType = "Block",
158+
ContentEncoding = string.Empty,
159+
ContentType = "text/plain",
160+
DateCreated = DateTimeOffset.UtcNow,
161+
Size = 1021702,
162+
};
188163
}
189164
}
190165
}

src/Samhammer.AzureBlobStorage/Services/AzureBlobStorageService.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ public async Task DeleteContainerAsync(string containerName = null)
6464
public async IAsyncEnumerable<BlobInfoContract> ListBlobsInContainerAsync(string containerName = null, string folderName = null)
6565
{
6666
var containerClient = await GetContainerClient(containerName);
67-
var blobs = containerClient.GetBlobsAsync(prefix: folderName);
67+
var folderNamePrefix = GetFolderNamePrefix(folderName);
68+
69+
var blobs = containerClient.GetBlobsAsync(prefix: folderNamePrefix);
6870

6971
await foreach (var blob in blobs)
7072
{
@@ -73,6 +75,11 @@ public async IAsyncEnumerable<BlobInfoContract> ListBlobsInContainerAsync(string
7375
}
7476
}
7577

78+
private string GetFolderNamePrefix(string folderName)
79+
{
80+
return string.IsNullOrEmpty(folderName) ? folderName : $"{folderName.TrimEnd('/')}/";
81+
}
82+
7683
public async Task<BlobContract> GetBlobContentsAsync(string blobName, string containerName = null, bool ignoreNonExistentContainer = false)
7784
{
7885
var containerClient = await GetContainerClient(containerName, ignoreNonExistentContainer);
@@ -147,8 +154,8 @@ public async Task DeleteBlobAsync(string blobName, string containerName = null)
147154
public async Task DeleteFolderAsync(string folderName, string containerName = null)
148155
{
149156
var containerClient = await GetContainerClient(containerName);
150-
folderName = folderName.TrimEnd('/');
151-
var blobs = containerClient.GetBlobsAsync(prefix: $"{folderName}/");
157+
var folderNamePrefix = GetFolderNamePrefix(folderName);
158+
var blobs = containerClient.GetBlobsAsync(prefix: folderNamePrefix);
152159

153160
await foreach (var blob in blobs)
154161
{

0 commit comments

Comments
 (0)