diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadFileRequest.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadFileRequest.java new file mode 100644 index 000000000000..b165292b22e5 --- /dev/null +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadFileRequest.java @@ -0,0 +1,286 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.transfer.s3.model; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.services.s3.presignedurl.model.PresignedUrlDownloadRequest; +import software.amazon.awssdk.transfer.s3.S3TransferManager; +import software.amazon.awssdk.transfer.s3.progress.TransferListener; +import software.amazon.awssdk.utils.ToString; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * Download an object using a pre-signed URL to a local file. For non-file-based downloads, you may use {@link + * PresignedDownloadRequest} instead. + * + * @see S3TransferManager#downloadFileWithPresignedUrl(PresignedDownloadFileRequest) + */ +@SdkPublicApi +public final class PresignedDownloadFileRequest + implements TransferObjectRequest, ToCopyableBuilder { + + private final Path destination; + private final PresignedUrlDownloadRequest presignedUrlDownloadRequest; + private final List transferListeners; + + private PresignedDownloadFileRequest(DefaultBuilder builder) { + this.destination = Validate.paramNotNull(builder.destination, "destination"); + this.presignedUrlDownloadRequest = Validate.paramNotNull(builder.presignedUrlDownloadRequest, + "presignedUrlDownloadRequest"); + this.transferListeners = builder.transferListeners; + } + + /** + * Creates a builder that can be used to create a {@link PresignedDownloadFileRequest}. + * + * @see S3TransferManager#downloadFileWithPresignedUrl(PresignedDownloadFileRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + /** + * The {@link Path} to file that response contents will be written to. The file must not exist or this method + * will throw an exception. If the file is not writable by the current user then an exception will be thrown. + * + * @return the destination path + */ + public Path destination() { + return destination; + } + + /** + * @return The {@link PresignedUrlDownloadRequest} request that should be used for the download + */ + public PresignedUrlDownloadRequest presignedUrlDownloadRequest() { + return presignedUrlDownloadRequest; + } + + /** + * + * @return List of {@link TransferListener}s that will be notified as part of this request. + */ + @Override + public List transferListeners() { + return transferListeners; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + PresignedDownloadFileRequest that = (PresignedDownloadFileRequest) o; + + if (!Objects.equals(destination, that.destination)) { + return false; + } + if (!Objects.equals(presignedUrlDownloadRequest, that.presignedUrlDownloadRequest)) { + return false; + } + return Objects.equals(transferListeners, that.transferListeners); + } + + @Override + public int hashCode() { + int result = destination != null ? destination.hashCode() : 0; + result = 31 * result + (presignedUrlDownloadRequest != null ? presignedUrlDownloadRequest.hashCode() : 0); + result = 31 * result + (transferListeners != null ? transferListeners.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return ToString.builder("PresignedDownloadFileRequest") + .add("destination", destination) + .add("presignedUrlDownloadRequest", presignedUrlDownloadRequest) + .add("transferListeners", transferListeners) + .build(); + } + + public static Class serializableBuilderClass() { + return DefaultBuilder.class; + } + + /** + * A builder for a {@link PresignedDownloadFileRequest}, created with {@link #builder()} + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder extends CopyableBuilder { + + /** + * The {@link Path} to file that response contents will be written to. The file must not exist or this method + * will throw an exception. If the file is not writable by the current user then an exception will be thrown. + * + * @param destination the destination path + * @return Returns a reference to this object so that method calls can be chained together. + */ + Builder destination(Path destination); + + /** + * The file that response contents will be written to. The file must not exist or this method + * will throw an exception. If the file is not writable by the current user then an exception will be thrown. + * + * @param destination the destination path + * @return Returns a reference to this object so that method calls can be chained together. + */ + default Builder destination(File destination) { + Validate.paramNotNull(destination, "destination"); + return destination(destination.toPath()); + } + + /** + * The {@link PresignedUrlDownloadRequest} request that should be used for the download + * + * @param presignedUrlDownloadRequest the presigned URL download request + * @return a reference to this object so that method calls can be chained together. + * @see #presignedUrlDownloadRequest(Consumer) + */ + Builder presignedUrlDownloadRequest(PresignedUrlDownloadRequest presignedUrlDownloadRequest); + + /** + * The {@link PresignedUrlDownloadRequest} request that should be used for the download + * + *

+ * This is a convenience method that creates an instance of the {@link PresignedUrlDownloadRequest} builder avoiding the + * need to create one manually via {@link PresignedUrlDownloadRequest#builder()}. + * + * @param presignedUrlDownloadRequestBuilder the presigned URL download request + * @return a reference to this object so that method calls can be chained together. + * @see #presignedUrlDownloadRequest(PresignedUrlDownloadRequest) + */ + default Builder presignedUrlDownloadRequest( + Consumer presignedUrlDownloadRequestBuilder) { + PresignedUrlDownloadRequest request = PresignedUrlDownloadRequest.builder() + .applyMutation(presignedUrlDownloadRequestBuilder) + .build(); + presignedUrlDownloadRequest(request); + return this; + } + + /** + * The {@link TransferListener}s that will be notified as part of this request. This method overrides and replaces any + * transferListeners that have already been set. Add an optional request override configuration. + * + * @param transferListeners the collection of transferListeners + * @return Returns a reference to this object so that method calls can be chained together. + * @return This builder for method chaining. + * @see TransferListener + */ + Builder transferListeners(Collection transferListeners); + + /** + * Add a {@link TransferListener} that will be notified as part of this request. + * + * @param transferListener the transferListener to add + * @return Returns a reference to this object so that method calls can be chained together. + * @see TransferListener + */ + Builder addTransferListener(TransferListener transferListener); + + } + + private static final class DefaultBuilder implements Builder { + private Path destination; + private PresignedUrlDownloadRequest presignedUrlDownloadRequest; + private List transferListeners; + + private DefaultBuilder() { + } + + private DefaultBuilder(PresignedDownloadFileRequest presignedDownloadFileRequest) { + this.destination = presignedDownloadFileRequest.destination; + this.presignedUrlDownloadRequest = presignedDownloadFileRequest.presignedUrlDownloadRequest; + this.transferListeners = presignedDownloadFileRequest.transferListeners; + } + + @Override + public Builder destination(Path destination) { + this.destination = Validate.paramNotNull(destination, "destination"); + return this; + } + + public Path getDestination() { + return destination; + } + + public void setDestination(Path destination) { + destination(destination); + } + + @Override + public DefaultBuilder presignedUrlDownloadRequest(PresignedUrlDownloadRequest presignedUrlDownloadRequest) { + this.presignedUrlDownloadRequest = presignedUrlDownloadRequest; + return this; + } + + public PresignedUrlDownloadRequest getPresignedUrlDownloadRequest() { + return presignedUrlDownloadRequest; + } + + public void setPresignedUrlDownloadRequest(PresignedUrlDownloadRequest presignedUrlDownloadRequest) { + presignedUrlDownloadRequest(presignedUrlDownloadRequest); + } + + @Override + public DefaultBuilder transferListeners(Collection transferListeners) { + this.transferListeners = transferListeners != null ? new ArrayList<>(transferListeners) : null; + return this; + } + + @Override + public Builder addTransferListener(TransferListener transferListener) { + if (transferListeners == null) { + transferListeners = new ArrayList<>(); + } + transferListeners.add(transferListener); + return this; + } + + public List getTransferListeners() { + return transferListeners; + } + + public void setTransferListeners(Collection transferListeners) { + transferListeners(transferListeners); + } + + @Override + public PresignedDownloadFileRequest build() { + return new PresignedDownloadFileRequest(this); + } + } +} \ No newline at end of file diff --git a/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadRequest.java b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadRequest.java new file mode 100644 index 000000000000..1a45eeb21254 --- /dev/null +++ b/services-custom/s3-transfer-manager/src/main/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadRequest.java @@ -0,0 +1,391 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.transfer.s3.model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.presignedurl.model.PresignedUrlDownloadRequest; +import software.amazon.awssdk.transfer.s3.S3TransferManager; +import software.amazon.awssdk.transfer.s3.model.PresignedDownloadRequest.TypedBuilder; +import software.amazon.awssdk.transfer.s3.progress.TransferListener; +import software.amazon.awssdk.utils.ToString; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * Represents the request to download an object using a pre-signed URL through the given + * {@link AsyncResponseTransformer}. For downloading to a file, + * you may use {@link PresignedDownloadFileRequest} instead. + * + * @see S3TransferManager#downloadWithPresignedUrl(PresignedDownloadRequest) + */ +@SdkPublicApi +public final class PresignedDownloadRequest + implements TransferObjectRequest, + ToCopyableBuilder, PresignedDownloadRequest> { + + private final AsyncResponseTransformer responseTransformer; + private final PresignedUrlDownloadRequest presignedUrlDownloadRequest; + private final List transferListeners; + + private PresignedDownloadRequest(DefaultTypedBuilder builder) { + this.responseTransformer = Validate.paramNotNull(builder.responseTransformer, "responseTransformer"); + this.presignedUrlDownloadRequest = Validate.paramNotNull(builder.presignedUrlDownloadRequest, + "presignedUrlDownloadRequest"); + this.transferListeners = builder.transferListeners; + } + + /** + * Creates a builder that can be used to create a {@link PresignedDownloadRequest}. + * + * @see UntypedBuilder + */ + public static UntypedBuilder builder() { + return new DefaultUntypedBuilder(); + } + + + @Override + public TypedBuilder toBuilder() { + return new DefaultTypedBuilder<>(this); + } + + /** + * The {@link AsyncResponseTransformer} that response contents will be written to. + * + * @return the response transformer + */ + public AsyncResponseTransformer responseTransformer() { + return responseTransformer; + } + + /** + * @return The {@link PresignedUrlDownloadRequest} request that should be used for the download + */ + public PresignedUrlDownloadRequest presignedUrlDownloadRequest() { + return presignedUrlDownloadRequest; + } + + /** + * @return the List of transferListeners. + * @see TypedBuilder#transferListeners(Collection) + */ + @Override + public List transferListeners() { + return transferListeners; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + PresignedDownloadRequest that = (PresignedDownloadRequest) o; + + if (!Objects.equals(responseTransformer, that.responseTransformer)) { + return false; + } + if (!Objects.equals(presignedUrlDownloadRequest, that.presignedUrlDownloadRequest)) { + return false; + } + return Objects.equals(transferListeners, that.transferListeners); + } + + @Override + public int hashCode() { + int result = responseTransformer != null ? responseTransformer.hashCode() : 0; + result = 31 * result + (presignedUrlDownloadRequest != null ? presignedUrlDownloadRequest.hashCode() : 0); + result = 31 * result + (transferListeners != null ? transferListeners.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return ToString.builder("PresignedDownloadRequest") + .add("responseTransformer", responseTransformer) + .add("presignedUrlDownloadRequest", presignedUrlDownloadRequest) + .add("transferListeners", transferListeners) + .build(); + } + + /** + * Initial calls to {@link PresignedDownloadRequest#builder()} return an {@link UntypedBuilder}, where the builder is not yet + * parameterized with the generic type associated with {@link PresignedDownloadRequest}. + * This prevents the otherwise awkward syntax of having to explicitly cast the builder type, e.g., + *

+     * {@code PresignedDownloadRequest.>builder()}
+     * 
+ * Instead, the type may be inferred as part of specifying the {@link #responseTransformer(AsyncResponseTransformer)} + * parameter, at which point the builder chain will return a new {@link TypedBuilder}. + */ + public interface UntypedBuilder { + + /** + * The {@link PresignedUrlDownloadRequest} request that should be used for the download + * + * @param presignedUrlDownloadRequest the presigned URL download request + * @return a reference to this object so that method calls can be chained together. + * @see #presignedUrlDownloadRequest(Consumer) + */ + UntypedBuilder presignedUrlDownloadRequest(PresignedUrlDownloadRequest presignedUrlDownloadRequest); + + /** + * The {@link PresignedUrlDownloadRequest} request that should be used for the download + *

+ * This is a convenience method that creates an instance of the {@link PresignedUrlDownloadRequest} + * builder, avoiding the need to create one manually via {@link PresignedUrlDownloadRequest#builder()}. + * + * @param presignedUrlDownloadRequestBuilder the presigned URL download request + * @return a reference to this object so that method calls can be chained together. + * @see #presignedUrlDownloadRequest(PresignedUrlDownloadRequest) + */ + default UntypedBuilder presignedUrlDownloadRequest( + Consumer presignedUrlDownloadRequestBuilder) { + PresignedUrlDownloadRequest request = PresignedUrlDownloadRequest.builder() + .applyMutation(presignedUrlDownloadRequestBuilder) + .build(); + presignedUrlDownloadRequest(request); + return this; + } + + /** + * The {@link TransferListener}s that will be notified as part of this request. This method + * overrides and replaces any transferListeners that have already been set. Add an optional + * request override configuration. + * + * @param transferListeners the collection of transferListeners + * @return Returns a reference to this object so that method calls can be chained together. + * @return This builder for method chaining. + * @see TransferListener + */ + UntypedBuilder transferListeners( + Collection transferListeners); + + /** + * Adds a {@link TransferListener} that will be notified as part of this request. + * + * @param transferListener the transferListener to add + * @return Returns a reference to this object so that method calls can be chained together. + * @see TransferListener + */ + UntypedBuilder addTransferListener(TransferListener transferListener); + + /** + * Specifies the {@link AsyncResponseTransformer} that should be used for the download. This + * method also infers the generic type of {@link PresignedDownloadRequest} to create, + * inferred from the second type parameter of the provided {@link AsyncResponseTransformer}. + * E.g, specifying {@link AsyncResponseTransformer#toBytes()} would result in inferring the + * type of the {@link PresignedDownloadRequest} to be of {@code ResponseBytes}. + * See the static factory methods available in {@link AsyncResponseTransformer}. + * + * @param responseTransformer the AsyncResponseTransformer + * @param the type of {@link PresignedDownloadRequest} to create + * @return a reference to this object so that method calls can be chained together. + * @see AsyncResponseTransformer + */ + TypedBuilder responseTransformer( + AsyncResponseTransformer responseTransformer); + } + + private static final class DefaultUntypedBuilder implements UntypedBuilder { + private PresignedUrlDownloadRequest presignedUrlDownloadRequest; + private List transferListeners; + + private DefaultUntypedBuilder() { + } + + @Override + public UntypedBuilder presignedUrlDownloadRequest(PresignedUrlDownloadRequest presignedUrlDownloadRequest) { + this.presignedUrlDownloadRequest = presignedUrlDownloadRequest; + return this; + } + + @Override + public UntypedBuilder transferListeners( + Collection transferListeners) { + this.transferListeners = transferListeners != null ? + new ArrayList<>(transferListeners) : null; + return this; + } + + @Override + public UntypedBuilder addTransferListener(TransferListener transferListener) { + if (transferListeners == null) { + transferListeners = new ArrayList<>(); + } + transferListeners.add(transferListener); + return this; + } + + public List getTransferListeners() { + return transferListeners; + } + + public void setTransferListeners( + Collection transferListeners) { + transferListeners(transferListeners); + } + + @Override + public TypedBuilder responseTransformer( + AsyncResponseTransformer responseTransformer) { + return new DefaultTypedBuilder() + .presignedUrlDownloadRequest(presignedUrlDownloadRequest) + .transferListeners(transferListeners) + .responseTransformer(responseTransformer); + } + } + + /** + * The type-parameterized version of {@link UntypedBuilder}. This builder's type is inferred as part of specifying {@link + * UntypedBuilder#responseTransformer(AsyncResponseTransformer)}, after which this builder can be used to construct a {@link + * PresignedDownloadRequest} with the same generic type. + */ + public interface TypedBuilder extends CopyableBuilder, PresignedDownloadRequest> { + + /** + * The {@link PresignedUrlDownloadRequest} request that should be used for the download + * + * @param presignedUrlDownloadRequest the presigned URL download request + * @return a reference to this object so that method calls can be chained together. + * @see #presignedUrlDownloadRequest(Consumer) + */ + TypedBuilder presignedUrlDownloadRequest(PresignedUrlDownloadRequest presignedUrlDownloadRequest); + + /** + * The {@link PresignedUrlDownloadRequest} request that should be used for the download + *

+ * This is a convenience method that creates an instance of the {@link PresignedUrlDownloadRequest} builder, + * avoiding the need to create one manually via {@link PresignedUrlDownloadRequest#builder()}. + * + * @param presignedUrlDownloadRequestBuilder the presigned URL download request + * @return a reference to this object so that method calls can be chained together. + * @see #presignedUrlDownloadRequest(PresignedUrlDownloadRequest) + */ + default TypedBuilder presignedUrlDownloadRequest( + Consumer presignedUrlDownloadRequestBuilder) { + PresignedUrlDownloadRequest request = PresignedUrlDownloadRequest.builder() + .applyMutation(presignedUrlDownloadRequestBuilder) + .build(); + presignedUrlDownloadRequest(request); + return this; + } + + /** + * The {@link TransferListener}s that will be notified as part of this request. This method overrides and + * replaces any transferListeners that have already been set. Add an optional request override + * configuration. + * + * @param transferListeners the collection of transferListeners + * @return Returns a reference to this object so that method calls can be chained together. + * @return This builder for method chaining. + * @see TransferListener + */ + TypedBuilder transferListeners( + Collection transferListeners); + + /** + * Add a {@link TransferListener} that will be notified as part of this request. + * + * @param transferListener the transferListener to add + * @return Returns a reference to this object so that method calls can be chained together. + * @see TransferListener + */ + TypedBuilder addTransferListener(TransferListener transferListener); + + /** + * Specifies the {@link AsyncResponseTransformer} that should be used for the download. The generic type used is + * constrained by the {@link UntypedBuilder#responseTransformer(AsyncResponseTransformer)} that was previously used to + * create this {@link TypedBuilder}. + * + * @param responseTransformer the AsyncResponseTransformer + * @return a reference to this object so that method calls can be chained together. + * @see AsyncResponseTransformer + */ + TypedBuilder responseTransformer( + AsyncResponseTransformer responseTransformer); + } + + private static class DefaultTypedBuilder implements TypedBuilder { + private PresignedUrlDownloadRequest presignedUrlDownloadRequest; + private List transferListeners; + private AsyncResponseTransformer responseTransformer; + + private DefaultTypedBuilder() { + } + + private DefaultTypedBuilder(PresignedDownloadRequest request) { + this.presignedUrlDownloadRequest = request.presignedUrlDownloadRequest; + this.responseTransformer = request.responseTransformer; + this.transferListeners = request.transferListeners; + } + + @Override + public TypedBuilder presignedUrlDownloadRequest(PresignedUrlDownloadRequest presignedUrlDownloadRequest) { + this.presignedUrlDownloadRequest = presignedUrlDownloadRequest; + return this; + } + + @Override + public TypedBuilder responseTransformer( + AsyncResponseTransformer responseTransformer) { + this.responseTransformer = responseTransformer; + return this; + } + + @Override + public TypedBuilder transferListeners( + Collection transferListeners) { + this.transferListeners = transferListeners != null ? + new ArrayList<>(transferListeners) : null; + return this; + } + + @Override + public TypedBuilder addTransferListener(TransferListener transferListener) { + if (transferListeners == null) { + transferListeners = new ArrayList<>(); + } + transferListeners.add(transferListener); + return this; + } + + public List getTransferListeners() { + return transferListeners; + } + + public void setTransferListeners( + Collection transferListeners) { + transferListeners(transferListeners); + } + + @Override + public PresignedDownloadRequest build() { + return new PresignedDownloadRequest<>(this); + } + } +} \ No newline at end of file diff --git a/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadFileRequestTest.java b/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadFileRequestTest.java new file mode 100644 index 000000000000..fb437e5f8796 --- /dev/null +++ b/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadFileRequestTest.java @@ -0,0 +1,119 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.transfer.s3.model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.File; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.services.s3.presignedurl.model.PresignedUrlDownloadRequest; + +class PresignedDownloadFileRequestTest { + + @Test + void build_withoutPresignedUrlDownloadRequest_throwsNullPointerException() { + assertThatThrownBy(() -> PresignedDownloadFileRequest.builder() + .destination(Paths.get(".")) + .build()) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("presignedUrlDownloadRequest"); + } + + @Test + void build_withoutDestination_throwsNullPointerException() { + assertThatThrownBy(() -> PresignedDownloadFileRequest.builder() + .presignedUrlDownloadRequest(createPresignedRequest()) + .build()) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("destination"); + } + + @Test + void build_withPath_setsDestinationCorrectly() { + Path path = Paths.get("."); + PresignedUrlDownloadRequest presignedRequest = createPresignedRequest(); + + PresignedDownloadFileRequest request = PresignedDownloadFileRequest.builder() + .destination(path) + .presignedUrlDownloadRequest(presignedRequest) + .build(); + + assertThat(request.destination()).isEqualTo(path); + assertThat(request.presignedUrlDownloadRequest()).isEqualTo(presignedRequest); + } + + @Test + void build_withFile_convertsToPathCorrectly() { + Path path = Paths.get("."); + PresignedUrlDownloadRequest presignedRequest = createPresignedRequest(); + + PresignedDownloadFileRequest request = PresignedDownloadFileRequest.builder() + .destination(path.toFile()) + .presignedUrlDownloadRequest(presignedRequest) + .build(); + + assertThat(request.destination()).isEqualTo(path); + } + + @Test + void destination_withNullFile_throwsNullPointerException() { + File file = null; + + assertThatThrownBy(() -> PresignedDownloadFileRequest.builder() + .destination(file)) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("destination"); + } + + @Test + void presignedUrlDownloadRequest_withConsumerBuilder_buildsCorrectly() { + Path path = Paths.get("."); + + PresignedDownloadFileRequest request = PresignedDownloadFileRequest.builder() + .destination(path) + .presignedUrlDownloadRequest(b -> b.presignedUrl(createTestUrl())) + .build(); + + assertThat(request.presignedUrlDownloadRequest()).isNotNull(); + assertThat(request.presignedUrlDownloadRequest().presignedUrl()).isEqualTo(createTestUrl()); + } + + @Test + void equalsHashCodeTest() { + EqualsVerifier.forClass(PresignedDownloadFileRequest.class) + .withNonnullFields("destination", "presignedUrlDownloadRequest") + .verify(); + } + + private PresignedUrlDownloadRequest createPresignedRequest() { + return PresignedUrlDownloadRequest.builder() + .presignedUrl(createTestUrl()) + .build(); + } + + private URL createTestUrl() { + try { + return new URL("https://example.com/test"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadRequestTest.java b/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadRequestTest.java new file mode 100644 index 000000000000..15381bfd50b6 --- /dev/null +++ b/services-custom/s3-transfer-manager/src/test/java/software/amazon/awssdk/transfer/s3/model/PresignedDownloadRequestTest.java @@ -0,0 +1,136 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.transfer.s3.model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URL; +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.core.ResponseBytes; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.presignedurl.model.PresignedUrlDownloadRequest; + +class PresignedDownloadRequestTest { + + @Test + void build_withoutPresignedUrlDownloadRequest_throwsNullPointerException() { + assertThatThrownBy(() -> PresignedDownloadRequest.builder() + .responseTransformer(AsyncResponseTransformer.toBytes()) + .build()) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("presignedUrlDownloadRequest"); + } + + @Test + void builder_withoutResponseTransformer_returnsUntypedBuilder() { + PresignedDownloadRequest.UntypedBuilder untypedBuilder = PresignedDownloadRequest.builder() + .presignedUrlDownloadRequest(createPresignedRequest()); + + assertThat(untypedBuilder).isNotNull(); + } + + @Test + void build_withResponseTransformer_createsRequestCorrectly() { + AsyncResponseTransformer> responseTransformer = + AsyncResponseTransformer.toBytes(); + PresignedUrlDownloadRequest presignedRequest = createPresignedRequest(); + + PresignedDownloadRequest> request = + PresignedDownloadRequest.builder() + .presignedUrlDownloadRequest(presignedRequest) + .responseTransformer(responseTransformer) + .build(); + + assertThat(request.responseTransformer()).isEqualTo(responseTransformer); + assertThat(request.presignedUrlDownloadRequest()).isEqualTo(presignedRequest); + } + + @Test + void presignedUrlDownloadRequest_withConsumerBuilder_buildsCorrectly() { + AsyncResponseTransformer> responseTransformer = + AsyncResponseTransformer.toBytes(); + + PresignedDownloadRequest> request = + PresignedDownloadRequest.builder() + .presignedUrlDownloadRequest(b -> b.presignedUrl(createTestUrl())) + .responseTransformer(responseTransformer) + .build(); + + assertThat(request.presignedUrlDownloadRequest()).isNotNull(); + assertThat(request.presignedUrlDownloadRequest().presignedUrl()).isEqualTo(createTestUrl()); + } + + @Test + void responseTransformer_withNull_throwsNullPointerException() { + assertThatThrownBy(() -> PresignedDownloadRequest.builder() + .presignedUrlDownloadRequest(createPresignedRequest()) + .responseTransformer(null) + .build()) + .isInstanceOf(NullPointerException.class) + .hasMessageContaining("responseTransformer"); + } + + @Test + void builder_untypedToTypedTransition_worksCorrectly() { + PresignedDownloadRequest.UntypedBuilder untypedBuilder = PresignedDownloadRequest.builder() + .presignedUrlDownloadRequest(createPresignedRequest()); + + PresignedDownloadRequest> request = + untypedBuilder.responseTransformer(AsyncResponseTransformer.toBytes()) + .build(); + + assertThat(request.responseTransformer()).isNotNull(); + assertThat(request.presignedUrlDownloadRequest()).isNotNull(); + } + + @Test + void toBuilder_copiesAllFields() { + PresignedDownloadRequest> original = + PresignedDownloadRequest.builder() + .presignedUrlDownloadRequest(createPresignedRequest()) + .responseTransformer(AsyncResponseTransformer.toBytes()) + .build(); + + PresignedDownloadRequest> copy = original.toBuilder().build(); + + assertThat(copy.responseTransformer()).isEqualTo(original.responseTransformer()); + assertThat(copy.presignedUrlDownloadRequest()).isEqualTo(original.presignedUrlDownloadRequest()); + } + + @Test + void equalsHashCodeTest() { + EqualsVerifier.forClass(PresignedDownloadRequest.class) + .withNonnullFields("responseTransformer", "presignedUrlDownloadRequest") + .verify(); + } + + private PresignedUrlDownloadRequest createPresignedRequest() { + return PresignedUrlDownloadRequest.builder() + .presignedUrl(createTestUrl()) + .build(); + } + + private URL createTestUrl() { + try { + return new URL("https://example.com/test"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file