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 eb953e63e52f47935d25494e86ced18edb78819d
Author: Xuanwo <[email protected]>
AuthorDate: Wed Nov 22 19:09:42 2023 +0800

    Fix gcs
    
    Signed-off-by: Xuanwo <[email protected]>
---
 core/src/services/gcs/backend.rs |  32 ++++---
 core/src/services/gcs/core.rs    | 175 ++++++++++++++++++++++++++++++++++++++
 core/src/services/gcs/lister.rs  | 177 +--------------------------------------
 3 files changed, 198 insertions(+), 186 deletions(-)

diff --git a/core/src/services/gcs/backend.rs b/core/src/services/gcs/backend.rs
index d528479dc..b3cc960a3 100644
--- a/core/src/services/gcs/backend.rs
+++ b/core/src/services/gcs/backend.rs
@@ -30,7 +30,7 @@ use reqsign::GoogleTokenLoader;
 use serde::Deserialize;
 use serde_json;
 
-use super::core::GcsCore;
+use super::core::*;
 use super::error::parse_error;
 use super::lister::GcsLister;
 use super::writer::GcsWriter;
@@ -422,21 +422,35 @@ impl Accessor for GcsBackend {
             return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
         }
 
+        if path.ends_with('/') {
+            let resp = self
+                .core
+                .gcs_list_objects(path, "", "", Some(1), None)
+                .await?;
+
+            let bs = resp.into_body().bytes().await?;
+            let output: ListResponse =
+                
serde_json::from_slice(&bs).map_err(new_json_deserialize_error)?;
+
+            return if !output.items.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.gcs_get_object_metadata(path, &args).await?;
 
         if resp.status().is_success() {
-            // read http response body
             let slc = resp.into_body().bytes().await?;
 
             let meta: GetObjectJsonResponse =
                 
serde_json::from_slice(&slc).map_err(new_json_deserialize_error)?;
 
-            let mode = if path.ends_with('/') {
-                EntryMode::DIR
-            } else {
-                EntryMode::FILE
-            };
-            let mut m = Metadata::new(mode);
+            let mut m = Metadata::new(EntryMode::FILE);
 
             m.set_etag(&meta.etag);
             m.set_content_md5(&meta.md5_hash);
@@ -453,8 +467,6 @@ impl Accessor for GcsBackend {
             m.set_last_modified(parse_datetime_from_rfc3339(&meta.updated)?);
 
             Ok(RpStat::new(m))
-        } else if resp.status() == StatusCode::NOT_FOUND && 
path.ends_with('/') {
-            Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
         } else {
             Err(parse_error(resp).await?)
         }
diff --git a/core/src/services/gcs/core.rs b/core/src/services/gcs/core.rs
index 5720db2cb..df112e504 100644
--- a/core/src/services/gcs/core.rs
+++ b/core/src/services/gcs/core.rs
@@ -37,6 +37,7 @@ use reqsign::GoogleCredentialLoader;
 use reqsign::GoogleSigner;
 use reqsign::GoogleToken;
 use reqsign::GoogleTokenLoader;
+use serde::Deserialize;
 use serde_json::json;
 
 use super::uri::percent_encode_path;
@@ -597,3 +598,177 @@ impl GcsCore {
         self.send(req).await
     }
 }
+
+/// Response JSON from GCS list objects API.
+///
+/// refer to https://cloud.google.com/storage/docs/json_api/v1/objects/list 
for details
+#[derive(Default, Debug, Deserialize)]
+#[serde(default, rename_all = "camelCase")]
+pub struct ListResponse {
+    /// The continuation token.
+    ///
+    /// If this is the last page of results, then no continuation token is 
returned.
+    pub next_page_token: Option<String>,
+    /// Object name prefixes for objects that matched the listing request
+    /// but were excluded from [items] because of a delimiter.
+    pub prefixes: Vec<String>,
+    /// The list of objects, ordered lexicographically by name.
+    pub items: Vec<ListResponseItem>,
+}
+
+#[derive(Default, Debug, Eq, PartialEq, Deserialize)]
+#[serde(default, rename_all = "camelCase")]
+pub struct ListResponseItem {
+    pub name: String,
+    pub size: String,
+    // metadata
+    pub etag: String,
+    pub md5_hash: String,
+    pub updated: String,
+    pub content_type: String,
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_deserialize_list_response() {
+        let content = r#"
+    {
+  "kind": "storage#objects",
+  "prefixes": [
+    "dir/",
+    "test/"
+  ],
+  "items": [
+    {
+      "kind": "storage#object",
+      "id": "example/1.png/1660563214863653",
+      "selfLink": "https://www.googleapis.com/storage/v1/b/example/o/1.png";,
+      "mediaLink": 
"https://content-storage.googleapis.com/download/storage/v1/b/example/o/1.png?generation=1660563214863653&alt=media";,
+      "name": "1.png",
+      "bucket": "example",
+      "generation": "1660563214863653",
+      "metageneration": "1",
+      "contentType": "image/png",
+      "storageClass": "STANDARD",
+      "size": "56535",
+      "md5Hash": "fHcEH1vPwA6eTPqxuasXcg==",
+      "crc32c": "j/un9g==",
+      "etag": "CKWasoTgyPkCEAE=",
+      "timeCreated": "2022-08-15T11:33:34.866Z",
+      "updated": "2022-08-15T11:33:34.866Z",
+      "timeStorageClassUpdated": "2022-08-15T11:33:34.866Z"
+    },
+    {
+      "kind": "storage#object",
+      "id": "example/2.png/1660563214883337",
+      "selfLink": "https://www.googleapis.com/storage/v1/b/example/o/2.png";,
+      "mediaLink": 
"https://content-storage.googleapis.com/download/storage/v1/b/example/o/2.png?generation=1660563214883337&alt=media";,
+      "name": "2.png",
+      "bucket": "example",
+      "generation": "1660563214883337",
+      "metageneration": "1",
+      "contentType": "image/png",
+      "storageClass": "STANDARD",
+      "size": "45506",
+      "md5Hash": "e6LsGusU7pFJZk+114NV1g==",
+      "crc32c": "L00QAg==",
+      "etag": "CIm0s4TgyPkCEAE=",
+      "timeCreated": "2022-08-15T11:33:34.886Z",
+      "updated": "2022-08-15T11:33:34.886Z",
+      "timeStorageClassUpdated": "2022-08-15T11:33:34.886Z"
+    }
+  ]
+}
+    "#;
+
+        let output: ListResponse =
+            serde_json::from_str(content).expect("JSON deserialize must 
succeed");
+        assert!(output.next_page_token.is_none());
+        assert_eq!(output.items.len(), 2);
+        assert_eq!(output.items[0].name, "1.png");
+        assert_eq!(output.items[0].size, "56535");
+        assert_eq!(output.items[0].md5_hash, "fHcEH1vPwA6eTPqxuasXcg==");
+        assert_eq!(output.items[0].etag, "CKWasoTgyPkCEAE=");
+        assert_eq!(output.items[0].updated, "2022-08-15T11:33:34.866Z");
+        assert_eq!(output.items[1].name, "2.png");
+        assert_eq!(output.items[1].size, "45506");
+        assert_eq!(output.items[1].md5_hash, "e6LsGusU7pFJZk+114NV1g==");
+        assert_eq!(output.items[1].etag, "CIm0s4TgyPkCEAE=");
+        assert_eq!(output.items[1].updated, "2022-08-15T11:33:34.886Z");
+        assert_eq!(output.items[1].content_type, "image/png");
+        assert_eq!(output.prefixes, vec!["dir/", "test/"])
+    }
+
+    #[test]
+    fn test_deserialize_list_response_with_next_page_token() {
+        let content = r#"
+    {
+  "kind": "storage#objects",
+  "prefixes": [
+    "dir/",
+    "test/"
+  ],
+  "nextPageToken": "CgYxMC5wbmc=",
+  "items": [
+    {
+      "kind": "storage#object",
+      "id": "example/1.png/1660563214863653",
+      "selfLink": "https://www.googleapis.com/storage/v1/b/example/o/1.png";,
+      "mediaLink": 
"https://content-storage.googleapis.com/download/storage/v1/b/example/o/1.png?generation=1660563214863653&alt=media";,
+      "name": "1.png",
+      "bucket": "example",
+      "generation": "1660563214863653",
+      "metageneration": "1",
+      "contentType": "image/png",
+      "storageClass": "STANDARD",
+      "size": "56535",
+      "md5Hash": "fHcEH1vPwA6eTPqxuasXcg==",
+      "crc32c": "j/un9g==",
+      "etag": "CKWasoTgyPkCEAE=",
+      "timeCreated": "2022-08-15T11:33:34.866Z",
+      "updated": "2022-08-15T11:33:34.866Z",
+      "timeStorageClassUpdated": "2022-08-15T11:33:34.866Z"
+    },
+    {
+      "kind": "storage#object",
+      "id": "example/2.png/1660563214883337",
+      "selfLink": "https://www.googleapis.com/storage/v1/b/example/o/2.png";,
+      "mediaLink": 
"https://content-storage.googleapis.com/download/storage/v1/b/example/o/2.png?generation=1660563214883337&alt=media";,
+      "name": "2.png",
+      "bucket": "example",
+      "generation": "1660563214883337",
+      "metageneration": "1",
+      "contentType": "image/png",
+      "storageClass": "STANDARD",
+      "size": "45506",
+      "md5Hash": "e6LsGusU7pFJZk+114NV1g==",
+      "crc32c": "L00QAg==",
+      "etag": "CIm0s4TgyPkCEAE=",
+      "timeCreated": "2022-08-15T11:33:34.886Z",
+      "updated": "2022-08-15T11:33:34.886Z",
+      "timeStorageClassUpdated": "2022-08-15T11:33:34.886Z"
+    }
+  ]
+}
+    "#;
+
+        let output: ListResponse =
+            serde_json::from_str(content).expect("JSON deserialize must 
succeed");
+        assert_eq!(output.next_page_token, Some("CgYxMC5wbmc=".to_string()));
+        assert_eq!(output.items.len(), 2);
+        assert_eq!(output.items[0].name, "1.png");
+        assert_eq!(output.items[0].size, "56535");
+        assert_eq!(output.items[0].md5_hash, "fHcEH1vPwA6eTPqxuasXcg==");
+        assert_eq!(output.items[0].etag, "CKWasoTgyPkCEAE=");
+        assert_eq!(output.items[0].updated, "2022-08-15T11:33:34.866Z");
+        assert_eq!(output.items[1].name, "2.png");
+        assert_eq!(output.items[1].size, "45506");
+        assert_eq!(output.items[1].md5_hash, "e6LsGusU7pFJZk+114NV1g==");
+        assert_eq!(output.items[1].etag, "CIm0s4TgyPkCEAE=");
+        assert_eq!(output.items[1].updated, "2022-08-15T11:33:34.886Z");
+        assert_eq!(output.prefixes, vec!["dir/", "test/"])
+    }
+}
diff --git a/core/src/services/gcs/lister.rs b/core/src/services/gcs/lister.rs
index 244ca2a93..b9b61f542 100644
--- a/core/src/services/gcs/lister.rs
+++ b/core/src/services/gcs/lister.rs
@@ -18,10 +18,9 @@
 use std::sync::Arc;
 
 use async_trait::async_trait;
-use serde::Deserialize;
 use serde_json;
 
-use super::core::GcsCore;
+use super::core::*;
 use super::error::parse_error;
 use crate::raw::*;
 use crate::*;
@@ -137,177 +136,3 @@ impl oio::PageList for GcsLister {
         Ok(())
     }
 }
-
-/// Response JSON from GCS list objects API.
-///
-/// refer to https://cloud.google.com/storage/docs/json_api/v1/objects/list 
for details
-#[derive(Default, Debug, Deserialize)]
-#[serde(default, rename_all = "camelCase")]
-struct ListResponse {
-    /// The continuation token.
-    ///
-    /// If this is the last page of results, then no continuation token is 
returned.
-    next_page_token: Option<String>,
-    /// Object name prefixes for objects that matched the listing request
-    /// but were excluded from [items] because of a delimiter.
-    prefixes: Vec<String>,
-    /// The list of objects, ordered lexicographically by name.
-    items: Vec<ListResponseItem>,
-}
-
-#[derive(Default, Debug, Eq, PartialEq, Deserialize)]
-#[serde(default, rename_all = "camelCase")]
-struct ListResponseItem {
-    name: String,
-    size: String,
-    // metadata
-    etag: String,
-    md5_hash: String,
-    updated: String,
-    content_type: String,
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_deserialize_list_response() {
-        let content = r#"
-    {
-  "kind": "storage#objects",
-  "prefixes": [
-    "dir/",
-    "test/"
-  ],
-  "items": [
-    {
-      "kind": "storage#object",
-      "id": "example/1.png/1660563214863653",
-      "selfLink": "https://www.googleapis.com/storage/v1/b/example/o/1.png";,
-      "mediaLink": 
"https://content-storage.googleapis.com/download/storage/v1/b/example/o/1.png?generation=1660563214863653&alt=media";,
-      "name": "1.png",
-      "bucket": "example",
-      "generation": "1660563214863653",
-      "metageneration": "1",
-      "contentType": "image/png",
-      "storageClass": "STANDARD",
-      "size": "56535",
-      "md5Hash": "fHcEH1vPwA6eTPqxuasXcg==",
-      "crc32c": "j/un9g==",
-      "etag": "CKWasoTgyPkCEAE=",
-      "timeCreated": "2022-08-15T11:33:34.866Z",
-      "updated": "2022-08-15T11:33:34.866Z",
-      "timeStorageClassUpdated": "2022-08-15T11:33:34.866Z"
-    },
-    {
-      "kind": "storage#object",
-      "id": "example/2.png/1660563214883337",
-      "selfLink": "https://www.googleapis.com/storage/v1/b/example/o/2.png";,
-      "mediaLink": 
"https://content-storage.googleapis.com/download/storage/v1/b/example/o/2.png?generation=1660563214883337&alt=media";,
-      "name": "2.png",
-      "bucket": "example",
-      "generation": "1660563214883337",
-      "metageneration": "1",
-      "contentType": "image/png",
-      "storageClass": "STANDARD",
-      "size": "45506",
-      "md5Hash": "e6LsGusU7pFJZk+114NV1g==",
-      "crc32c": "L00QAg==",
-      "etag": "CIm0s4TgyPkCEAE=",
-      "timeCreated": "2022-08-15T11:33:34.886Z",
-      "updated": "2022-08-15T11:33:34.886Z",
-      "timeStorageClassUpdated": "2022-08-15T11:33:34.886Z"
-    }
-  ]
-}
-    "#;
-
-        let output: ListResponse =
-            serde_json::from_str(content).expect("JSON deserialize must 
succeed");
-        assert!(output.next_page_token.is_none());
-        assert_eq!(output.items.len(), 2);
-        assert_eq!(output.items[0].name, "1.png");
-        assert_eq!(output.items[0].size, "56535");
-        assert_eq!(output.items[0].md5_hash, "fHcEH1vPwA6eTPqxuasXcg==");
-        assert_eq!(output.items[0].etag, "CKWasoTgyPkCEAE=");
-        assert_eq!(output.items[0].updated, "2022-08-15T11:33:34.866Z");
-        assert_eq!(output.items[1].name, "2.png");
-        assert_eq!(output.items[1].size, "45506");
-        assert_eq!(output.items[1].md5_hash, "e6LsGusU7pFJZk+114NV1g==");
-        assert_eq!(output.items[1].etag, "CIm0s4TgyPkCEAE=");
-        assert_eq!(output.items[1].updated, "2022-08-15T11:33:34.886Z");
-        assert_eq!(output.items[1].content_type, "image/png");
-        assert_eq!(output.prefixes, vec!["dir/", "test/"])
-    }
-
-    #[test]
-    fn test_deserialize_list_response_with_next_page_token() {
-        let content = r#"
-    {
-  "kind": "storage#objects",
-  "prefixes": [
-    "dir/",
-    "test/"
-  ],
-  "nextPageToken": "CgYxMC5wbmc=",
-  "items": [
-    {
-      "kind": "storage#object",
-      "id": "example/1.png/1660563214863653",
-      "selfLink": "https://www.googleapis.com/storage/v1/b/example/o/1.png";,
-      "mediaLink": 
"https://content-storage.googleapis.com/download/storage/v1/b/example/o/1.png?generation=1660563214863653&alt=media";,
-      "name": "1.png",
-      "bucket": "example",
-      "generation": "1660563214863653",
-      "metageneration": "1",
-      "contentType": "image/png",
-      "storageClass": "STANDARD",
-      "size": "56535",
-      "md5Hash": "fHcEH1vPwA6eTPqxuasXcg==",
-      "crc32c": "j/un9g==",
-      "etag": "CKWasoTgyPkCEAE=",
-      "timeCreated": "2022-08-15T11:33:34.866Z",
-      "updated": "2022-08-15T11:33:34.866Z",
-      "timeStorageClassUpdated": "2022-08-15T11:33:34.866Z"
-    },
-    {
-      "kind": "storage#object",
-      "id": "example/2.png/1660563214883337",
-      "selfLink": "https://www.googleapis.com/storage/v1/b/example/o/2.png";,
-      "mediaLink": 
"https://content-storage.googleapis.com/download/storage/v1/b/example/o/2.png?generation=1660563214883337&alt=media";,
-      "name": "2.png",
-      "bucket": "example",
-      "generation": "1660563214883337",
-      "metageneration": "1",
-      "contentType": "image/png",
-      "storageClass": "STANDARD",
-      "size": "45506",
-      "md5Hash": "e6LsGusU7pFJZk+114NV1g==",
-      "crc32c": "L00QAg==",
-      "etag": "CIm0s4TgyPkCEAE=",
-      "timeCreated": "2022-08-15T11:33:34.886Z",
-      "updated": "2022-08-15T11:33:34.886Z",
-      "timeStorageClassUpdated": "2022-08-15T11:33:34.886Z"
-    }
-  ]
-}
-    "#;
-
-        let output: ListResponse =
-            serde_json::from_str(content).expect("JSON deserialize must 
succeed");
-        assert_eq!(output.next_page_token, Some("CgYxMC5wbmc=".to_string()));
-        assert_eq!(output.items.len(), 2);
-        assert_eq!(output.items[0].name, "1.png");
-        assert_eq!(output.items[0].size, "56535");
-        assert_eq!(output.items[0].md5_hash, "fHcEH1vPwA6eTPqxuasXcg==");
-        assert_eq!(output.items[0].etag, "CKWasoTgyPkCEAE=");
-        assert_eq!(output.items[0].updated, "2022-08-15T11:33:34.866Z");
-        assert_eq!(output.items[1].name, "2.png");
-        assert_eq!(output.items[1].size, "45506");
-        assert_eq!(output.items[1].md5_hash, "e6LsGusU7pFJZk+114NV1g==");
-        assert_eq!(output.items[1].etag, "CIm0s4TgyPkCEAE=");
-        assert_eq!(output.items[1].updated, "2022-08-15T11:33:34.886Z");
-        assert_eq!(output.prefixes, vec!["dir/", "test/"])
-    }
-}

Reply via email to