diff --git a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java index 9eb26202369a..f8a83e2e89bf 100644 --- a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java +++ b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java @@ -64,9 +64,9 @@ void objectmetadata_unsupportedSetters(Date dateVal) { /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - ongoingRestore - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.ongoingRestore(false); /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - requesterCharged - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.requesterCharged(false); - /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - lastModified - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.lastModified(dateVal); - /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - expirationTime - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.expirationTime(dateVal); - /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - restoreExpirationTime - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.restoreExpirationTime(dateVal); + /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - lastModified - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.lastModified(dateVal.toInstant()); + /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - expirationTime - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.expirationTime(dateVal.toInstant()); + /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - restoreExpirationTime - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.restoreExpirationTime(dateVal.toInstant()); /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - header - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.header("key", "val"); /*AWS SDK for Java v2 migration: Transform for ObjectMetadata setter - addUserMetadata - is not supported, please manually migrate the code by setting it on the v2 request/response object.*/metadata.addUserMetadata("a", "b"); diff --git a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/Application.java b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/Application.java index 68ad13db7cea..fcb5b1da2862 100644 --- a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/Application.java +++ b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/after/src/main/java/foo/bar/Application.java @@ -35,6 +35,7 @@ import software.amazon.awssdk.services.sqs.model.ListQueuesRequest; import software.amazon.awssdk.services.sqs.model.ListQueuesResponse; import software.amazon.awssdk.services.sqs.model.QueueDoesNotExistException; +import software.amazon.awssdk.services.sqs.model.SendMessageRequest; import software.amazon.awssdk.services.sqs.model.SqsException; public class Application { @@ -113,5 +114,17 @@ void dateToInstant(Date start, Date end) { .startTime(start.toInstant()) .endTime(end.toInstant()) .build(); + + GetMetricStatisticsRequest getMetricStatisticsRequest2 = GetMetricStatisticsRequest.builder() + .build(); + getMetricStatisticsRequest2 = getMetricStatisticsRequest2.toBuilder().startTime(start.toInstant()).build(); + getMetricStatisticsRequest2 = getMetricStatisticsRequest2.toBuilder().endTime(end.toInstant()).build(); + } + + void pojoSettersAfterCreation() { + SendMessageRequest sendMessageRequest = SendMessageRequest.builder() + .build(); + sendMessageRequest = sendMessageRequest.toBuilder().messageGroupId("groupId").build(); + sendMessageRequest = sendMessageRequest.toBuilder().queueUrl("queueUrl").build(); } } diff --git a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/before/src/main/java/foo/bar/Application.java b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/before/src/main/java/foo/bar/Application.java index 8f4304898c7a..3d059403886c 100644 --- a/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/before/src/main/java/foo/bar/Application.java +++ b/test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven/before/src/main/java/foo/bar/Application.java @@ -28,6 +28,7 @@ import com.amazonaws.services.sqs.model.AmazonSQSException; import com.amazonaws.services.sqs.model.ListQueuesRequest; import com.amazonaws.services.sqs.model.ListQueuesResult; +import com.amazonaws.services.sqs.model.SendMessageRequest; import java.io.IOException; import java.io.InputStream; @@ -107,5 +108,15 @@ void dateToInstant(Date start, Date end) { GetMetricStatisticsRequest getMetricStatisticsRequest = new GetMetricStatisticsRequest() .withStartTime(start) .withEndTime(end); + + GetMetricStatisticsRequest getMetricStatisticsRequest2 = new GetMetricStatisticsRequest(); + getMetricStatisticsRequest2.setStartTime(start); + getMetricStatisticsRequest2.setEndTime(end); + } + + void pojoSettersAfterCreation() { + SendMessageRequest sendMessageRequest = new SendMessageRequest(); + sendMessageRequest.setMessageGroupId("groupId"); + sendMessageRequest.setQueueUrl("queueUrl"); } } diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/DateToInstant.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/DateToInstant.java index fc32241a63b4..925771386ad3 100644 --- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/DateToInstant.java +++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/DateToInstant.java @@ -82,7 +82,8 @@ private static boolean isV2ModelSetterWithDateParam(J.MethodInvocation method) { } boolean isDateParam = javaType.isAssignableFrom(DATE_PATTERN); - return SdkTypeUtils.isV2ModelBuilder(declaringType) && isDateParam; + boolean isV2Model = SdkTypeUtils.isV2ModelBuilder(declaringType) || SdkTypeUtils.isV2ModelClass(declaringType); + return isDateParam && isV2Model; } } } diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3NonStreamingRequestToV2Complex.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3NonStreamingRequestToV2Complex.java index d078a8cbbada..f3b86e29c6a2 100644 --- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3NonStreamingRequestToV2Complex.java +++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3NonStreamingRequestToV2Complex.java @@ -57,7 +57,7 @@ public class S3NonStreamingRequestToV2Complex extends Recipe { private static final MethodMatcher LIST_BUCKETS = v2S3MethodMatcher("listBuckets()"); private static final MethodMatcher RESTORE_OBJECT = v2S3MethodMatcher("restoreObject(String, String, int)"); private static final MethodMatcher SET_OBJECT_REDIRECT_LOCATION = - v2S3MethodMatcher("objectRedirectLocation(String, String, String)"); + v2S3MethodMatcher("setObjectRedirectLocation(String, String, String)"); private static final MethodMatcher CHANGE_OBJECT_STORAGE_CLASS = v2S3MethodMatcher( String.format("changeObjectStorageClass(String, String, %sStorageClass)", V2_S3_MODEL_PKG)); private static final MethodMatcher CREATE_BUCKET = v2S3MethodMatcher("createBucket(String, String)"); diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3PutObjectRequestToV2.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3PutObjectRequestToV2.java index cd52fb274d4a..94fb449296ad 100644 --- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3PutObjectRequestToV2.java +++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3PutObjectRequestToV2.java @@ -22,7 +22,10 @@ import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.createComments; import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.getArgumentName; import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.getSelectName; +import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isObjectMetadataSetter; import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isPayloadSetter; +import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isPutObjectRequestBuilderSetter; +import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isPutObjectRequestSetter; import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isRequestMetadataSetter; import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isRequestPayerSetter; import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.v2S3MethodMatcher; @@ -36,7 +39,6 @@ import java.util.List; import java.util.Map; import java.util.Queue; -import java.util.regex.Pattern; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; @@ -47,7 +49,6 @@ import org.openrewrite.java.tree.Comment; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.TypeUtils; import software.amazon.awssdk.annotations.SdkInternalApi; @SdkInternalApi @@ -63,8 +64,6 @@ public class S3PutObjectRequestToV2 extends Recipe { private static final MethodMatcher UPLOAD_WITH_REQUEST = v2TmMethodMatcher(String.format("upload(%sPutObjectRequest)", V2_S3_MODEL_PKG)); - private static final Pattern METADATA_V2 = Pattern.compile(V2_S3_MODEL_PKG + "HeadObjectResponse"); - @Override public String getDisplayName() { return "V1 S3 PutObjectRequest, AmazonS3.putObject(PutObjectRequest), and TransferManager.upload(PutObjectRequest) to V2"; @@ -412,15 +411,6 @@ private Expression retrieveContentLengthForMetadataIfSet(String metadataName) { return map.get("contentLength"); } - private boolean isObjectMetadataSetter(J.MethodInvocation method) { - if (method.getSelect() == null || method.getSelect().getType() == null) { - return false; - } - - return method.getSelect().getType().isAssignableFrom(METADATA_V2) - && !method.getArguments().isEmpty(); - } - private J.MethodInvocation saveMetadataValueAndRemoveStatement(J.MethodInvocation method) { J.Identifier metadataPojo = (J.Identifier) method.getSelect(); String variableName = metadataPojo.getSimpleName(); @@ -464,28 +454,6 @@ private J.MethodInvocation transformWithRequesterPays(J.MethodInvocation method) .apply(getCursor(), method.getCoordinates().replaceArguments()); } - /** Field set during POJO instantiation, e.g., - * PutObjectRequest request = new PutObjectRequest("bucket" "key", "redirectLocation").withFile(file); - */ - private boolean isPutObjectRequestBuilderSetter(J.MethodInvocation method) { - return isSetterForClassType(method, "software.amazon.awssdk.services.s3.model.PutObjectRequest$Builder"); - } - - /** Field set after POJO instantiation, e.g., - * PutObjectRequest request = new PutObjectRequest("bucket" "key", "redirectLocation"); - * request.setFile(file); - */ - private boolean isPutObjectRequestSetter(J.MethodInvocation method) { - return isSetterForClassType(method, "software.amazon.awssdk.services.s3.model.PutObjectRequest"); - } - - private boolean isSetterForClassType(J.MethodInvocation method, String fqcn) { - if (method.getSelect() == null || method.getSelect().getType() == null) { - return false; - } - return TypeUtils.isOfClassType(method.getSelect().getType(), fqcn); - } - private void addRequestBodyImport() { String fqcn = "software.amazon.awssdk.core.sync.RequestBody"; doAfterVisit(new AddImport<>(fqcn, null, false)); diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/SettersToBuilderV2.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/SettersToBuilderV2.java new file mode 100644 index 000000000000..5f77e09a80d4 --- /dev/null +++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/SettersToBuilderV2.java @@ -0,0 +1,77 @@ +/* + * 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.v2migration; + +import static software.amazon.awssdk.v2migration.internal.utils.NamingUtils.isSetter; +import static software.amazon.awssdk.v2migration.internal.utils.NamingUtils.removeSet; +import static software.amazon.awssdk.v2migration.internal.utils.SdkTypeUtils.isV2ModelClass; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.NlsRewrite; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.tree.J; +import software.amazon.awssdk.annotations.SdkInternalApi; + +@SdkInternalApi +public class SettersToBuilderV2 extends Recipe { + + @Override + public @NlsRewrite.DisplayName String getDisplayName() { + return "Convert V1 setters to V2 toBuilder setters"; + } + + @Override + public @NlsRewrite.Description String getDescription() { + return "Convert V1 setters to V2 toBuilder setters"; + } + + @Override + public TreeVisitor getVisitor() { + return new Visitor(); + } + + private static final class Visitor extends JavaVisitor { + + + @Override + public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + + if (isV2ModelClassSetter(method)) { + return convertSetterToBuilder(method); + } + + return method; + } + + private boolean isV2ModelClassSetter(J.MethodInvocation method) { + return isV2ModelClass(method.getType()) + && method.getArguments().size() == 1 + && isSetter(method.getSimpleName()); + } + + private J convertSetterToBuilder(J.MethodInvocation method) { + String v2Method = String.format("#{any()} = #{any()}.toBuilder().%s(#{any()}).build()", + removeSet(method.getSimpleName())); + + return JavaTemplate.builder(v2Method).build() + .apply(getCursor(), method.getCoordinates().replace(), + method.getSelect(), method.getSelect(), method.getArguments().get(0)); + } + } +} diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/V1SetterToV2.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/V1SetterToV2.java index e5cbfeb8c9b3..edc55c401823 100644 --- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/V1SetterToV2.java +++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/V1SetterToV2.java @@ -15,6 +15,8 @@ package software.amazon.awssdk.v2migration; +import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.hasArguments; +import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isS3PutObjectOrObjectMetadata; import static software.amazon.awssdk.v2migration.internal.utils.SdkTypeUtils.isEligibleToConvertToBuilder; import static software.amazon.awssdk.v2migration.internal.utils.SdkTypeUtils.isV2CoreClassBuilder; import static software.amazon.awssdk.v2migration.internal.utils.SdkTypeUtils.isV2ModelBuilder; @@ -73,6 +75,10 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation previousMetho ExecutionContext executionContext) { J.MethodInvocation method = super.visitMethodInvocation(previousMethodInvocation, executionContext); + if (!hasArguments(method)) { + return method; + } + JavaType selectType = null; Expression select = method.getSelect(); @@ -98,7 +104,8 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation previousMetho if (NamingUtils.isWither(methodName)) { methodName = NamingUtils.removeWith(methodName); - } else if (NamingUtils.isSetter(methodName)) { + } else if (NamingUtils.isSetter(methodName) && isS3PutObjectOrObjectMetadata(method)) { + // We will change remaining setters to `request = request.toBuilder().setter(val).build()` in SettersToBuilderV2 methodName = NamingUtils.removeSet(methodName); } diff --git a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java index ef6628abd2be..386cf7aa575c 100644 --- a/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java +++ b/v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java @@ -139,7 +139,7 @@ public static void addMetadataFields(StringBuilder sb, String metadataName, } Expression expiresDate = map.get("httpExpiresDate"); if (expiresDate != null) { - sb.append(".expires(").append(expiresDate).append(".toInstant())\n"); + sb.append(".expires(").append(expiresDate).append(")\n"); } } @@ -158,6 +158,42 @@ public static List createComments(String comment) { new TextComment(true, "AWS SDK for Java v2 migration: " + comment, "", Markers.EMPTY)); } + public static boolean isObjectMetadataSetter(J.MethodInvocation method) { + return isSetterForClassType(method, V2_S3_MODEL_PKG + "HeadObjectResponse"); + } + + /** Field set during POJO instantiation, e.g., + * PutObjectRequest request = new PutObjectRequest("bucket" "key", "redirectLocation").withFile(file); + */ + public static boolean isPutObjectRequestBuilderSetter(J.MethodInvocation method) { + return isSetterForClassType(method, "software.amazon.awssdk.services.s3.model.PutObjectRequest$Builder"); + } + + /** Field set after POJO instantiation, e.g., + * PutObjectRequest request = new PutObjectRequest("bucket" "key", "redirectLocation"); + * request.setFile(file); + */ + public static boolean isPutObjectRequestSetter(J.MethodInvocation method) { + return isSetterForClassType(method, "software.amazon.awssdk.services.s3.model.PutObjectRequest"); + } + + public static boolean isS3PutObjectOrObjectMetadata(J.MethodInvocation method) { + return isObjectMetadataSetter(method) + || isPutObjectRequestSetter(method) + || isPutObjectRequestBuilderSetter(method); + } + + public static boolean isSetterForClassType(J.MethodInvocation method, String fqcn) { + if (method.getSelect() == null || method.getSelect().getType() == null) { + return false; + } + return hasArguments(method) && TypeUtils.isOfClassType(method.getSelect().getType(), fqcn); + } + + public static boolean hasArguments(J.MethodInvocation method) { + return !method.getArguments().isEmpty(); + } + public static boolean isPayloadSetter(J.MethodInvocation method) { return "file".equals(method.getSimpleName()) || "inputStream".equals(method.getSimpleName()); } diff --git a/v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2-with-tm.yml b/v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2-with-tm.yml index 6e88b699ac6c..b1b29f80ad3f 100644 --- a/v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2-with-tm.yml +++ b/v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2-with-tm.yml @@ -46,4 +46,5 @@ recipeList: - software.amazon.awssdk.v2migration.DateToInstant - software.amazon.awssdk.v2migration.S3NonStreamingRequestToV2Complex - software.amazon.awssdk.v2migration.S3PutObjectRequestToV2 + - software.amazon.awssdk.v2migration.SettersToBuilderV2 - software.amazon.awssdk.v2migration.TransferManagerMethodsToV2 \ No newline at end of file diff --git a/v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2.yml b/v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2.yml index d395fd75b2e9..3b1aec3c6677 100644 --- a/v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2.yml +++ b/v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2.yml @@ -43,4 +43,5 @@ recipeList: - software.amazon.awssdk.v2migration.ByteBufferToSdkBytes - software.amazon.awssdk.v2migration.DateToInstant - software.amazon.awssdk.v2migration.S3NonStreamingRequestToV2Complex - - software.amazon.awssdk.v2migration.S3PutObjectRequestToV2 \ No newline at end of file + - software.amazon.awssdk.v2migration.S3PutObjectRequestToV2 + - software.amazon.awssdk.v2migration.SettersToBuilderV2 \ No newline at end of file