From a4e8e1fd5e108bbfa87028b66eed58bad664f7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=AD=A3=E8=B6=85?= Date: Thu, 19 Feb 2026 22:35:47 +0800 Subject: [PATCH] refactor(storage): extract ListBuckets response assembly (#1879) --- rustfs/src/storage/ecfs.rs | 17 +------- rustfs/src/storage/s3_api/bucket.rs | 60 ++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index fb4e42a2a..709bd1cda 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -24,7 +24,7 @@ use crate::storage::helper::OperationHelper; use crate::storage::options::{filter_object_metadata, get_content_sha256}; use crate::storage::readers::InMemoryAsyncReader; use crate::storage::s3_api::bucket::{ - build_list_object_versions_output, build_list_objects_output, build_list_objects_v2_output, + build_list_buckets_output, build_list_object_versions_output, build_list_objects_output, build_list_objects_v2_output, parse_list_object_versions_params, parse_list_objects_v2_params, }; use crate::storage::s3_api::common::rustfs_owner; @@ -3539,20 +3539,7 @@ impl S3 for FS { store.list_bucket(&BucketOptions::default()).await.map_err(ApiError::from)? }; - let buckets: Vec = bucket_infos - .iter() - .map(|v| Bucket { - creation_date: v.created.map(Timestamp::from), - name: Some(v.name.clone()), - ..Default::default() - }) - .collect(); - - let output = ListBucketsOutput { - buckets: Some(buckets), - owner: Some(RUSTFS_OWNER.to_owned()), - ..Default::default() - }; + let output = build_list_buckets_output(&bucket_infos); Ok(s3_response(output)) } diff --git a/rustfs/src/storage/s3_api/bucket.rs b/rustfs/src/storage/s3_api/bucket.rs index d2c2803ee..ceed9ef65 100644 --- a/rustfs/src/storage/s3_api/bucket.rs +++ b/rustfs/src/storage/s3_api/bucket.rs @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::storage::s3_api::common::rustfs_owner; use rustfs_ecstore::client::object_api_utils::to_s3s_etag; -use rustfs_ecstore::store_api::{ListObjectVersionsInfo, ListObjectsV2Info}; +use rustfs_ecstore::store_api::{BucketInfo, ListObjectVersionsInfo, ListObjectsV2Info}; use s3s::dto::{ - CommonPrefix, DeleteMarkerEntry, EncodingType, ListObjectVersionsOutput, ListObjectsOutput, ListObjectsV2Output, Object, - ObjectStorageClass, ObjectVersion, ObjectVersionStorageClass, Owner, Timestamp, + Bucket, CommonPrefix, DeleteMarkerEntry, EncodingType, ListBucketsOutput, ListObjectVersionsOutput, ListObjectsOutput, + ListObjectsV2Output, Object, ObjectStorageClass, ObjectVersion, ObjectVersionStorageClass, Owner, Timestamp, }; use s3s::{S3Error, S3ErrorCode}; use tracing::debug; @@ -63,6 +64,23 @@ pub(crate) fn build_list_objects_output(v2: ListObjectsV2Output, request_marker: } } +pub(crate) fn build_list_buckets_output(bucket_infos: &[BucketInfo]) -> ListBucketsOutput { + let buckets: Vec = bucket_infos + .iter() + .map(|bucket_info| Bucket { + creation_date: bucket_info.created.map(Timestamp::from), + name: Some(bucket_info.name.clone()), + ..Default::default() + }) + .collect(); + + ListBucketsOutput { + buckets: Some(buckets), + owner: Some(rustfs_owner()), + ..Default::default() + } +} + pub(crate) fn parse_list_object_versions_params( prefix: Option, delimiter: Option, @@ -332,14 +350,46 @@ fn calculate_next_marker(v2: &ListObjectsV2Output) -> Option { #[cfg(test)] mod tests { use super::{ - build_list_object_versions_output, build_list_objects_output, build_list_objects_v2_output, + build_list_buckets_output, build_list_object_versions_output, build_list_objects_output, build_list_objects_v2_output, parse_list_object_versions_params, parse_list_objects_v2_params, }; - use rustfs_ecstore::store_api::{ListObjectVersionsInfo, ListObjectsV2Info, ObjectInfo}; + use crate::storage::s3_api::common::rustfs_owner; + use rustfs_ecstore::store_api::{BucketInfo, ListObjectVersionsInfo, ListObjectsV2Info, ObjectInfo}; use s3s::S3ErrorCode; use s3s::dto::{CommonPrefix, EncodingType, ListObjectsV2Output, Object}; + use time::OffsetDateTime; use uuid::Uuid; + #[test] + fn test_build_list_buckets_output_maps_bucket_infos_and_owner() { + let bucket_infos = vec![ + BucketInfo { + name: "bucket-a".to_string(), + created: Some(OffsetDateTime::UNIX_EPOCH), + ..Default::default() + }, + BucketInfo { + name: "bucket-b".to_string(), + created: None, + ..Default::default() + }, + ]; + + let output = build_list_buckets_output(&bucket_infos); + let buckets = output.buckets.as_ref().expect("buckets should be present"); + let owner = output.owner.as_ref().expect("owner should be present"); + + assert_eq!(buckets.len(), 2); + assert_eq!(buckets[0].name.as_deref(), Some("bucket-a")); + assert_eq!(buckets[0].creation_date, Some(s3s::dto::Timestamp::from(OffsetDateTime::UNIX_EPOCH))); + assert_eq!(buckets[1].name.as_deref(), Some("bucket-b")); + assert_eq!(buckets[1].creation_date, None); + + let expected_owner = rustfs_owner(); + assert_eq!(owner.display_name, expected_owner.display_name); + assert_eq!(owner.id, expected_owner.id); + } + #[test] fn test_list_objects_marker_echoes_request_value() { let output = build_list_objects_output(ListObjectsV2Output::default(), Some("m-1".to_string()));