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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 8bc49f315393 CAMEL-25903: camel-aws2-s3 - a multipart upload stores 
the user metadata of the CamelAwsS3Metadata header (#28427)
8bc49f315393 is described below

commit 8bc49f315393db3c24b8251f549023d2995f3293
Author: allthingssecurity <[email protected]>
AuthorDate: Sun Oct 25 13:00:46 2026 +0530

    CAMEL-25903: camel-aws2-s3 - a multipart upload stores the user metadata of 
the CamelAwsS3Metadata header (#28427)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/component/aws2/s3/AWS2S3Producer.java    |   3 +-
 .../s3/AWS2S3ProducerMultipartMetadataTest.java    | 121 +++++++++++++++++++++
 2 files changed, 122 insertions(+), 2 deletions(-)

diff --git 
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/AWS2S3Producer.java
 
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/AWS2S3Producer.java
index b4c248943fe9..ae8f0d3fb41e 100644
--- 
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/AWS2S3Producer.java
+++ 
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/AWS2S3Producer.java
@@ -220,12 +220,11 @@ public class AWS2S3Producer extends DefaultProducer {
         }
 
         LOG.debug("Payload size >= partSize ({} > {}). Uploading payload using 
multi-part operation", contentLength, partSize);
-        objectMetadata.put("Content-Length", "" + contentLength);
 
         final String keyName = AWS2S3Utils.determineKey(exchange, 
getConfiguration());
         final String bucketName = AWS2S3Utils.determineBucketName(exchange, 
getConfiguration());
         CreateMultipartUploadRequest.Builder createMultipartUploadRequest
-                = 
CreateMultipartUploadRequest.builder().bucket(bucketName).key(keyName);
+                = 
CreateMultipartUploadRequest.builder().bucket(bucketName).key(keyName).metadata(objectMetadata);
 
         String storageClass = AWS2S3Utils.determineStorageClass(exchange, 
getConfiguration());
         if (ObjectHelper.isNotEmpty(storageClass)) {
diff --git 
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/AWS2S3ProducerMultipartMetadataTest.java
 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/AWS2S3ProducerMultipartMetadataTest.java
new file mode 100644
index 000000000000..11fc398ae0aa
--- /dev/null
+++ 
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/AWS2S3ProducerMultipartMetadataTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.aws2.s3;
+
+import java.util.Map;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.DefaultExchange;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import software.amazon.awssdk.core.sync.RequestBody;
+import software.amazon.awssdk.http.SdkHttpResponse;
+import software.amazon.awssdk.services.s3.S3Client;
+import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest;
+import 
software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse;
+import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest;
+import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse;
+import software.amazon.awssdk.services.s3.model.PutObjectRequest;
+import software.amazon.awssdk.services.s3.model.PutObjectResponse;
+import software.amazon.awssdk.services.s3.model.UploadPartRequest;
+import software.amazon.awssdk.services.s3.model.UploadPartResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * With multiPartUpload=true, the user metadata of the {@code 
CamelAwsS3Metadata} header is stored with the object
+ * whether the body is uploaded in parts or, when it is smaller than the part 
size, with a single put.
+ */
+class AWS2S3ProducerMultipartMetadataTest {
+
+    private static final Map<String, String> METADATA = Map.of("origin", 
"camel", "checksum", "abc123");
+
+    @Mock
+    private AWS2S3Endpoint endpoint;
+
+    @Mock
+    private AWS2S3Configuration configuration;
+
+    @Mock
+    private S3Client s3Client;
+
+    private AWS2S3Producer producer;
+    private DefaultCamelContext camelContext;
+
+    @BeforeEach
+    void setUp() {
+        MockitoAnnotations.openMocks(this);
+        camelContext = new DefaultCamelContext();
+        when(endpoint.getConfiguration()).thenReturn(configuration);
+        when(endpoint.getCamelContext()).thenReturn(camelContext);
+        when(endpoint.getS3Client()).thenReturn(s3Client);
+        when(configuration.getBucketName()).thenReturn("test-bucket");
+        when(configuration.isMultiPartUpload()).thenReturn(true);
+        when(configuration.getPartSize()).thenReturn(5L);
+        
when(s3Client.createMultipartUpload(any(CreateMultipartUploadRequest.class)))
+                
.thenReturn(CreateMultipartUploadResponse.builder().uploadId("upload-1").build());
+        when(s3Client.uploadPart(any(UploadPartRequest.class), 
any(RequestBody.class)))
+                .thenReturn(UploadPartResponse.builder().eTag("part").build());
+        
when(s3Client.completeMultipartUpload(any(CompleteMultipartUploadRequest.class)))
+                .thenReturn((CompleteMultipartUploadResponse) 
CompleteMultipartUploadResponse.builder().eTag("etag")
+                        
.sdkHttpResponse(SdkHttpResponse.builder().statusCode(200).build())
+                        .build());
+        when(s3Client.putObject(any(PutObjectRequest.class), 
any(RequestBody.class)))
+                .thenReturn((PutObjectResponse) 
PutObjectResponse.builder().eTag("etag")
+                        
.sdkHttpResponse(SdkHttpResponse.builder().statusCode(200).build())
+                        .build());
+        producer = new AWS2S3Producer(endpoint);
+    }
+
+    private Exchange exchange(String body) {
+        Exchange exchange = new DefaultExchange(camelContext);
+        exchange.getIn().setHeader(AWS2S3Constants.KEY, "my-key");
+        exchange.getIn().setHeader(AWS2S3Constants.METADATA, METADATA);
+        exchange.getIn().setBody(body);
+        return exchange;
+    }
+
+    @Test
+    void multipartUploadStoresTheUserMetadata() throws Exception {
+        producer.process(exchange("twelve bytes"));
+
+        ArgumentCaptor<CreateMultipartUploadRequest> captor = 
ArgumentCaptor.forClass(CreateMultipartUploadRequest.class);
+        verify(s3Client).createMultipartUpload(captor.capture());
+        verify(s3Client, times(3)).uploadPart(any(UploadPartRequest.class), 
any(RequestBody.class));
+        verify(s3Client, never()).putObject(any(PutObjectRequest.class), 
any(RequestBody.class));
+        assertThat(captor.getValue().metadata()).isEqualTo(METADATA);
+    }
+
+    @Test
+    void bodySmallerThanThePartSizeStoresTheUserMetadata() throws Exception {
+        producer.process(exchange("four"));
+
+        ArgumentCaptor<PutObjectRequest> captor = 
ArgumentCaptor.forClass(PutObjectRequest.class);
+        verify(s3Client).putObject(captor.capture(), any(RequestBody.class));
+        verify(s3Client, 
never()).createMultipartUpload(any(CreateMultipartUploadRequest.class));
+        assertThat(captor.getValue().metadata()).isEqualTo(METADATA);
+    }
+}

Reply via email to