refactor(storage): inline list response params (#2476)

This commit is contained in:
安正超
2026-04-11 09:25:40 +08:00
committed by GitHub
parent 70fdf79297
commit d70ca1990e
+86 -128
View File
@@ -130,29 +130,6 @@ struct ObjectMetadataPermissions {
tags_allowed: bool, tags_allowed: bool,
} }
#[derive(Debug, Clone)]
struct ListObjectVersionsMResponseContext {
bucket: String,
prefix: String,
delimiter: Option<String>,
max_keys: i32,
encoding_type: Option<EncodingType>,
key_marker: Option<String>,
version_id_marker: Option<String>,
}
#[derive(Debug, Clone)]
struct ListObjectsV2MResponseContext {
bucket: String,
prefix: String,
delimiter: Option<String>,
max_keys: i32,
encoding_type: Option<EncodingType>,
continuation_token: Option<String>,
start_after: Option<String>,
fetch_owner: bool,
}
fn encode_list_versions_value(value: &str, encoding_type: Option<&EncodingType>) -> String { fn encode_list_versions_value(value: &str, encoding_type: Option<&EncodingType>) -> String {
if encoding_type.is_some_and(|encoding| encoding.as_str() == EncodingType::URL) { if encoding_type.is_some_and(|encoding| encoding.as_str() == EncodingType::URL) {
encode(value).into_owned() encode(value).into_owned()
@@ -250,7 +227,9 @@ async fn collect_list_objects_metadata_permissions<T>(
fn build_list_object_versions_m_output( fn build_list_object_versions_m_output(
object_infos: ListObjectVersionsInfo, object_infos: ListObjectVersionsInfo,
context: &ListObjectVersionsMResponseContext, bucket: &str,
params: &ListObjectVersionsParams,
encoding_type: Option<&EncodingType>,
permissions: &HashMap<String, ObjectMetadataPermissions>, permissions: &HashMap<String, ObjectMetadataPermissions>,
) -> ListObjectVersionsMOutput { ) -> ListObjectVersionsMOutput {
let owner = rustfs_owner(); let owner = rustfs_owner();
@@ -258,7 +237,7 @@ fn build_list_object_versions_m_output(
.prefixes .prefixes
.into_iter() .into_iter()
.map(|prefix_value| CommonPrefix { .map(|prefix_value| CommonPrefix {
prefix: Some(encode_list_versions_value(&prefix_value, context.encoding_type.as_ref())), prefix: Some(encode_list_versions_value(&prefix_value, encoding_type)),
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -267,7 +246,7 @@ fn build_list_object_versions_m_output(
.into_iter() .into_iter()
.filter(|object| !object.name.is_empty()) .filter(|object| !object.name.is_empty())
.map(|object| { .map(|object| {
let object_name = encode_list_versions_value(&object.name, context.encoding_type.as_ref()); let object_name = encode_list_versions_value(&object.name, encoding_type);
let version_id = object let version_id = object
.version_id .version_id
.map(|version| version.to_string()) .map(|version| version.to_string())
@@ -328,34 +307,37 @@ fn build_list_object_versions_m_output(
let next_key_marker = object_infos let next_key_marker = object_infos
.next_marker .next_marker
.filter(|marker| !marker.is_empty()) .filter(|marker| !marker.is_empty())
.map(|marker| encode_list_versions_value(&marker, context.encoding_type.as_ref())); .map(|marker| encode_list_versions_value(&marker, encoding_type));
ListObjectVersionsMOutput { ListObjectVersionsMOutput {
common_prefixes: Some(common_prefixes), common_prefixes: Some(common_prefixes),
delimiter: context delimiter: params
.delimiter .delimiter
.clone() .clone()
.map(|value| encode_list_versions_value(&value, context.encoding_type.as_ref())), .map(|value| encode_list_versions_value(&value, encoding_type)),
encoding_type: context.encoding_type.clone(), encoding_type: encoding_type.cloned(),
is_truncated: Some(object_infos.is_truncated), is_truncated: Some(object_infos.is_truncated),
key_marker: Some(encode_list_versions_value( key_marker: Some(encode_list_versions_value(
context.key_marker.as_deref().unwrap_or_default(), params.key_marker.as_deref().unwrap_or_default(),
context.encoding_type.as_ref(), encoding_type,
)), )),
max_keys: Some(context.max_keys), max_keys: Some(params.max_keys),
name: Some(context.bucket.clone()), name: Some(bucket.to_owned()),
next_key_marker, next_key_marker,
next_version_id_marker: Some(object_infos.next_version_idmarker.unwrap_or_default()), next_version_id_marker: Some(object_infos.next_version_idmarker.unwrap_or_default()),
prefix: Some(encode_list_versions_value(&context.prefix, context.encoding_type.as_ref())), prefix: Some(encode_list_versions_value(&params.prefix, encoding_type)),
request_charged: None, request_charged: None,
version_id_marker: Some(context.version_id_marker.clone().unwrap_or_default()), version_id_marker: Some(params.version_id_marker.clone().unwrap_or_default()),
entries, entries,
} }
} }
fn build_list_objects_v2m_output( fn build_list_objects_v2m_output(
object_infos: ListObjectsV2Info, object_infos: ListObjectsV2Info,
context: &ListObjectsV2MResponseContext, bucket: &str,
params: &ListObjectsV2Params,
encoding_type: Option<&EncodingType>,
fetch_owner: bool,
permissions: &HashMap<String, ObjectMetadataPermissions>, permissions: &HashMap<String, ObjectMetadataPermissions>,
) -> ListObjectsV2MOutput { ) -> ListObjectsV2MOutput {
let owner = rustfs_owner(); let owner = rustfs_owner();
@@ -386,7 +368,7 @@ fn build_list_objects_v2m_output(
}; };
ObjectM { ObjectM {
key: Some(encode_list_objects_v2_value(&object.name, context.encoding_type.as_ref())), key: Some(encode_list_objects_v2_value(&object.name, encoding_type)),
last_modified: object.mod_time.map(Timestamp::from), last_modified: object.mod_time.map(Timestamp::from),
size: Some(object.get_actual_size().unwrap_or_default()), size: Some(object.get_actual_size().unwrap_or_default()),
e_tag: object.etag.clone().map(|etag| to_s3s_etag(&etag)), e_tag: object.etag.clone().map(|etag| to_s3s_etag(&etag)),
@@ -396,7 +378,7 @@ fn build_list_objects_v2m_output(
.clone() .clone()
.unwrap_or_else(|| ObjectStorageClass::STANDARD.to_string()), .unwrap_or_else(|| ObjectStorageClass::STANDARD.to_string()),
)), )),
owner: context.fetch_owner.then_some(owner.clone()), owner: fetch_owner.then_some(owner.clone()),
user_metadata, user_metadata,
user_tags, user_tags,
internal, internal,
@@ -408,7 +390,7 @@ fn build_list_objects_v2m_output(
.prefixes .prefixes
.into_iter() .into_iter()
.map(|prefix| CommonPrefix { .map(|prefix| CommonPrefix {
prefix: Some(encode_list_objects_v2_value(&prefix, context.encoding_type.as_ref())), prefix: Some(encode_list_objects_v2_value(&prefix, encoding_type)),
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@@ -418,18 +400,18 @@ fn build_list_objects_v2m_output(
.map(|token| base64_simd::STANDARD.encode_to_string(token.as_bytes())); .map(|token| base64_simd::STANDARD.encode_to_string(token.as_bytes()));
ListObjectsV2MOutput { ListObjectsV2MOutput {
name: Some(context.bucket.clone()), name: Some(bucket.to_owned()),
prefix: Some(context.prefix.clone()), prefix: Some(params.prefix.clone()),
max_keys: Some(context.max_keys), max_keys: Some(params.max_keys),
key_count: Some(key_count), key_count: Some(key_count),
continuation_token: context.continuation_token.clone(), continuation_token: params.response_continuation_token.clone(),
is_truncated: Some(object_infos.is_truncated), is_truncated: Some(object_infos.is_truncated),
next_continuation_token, next_continuation_token,
contents: Some(contents), contents: Some(contents),
common_prefixes: Some(common_prefixes), common_prefixes: Some(common_prefixes),
delimiter: context.delimiter.clone(), delimiter: params.delimiter.clone(),
encoding_type: context.encoding_type.clone(), encoding_type: encoding_type.cloned(),
start_after: context.start_after.clone(), start_after: params.response_start_after.clone(),
..Default::default() ..Default::default()
} }
} }
@@ -1709,17 +1691,9 @@ impl DefaultBucketUsecase {
.. ..
} = req.input; } = req.input;
let ListObjectsV2Params { let params = parse_list_objects_v2_params(prefix, delimiter, max_keys, continuation_token, start_after)?;
prefix,
max_keys,
delimiter,
response_start_after,
start_after_for_query,
response_continuation_token,
decoded_continuation_token,
} = parse_list_objects_v2_params(prefix, delimiter, max_keys, continuation_token, start_after)?;
validate_list_object_unordered_with_delimiter(delimiter.as_ref(), req.uri.query())?; validate_list_object_unordered_with_delimiter(params.delimiter.as_ref(), req.uri.query())?;
let store = get_validated_store(&bucket).await?; let store = get_validated_store(&bucket).await?;
@@ -1730,12 +1704,12 @@ impl DefaultBucketUsecase {
let object_infos = store let object_infos = store
.list_objects_v2( .list_objects_v2(
&bucket, &bucket,
&prefix, &params.prefix,
decoded_continuation_token, params.decoded_continuation_token.clone(),
delimiter.clone(), params.delimiter.clone(),
max_keys, params.max_keys,
fetch_owner.unwrap_or_default(), fetch_owner.unwrap_or_default(),
start_after_for_query, params.start_after_for_query.clone(),
incl_deleted, incl_deleted,
) )
.await .await
@@ -1744,13 +1718,13 @@ impl DefaultBucketUsecase {
let output = build_list_objects_v2_output( let output = build_list_objects_v2_output(
object_infos, object_infos,
fetch_owner.unwrap_or_default(), fetch_owner.unwrap_or_default(),
max_keys, params.max_keys,
bucket, bucket,
prefix, params.prefix,
delimiter, params.delimiter,
encoding_type, encoding_type,
response_continuation_token, params.response_continuation_token,
response_start_after, params.response_start_after,
); );
Ok(S3Response::new(output)) Ok(S3Response::new(output))
@@ -1773,17 +1747,9 @@ impl DefaultBucketUsecase {
.. ..
} = input; } = input;
let ListObjectsV2Params { let params = parse_list_objects_v2_params(prefix, delimiter, max_keys, continuation_token, start_after)?;
prefix,
max_keys,
delimiter,
response_start_after,
start_after_for_query,
response_continuation_token,
decoded_continuation_token,
} = parse_list_objects_v2_params(prefix, delimiter, max_keys, continuation_token, start_after)?;
validate_list_object_unordered_with_delimiter(delimiter.as_ref(), req.uri.query())?; validate_list_object_unordered_with_delimiter(params.delimiter.as_ref(), req.uri.query())?;
let store = get_validated_store(&bucket).await?; let store = get_validated_store(&bucket).await?;
let incl_deleted = get_header(&req.headers, rustfs_utils::http::SUFFIX_INCLUDE_DELETED) let incl_deleted = get_header(&req.headers, rustfs_utils::http::SUFFIX_INCLUDE_DELETED)
@@ -1793,29 +1759,26 @@ impl DefaultBucketUsecase {
let object_infos = store let object_infos = store
.list_objects_v2( .list_objects_v2(
&bucket, &bucket,
&prefix, &params.prefix,
decoded_continuation_token, params.decoded_continuation_token.clone(),
delimiter.clone(), params.delimiter.clone(),
max_keys, params.max_keys,
fetch_owner.unwrap_or_default(), fetch_owner.unwrap_or_default(),
start_after_for_query, params.start_after_for_query.clone(),
incl_deleted, incl_deleted,
) )
.await .await
.map_err(ApiError::from)?; .map_err(ApiError::from)?;
let permissions = collect_list_objects_metadata_permissions(&req, &bucket, &object_infos.objects).await?; let permissions = collect_list_objects_metadata_permissions(&req, &bucket, &object_infos.objects).await?;
let context = ListObjectsV2MResponseContext { let output = build_list_objects_v2m_output(
bucket, object_infos,
prefix, &bucket,
delimiter, &params,
max_keys, encoding_type.as_ref(),
encoding_type, fetch_owner.unwrap_or_default(),
continuation_token: response_continuation_token, &permissions,
start_after: response_start_after, );
fetch_owner: fetch_owner.unwrap_or_default(),
};
let output = build_list_objects_v2m_output(object_infos, &context, &permissions);
Ok(S3Response::new(output)) Ok(S3Response::new(output))
} }
@@ -1870,38 +1833,23 @@ impl DefaultBucketUsecase {
.. ..
} = input; } = input;
let ListObjectVersionsParams { let params = parse_list_object_versions_params(prefix, delimiter, key_marker, version_id_marker, max_keys)?;
prefix,
delimiter,
key_marker,
version_id_marker,
max_keys,
} = parse_list_object_versions_params(prefix, delimiter, key_marker, version_id_marker, max_keys)?;
let store = get_validated_store(&bucket).await?; let store = get_validated_store(&bucket).await?;
let object_infos = store let object_infos = store
.list_object_versions( .list_object_versions(
&bucket, &bucket,
&prefix, &params.prefix,
key_marker.clone(), params.key_marker.clone(),
version_id_marker.clone(), params.version_id_marker.clone(),
delimiter.clone(), params.delimiter.clone(),
max_keys, params.max_keys,
) )
.await .await
.map_err(ApiError::from)?; .map_err(ApiError::from)?;
let permissions = collect_list_objects_metadata_permissions(&req, &bucket, &object_infos.objects).await?; let permissions = collect_list_objects_metadata_permissions(&req, &bucket, &object_infos.objects).await?;
let context = ListObjectVersionsMResponseContext { let output = build_list_object_versions_m_output(object_infos, &bucket, &params, encoding_type.as_ref(), &permissions);
bucket,
prefix,
delimiter,
max_keys,
encoding_type,
key_marker,
version_id_marker,
};
let output = build_list_object_versions_m_output(object_infos, &context, &permissions);
Ok(S3Response::new(output)) Ok(S3Response::new(output))
} }
@@ -2448,16 +2396,20 @@ mod tests {
), ),
]); ]);
let context = ListObjectVersionsMResponseContext { let params = ListObjectVersionsParams {
bucket: "demo-bucket".to_string(),
prefix: "pre".to_string(), prefix: "pre".to_string(),
delimiter: Some("/".to_string()), delimiter: Some("/".to_string()),
max_keys: 1000,
encoding_type: Some(EncodingType::from_static(EncodingType::URL)),
key_marker: Some("start marker".to_string()), key_marker: Some("start marker".to_string()),
version_id_marker: Some("vid-1".to_string()), version_id_marker: Some("vid-1".to_string()),
max_keys: 1000,
}; };
let output = build_list_object_versions_m_output(object_infos, &context, &permissions); let output = build_list_object_versions_m_output(
object_infos,
"demo-bucket",
&params,
Some(&EncodingType::from_static(EncodingType::URL)),
&permissions,
);
assert_eq!(output.name.as_deref(), Some("demo-bucket")); assert_eq!(output.name.as_deref(), Some("demo-bucket"));
assert_eq!(output.prefix.as_deref(), Some("pre")); assert_eq!(output.prefix.as_deref(), Some("pre"));
@@ -2556,18 +2508,24 @@ mod tests {
}, },
)]); )]);
let context = ListObjectsV2MResponseContext { let params = ListObjectsV2Params {
bucket: "demo-bucket".to_string(),
prefix: "logs/".to_string(), prefix: "logs/".to_string(),
delimiter: Some("/".to_string()),
max_keys: 1000, max_keys: 1000,
encoding_type: Some(EncodingType::from_static(EncodingType::URL)), delimiter: Some("/".to_string()),
continuation_token: Some("start token".to_string()), response_start_after: Some("logs/start after".to_string()),
start_after: Some("logs/start after".to_string()), start_after_for_query: None,
fetch_owner: true, response_continuation_token: Some("start token".to_string()),
decoded_continuation_token: None,
}; };
let output = build_list_objects_v2m_output(object_infos, &context, &permissions); let output = build_list_objects_v2m_output(
object_infos,
"demo-bucket",
&params,
Some(&EncodingType::from_static(EncodingType::URL)),
true,
&permissions,
);
assert_eq!(output.name.as_deref(), Some("demo-bucket")); assert_eq!(output.name.as_deref(), Some("demo-bucket"));
assert_eq!(output.prefix.as_deref(), Some("logs/")); assert_eq!(output.prefix.as_deref(), Some("logs/"));