Skip to content

Commit b487aad

Browse files
Merge branch 'main' into spal/checksum-mpu
2 parents 061f09d + be14812 commit b487aad

File tree

5 files changed

+134
-3
lines changed

5 files changed

+134
-3
lines changed

blob/blob-aws/src/main/java/com/salesforce/multicloudj/blob/aws/AwsTransformer.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,11 +521,14 @@ public PutObjectPresignRequest toPutObjectPresignRequest(PresignedUrlRequest req
521521
}
522522

523523
public GetObjectPresignRequest toGetObjectPresignRequest(PresignedUrlRequest request) {
524-
GetObjectRequest getObjectRequest =
525-
GetObjectRequest.builder().bucket(getBucket()).key(request.getKey()).build();
524+
GetObjectRequest.Builder getObjectBuilder =
525+
GetObjectRequest.builder().bucket(getBucket()).key(request.getKey());
526+
if (request.getContentDisposition() != null) {
527+
getObjectBuilder.responseContentDisposition(request.getContentDisposition());
528+
}
526529
return GetObjectPresignRequest.builder()
527530
.signatureDuration(request.getDuration())
528-
.getObjectRequest(getObjectRequest)
531+
.getObjectRequest(getObjectBuilder.build())
529532
.build();
530533
}
531534

blob/blob-aws/src/test/java/com/salesforce/multicloudj/blob/aws/AwsTransformerTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,43 @@ void testToGetObjectPresignRequest() {
587587
assertEquals(BUCKET, actualRequest.getObjectRequest().bucket());
588588
assertEquals("object-1", actualRequest.getObjectRequest().key());
589589
assertEquals(Duration.ofHours(4), actualRequest.signatureDuration());
590+
assertNull(actualRequest.getObjectRequest().responseContentDisposition());
591+
}
592+
593+
@Test
594+
void testToGetObjectPresignRequest_WithContentDisposition() {
595+
PresignedUrlRequest presignedUrlRequest =
596+
PresignedUrlRequest.builder()
597+
.type(PresignedOperation.DOWNLOAD)
598+
.key("object-1")
599+
.duration(Duration.ofHours(4))
600+
.contentDisposition("attachment; filename=\"report.pdf\"")
601+
.build();
602+
GetObjectPresignRequest actualRequest =
603+
transformer.toGetObjectPresignRequest(presignedUrlRequest);
604+
assertEquals(BUCKET, actualRequest.getObjectRequest().bucket());
605+
assertEquals("object-1", actualRequest.getObjectRequest().key());
606+
assertEquals(Duration.ofHours(4), actualRequest.signatureDuration());
607+
assertEquals(
608+
"attachment; filename=\"report.pdf\"",
609+
actualRequest.getObjectRequest().responseContentDisposition());
610+
}
611+
612+
@Test
613+
void testToPutObjectPresignRequest_IgnoresContentDisposition() {
614+
PresignedUrlRequest presignedUrlRequest =
615+
PresignedUrlRequest.builder()
616+
.type(PresignedOperation.UPLOAD)
617+
.key("object-1")
618+
.duration(Duration.ofHours(4))
619+
.contentDisposition("attachment; filename=\"report.pdf\"")
620+
.build();
621+
PutObjectPresignRequest actualRequest =
622+
transformer.toPutObjectPresignRequest(presignedUrlRequest);
623+
assertEquals(BUCKET, actualRequest.putObjectRequest().bucket());
624+
assertEquals("object-1", actualRequest.putObjectRequest().key());
625+
assertEquals(Duration.ofHours(4), actualRequest.signatureDuration());
626+
assertNull(actualRequest.putObjectRequest().contentDisposition());
590627
}
591628

592629
@Test

blob/blob-client/src/main/java/com/salesforce/multicloudj/blob/driver/PresignedUrlRequest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,9 @@ public class PresignedUrlRequest {
3030

3131
/** Optional: Specify the KMS key ID to be used for encryption in a presignedUrl upload */
3232
private final String kmsKeyId;
33+
34+
/**
35+
* Optional: Specify the Content-Disposition header to override in a presigned download URL.
36+
*/
37+
private final String contentDisposition;
3338
}

blob/blob-gcp/src/main/java/com/salesforce/multicloudj/blob/gcp/GcpBlobStore.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import com.salesforce.multicloudj.blob.driver.MultipartUploadRequest;
5858
import com.salesforce.multicloudj.blob.driver.MultipartUploadResponse;
5959
import com.salesforce.multicloudj.blob.driver.ObjectLockInfo;
60+
import com.salesforce.multicloudj.blob.driver.PresignedOperation;
6061
import com.salesforce.multicloudj.blob.driver.PresignedUrlRequest;
6162
import com.salesforce.multicloudj.blob.driver.RetentionMode;
6263
import com.salesforce.multicloudj.blob.driver.UploadRequest;
@@ -109,6 +110,7 @@ public class GcpBlobStore extends AbstractBlobStore {
109110
private final MultipartUploadClient multipartUploadClient;
110111
private final GcpTransformer transformer;
111112
private static final String TAG_PREFIX = "gcp-tag-";
113+
private static final String RESPONSE_CONTENT_DISPOSITION = "response-content-disposition";
112114

113115
public GcpBlobStore() {
114116
this(new Builder(), null, null);
@@ -566,6 +568,12 @@ protected URL doGeneratePresignedUrl(PresignedUrlRequest request) {
566568
if (request.getMetadata() != null) {
567569
options.add(Storage.SignUrlOption.withExtHeaders(request.getMetadata()));
568570
}
571+
if (request.getContentDisposition() != null
572+
&& request.getType() == PresignedOperation.DOWNLOAD) {
573+
Map<String, String> queryParams = new HashMap<>();
574+
queryParams.put(RESPONSE_CONTENT_DISPOSITION, request.getContentDisposition());
575+
options.add(Storage.SignUrlOption.withQueryParams(queryParams));
576+
}
569577

570578
return storage.signUrl(
571579
blobInfo,

blob/blob-gcp/src/test/java/com/salesforce/multicloudj/blob/gcp/GcpBlobStoreTest.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,84 @@ void testDoGeneratePresignedUrl_WithoutKmsKey() throws Exception {
12821282
any(Storage.SignUrlOption[].class));
12831283
}
12841284

1285+
@Test
1286+
void testDoGeneratePresignedUrl_WithContentDisposition() throws Exception {
1287+
Duration duration = Duration.ofHours(4);
1288+
String contentDisposition = "attachment; filename=\"report.pdf\"";
1289+
PresignedUrlRequest presignedUrlRequest =
1290+
PresignedUrlRequest.builder()
1291+
.type(PresignedOperation.DOWNLOAD)
1292+
.key(TEST_KEY)
1293+
.duration(duration)
1294+
.contentDisposition(contentDisposition)
1295+
.build();
1296+
1297+
URL expectedUrl = new URL("https://signed-url-with-cd.example.com");
1298+
1299+
when(mockTransformer.toBlobInfo(presignedUrlRequest)).thenReturn(mockBlobInfo);
1300+
when(mockStorage.signUrl(
1301+
eq(mockBlobInfo),
1302+
any(Long.class),
1303+
eq(TimeUnit.MILLISECONDS),
1304+
any(Storage.SignUrlOption[].class)))
1305+
.thenReturn(expectedUrl);
1306+
1307+
URL actualUrl = gcpBlobStore.doGeneratePresignedUrl(presignedUrlRequest);
1308+
1309+
assertEquals(expectedUrl, actualUrl);
1310+
ArgumentCaptor<Storage.SignUrlOption[]> optionsCaptor =
1311+
ArgumentCaptor.forClass(Storage.SignUrlOption[].class);
1312+
verify(mockStorage)
1313+
.signUrl(
1314+
eq(mockBlobInfo),
1315+
eq(duration.toMillis()),
1316+
eq(TimeUnit.MILLISECONDS),
1317+
optionsCaptor.capture());
1318+
Storage.SignUrlOption[] options = optionsCaptor.getValue();
1319+
assertEquals(
1320+
3, options.length, "Download with contentDisposition should have httpMethod, v4Signature,"
1321+
+ " and queryParams options");
1322+
}
1323+
1324+
@Test
1325+
void testDoGeneratePresignedUrl_UploadIgnoresContentDisposition() throws Exception {
1326+
Duration duration = Duration.ofHours(4);
1327+
String contentDisposition = "attachment; filename=\"report.pdf\"";
1328+
PresignedUrlRequest presignedUrlRequest =
1329+
PresignedUrlRequest.builder()
1330+
.type(PresignedOperation.UPLOAD)
1331+
.key(TEST_KEY)
1332+
.duration(duration)
1333+
.contentDisposition(contentDisposition)
1334+
.build();
1335+
1336+
URL expectedUrl = new URL("https://signed-url-for-upload.example.com");
1337+
1338+
when(mockTransformer.toBlobInfo(presignedUrlRequest)).thenReturn(mockBlobInfo);
1339+
when(mockStorage.signUrl(
1340+
eq(mockBlobInfo),
1341+
any(Long.class),
1342+
eq(TimeUnit.MILLISECONDS),
1343+
any(Storage.SignUrlOption[].class)))
1344+
.thenReturn(expectedUrl);
1345+
1346+
URL actualUrl = gcpBlobStore.doGeneratePresignedUrl(presignedUrlRequest);
1347+
1348+
assertEquals(expectedUrl, actualUrl);
1349+
ArgumentCaptor<Storage.SignUrlOption[]> optionsCaptor =
1350+
ArgumentCaptor.forClass(Storage.SignUrlOption[].class);
1351+
verify(mockStorage)
1352+
.signUrl(
1353+
eq(mockBlobInfo),
1354+
eq(duration.toMillis()),
1355+
eq(TimeUnit.MILLISECONDS),
1356+
optionsCaptor.capture());
1357+
Storage.SignUrlOption[] options = optionsCaptor.getValue();
1358+
assertEquals(
1359+
2, options.length,
1360+
"Upload should only have httpMethod and v4Signature options, no queryParams");
1361+
}
1362+
12851363
// Test class to access protected methods
12861364
private static class TestGcpBlobStore extends GcpBlobStore {
12871365
public TestGcpBlobStore(Builder builder, Storage storage, MultipartUploadClient client) {

0 commit comments

Comments
 (0)