From 208c03cafad0f06f6f1ce6250c7b9ccf5c63abf8 Mon Sep 17 00:00:00 2001 From: Robert Pack Date: Tue, 14 Feb 2023 07:48:46 +0100 Subject: [PATCH 1/2] fix: consistent list responses for gen1 and gen2 accounts --- object_store/src/azure/client.rs | 58 +++++++++++++++++--------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/object_store/src/azure/client.rs b/object_store/src/azure/client.rs index 76bb45124a66..e57da37300a2 100644 --- a/object_store/src/azure/client.rs +++ b/object_store/src/azure/client.rs @@ -388,7 +388,7 @@ impl AzureClient { .context(InvalidListResponseSnafu)?; let token = response.next_marker.take(); - Ok((response.try_into()?, token)) + Ok((to_list_result(response, prefix)?, token)) } /// Perform a list operation automatically handling pagination @@ -419,33 +419,37 @@ struct ListResultInternal { pub blobs: Blobs, } -impl TryFrom for ListResult { - type Error = crate::Error; - - fn try_from(value: ListResultInternal) -> Result { - let common_prefixes = value - .blobs - .blob_prefix - .into_iter() - .map(|x| Ok(Path::parse(x.name)?)) - .collect::>()?; - - let objects = value - .blobs - .blobs - .into_iter() - .map(ObjectMeta::try_from) - // Note: workaround for gen2 accounts with hierarchical namespaces. These accounts also - // return path segments as "directories". When we cant directories, its always via - // the BlobPrefix mechanics. - .filter_map_ok(|obj| if obj.size > 0 { Some(obj) } else { None }) - .collect::>()?; - - Ok(Self { - common_prefixes, - objects, +fn to_list_result(value: ListResultInternal, prefix: Option<&str>) -> Result { + let prefix = prefix.map(Path::from).unwrap_or_else(Path::default); + let common_prefixes = value + .blobs + .blob_prefix + .into_iter() + .map(|x| Ok(Path::parse(x.name)?)) + .collect::>()?; + + let objects = value + .blobs + .blobs + .into_iter() + .map(ObjectMeta::try_from) + // Note: workaround for gen2 accounts with hierarchical namespaces. These accounts also + // return path segments as "directories" and include blobs in list requests with prefix, + // if the prefix mateches the blob. When we want directories, its always via + // the BlobPrefix mechanics, and during lists we state that prefixes are evaluated on path segement basis. + .filter_map_ok(|obj| { + if obj.size > 0 && obj.location != prefix { + Some(obj) + } else { + None + } }) - } + .collect::>()?; + + Ok(ListResult { + common_prefixes, + objects, + }) } /// Collection of blobs and potentially shared prefixes returned from list requests. From f3c3646627a354e334936cfeccc10d8da3b43ce9 Mon Sep 17 00:00:00 2001 From: Robert Pack <42610831+roeap@users.noreply.github.com> Date: Tue, 14 Feb 2023 12:05:49 +0100 Subject: [PATCH 2/2] Update object_store/src/azure/client.rs Co-authored-by: Raphael Taylor-Davies <1781103+tustvold@users.noreply.github.com> --- object_store/src/azure/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_store/src/azure/client.rs b/object_store/src/azure/client.rs index e57da37300a2..c5a5652ab4d1 100644 --- a/object_store/src/azure/client.rs +++ b/object_store/src/azure/client.rs @@ -438,7 +438,7 @@ fn to_list_result(value: ListResultInternal, prefix: Option<&str>) -> Result 0 && obj.location != prefix { + if obj.size > 0 && obj.location.as_ref().len() > prefix.as_ref().len() { Some(obj) } else { None