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 cfdd0ea5a4690ce3ab44d7a622b794c6c047c916
Author: Xuanwo <[email protected]>
AuthorDate: Wed Nov 22 19:23:00 2023 +0800

    Fix obs
    
    Signed-off-by: Xuanwo <[email protected]>
---
 core/src/services/obs/backend.rs |  24 ++++++++-
 core/src/services/obs/core.rs    |  96 +++++++++++++++++++++++++++++++++++
 core/src/services/obs/lister.rs  | 105 ++-------------------------------------
 3 files changed, 122 insertions(+), 103 deletions(-)

diff --git a/core/src/services/obs/backend.rs b/core/src/services/obs/backend.rs
index 4f84852b5..2449913a6 100644
--- a/core/src/services/obs/backend.rs
+++ b/core/src/services/obs/backend.rs
@@ -20,6 +20,7 @@ use std::fmt::Debug;
 use std::sync::Arc;
 
 use async_trait::async_trait;
+use bytes::Buf;
 use http::StatusCode;
 use http::Uri;
 use log::debug;
@@ -27,7 +28,7 @@ use reqsign::HuaweicloudObsConfig;
 use reqsign::HuaweicloudObsCredentialLoader;
 use reqsign::HuaweicloudObsSigner;
 
-use super::core::ObsCore;
+use super::core::{ListObjectsOutput, ObsCore};
 use super::error::parse_error;
 use super::lister::ObsLister;
 use super::writer::ObsWriter;
@@ -400,6 +401,27 @@ impl Accessor for ObsBackend {
             return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
         }
 
+        if path.ends_with('/') {
+            let resp = self.core.obs_list_objects(path, "", "", 
Some(1)).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(new_xml_deserialize_error)?;
+
+            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.obs_head_object(path, &args).await?;
 
         let status = resp.status();
diff --git a/core/src/services/obs/core.rs b/core/src/services/obs/core.rs
index de9d2a735..344f10456 100644
--- a/core/src/services/obs/core.rs
+++ b/core/src/services/obs/core.rs
@@ -476,3 +476,99 @@ pub struct CompleteMultipartUploadRequestPart {
     #[serde(rename = "ETag")]
     pub etag: String,
 }
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default, rename_all = "PascalCase")]
+pub struct ListObjectsOutput {
+    pub name: String,
+    pub prefix: String,
+    pub contents: Vec<ListObjectsOutputContent>,
+    pub common_prefixes: Vec<CommonPrefix>,
+    pub marker: String,
+    pub next_marker: Option<String>,
+}
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default, rename_all = "PascalCase")]
+pub struct CommonPrefix {
+    pub prefix: String,
+}
+
+#[derive(Default, Debug, Deserialize)]
+#[serde(default, rename_all = "PascalCase")]
+pub struct ListObjectsOutputContent {
+    pub key: String,
+    pub size: u64,
+}
+
+#[cfg(test)]
+mod tests {
+    use bytes::Buf;
+
+    use super::*;
+
+    #[test]
+    fn test_parse_xml() {
+        let bs = bytes::Bytes::from(
+            r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ListBucketResult 
xmlns="http://obs.cn-north-4.myhuaweicloud.com/doc/2015-06-30/";>
+    <Name>examplebucket</Name>
+    <Prefix>obj</Prefix>
+    <Marker>obj002</Marker>
+    <NextMarker>obj004</NextMarker>
+    <MaxKeys>1000</MaxKeys>
+    <IsTruncated>false</IsTruncated>
+    <Contents>
+        <Key>obj002</Key>
+        <LastModified>2015-07-01T02:11:19.775Z</LastModified>
+        <ETag>"a72e382246ac83e86bd203389849e71d"</ETag>
+        <Size>9</Size>
+        <Owner>
+            <ID>b4bf1b36d9ca43d984fbcb9491b6fce9</ID>
+        </Owner>
+        <StorageClass>STANDARD</StorageClass>
+    </Contents>
+    <Contents>
+        <Key>obj003</Key>
+        <LastModified>2015-07-01T02:11:19.775Z</LastModified>
+        <ETag>"a72e382246ac83e86bd203389849e71d"</ETag>
+        <Size>10</Size>
+        <Owner>
+            <ID>b4bf1b36d9ca43d984fbcb9491b6fce9</ID>
+        </Owner>
+        <StorageClass>STANDARD</StorageClass>
+    </Contents>
+    <CommonPrefixes>
+        <Prefix>hello</Prefix>
+    </CommonPrefixes>
+    <CommonPrefixes>
+        <Prefix>world</Prefix>
+    </CommonPrefixes>
+</ListBucketResult>"#,
+        );
+        let out: ListObjectsOutput = 
quick_xml::de::from_reader(bs.reader()).expect("must success");
+
+        assert_eq!(out.name, "examplebucket".to_string());
+        assert_eq!(out.prefix, "obj".to_string());
+        assert_eq!(out.marker, "obj002".to_string());
+        assert_eq!(out.next_marker, Some("obj004".to_string()),);
+        assert_eq!(
+            out.contents
+                .iter()
+                .map(|v| v.key.clone())
+                .collect::<Vec<String>>(),
+            ["obj002", "obj003"],
+        );
+        assert_eq!(
+            out.contents.iter().map(|v| v.size).collect::<Vec<u64>>(),
+            [9, 10],
+        );
+        assert_eq!(
+            out.common_prefixes
+                .iter()
+                .map(|v| v.prefix.clone())
+                .collect::<Vec<String>>(),
+            ["hello", "world"],
+        )
+    }
+}
diff --git a/core/src/services/obs/lister.rs b/core/src/services/obs/lister.rs
index 831427ab1..7aa6c7631 100644
--- a/core/src/services/obs/lister.rs
+++ b/core/src/services/obs/lister.rs
@@ -20,14 +20,11 @@ use std::sync::Arc;
 use async_trait::async_trait;
 use bytes::Buf;
 use quick_xml::de;
-use serde::Deserialize;
 
-use super::core::ObsCore;
+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;
 
@@ -65,8 +62,8 @@ impl oio::PageList for ObsLister {
 
         let bs = resp.into_body().bytes().await?;
 
-        let output: Output = de::from_reader(bs.reader())
-            .map_err(|e| Error::new(ErrorKind::Unexpected, "deserialize 
xml").set_source(e))?;
+        let output: ListObjectsOutput =
+            de::from_reader(bs.reader()).map_err(new_xml_deserialize_error)?;
 
         // Try our best to check whether this list is done.
         //
@@ -103,99 +100,3 @@ impl oio::PageList for ObsLister {
         Ok(())
     }
 }
-
-#[derive(Default, Debug, Deserialize)]
-#[serde(default, rename_all = "PascalCase")]
-struct Output {
-    name: String,
-    prefix: String,
-    contents: Vec<Content>,
-    common_prefixes: Vec<CommonPrefix>,
-    marker: String,
-    next_marker: Option<String>,
-}
-
-#[derive(Default, Debug, Deserialize)]
-#[serde(default, rename_all = "PascalCase")]
-struct CommonPrefix {
-    prefix: String,
-}
-
-#[derive(Default, Debug, Deserialize)]
-#[serde(default, rename_all = "PascalCase")]
-struct Content {
-    key: String,
-    size: u64,
-}
-
-#[cfg(test)]
-mod tests {
-    use bytes::Buf;
-
-    use super::*;
-
-    #[test]
-    fn test_parse_xml() {
-        let bs = bytes::Bytes::from(
-            r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<ListBucketResult 
xmlns="http://obs.cn-north-4.myhuaweicloud.com/doc/2015-06-30/";>
-    <Name>examplebucket</Name>
-    <Prefix>obj</Prefix>
-    <Marker>obj002</Marker>
-    <NextMarker>obj004</NextMarker>
-    <MaxKeys>1000</MaxKeys>
-    <IsTruncated>false</IsTruncated>
-    <Contents>
-        <Key>obj002</Key>
-        <LastModified>2015-07-01T02:11:19.775Z</LastModified>
-        <ETag>"a72e382246ac83e86bd203389849e71d"</ETag>
-        <Size>9</Size>
-        <Owner>
-            <ID>b4bf1b36d9ca43d984fbcb9491b6fce9</ID>
-        </Owner>
-        <StorageClass>STANDARD</StorageClass>
-    </Contents>
-    <Contents>
-        <Key>obj003</Key>
-        <LastModified>2015-07-01T02:11:19.775Z</LastModified>
-        <ETag>"a72e382246ac83e86bd203389849e71d"</ETag>
-        <Size>10</Size>
-        <Owner>
-            <ID>b4bf1b36d9ca43d984fbcb9491b6fce9</ID>
-        </Owner>
-        <StorageClass>STANDARD</StorageClass>
-    </Contents>
-    <CommonPrefixes>
-        <Prefix>hello</Prefix>
-    </CommonPrefixes>
-    <CommonPrefixes>
-        <Prefix>world</Prefix>
-    </CommonPrefixes>
-</ListBucketResult>"#,
-        );
-        let out: Output = de::from_reader(bs.reader()).expect("must success");
-
-        assert_eq!(out.name, "examplebucket".to_string());
-        assert_eq!(out.prefix, "obj".to_string());
-        assert_eq!(out.marker, "obj002".to_string());
-        assert_eq!(out.next_marker, Some("obj004".to_string()),);
-        assert_eq!(
-            out.contents
-                .iter()
-                .map(|v| v.key.clone())
-                .collect::<Vec<String>>(),
-            ["obj002", "obj003"],
-        );
-        assert_eq!(
-            out.contents.iter().map(|v| v.size).collect::<Vec<u64>>(),
-            [9, 10],
-        );
-        assert_eq!(
-            out.common_prefixes
-                .iter()
-                .map(|v| v.prefix.clone())
-                .collect::<Vec<String>>(),
-            ["hello", "world"],
-        )
-    }
-}

Reply via email to