Skip to content

Commit 9c355a2

Browse files
authored
add support for <BlobPrefix> when listing blobs (#1097)
1 parent 57463d6 commit 9c355a2

11 files changed

Lines changed: 139 additions & 35 deletions

File tree

sdk/storage_blobs/examples/anonymous_access.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async fn main() -> azure_core::Result<()> {
1515
let mut blob_stream = container_client.list_blobs().into_stream();
1616
while let Some(blob_entry) = blob_stream.next().await {
1717
let blob_entry = blob_entry?;
18-
for blob in blob_entry.blobs.blobs {
18+
for blob in blob_entry.blobs.blobs() {
1919
println!("\t{}", blob.name);
2020
}
2121
}

sdk/storage_blobs/examples/connection_string.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async fn main() -> azure_core::Result<()> {
5959

6060
let mut cnt: i32 = 0;
6161
while let Some(value) = stream.next().await {
62-
let len = value?.blobs.blobs.len();
62+
let len = value?.blobs.blobs().count();
6363
println!("received {} blobs", len);
6464
match cnt {
6565
0 | 1 | 2 => assert_eq!(len, 3),

sdk/storage_blobs/examples/container_00.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ async fn main() -> azure_core::Result<()> {
4444
let mut count = 0;
4545
while let Some(result) = stream.next().await {
4646
let result = result?;
47-
count += result.blobs.blobs.len();
48-
for blob in result.blobs.blobs.iter() {
47+
for blob in result.blobs.blobs() {
48+
count += 1;
4949
println!(
5050
"\t{}\t{} MB",
5151
blob.name,

sdk/storage_blobs/examples/count_blobs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ async fn main() -> azure_core::Result<()> {
2121
let mut list_blobs = container_client.list_blobs().into_stream();
2222
while let Some(list_blobs_response) = list_blobs.next().await {
2323
let list_blobs_response = list_blobs_response?;
24-
count += list_blobs_response.blobs.blobs.len();
24+
count += list_blobs_response.blobs.blobs().count();
2525
}
2626

2727
println!("blob count {}", count);

sdk/storage_blobs/examples/list_blobs_00.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ async fn main() -> azure_core::Result<()> {
6666
.await
6767
.expect("stream failed")?;
6868

69-
println!("List blob returned {} blobs.", page.blobs.blobs.len());
70-
for cont in page.blobs.blobs.iter() {
69+
println!("List blob returned {} blobs.", page.blobs.blobs().count());
70+
for cont in page.blobs.blobs() {
7171
println!("\t{}\t{} bytes", cont.name, cont.properties.content_length);
7272
}
7373

@@ -78,7 +78,7 @@ async fn main() -> azure_core::Result<()> {
7878

7979
let mut cnt: i32 = 0;
8080
while let Some(value) = stream.next().await {
81-
let len = value?.blobs.blobs.len();
81+
let len = value?.blobs.blobs().count();
8282
println!("received {} blobs", len);
8383
match cnt {
8484
0 | 1 | 2 => assert_eq!(len, 3),

sdk/storage_blobs/examples/list_blobs_01.rs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ async fn main() -> azure_core::Result<()> {
5353
.await
5454
.expect("stream failed")?;
5555

56-
assert!(page.blobs.blobs.is_empty());
56+
assert!(page.blobs.blobs().next().is_some());
5757

5858
println!("Adding blobs");
5959

@@ -131,14 +131,11 @@ async fn main() -> azure_core::Result<()> {
131131

132132
println!(
133133
"List blob / returned {} blobs with blob_prefix == {:?}",
134-
page.blobs.blobs.len(),
135-
page.blobs.blob_prefix
134+
page.blobs.blobs().count(),
135+
page.blobs.prefixes().collect::<Vec<_>>()
136136
);
137-
page.blobs
138-
.blobs
139-
.iter()
140-
.for_each(|b| println!("\t{}", b.name));
141-
assert_eq!(page.blobs.blobs.len(), 4);
137+
page.blobs.blobs().for_each(|b| println!("\t{}", b.name));
138+
assert_eq!(page.blobs.blobs().count(), 4);
142139

143140
let page = container_client
144141
.list_blobs()
@@ -152,14 +149,11 @@ async fn main() -> azure_core::Result<()> {
152149

153150
println!(
154151
"List blob firstfolder/ returned {} blobs with blob_prefix == {:?}",
155-
page.blobs.blobs.len(),
156-
page.blobs.blob_prefix
152+
page.blobs.blobs().count(),
153+
page.blobs.prefixes().collect::<Vec<_>>()
157154
);
158-
page.blobs
159-
.blobs
160-
.iter()
161-
.for_each(|b| println!("\t{}", b.name));
162-
assert_eq!(page.blobs.blobs.len(), 3);
155+
page.blobs.blobs().for_each(|b| println!("\t{}", b.name));
156+
assert_eq!(page.blobs.blobs().count(), 3);
163157

164158
let mut stream = container_client
165159
.list_blobs()
@@ -169,7 +163,7 @@ async fn main() -> azure_core::Result<()> {
169163
println!("Streaming results without prefix");
170164
let mut cnt: i32 = 0;
171165
while let Some(value) = stream.next().await {
172-
let len = value?.blobs.blobs.len();
166+
let len = value?.blobs.blobs().count();
173167
println!("\treceived {} blobs", len);
174168
match cnt {
175169
// we added 21 blobs so 5x4 + 1

sdk/storage_blobs/examples/list_blobs_02.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ async fn main() -> azure_core::Result<()> {
2727
.next()
2828
.await
2929
.expect("stream failed")?;
30-
println!("List blob returned {} blobs.", page.blobs.blobs.len());
30+
println!("List blob returned {} blobs.", page.blobs.blobs().count());
3131

3232
for i in 0..3 {
3333
container_client
@@ -46,7 +46,7 @@ async fn main() -> azure_core::Result<()> {
4646
.next()
4747
.await
4848
.expect("stream failed")?;
49-
println!("List blob returned {} blobs.", page.blobs.blobs.len());
49+
println!("List blob returned {} blobs.", page.blobs.blobs().count());
5050

5151
container_client.delete().into_future().await?;
5252
println!("Container {} deleted", container_name);

sdk/storage_blobs/examples/list_containers_and_blobs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ async fn main() -> azure_core::Result<()> {
2525
let mut blob_stream = container_client.list_blobs().into_stream();
2626
while let Some(blob_entry) = blob_stream.next().await {
2727
let blob_entry = blob_entry?;
28-
for blob in blob_entry.blobs.blobs {
28+
for blob in blob_entry.blobs.blobs() {
2929
println!("\t{}", blob.name);
3030
}
3131
}

sdk/storage_blobs/src/clients/container_client.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,11 @@ mod integration_tests {
213213
.await
214214
.expect("list blobs next() should return value")
215215
.expect("list blobs should succeed");
216-
assert_eq!(list.blobs.blobs.len(), 1);
217-
assert_eq!(list.blobs.blobs[0].name, "hello.txt");
216+
let blobs: Vec<_> = list.blobs.blobs().collect();
217+
assert_eq!(blobs.len(), 1);
218+
assert_eq!(blobs[0].name, "hello.txt");
218219
assert_eq!(
219-
list.blobs.blobs[0]
220+
blobs[0]
220221
.properties
221222
.content_md5
222223
.as_ref()

sdk/storage_blobs/src/container/operations/list_blobs.rs

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,35 @@ struct ListBlobsResponseInternal {
109109
pub blobs: Blobs,
110110
}
111111

112-
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
112+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Default)]
113113
#[serde(rename_all = "PascalCase")]
114114
pub struct Blobs {
115-
pub blob_prefix: Option<Vec<BlobPrefix>>,
116-
#[serde(rename = "Blob", default)]
117-
pub blobs: Vec<Blob>,
115+
#[serde(rename = "$value", default)]
116+
pub items: Vec<BlobItem>,
117+
}
118+
119+
impl Blobs {
120+
pub fn blobs(&self) -> impl Iterator<Item = &Blob> {
121+
self.items.iter().filter_map(|item| match item {
122+
BlobItem::Blob(blob) => Some(blob),
123+
_ => None,
124+
})
125+
}
126+
127+
pub fn prefixes(&self) -> impl Iterator<Item = &BlobPrefix> {
128+
self.items.iter().filter_map(|item| match item {
129+
BlobItem::BlobPrefix(prefix) => Some(prefix),
130+
_ => None,
131+
})
132+
}
133+
}
134+
135+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
136+
#[serde(rename_all = "PascalCase")]
137+
#[allow(clippy::large_enum_variant)]
138+
pub enum BlobItem {
139+
Blob(Blob),
140+
BlobPrefix(BlobPrefix),
118141
}
119142

120143
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
@@ -315,4 +338,90 @@ mod tests {
315338
let bytes = Bytes::from(S);
316339
let _list_blobs_response_internal: ListBlobsResponseInternal = read_xml(&bytes).unwrap();
317340
}
341+
342+
#[test]
343+
fn parse_xml_with_blob_prefix() {
344+
const XML: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
345+
<EnumerationResults ServiceEndpoint="https://sisuautomatedtest.blob.core.windows.net/" ContainerName="lowlatencyrequests">
346+
<Prefix>get-most-recent-key-5/</Prefix>
347+
<Delimiter>/</Delimiter>
348+
<Blobs>
349+
<Blob>
350+
<Name>get-most-recent-key-5/2021-08-04-testfile1</Name>
351+
<Properties>
352+
<Creation-Time>Tue, 13 Sep 2022 08:20:48 GMT</Creation-Time>
353+
<Last-Modified>Tue, 13 Sep 2022 08:20:48 GMT</Last-Modified>
354+
<Etag>0x8DA9560DD170CFD</Etag>
355+
<Content-Length>19</Content-Length>
356+
<Content-Type>text/plain</Content-Type>
357+
<Content-Encoding />
358+
<Content-Language />
359+
<Content-CRC64 />
360+
<Content-MD5>3X/+gWTy92gIJFXx57gLYA==</Content-MD5>
361+
<Cache-Control />
362+
<Content-Disposition />
363+
<BlobType>BlockBlob</BlobType>
364+
<AccessTier>Hot</AccessTier>
365+
<AccessTierInferred>true</AccessTierInferred>
366+
<LeaseStatus>unlocked</LeaseStatus>
367+
<LeaseState>available</LeaseState>
368+
<ServerEncrypted>true</ServerEncrypted>
369+
</Properties>
370+
<OrMetadata />
371+
</Blob>
372+
<BlobPrefix>
373+
<Name>get-most-recent-key-5/2021-08-04T21:48:48.592953Z-15839722113750148182/</Name>
374+
</BlobPrefix>
375+
<Blob>
376+
<Name>get-most-recent-key-5/2021-09-04-testfile2</Name>
377+
<Properties>
378+
<Creation-Time>Tue, 13 Sep 2022 08:07:01 GMT</Creation-Time>
379+
<Last-Modified>Tue, 13 Sep 2022 08:19:21 GMT</Last-Modified>
380+
<Etag>0x8DA9560A916932D</Etag>
381+
<Content-Length>19</Content-Length>
382+
<Content-Type>text/plain</Content-Type>
383+
<Content-Encoding />
384+
<Content-Language />
385+
<Content-CRC64 />
386+
<Content-MD5>b0CPJB6eDfKUzzW7dlboKQ==</Content-MD5>
387+
<Cache-Control />
388+
<Content-Disposition />
389+
<BlobType>BlockBlob</BlobType>
390+
<AccessTier>Hot</AccessTier>
391+
<AccessTierInferred>true</AccessTierInferred>
392+
<LeaseStatus>unlocked</LeaseStatus>
393+
<LeaseState>available</LeaseState>
394+
<ServerEncrypted>true</ServerEncrypted>
395+
</Properties>
396+
<OrMetadata />
397+
</Blob>
398+
<Blob>
399+
<Name>get-most-recent-key-5/2022-08-04-testfile3</Name>
400+
<Properties>
401+
<Creation-Time>Tue, 13 Sep 2022 08:07:01 GMT</Creation-Time>
402+
<Last-Modified>Tue, 13 Sep 2022 08:19:21 GMT</Last-Modified>
403+
<Etag>0x8DA9560A91F9296</Etag>
404+
<Content-Length>34</Content-Length>
405+
<Content-Type>text/plain</Content-Type>
406+
<Content-Encoding />
407+
<Content-Language />
408+
<Content-CRC64 />
409+
<Content-MD5>1F1MssyZOvhY4OZevHWEsw==</Content-MD5>
410+
<Cache-Control />
411+
<Content-Disposition />
412+
<BlobType>BlockBlob</BlobType>
413+
<AccessTier>Hot</AccessTier>
414+
<AccessTierInferred>true</AccessTierInferred>
415+
<LeaseStatus>unlocked</LeaseStatus>
416+
<LeaseState>available</LeaseState>
417+
<ServerEncrypted>true</ServerEncrypted>
418+
</Properties>
419+
<OrMetadata />
420+
</Blob>
421+
</Blobs>
422+
<NextMarker />
423+
</EnumerationResults>"#;
424+
425+
let _list_blobs_response_internal: ListBlobsResponseInternal = read_xml(XML).unwrap();
426+
}
318427
}

0 commit comments

Comments
 (0)