Skip to content

Commit d1f1bb4

Browse files
Fred1155davidh44
andauthored
Added recipe for adding comments for unsupported transforms (#6033)
* Add new recipe S3AddImportsAndComments * Do not remove AccessControlList import * Fix pattern for InitiateMpu * more unsupported method comments added * listNextBatchOfObjects comments added * more unsupported method comments added * checkstyle fixed * more comments added * comments addressed * integration test fixed * minor fix --------- Co-authored-by: hdavidh <hdavidh@amazon.com>
1 parent 877bff8 commit d1f1bb4

File tree

5 files changed

+248
-11
lines changed

5 files changed

+248
-11
lines changed

test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/after/src/main/java/foo/bar/S3Transforms.java

+21-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package foo.bar;
1717

1818
import com.amazonaws.HttpMethod;
19-
import com.amazonaws.services.s3.model.MultiFactorAuthentication;
2019
import com.amazonaws.services.s3.model.SSEAwsKeyManagementParams;
2120
import com.amazonaws.services.s3.model.SSECustomerKey;
2221
import java.io.ByteArrayInputStream;
@@ -26,22 +25,38 @@
2625
import software.amazon.awssdk.core.async.AsyncRequestBody;
2726
import software.amazon.awssdk.services.s3.S3Client;
2827
import software.amazon.awssdk.services.s3.model.AccessControlPolicy;
28+
import software.amazon.awssdk.services.s3.model.BucketCannedACL;
2929
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
3030
import software.amazon.awssdk.services.s3.model.GeneratePresignedUrlRequest;
3131
import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
32+
import software.amazon.awssdk.services.s3.model.ObjectCannedACL;
3233
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
3334
import software.amazon.awssdk.transfer.s3.S3TransferManager;
3435
import software.amazon.awssdk.transfer.s3.model.UploadRequest;
3536

3637
public class S3Transforms {
3738

39+
S3Client s3;
40+
String bucket;
41+
String key;
42+
3843
void s3Pojos() {
44+
MultiFactorAuthentication mfa = /*AWS SDK for Java v2 migration: v2 does not have a MultiFactorAuthentication POJO. Please manually set the String value on the request POJO.*/new MultiFactorAuthentication("serialNum", "token");
45+
3946
DeleteObjectRequest deleteVersionRequest =
40-
DeleteObjectRequest.builder().bucket("bucket").key("key").versionId("id").mfa(new MultiFactorAuthentication("serialNum", "token"))
47+
DeleteObjectRequest.builder().bucket("bucket").key("key").versionId("id").mfa(mfa)
4148
.build();
4249
}
4350

44-
void upload_streamWithLiteralLength(S3TransferManager tm, String bucket, String key) {
51+
void setBucketAcl() {
52+
/*AWS SDK for Java v2 migration: Transform for AccessControlList and CannedAccessControlList not supported. In v2, CannedAccessControlList is replaced by BucketCannedACL for buckets and ObjectCannedACL for objects.*/s3.setBucketAcl(bucket, ObjectCannedACL.AUTHENTICATED_READ);
53+
}
54+
55+
void setObjectAcl() {
56+
/*AWS SDK for Java v2 migration: Transform for AccessControlList and CannedAccessControlList not supported. In v2, CannedAccessControlList is replaced by BucketCannedACL for buckets and ObjectCannedACL for objects.*/s3.setObjectAcl(bucket, key, ObjectCannedACL.PUBLIC_READ);
57+
}
58+
59+
void upload_streamWithLiteralLength(S3TransferManager tm) {
4560
HeadObjectResponse metadata = HeadObjectResponse.builder()
4661
.build();
4762
InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
@@ -50,7 +65,7 @@ void upload_streamWithLiteralLength(S3TransferManager tm, String bucket, String
5065
/*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndLiteralLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, 333, newExecutorServiceVariableToDefine)).build());
5166
}
5267

53-
void upload_streamWithAssignedLength(S3TransferManager tm, String bucket, String key) {
68+
void upload_streamWithAssignedLength(S3TransferManager tm) {
5469
HeadObjectResponse metadata = HeadObjectResponse.builder()
5570
.build();
5671
long contentLen = 777;
@@ -60,7 +75,7 @@ void upload_streamWithAssignedLength(S3TransferManager tm, String bucket, String
6075
/*AWS SDK for Java v2 migration: When using InputStream to upload with TransferManager, you must specify Content-Length and ExecutorService.*/tm.upload(UploadRequest.builder().putObjectRequest(requestWithStreamAndAssignedLength).requestBody(AsyncRequestBody.fromInputStream(inputStream, contentLen, newExecutorServiceVariableToDefine)).build());
6176
}
6277

63-
void upload_streamWithoutLength(S3TransferManager tm, String bucket, String key) {
78+
void upload_streamWithoutLength(S3TransferManager tm) {
6479
InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
6580
PutObjectRequest requestWithStreamAndNoLength = PutObjectRequest.builder().bucket(bucket).key(key).websiteRedirectLocation("location")
6681
.build();
@@ -99,7 +114,7 @@ void objectmetadata_unsupportedSetters(Date dateVal) {
99114
/*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");
100115
}
101116

102-
private void generatePresignedUrl(S3Client s3, String bucket, String key, Date expiration) {
117+
private void generatePresignedUrl(Date expiration) {
103118
URL urlHead = /*AWS SDK for Java v2 migration: S3 generatePresignedUrl() with HEAD HTTP method is not supported in v2. Only GET, PUT, and DELETE are supported - https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/presigner/S3Presigner.html*/s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.HEAD);
104119

105120
URL urlPatch = /*AWS SDK for Java v2 migration: S3 generatePresignedUrl() with PATCH HTTP method is not supported in v2. Only GET, PUT, and DELETE are supported - https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/presigner/S3Presigner.html*/s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.PATCH);

test/v2-migration-tests/src/test/resources/software/amazon/awssdk/v2migrationtests/maven-nocompile/before/src/main/java/foo/bar/S3Transforms.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.amazonaws.HttpMethod;
1919
import com.amazonaws.services.s3.AmazonS3;
2020
import com.amazonaws.services.s3.model.AccessControlList;
21+
import com.amazonaws.services.s3.model.CannedAccessControlList;
2122
import com.amazonaws.services.s3.model.DeleteVersionRequest;
2223
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
2324
import com.amazonaws.services.s3.model.MultiFactorAuthentication;
@@ -33,12 +34,26 @@
3334

3435
public class S3Transforms {
3536

37+
AmazonS3 s3;
38+
String bucket;
39+
String key;
40+
3641
void s3Pojos() {
42+
MultiFactorAuthentication mfa = new MultiFactorAuthentication("serialNum", "token");
43+
3744
DeleteVersionRequest deleteVersionRequest =
38-
new DeleteVersionRequest("bucket", "key", "id", new MultiFactorAuthentication("serialNum", "token"));
45+
new DeleteVersionRequest("bucket", "key", "id", mfa);
46+
}
47+
48+
void setBucketAcl() {
49+
s3.setBucketAcl(bucket, CannedAccessControlList.AuthenticatedRead);
50+
}
51+
52+
void setObjectAcl() {
53+
s3.setObjectAcl(bucket, key, CannedAccessControlList.PublicRead);
3954
}
4055

41-
void upload_streamWithLiteralLength(TransferManager tm, String bucket, String key) {
56+
void upload_streamWithLiteralLength(TransferManager tm) {
4257
ObjectMetadata metadata = new ObjectMetadata();
4358
metadata.setContentLength(333);
4459
InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
@@ -47,7 +62,7 @@ void upload_streamWithLiteralLength(TransferManager tm, String bucket, String ke
4762
tm.upload(requestWithStreamAndLiteralLength);
4863
}
4964

50-
void upload_streamWithAssignedLength(TransferManager tm, String bucket, String key) {
65+
void upload_streamWithAssignedLength(TransferManager tm) {
5166
ObjectMetadata metadata = new ObjectMetadata();
5267
long contentLen = 777;
5368
metadata.setContentLength(contentLen);
@@ -57,7 +72,7 @@ void upload_streamWithAssignedLength(TransferManager tm, String bucket, String k
5772
tm.upload(requestWithStreamAndAssignedLength);
5873
}
5974

60-
void upload_streamWithoutLength(TransferManager tm, String bucket, String key) {
75+
void upload_streamWithoutLength(TransferManager tm) {
6176
InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
6277
PutObjectRequest requestWithStreamAndNoLength = new PutObjectRequest(bucket, key, "location");
6378
requestWithStreamAndNoLength.setInputStream(inputStream);
@@ -90,7 +105,7 @@ void objectmetadata_unsupportedSetters(Date dateVal) {
90105
metadata.addUserMetadata("a", "b");
91106
}
92107

93-
private void generatePresignedUrl(AmazonS3 s3, String bucket, String key, Date expiration) {
108+
private void generatePresignedUrl(Date expiration) {
94109
URL urlHead = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.HEAD);
95110

96111
URL urlPatch = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.PATCH);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.v2migration;
17+
18+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.V1_S3_MODEL_PKG;
19+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.V2_S3_MODEL_PKG;
20+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.createComments;
21+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.v1S3MethodMatcher;
22+
23+
import java.util.List;
24+
import java.util.regex.Pattern;
25+
import org.openrewrite.ExecutionContext;
26+
import org.openrewrite.Recipe;
27+
import org.openrewrite.TreeVisitor;
28+
import org.openrewrite.java.AddImport;
29+
import org.openrewrite.java.JavaIsoVisitor;
30+
import org.openrewrite.java.MethodMatcher;
31+
import org.openrewrite.java.RemoveImport;
32+
import org.openrewrite.java.tree.Expression;
33+
import org.openrewrite.java.tree.J;
34+
import org.openrewrite.java.tree.JavaType;
35+
import software.amazon.awssdk.annotations.SdkInternalApi;
36+
37+
@SdkInternalApi
38+
public class S3AddImportsAndComments extends Recipe {
39+
40+
private static final MethodMatcher CREATE_BUCKET = v1S3MethodMatcher("createBucket(String, "
41+
+ V1_S3_MODEL_PKG + "Region");
42+
private static final MethodMatcher LIST_NEXT_BATCH_OBJECTS = v1S3MethodMatcher("listNextBatchOfObjects(..)");
43+
private static final MethodMatcher LIST_NEXT_BATCH_VERSIONS = v1S3MethodMatcher("listNextBatchOfVersions(..)");
44+
private static final MethodMatcher GET_METADATA = v1S3MethodMatcher("getCachedResponseMetadata(..)");
45+
private static final MethodMatcher SET_BUCKET_ACL = v1S3MethodMatcher("setBucketAcl(..)");
46+
private static final MethodMatcher SET_ENDPOINT = v1S3MethodMatcher("setEndpoint(..)");
47+
private static final MethodMatcher SET_OBJECT_ACL = v1S3MethodMatcher("setObjectAcl(..)");
48+
private static final MethodMatcher SET_REGION = v1S3MethodMatcher("setRegion(..)");
49+
private static final MethodMatcher SET_S3CLIENT_OPTIONS = v1S3MethodMatcher("setS3ClientOptions(..)");
50+
private static final MethodMatcher SELECT_OBJECT_CONTENT = v1S3MethodMatcher("selectObjectContent(..)");
51+
52+
private static final Pattern CANNED_ACL = Pattern.compile(V1_S3_MODEL_PKG + "CannedAccessControlList");
53+
private static final Pattern GET_OBJECT_REQUEST = Pattern.compile(V1_S3_MODEL_PKG + "GetObjectRequest");
54+
private static final Pattern INITIATE_MPU = Pattern.compile(V1_S3_MODEL_PKG + "InitiateMultipartUpload");
55+
private static final Pattern MULTI_FACTOR_AUTH = Pattern.compile(V1_S3_MODEL_PKG + "MultiFactorAuthentication");
56+
57+
@Override
58+
public String getDisplayName() {
59+
return "Add imports and comments to unsupported S3 transforms.";
60+
}
61+
62+
@Override
63+
public String getDescription() {
64+
return "Add imports and comments to unsupported S3 transforms.";
65+
}
66+
67+
@Override
68+
public TreeVisitor<?, ExecutionContext> getVisitor() {
69+
return new Visitor();
70+
}
71+
72+
private static class Visitor extends JavaIsoVisitor<ExecutionContext> {
73+
74+
@Override
75+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
76+
boolean isSetObjectAcl = SET_OBJECT_ACL.matches(method);
77+
boolean isSetBucketAcl = SET_BUCKET_ACL.matches(method);
78+
79+
if (isSetObjectAcl || isSetBucketAcl) {
80+
removeV1S3ModelImport("CannedAccessControlList");
81+
maybeAddV2CannedAclImport(method.getArguments(), isSetObjectAcl, isSetBucketAcl);
82+
83+
// TODO: add the developer guide link in the comments once the doc is published.
84+
String comment = "Transform for AccessControlList and CannedAccessControlList not supported. "
85+
+ "In v2, CannedAccessControlList is replaced by BucketCannedACL for buckets and "
86+
+ "ObjectCannedACL for objects.";
87+
return method.withComments(createComments(comment));
88+
}
89+
if (LIST_NEXT_BATCH_OBJECTS.matches(method)) {
90+
// TODO: add the developer guide link in the comments once the doc is published.
91+
String comment = "Transform for listNextBatchOfObjects method not supported. "
92+
+ "listNextBatchOfObjects() only exists in SDK v1, for SDK v2 use either "
93+
+ "listObjectsV2Paginator().stream() for automatic pagination"
94+
+ " or manually handle pagination with listObjectsV2() and nextToken in the response for more "
95+
+ "control";
96+
return method.withComments(createComments(comment));
97+
}
98+
if (LIST_NEXT_BATCH_VERSIONS.matches(method)) {
99+
// TODO: add the developer guide link in the comments once the doc is published.
100+
String comment = "Transform for listNextBatchOfVersions method not supported."
101+
+ "listNextBatchOfVersions() only exists in SDK v1, for SDK v2 use either "
102+
+ "listObjectVersionsPaginator().stream for automatic pagination"
103+
+ " or manually handle pagination with listObjectVersions() and VersionIdMarker/KeyMarker. ";
104+
return method.withComments(createComments(comment));
105+
}
106+
if (SET_REGION.matches(method)) {
107+
String comment = "Transform for setRegion method not supported. Please manually "
108+
+ "migrate your code by configuring the region in the s3 client builder";
109+
return method.withComments(createComments(comment));
110+
}
111+
if (SET_S3CLIENT_OPTIONS.matches(method)) {
112+
String comment = "Transform for setS3ClientOptions method not supported. Please manually "
113+
+ "migrate setS3ClientOptions by configuring the equivalent settings in "
114+
+ "S3Configuration.builder() when building your S3Client.";
115+
return method.withComments(createComments(comment));
116+
}
117+
if (SELECT_OBJECT_CONTENT.matches(method)) {
118+
String comment = "Note: selectObjectContent is only supported in AWS SDK v2 with S3AsyncClient. "
119+
+ "Please manually migrate to event-based response handling using "
120+
+ "SelectObjectContentEventStream";
121+
return method.withComments(createComments(comment));
122+
}
123+
124+
if (CREATE_BUCKET.matches(method)) {
125+
String comment = "Transform for createBucket(String bucketName, Region region) method not supported. Please "
126+
+ "manually migrate your code by using the following pattern: "
127+
+ "createBucket(builder -> builder.bucket(bucketName)"
128+
+ ".createBucketConfiguration(cfg -> cfg.locationConstraint(region)))";
129+
return method.withComments(createComments(comment));
130+
}
131+
132+
if (SET_ENDPOINT.matches(method)) {
133+
String comment = "Transform for setEndpoint method not supported. setEndpoint() method is removed in SDK v2. "
134+
+ "Please manually migrate your code by using endpointOverride(URI.create(endpoint)) in "
135+
+ "S3ClientBuilder";
136+
return method.withComments(createComments(comment));
137+
}
138+
139+
if (GET_METADATA.matches(method)) {
140+
String comment = "Transform for getCachedResponseMetadata method not "
141+
+ "supported. getCachedResponseMetadata() is removed in SDK v2. Please manually migrate your "
142+
+ "code by accessing metadata directly from specific response objects instead of cached "
143+
+ "metadata";
144+
return method.withComments(createComments(comment));
145+
}
146+
147+
return method;
148+
}
149+
150+
@Override
151+
public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
152+
JavaType type = newClass.getType();
153+
if (!(type instanceof JavaType.FullyQualified)) {
154+
return newClass;
155+
}
156+
157+
if (type.isAssignableFrom(MULTI_FACTOR_AUTH)) {
158+
removeV1S3ModelImport("MultiFactorAuthentication");
159+
String comment = "v2 does not have a MultiFactorAuthentication POJO. Please manually set the String value on "
160+
+ "the request POJO.";
161+
return newClass.withComments(createComments(comment));
162+
}
163+
164+
if (type.isAssignableFrom(GET_OBJECT_REQUEST) && newClass.getArguments().size() == 1) {
165+
removeV1S3ModelImport("S3ObjectId");
166+
String comment = "v2 does not have S3ObjectId class. Please manually migrate the code by setting the configs "
167+
+ "directly into the request builder pattern.";
168+
return newClass.withComments(createComments(comment));
169+
}
170+
171+
if (type.isAssignableFrom(INITIATE_MPU) && newClass.getArguments().size() == 3) {
172+
String comment = "Transform for ObjectMetadata in initiateMultipartUpload() method is not supported. Please "
173+
+ "manually migrate your code by replacing ObjectMetadata with individual setter methods "
174+
+ "or metadata map in the request builder.";
175+
return newClass.withComments(createComments(comment));
176+
}
177+
178+
return newClass;
179+
}
180+
181+
private void maybeAddV2CannedAclImport(List<Expression> args, boolean isSetObjectAcl, boolean isSetBucketAcl) {
182+
for (Expression expr : args) {
183+
JavaType type = expr.getType();
184+
if (type == null || !type.isAssignableFrom(CANNED_ACL)) {
185+
continue;
186+
}
187+
removeV1S3ModelImport("CannedAccessControlList");
188+
if (isSetBucketAcl) {
189+
addV2S3ModelImport("BucketCannedACL");
190+
}
191+
if (isSetObjectAcl) {
192+
addV2S3ModelImport("ObjectCannedACL");
193+
}
194+
}
195+
}
196+
197+
private void removeV1S3ModelImport(String className) {
198+
doAfterVisit(new RemoveImport<>(V1_S3_MODEL_PKG + className, true));
199+
}
200+
201+
private void addV2S3ModelImport(String className) {
202+
doAfterVisit(new AddImport<>(V2_S3_MODEL_PKG + className, null, false));
203+
}
204+
}
205+
}

v2-migration/src/main/resources/META-INF/rewrite/aws-sdk-java-v1-to-v2-with-tm.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ tags:
2323
recipeList:
2424
- software.amazon.awssdk.v2migration.AddTransferManagerDependency
2525
- software.amazon.awssdk.v2migration.UpgradeSdkDependencies
26+
- software.amazon.awssdk.v2migration.S3AddImportsAndComments
2627
- software.amazon.awssdk.v2migration.S3StreamingResponseToV2
2728
- software.amazon.awssdk.v2migration.S3StreamingRequestToV2
2829
- software.amazon.awssdk.v2migration.S3NonStreamingRequestToV2

0 commit comments

Comments
 (0)