From 73effa041d1ff7db4d9dafd23774af13bbcdd204 Mon Sep 17 00:00:00 2001 From: chrislu Date: Thu, 13 Nov 2025 13:52:23 -0800 Subject: [PATCH] versionId vs versions --- weed/s3api/s3_action_resolver.go | 24 ++++++++++++++---------- weed/s3api/s3_list_parts_action_test.go | 6 +++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/weed/s3api/s3_action_resolver.go b/weed/s3api/s3_action_resolver.go index 74bd56dbe..e12b6ca06 100644 --- a/weed/s3api/s3_action_resolver.go +++ b/weed/s3api/s3_action_resolver.go @@ -149,19 +149,23 @@ func resolveFromQueryParameters(query url.Values, method string, hasObject bool) } } - if query.Has("versions") { - if method == http.MethodGet { - // If there's an object, this is GetObjectVersion - // If there's no object (bucket level), this is ListBucketVersions - if hasObject { + // Versioning operations - distinguish between versionId (specific version) and versions (list versions) + // versionId: Used to access/delete a specific version of an object (e.g., GET /bucket/key?versionId=xyz) + if query.Has("versionId") { + if hasObject { + switch method { + case http.MethodGet, http.MethodHead: return s3_constants.S3_ACTION_GET_OBJECT_VERSION - } else { - return s3_constants.S3_ACTION_LIST_BUCKET_VERSIONS + case http.MethodDelete: + return s3_constants.S3_ACTION_DELETE_OBJECT_VERSION } } - // DELETE with versions could be DeleteObjectVersion - if method == http.MethodDelete && hasObject { - return s3_constants.S3_ACTION_DELETE_OBJECT_VERSION + } + + // versions: Used to list all versions of objects in a bucket (e.g., GET /bucket?versions) + if query.Has("versions") { + if method == http.MethodGet && !hasObject { + return s3_constants.S3_ACTION_LIST_BUCKET_VERSIONS } } diff --git a/weed/s3api/s3_list_parts_action_test.go b/weed/s3api/s3_list_parts_action_test.go index 016815633..6c00c23a9 100644 --- a/weed/s3api/s3_list_parts_action_test.go +++ b/weed/s3api/s3_list_parts_action_test.go @@ -57,14 +57,14 @@ func TestListPartsActionMapping(t *testing.T) { description: "GET request with uploadId plus other multipart params should map to s3:ListParts", }, { - name: "get_object_versions", + name: "get_object_with_versionId", method: "GET", bucket: "test-bucket", objectKey: "test-object.txt", - queryParams: map[string]string{"versions": ""}, + queryParams: map[string]string{"versionId": "version-123"}, fallbackAction: s3_constants.ACTION_READ, expectedAction: "s3:GetObjectVersion", - description: "GET request with versions should still map to s3:GetObjectVersion (precedence check)", + description: "GET request with versionId should map to s3:GetObjectVersion", }, { name: "get_object_acl_without_uploadId",