This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch fix-stat in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git
commit e39939b7530d132ad9c06f27305b8a873761e68e Author: Xuanwo <[email protected]> AuthorDate: Wed Nov 22 19:27:06 2023 +0800 FIx oss Signed-off-by: Xuanwo <[email protected]> --- core/src/services/oss/backend.rs | 30 ++++++++-- core/src/services/oss/core.rs | 110 ++++++++++++++++++++++++++++++++++ core/src/services/oss/lister.rs | 124 +-------------------------------------- 3 files changed, 137 insertions(+), 127 deletions(-) diff --git a/core/src/services/oss/backend.rs b/core/src/services/oss/backend.rs index a9e512429..c9d1bd0ad 100644 --- a/core/src/services/oss/backend.rs +++ b/core/src/services/oss/backend.rs @@ -514,6 +514,31 @@ impl Accessor for OssBackend { return Ok(RpStat::new(m)); } + if path.ends_with('/') { + let resp = self + .core + .oss_list_object(path, "", "", Some(1), None) + .await?; + + if resp.status() != StatusCode::OK { + return Err(parse_error(resp).await?); + } + + let bs = resp.into_body().bytes().await?; + + let output: ListObjectsOutput = quick_xml::de::from_reader(bs.reader()) + .map_err(|e| Error::new(ErrorKind::Unexpected, "deserialize xml").set_source(e))?; + + return if !output.contents.is_empty() { + Ok(RpStat::new(Metadata::new(EntryMode::DIR))) + } else { + Err( + Error::new(ErrorKind::NotFound, "The directory is not found") + .with_context("path", path), + ) + }; + } + let resp = self .core .oss_head_object(path, args.if_match(), args.if_none_match()) @@ -523,11 +548,6 @@ impl Accessor for OssBackend { match status { StatusCode::OK => parse_into_metadata(path, resp.headers()).map(RpStat::new), - StatusCode::NOT_FOUND if path.ends_with('/') => { - let m = Metadata::new(EntryMode::DIR); - Ok(RpStat::new(m)) - } - _ => Err(parse_error(resp).await?), } } diff --git a/core/src/services/oss/core.rs b/core/src/services/oss/core.rs index fad417c4c..66d4654b6 100644 --- a/core/src/services/oss/core.rs +++ b/core/src/services/oss/core.rs @@ -687,6 +687,36 @@ pub struct CompleteMultipartUploadResult { pub etag: String, } +#[derive(Default, Debug, Deserialize)] +#[serde(default, rename_all = "PascalCase")] +pub struct ListObjectsOutput { + pub prefix: String, + pub max_keys: u64, + pub encoding_type: String, + pub is_truncated: bool, + pub common_prefixes: Vec<CommonPrefix>, + pub contents: Vec<ListObjectsOutputContent>, + pub key_count: u64, + + pub next_continuation_token: Option<String>, +} + +#[derive(Default, Debug, Deserialize, PartialEq, Eq)] +#[serde(default, rename_all = "PascalCase")] +pub struct ListObjectsOutputContent { + pub key: String, + pub last_modified: String, + #[serde(rename = "ETag")] + pub etag: String, + pub size: u64, +} + +#[derive(Default, Debug, Deserialize)] +#[serde(default, rename_all = "PascalCase")] +pub struct CommonPrefix { + pub prefix: String, +} + #[cfg(test)] mod tests { use bytes::Buf; @@ -843,4 +873,84 @@ mod tests { assert_eq!("oss-example", result.bucket); assert_eq!("multipart.data", result.key); } + + #[test] + fn test_parse_list_output() { + let bs = bytes::Bytes::from( + r#"<?xml version="1.0" encoding="UTF-8"?> +<ListBucketResult xmlns="https://doc.oss-cn-hangzhou.aliyuncs.com"> + <Name>examplebucket</Name> + <Prefix></Prefix> + <StartAfter>b</StartAfter> + <MaxKeys>3</MaxKeys> + <EncodingType>url</EncodingType> + <IsTruncated>true</IsTruncated> + <NextContinuationToken>CgJiYw--</NextContinuationToken> + <Contents> + <Key>b/c</Key> + <LastModified>2020-05-18T05:45:54.000Z</LastModified> + <ETag>"35A27C2B9EAEEB6F48FD7FB5861D****"</ETag> + <Size>25</Size> + <StorageClass>STANDARD</StorageClass> + <Owner> + <ID>1686240967192623</ID> + <DisplayName>1686240967192623</DisplayName> + </Owner> + </Contents> + <Contents> + <Key>ba</Key> + <LastModified>2020-05-18T11:17:58.000Z</LastModified> + <ETag>"35A27C2B9EAEEB6F48FD7FB5861D****"</ETag> + <Size>25</Size> + <StorageClass>STANDARD</StorageClass> + <Owner> + <ID>1686240967192623</ID> + <DisplayName>1686240967192623</DisplayName> + </Owner> + </Contents> + <Contents> + <Key>bc</Key> + <LastModified>2020-05-18T05:45:59.000Z</LastModified> + <ETag>"35A27C2B9EAEEB6F48FD7FB5861D****"</ETag> + <Size>25</Size> + <StorageClass>STANDARD</StorageClass> + <Owner> + <ID>1686240967192623</ID> + <DisplayName>1686240967192623</DisplayName> + </Owner> + </Contents> + <KeyCount>3</KeyCount> +</ListBucketResult>"#, + ); + + let out: ListObjectsOutput = quick_xml::de::from_reader(bs.reader()).expect("must_success"); + + assert!(out.is_truncated); + assert_eq!(out.next_continuation_token, Some("CgJiYw--".to_string())); + assert!(out.common_prefixes.is_empty()); + + assert_eq!( + out.contents, + vec![ + ListObjectsOutputContent { + key: "b/c".to_string(), + last_modified: "2020-05-18T05:45:54.000Z".to_string(), + etag: "\"35A27C2B9EAEEB6F48FD7FB5861D****\"".to_string(), + size: 25, + }, + ListObjectsOutputContent { + key: "ba".to_string(), + last_modified: "2020-05-18T11:17:58.000Z".to_string(), + etag: "\"35A27C2B9EAEEB6F48FD7FB5861D****\"".to_string(), + size: 25, + }, + ListObjectsOutputContent { + key: "bc".to_string(), + last_modified: "2020-05-18T05:45:59.000Z".to_string(), + etag: "\"35A27C2B9EAEEB6F48FD7FB5861D****\"".to_string(), + size: 25, + } + ] + ) + } } diff --git a/core/src/services/oss/lister.rs b/core/src/services/oss/lister.rs index 6412cf8bf..9e6eb4652 100644 --- a/core/src/services/oss/lister.rs +++ b/core/src/services/oss/lister.rs @@ -21,16 +21,11 @@ use async_trait::async_trait; use bytes::Buf; use quick_xml::de; use quick_xml::escape::unescape; -use serde::Deserialize; use super::core::*; use super::error::parse_error; use crate::raw::*; -use crate::EntryMode; -use crate::Error; -use crate::ErrorKind; -use crate::Metadata; -use crate::Result; +use crate::*; pub struct OssLister { core: Arc<OssCore>, @@ -86,7 +81,7 @@ impl oio::PageList for OssLister { let bs = resp.into_body().bytes().await?; - let output: ListBucketOutput = de::from_reader(bs.reader()) + let output: ListObjectsOutput = de::from_reader(bs.reader()) .map_err(|e| Error::new(ErrorKind::Unexpected, "deserialize xml").set_source(e))?; ctx.done = !output.is_truncated; @@ -127,118 +122,3 @@ impl oio::PageList for OssLister { Ok(()) } } - -#[derive(Default, Debug, Deserialize)] -#[serde(default, rename_all = "PascalCase")] -struct ListBucketOutput { - prefix: String, - max_keys: u64, - encoding_type: String, - is_truncated: bool, - common_prefixes: Vec<CommonPrefix>, - contents: Vec<Content>, - key_count: u64, - - next_continuation_token: Option<String>, -} - -#[derive(Default, Debug, Deserialize, PartialEq, Eq)] -#[serde(default, rename_all = "PascalCase")] -struct Content { - key: String, - last_modified: String, - #[serde(rename = "ETag")] - etag: String, - size: u64, -} - -#[derive(Default, Debug, Deserialize)] -#[serde(default, rename_all = "PascalCase")] -struct CommonPrefix { - prefix: String, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_list_output() { - let bs = bytes::Bytes::from( - r#"<?xml version="1.0" encoding="UTF-8"?> -<ListBucketResult xmlns="https://doc.oss-cn-hangzhou.aliyuncs.com"> - <Name>examplebucket</Name> - <Prefix></Prefix> - <StartAfter>b</StartAfter> - <MaxKeys>3</MaxKeys> - <EncodingType>url</EncodingType> - <IsTruncated>true</IsTruncated> - <NextContinuationToken>CgJiYw--</NextContinuationToken> - <Contents> - <Key>b/c</Key> - <LastModified>2020-05-18T05:45:54.000Z</LastModified> - <ETag>"35A27C2B9EAEEB6F48FD7FB5861D****"</ETag> - <Size>25</Size> - <StorageClass>STANDARD</StorageClass> - <Owner> - <ID>1686240967192623</ID> - <DisplayName>1686240967192623</DisplayName> - </Owner> - </Contents> - <Contents> - <Key>ba</Key> - <LastModified>2020-05-18T11:17:58.000Z</LastModified> - <ETag>"35A27C2B9EAEEB6F48FD7FB5861D****"</ETag> - <Size>25</Size> - <StorageClass>STANDARD</StorageClass> - <Owner> - <ID>1686240967192623</ID> - <DisplayName>1686240967192623</DisplayName> - </Owner> - </Contents> - <Contents> - <Key>bc</Key> - <LastModified>2020-05-18T05:45:59.000Z</LastModified> - <ETag>"35A27C2B9EAEEB6F48FD7FB5861D****"</ETag> - <Size>25</Size> - <StorageClass>STANDARD</StorageClass> - <Owner> - <ID>1686240967192623</ID> - <DisplayName>1686240967192623</DisplayName> - </Owner> - </Contents> - <KeyCount>3</KeyCount> -</ListBucketResult>"#, - ); - - let out: ListBucketOutput = de::from_reader(bs.reader()).expect("must_success"); - - assert!(out.is_truncated); - assert_eq!(out.next_continuation_token, Some("CgJiYw--".to_string())); - assert!(out.common_prefixes.is_empty()); - - assert_eq!( - out.contents, - vec![ - Content { - key: "b/c".to_string(), - last_modified: "2020-05-18T05:45:54.000Z".to_string(), - etag: "\"35A27C2B9EAEEB6F48FD7FB5861D****\"".to_string(), - size: 25, - }, - Content { - key: "ba".to_string(), - last_modified: "2020-05-18T11:17:58.000Z".to_string(), - etag: "\"35A27C2B9EAEEB6F48FD7FB5861D****\"".to_string(), - size: 25, - }, - Content { - key: "bc".to_string(), - last_modified: "2020-05-18T05:45:59.000Z".to_string(), - etag: "\"35A27C2B9EAEEB6F48FD7FB5861D****\"".to_string(), - size: 25, - } - ] - ) - } -}
