This is an automated email from the ASF dual-hosted git repository.

Gargi-jais11 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 6ff162abdd5 HDDS-15283. GetObjectTagging should return TagSet in 
sorted order of key (#10277).
6ff162abdd5 is described below

commit 6ff162abdd591ebb9143cfabbec5f41ac276a812
Author: Gargi Jaiswal <[email protected]>
AuthorDate: Mon May 18 10:00:06 2026 +0530

    HDDS-15283. GetObjectTagging should return TagSet in sorted order of key 
(#10277).
---
 .../ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java   | 23 ++++++++++++++++++
 .../ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java   | 23 ++++++++++++++++++
 .../apache/hadoop/ozone/s3/endpoint/S3Tagging.java |  8 +++----
 .../ozone/s3/endpoint/TestObjectTaggingGet.java    | 27 ++++++++++++++++++----
 4 files changed, 72 insertions(+), 9 deletions(-)

diff --git 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
index 8e79e057c03..f1c47df8557 100644
--- 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
+++ 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
@@ -47,6 +47,8 @@
 import com.amazonaws.services.s3.model.CreateBucketRequest;
 import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
 import com.amazonaws.services.s3.model.GetObjectRequest;
+import com.amazonaws.services.s3.model.GetObjectTaggingRequest;
+import com.amazonaws.services.s3.model.GetObjectTaggingResult;
 import com.amazonaws.services.s3.model.Grantee;
 import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest;
 import com.amazonaws.services.s3.model.InitiateMultipartUploadResult;
@@ -1125,6 +1127,27 @@ public void testHeadObjectReturnsTaggingCount() {
     assertEquals(tags.size(), Integer.parseInt(tagCountHeader.toString()));
   }
 
+  @Test
+  public void testGetObjectTaggingReturnsTagsSortedByKey() {
+    final String bucketName = getBucketName();
+    final String keyName = getKeyName();
+    s3Client.createBucket(bucketName);
+    s3Client.putObject(bucketName, keyName, "");
+
+    List<Tag> tagsPutOrder = Arrays.asList(new Tag("key2", "val2"), new 
Tag("key", "val"));
+    s3Client.setObjectTagging(
+        new SetObjectTaggingRequest(bucketName, keyName, new 
ObjectTagging(tagsPutOrder)));
+
+    GetObjectTaggingResult taggingResult =
+        s3Client.getObjectTagging(new GetObjectTaggingRequest(bucketName, 
keyName));
+    List<Tag> tagSet = taggingResult.getTagSet();
+    assertEquals(2, tagSet.size());
+    assertEquals("key", tagSet.get(0).getKey());
+    assertEquals("val", tagSet.get(0).getValue());
+    assertEquals("key2", tagSet.get(1).getKey());
+    assertEquals("val2", tagSet.get(1).getValue());
+  }
+
   @Test
   public void testGetObjectWithoutETag() throws Exception {
     // Object uploaded using other protocols (e.g. ofs / ozone cli) will not
diff --git 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
index ea608437361..7b12bc6f4dd 100644
--- 
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
+++ 
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
@@ -120,6 +120,7 @@
 import software.amazon.awssdk.services.s3.model.GetObjectRequest;
 import software.amazon.awssdk.services.s3.model.GetObjectResponse;
 import software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest;
+import software.amazon.awssdk.services.s3.model.GetObjectTaggingResponse;
 import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
 import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
 import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
@@ -243,6 +244,28 @@ public void testPutObject() {
     assertEquals("\"37b51d194a7513e45b56f6524f2d51f2\"", 
getObjectResponse.eTag());
   }
 
+  @Test
+  public void testGetObjectTaggingReturnsTagsSortedByKey() {
+    final String bucketName = getBucketName();
+    final String keyName = getKeyName();
+    s3Client.createBucket(b -> b.bucket(bucketName));
+    s3Client.putObject(b -> b.bucket(bucketName).key(keyName), 
RequestBody.empty());
+
+    List<Tag> tagsPutOrder = Arrays.asList(
+        Tag.builder().key("key2").value("val2").build(),
+        Tag.builder().key("key").value("val").build());
+    s3Client.putObjectTagging(b -> b.bucket(bucketName).key(keyName)
+        .tagging(Tagging.builder().tagSet(tagsPutOrder).build()));
+
+    GetObjectTaggingResponse taggingResult = s3Client.getObjectTagging(b -> 
b.bucket(bucketName).key(keyName));
+    List<Tag> tagSet = taggingResult.tagSet();
+    assertEquals(2, tagSet.size());
+    assertEquals("key", tagSet.get(0).key());
+    assertEquals("val", tagSet.get(0).value());
+    assertEquals("key2", tagSet.get(1).key());
+    assertEquals("val2", tagSet.get(1).value());
+  }
+
   @Test
   public void testPutObjectIfNoneMatch() {
     final String bucketName = getBucketName();
diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3Tagging.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3Tagging.java
index 91910787017..fd48a7dcfe7 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3Tagging.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3Tagging.java
@@ -118,16 +118,16 @@ public void setValue(String value) {
 
   /**
    * Creates a S3 tagging instance (xml representation) from a Map retrieved
-   * from OM.
+   * from OM. Tags are ordered by key for stable responses,
+   * consistent with AWS S3 {@code GetObjectTagging}.
    * @param tagMap Map representing the tags.
    * @return {@link S3Tagging}
    */
   public static S3Tagging fromMap(Map<String, String> tagMap) {
     List<Tag> tags = tagMap.entrySet()
         .stream()
-        .map(
-            tagEntry -> new Tag(tagEntry.getKey(), tagEntry.getValue())
-        )
+        .sorted(Map.Entry.comparingByKey())
+        .map(tagEntry -> new Tag(tagEntry.getKey(), tagEntry.getValue()))
         .collect(Collectors.toList());
     return new S3Tagging(new TagSet(tags));
   }
diff --git 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectTaggingGet.java
 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectTaggingGet.java
index f1e166a138d..1f0f0d7b471 100644
--- 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectTaggingGet.java
+++ 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectTaggingGet.java
@@ -31,6 +31,7 @@
 import static org.junit.jupiter.api.Assertions.fail;
 
 import java.io.IOException;
+import java.util.List;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import org.apache.hadoop.ozone.client.OzoneClient;
@@ -50,6 +51,7 @@ public class TestObjectTaggingGet {
   private static final String BUCKET_NAME = "b1";
   private static final String KEY_WITH_TAG = "keyWithTag";
   private ObjectEndpoint rest;
+  private HttpHeaders headers;
 
   @BeforeEach
   public void init() throws Exception {
@@ -57,7 +59,7 @@ public void init() throws Exception {
     OzoneClient client = new OzoneClientStub();
     client.getObjectStore().createS3Bucket(BUCKET_NAME);
 
-    HttpHeaders headers = Mockito.mock(HttpHeaders.class);
+    headers = Mockito.mock(HttpHeaders.class);
     Mockito.when(headers.getHeaderString(X_AMZ_CONTENT_SHA256))
         .thenReturn("UNSIGNED-PAYLOAD");
 
@@ -65,14 +67,13 @@ public void init() throws Exception {
         .setClient(client)
         .setHeaders(headers)
         .build();
-
-    // Create a key with object tags
-    
Mockito.when(headers.getHeaderString(TAG_HEADER)).thenReturn("tag1=value1&tag2=value2");
-    assertSucceeds(() -> put(rest, BUCKET_NAME, KEY_WITH_TAG, CONTENT));
   }
 
   @Test
   public void testGetTagging() throws IOException, OS3Exception {
+    
Mockito.when(headers.getHeaderString(TAG_HEADER)).thenReturn("tag1=value1&tag2=value2");
+    assertSucceeds(() -> put(rest, BUCKET_NAME, KEY_WITH_TAG, CONTENT));
+
     //WHEN
     Response response = getTagging(rest, BUCKET_NAME, KEY_WITH_TAG);
 
@@ -92,6 +93,22 @@ public void testGetTagging() throws IOException, 
OS3Exception {
     }
   }
 
+  @Test
+  public void testGetTaggingReturnsTagsSortedByKey() throws IOException, 
OS3Exception {
+    final String reverseOrderKey = "keyReverseTagOrder";
+    
Mockito.when(headers.getHeaderString(TAG_HEADER)).thenReturn("tag2=value2&tag=value");
+    assertSucceeds(() -> put(rest, BUCKET_NAME, reverseOrderKey, CONTENT));
+
+    Response response = getTagging(rest, BUCKET_NAME, reverseOrderKey);
+    assertEquals(HTTP_OK, response.getStatus());
+    List<Tag> tags = ((S3Tagging) response.getEntity()).getTagSet().getTags();
+    assertEquals(2, tags.size());
+    assertEquals("tag", tags.get(0).getKey());
+    assertEquals("value", tags.get(0).getValue());
+    assertEquals("tag2", tags.get(1).getKey());
+    assertEquals("value2", tags.get(1).getValue());
+  }
+
   @Test
   public void testGetTaggingNoKeyFound() {
     assertErrorResponse(NO_SUCH_KEY, () -> getTagging(rest, BUCKET_NAME, 
"nonexistent"));


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to