Skip to content

Added recipe for adding comments for unsupported transforms #6033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package foo.bar;

import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.model.MultiFactorAuthentication;
import com.amazonaws.services.s3.model.SSEAwsKeyManagementParams;
import com.amazonaws.services.s3.model.SSECustomerKey;
import java.io.ByteArrayInputStream;
Expand All @@ -26,22 +25,38 @@
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.AccessControlPolicy;
import software.amazon.awssdk.services.s3.model.BucketCannedACL;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.GeneratePresignedUrlRequest;
import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
import software.amazon.awssdk.services.s3.model.ObjectCannedACL;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.UploadRequest;

public class S3Transforms {

S3Client s3;
String bucket;
String key;

void s3Pojos() {
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");

DeleteObjectRequest deleteVersionRequest =
DeleteObjectRequest.builder().bucket("bucket").key("key").versionId("id").mfa(new MultiFactorAuthentication("serialNum", "token"))
DeleteObjectRequest.builder().bucket("bucket").key("key").versionId("id").mfa(mfa)
.build();
}

void upload_streamWithLiteralLength(S3TransferManager tm, String bucket, String key) {
void setBucketAcl() {
/*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);
}

void setObjectAcl() {
/*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);
}

void upload_streamWithLiteralLength(S3TransferManager tm) {
HeadObjectResponse metadata = HeadObjectResponse.builder()
.build();
InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
Expand All @@ -50,7 +65,7 @@ void upload_streamWithLiteralLength(S3TransferManager tm, String bucket, String
/*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());
}

void upload_streamWithAssignedLength(S3TransferManager tm, String bucket, String key) {
void upload_streamWithAssignedLength(S3TransferManager tm) {
HeadObjectResponse metadata = HeadObjectResponse.builder()
.build();
long contentLen = 777;
Expand All @@ -60,7 +75,7 @@ void upload_streamWithAssignedLength(S3TransferManager tm, String bucket, String
/*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());
}

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

private void generatePresignedUrl(S3Client s3, String bucket, String key, Date expiration) {
private void generatePresignedUrl(Date expiration) {
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);

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.AccessControlList;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.DeleteVersionRequest;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.amazonaws.services.s3.model.MultiFactorAuthentication;
Expand All @@ -33,12 +34,26 @@

public class S3Transforms {

AmazonS3 s3;
String bucket;
String key;

void s3Pojos() {
MultiFactorAuthentication mfa = new MultiFactorAuthentication("serialNum", "token");

DeleteVersionRequest deleteVersionRequest =
new DeleteVersionRequest("bucket", "key", "id", new MultiFactorAuthentication("serialNum", "token"));
new DeleteVersionRequest("bucket", "key", "id", mfa);
}

void setBucketAcl() {
s3.setBucketAcl(bucket, CannedAccessControlList.AuthenticatedRead);
}

void setObjectAcl() {
s3.setObjectAcl(bucket, key, CannedAccessControlList.PublicRead);
}

void upload_streamWithLiteralLength(TransferManager tm, String bucket, String key) {
void upload_streamWithLiteralLength(TransferManager tm) {
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(333);
InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
Expand All @@ -47,7 +62,7 @@ void upload_streamWithLiteralLength(TransferManager tm, String bucket, String ke
tm.upload(requestWithStreamAndLiteralLength);
}

void upload_streamWithAssignedLength(TransferManager tm, String bucket, String key) {
void upload_streamWithAssignedLength(TransferManager tm) {
ObjectMetadata metadata = new ObjectMetadata();
long contentLen = 777;
metadata.setContentLength(contentLen);
Expand All @@ -57,7 +72,7 @@ void upload_streamWithAssignedLength(TransferManager tm, String bucket, String k
tm.upload(requestWithStreamAndAssignedLength);
}

void upload_streamWithoutLength(TransferManager tm, String bucket, String key) {
void upload_streamWithoutLength(TransferManager tm) {
InputStream inputStream = new ByteArrayInputStream(("HelloWorld").getBytes());
PutObjectRequest requestWithStreamAndNoLength = new PutObjectRequest(bucket, key, "location");
requestWithStreamAndNoLength.setInputStream(inputStream);
Expand Down Expand Up @@ -90,7 +105,7 @@ void objectmetadata_unsupportedSetters(Date dateVal) {
metadata.addUserMetadata("a", "b");
}

private void generatePresignedUrl(AmazonS3 s3, String bucket, String key, Date expiration) {
private void generatePresignedUrl(Date expiration) {
URL urlHead = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.HEAD);

URL urlPatch = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.PATCH);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* 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.S3TransformUtils.V1_S3_MODEL_PKG;
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.V2_S3_MODEL_PKG;
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.createComments;
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.v1S3MethodMatcher;

import java.util.List;
import java.util.regex.Pattern;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.AddImport;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.RemoveImport;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import software.amazon.awssdk.annotations.SdkInternalApi;

@SdkInternalApi
public class S3AddImportsAndComments extends Recipe {

private static final MethodMatcher CREATE_BUCKET = v1S3MethodMatcher("createBucket(String, "
+ V1_S3_MODEL_PKG + "Region");
private static final MethodMatcher LIST_NEXT_BATCH_OBJECTS = v1S3MethodMatcher("listNextBatchOfObjects(..)");
private static final MethodMatcher LIST_NEXT_BATCH_VERSIONS = v1S3MethodMatcher("listNextBatchOfVersions(..)");
private static final MethodMatcher GET_METADATA = v1S3MethodMatcher("getCachedResponseMetadata(..)");
private static final MethodMatcher SET_BUCKET_ACL = v1S3MethodMatcher("setBucketAcl(..)");
private static final MethodMatcher SET_ENDPOINT = v1S3MethodMatcher("setEndpoint(..)");
private static final MethodMatcher SET_OBJECT_ACL = v1S3MethodMatcher("setObjectAcl(..)");
private static final MethodMatcher SET_REGION = v1S3MethodMatcher("setRegion(..)");
private static final MethodMatcher SET_S3CLIENT_OPTIONS = v1S3MethodMatcher("setS3ClientOptions(..)");
private static final MethodMatcher SELECT_OBJECT_CONTENT = v1S3MethodMatcher("selectObjectContent(..)");

private static final Pattern CANNED_ACL = Pattern.compile(V1_S3_MODEL_PKG + "CannedAccessControlList");
private static final Pattern GET_OBJECT_REQUEST = Pattern.compile(V1_S3_MODEL_PKG + "GetObjectRequest");
private static final Pattern INITIATE_MPU = Pattern.compile(V1_S3_MODEL_PKG + "InitiateMultipartUpload");
private static final Pattern MULTI_FACTOR_AUTH = Pattern.compile(V1_S3_MODEL_PKG + "MultiFactorAuthentication");

@Override
public String getDisplayName() {
return "Add imports and comments to unsupported S3 transforms.";
}

@Override
public String getDescription() {
return "Add imports and comments to unsupported S3 transforms.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new Visitor();
}

private static class Visitor extends JavaIsoVisitor<ExecutionContext> {

@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
boolean isSetObjectAcl = SET_OBJECT_ACL.matches(method);
boolean isSetBucketAcl = SET_BUCKET_ACL.matches(method);

if (isSetObjectAcl || isSetBucketAcl) {
removeV1S3ModelImport("CannedAccessControlList");
maybeAddV2CannedAclImport(method.getArguments(), isSetObjectAcl, isSetBucketAcl);

// TODO: add the developer guide link in the comments once the doc is published.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be confusing since the developer guide is not published yet?

String comment = "Transform for AccessControlList and CannedAccessControlList not supported. "
+ "In v2, CannedAccessControlList is replaced by BucketCannedACL for buckets and "
+ "ObjectCannedACL for objects.";
return method.withComments(createComments(comment));
}
if (LIST_NEXT_BATCH_OBJECTS.matches(method)) {
// TODO: add the developer guide link in the comments once the doc is published.
String comment = "Transform for listNextBatchOfObjects method not supported. "
+ "listNextBatchOfObjects() only exists in SDK v1, for SDK v2 use either "
+ "listObjectsV2Paginator().stream() for automatic pagination"
+ " or manually handle pagination with listObjectsV2() and nextToken in the response for more "
+ "control";
return method.withComments(createComments(comment));
}
if (LIST_NEXT_BATCH_VERSIONS.matches(method)) {
// TODO: add the developer guide link in the comments once the doc is published.
String comment = "Transform for listNextBatchOfVersions method not supported."
+ "listNextBatchOfVersions() only exists in SDK v1, for SDK v2 use either "
+ "listObjectVersionsPaginator().stream for automatic pagination"
+ " or manually handle pagination with listObjectVersions() and VersionIdMarker/KeyMarker. ";
return method.withComments(createComments(comment));
}
if (SET_REGION.matches(method)) {
String comment = "Transform for setRegion method not supported. Please manually "
+ "migrate your code by configuring the region in the s3 client builder";
return method.withComments(createComments(comment));
}
if (SET_S3CLIENT_OPTIONS.matches(method)) {
String comment = "Transform for setS3ClientOptions method not supported. Please manually "
+ "migrate setS3ClientOptions by configuring the equivalent settings in "
+ "S3Configuration.builder() when building your S3Client.";
return method.withComments(createComments(comment));
}
if (SELECT_OBJECT_CONTENT.matches(method)) {
String comment = "Note: selectObjectContent is only supported in AWS SDK v2 with S3AsyncClient. "
+ "Please manually migrate to event-based response handling using "
+ "SelectObjectContentEventStream";
return method.withComments(createComments(comment));
}

if (CREATE_BUCKET.matches(method)) {
String comment = "Transform for createBucket(String bucketName, Region region) method not supported. Please "
+ "manually migrate your code by using the following pattern: "
+ "createBucket(builder -> builder.bucket(bucketName)"
+ ".createBucketConfiguration(cfg -> cfg.locationConstraint(region)))";
return method.withComments(createComments(comment));
}

if (SET_ENDPOINT.matches(method)) {
String comment = "Transform for setEndpoint method not supported. setEndpoint() method is removed in SDK v2. "
+ "Please manually migrate your code by using endpointOverride(URI.create(endpoint)) in "
+ "S3ClientBuilder";
return method.withComments(createComments(comment));
}

if (GET_METADATA.matches(method)) {
String comment = "Transform for getCachedResponseMetadata method not "
+ "supported. getCachedResponseMetadata() is removed in SDK v2. Please manually migrate your "
+ "code by accessing metadata directly from specific response objects instead of cached "
+ "metadata";
return method.withComments(createComments(comment));
}

return method;
}

@Override
public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
JavaType type = newClass.getType();
if (!(type instanceof JavaType.FullyQualified)) {
return newClass;
}

if (type.isAssignableFrom(MULTI_FACTOR_AUTH)) {
removeV1S3ModelImport("MultiFactorAuthentication");
String comment = "v2 does not have a MultiFactorAuthentication POJO. Please manually set the String value on "
+ "the request POJO.";
return newClass.withComments(createComments(comment));
}

if (type.isAssignableFrom(GET_OBJECT_REQUEST) && newClass.getArguments().size() == 1) {
removeV1S3ModelImport("S3ObjectId");
String comment = "v2 does not have S3ObjectId class. Please manually migrate the code by setting the configs "
+ "directly into the request builder pattern.";
return newClass.withComments(createComments(comment));
}

if (type.isAssignableFrom(INITIATE_MPU) && newClass.getArguments().size() == 3) {
String comment = "Transform for ObjectMetadata in initiateMultipartUpload() method is not supported. Please "
+ "manually migrate your code by replacing ObjectMetadata with individual setter methods "
+ "or metadata map in the request builder.";
return newClass.withComments(createComments(comment));
}

return newClass;
}

private void maybeAddV2CannedAclImport(List<Expression> args, boolean isSetObjectAcl, boolean isSetBucketAcl) {
for (Expression expr : args) {
JavaType type = expr.getType();
if (type == null || !type.isAssignableFrom(CANNED_ACL)) {
continue;
}
removeV1S3ModelImport("CannedAccessControlList");
if (isSetBucketAcl) {
addV2S3ModelImport("BucketCannedACL");
}
if (isSetObjectAcl) {
addV2S3ModelImport("ObjectCannedACL");
}
}
}

private void removeV1S3ModelImport(String className) {
doAfterVisit(new RemoveImport<>(V1_S3_MODEL_PKG + className, true));
}

private void addV2S3ModelImport(String className) {
doAfterVisit(new AddImport<>(V2_S3_MODEL_PKG + className, null, false));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ tags:
recipeList:
- software.amazon.awssdk.v2migration.AddTransferManagerDependency
- software.amazon.awssdk.v2migration.UpgradeSdkDependencies
- software.amazon.awssdk.v2migration.S3AddImportsAndComments
- software.amazon.awssdk.v2migration.S3StreamingResponseToV2
- software.amazon.awssdk.v2migration.S3StreamingRequestToV2
- software.amazon.awssdk.v2migration.S3NonStreamingRequestToV2
Expand Down
Loading
Loading