Skip to content

Commit 591e1cc

Browse files
committed
Migration Tool : S3 generatePresignedUrl
1 parent 4089dca commit 591e1cc

File tree

6 files changed

+205
-34
lines changed

6 files changed

+205
-34
lines changed

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@
1515

1616
package foo.bar;
1717

18+
import com.amazonaws.HttpMethod;
19+
import java.io.ByteArrayInputStream;
20+
import java.io.InputStream;
21+
import java.net.URL;
22+
import java.util.Date;
1823
import software.amazon.awssdk.core.async.AsyncRequestBody;
24+
import software.amazon.awssdk.services.s3.S3Client;
1925
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
2026
import software.amazon.awssdk.transfer.s3.S3TransferManager;
2127
import software.amazon.awssdk.transfer.s3.model.UploadRequest;
2228

23-
import java.io.ByteArrayInputStream;
24-
import java.io.InputStream;
25-
2629
public class S3Transforms {
2730

2831
void upload(S3TransferManager tm, String bucket, String key) {
@@ -31,4 +34,16 @@ void upload(S3TransferManager tm, String bucket, String key) {
3134
.build();
3235
/*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(requestWithInputStream).requestBody(AsyncRequestBody.fromInputStream(inputStream, -1L, newExecutorServiceVariableToDefine)).build());
3336
}
37+
38+
private void generatePresignedUrl(S3Client s3, String bucket, String key, Date expiration) {
39+
URL urlHead = /*AWS SDK for Java v2 migration: S3 generatePresignedUrl() with HEAD HTTP method is not supported in v2.*/s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.HEAD);
40+
41+
URL urlPatch = /*AWS SDK for Java v2 migration: S3 generatePresignedUrl() with PATCH HTTP method is not supported in v2.*/s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.PATCH);
42+
43+
URL urlPost = /*AWS SDK for Java v2 migration: S3 generatePresignedUrl() with POST HTTP method is not supported in v2.*/s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.POST);
44+
45+
46+
HttpMethod httpMethod = HttpMethod.PUT;
47+
URL urlWithHttpMethodVariable = /*AWS SDK for Java v2 migration: Transform for S3 generatePresignedUrl() with an assigned variable for HttpMethod is not supported. Update your code to pass in HttpMethod literal enum, or manually migrate your code.*/s3.generatePresignedUrl(bucket, key, expiration, httpMethod);
48+
}
3449
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515

1616
package foo.bar;
1717

18+
import com.amazonaws.HttpMethod;
19+
import com.amazonaws.services.s3.AmazonS3;
1820
import com.amazonaws.services.s3.model.PutObjectRequest;
1921
import com.amazonaws.services.s3.transfer.TransferManager;
2022
import java.io.ByteArrayInputStream;
2123
import java.io.InputStream;
24+
import java.net.URL;
25+
import java.util.Date;
2226

2327
public class S3Transforms {
2428

@@ -28,4 +32,16 @@ void upload(TransferManager tm, String bucket, String key) {
2832
requestWithInputStream.setInputStream(inputStream);
2933
tm.upload(requestWithInputStream);
3034
}
35+
36+
private void generatePresignedUrl(AmazonS3 s3, String bucket, String key, Date expiration) {
37+
URL urlHead = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.HEAD);
38+
39+
URL urlPatch = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.PATCH);
40+
41+
URL urlPost = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.POST);
42+
43+
44+
HttpMethod httpMethod = HttpMethod.PUT;
45+
URL urlWithHttpMethodVariable = s3.generatePresignedUrl(bucket, key, expiration, httpMethod);
46+
}
3147
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
package foo.bar;
1717

1818
import java.net.URI;
19+
import java.net.URL;
20+
import java.time.Duration;
21+
import java.time.Instant;
1922
import java.util.ArrayList;
23+
import java.util.Date;
2024
import java.util.List;
2125
import software.amazon.awssdk.regions.Region;
2226
import software.amazon.awssdk.services.s3.S3Client;
@@ -110,6 +114,7 @@
110114
import software.amazon.awssdk.services.s3.model.UploadPartCopyRequest;
111115
import software.amazon.awssdk.services.s3.model.UploadPartCopyResponse;
112116
import software.amazon.awssdk.services.s3.model.WebsiteConfiguration;
117+
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
113118

114119
public class S3 {
115120

@@ -402,4 +407,14 @@ private void s3Uri(URI uri, String uriAsString) {
402407

403408
S3Uri s3UriFromStringWithUrlEncodeFalse = S3Utilities.builder().build().parseUri(URI.create(uriAsString));
404409
}
410+
411+
private void generatePresignedUrl(S3Client s3, String bucket, String key, Date expiration) {
412+
URL urlGet1 = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/S3Presigner.builder().s3Client(s3).build().presignGetObject(p -> p.getObjectRequest(r -> r.bucket(bucket).key(key)).signatureDuration(Duration.between(Instant.now(), expiration.toInstant()))).url();
413+
414+
URL urlPut = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/S3Presigner.builder().s3Client(s3).build().presignPutObject(p -> p.putObjectRequest(r -> r.bucket(bucket).key(key)).signatureDuration(Duration.between(Instant.now(), expiration.toInstant()))).url();
415+
416+
URL urlGet2 = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/S3Presigner.builder().s3Client(s3).build().presignGetObject(p -> p.getObjectRequest(r -> r.bucket(bucket).key(key)).signatureDuration(Duration.between(Instant.now(), expiration.toInstant()))).url();
417+
418+
URL urlDelete = /*AWS SDK for Java v2 migration: If generating multiple pre-signed URLs, it is recommended to create a single instance of S3Presigner, since creating a presigner can be expensive. If applicable, please manually refactor the transformed code.*/S3Presigner.builder().s3Client(s3).build().presignDeleteObject(p -> p.deleteObjectRequest(r -> r.bucket(bucket).key(key)).signatureDuration(Duration.between(Instant.now(), expiration.toInstant()))).url();
419+
}
405420
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
package foo.bar;
1717

18+
import com.amazonaws.HttpMethod;
1819
import com.amazonaws.services.s3.AmazonS3;
1920
import com.amazonaws.services.s3.AmazonS3URI;
2021
import com.amazonaws.services.s3.model.AbortMultipartUploadRequest;
@@ -59,7 +60,9 @@
5960
import com.amazonaws.services.s3.model.metrics.MetricsConfiguration;
6061
import com.amazonaws.services.s3.model.ownership.OwnershipControls;
6162
import java.net.URI;
63+
import java.net.URL;
6264
import java.util.ArrayList;
65+
import java.util.Date;
6366
import java.util.List;
6467

6568
public class S3 {
@@ -259,4 +262,14 @@ private void s3Uri(URI uri, String uriAsString) {
259262

260263
AmazonS3URI s3UriFromStringWithUrlEncodeFalse = new AmazonS3URI(uriAsString, false);
261264
}
265+
266+
private void generatePresignedUrl(AmazonS3 s3, String bucket, String key, Date expiration) {
267+
URL urlGet1 = s3.generatePresignedUrl(bucket, key, expiration);
268+
269+
URL urlPut = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.PUT);
270+
271+
URL urlGet2 = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.GET);
272+
273+
URL urlDelete = s3.generatePresignedUrl(bucket, key, expiration, HttpMethod.DELETE);
274+
}
262275
}

v2-migration/src/main/java/software/amazon/awssdk/v2migration/S3NonStreamingRequestToV2Complex.java

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,28 @@
1616
package software.amazon.awssdk.v2migration;
1717

1818
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.V2_S3_MODEL_PKG;
19+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.V2_S3_PKG;
20+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.assignedVariableHttpMethodNotSupportedComment;
21+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.httpMethodNotSupportedComment;
22+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isCompleteMpuRequestMultipartUploadSetter;
23+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isGeneratePresignedUrl;
24+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.isUnsupportedHttpMethod;
25+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.presignerSingleInstanceSuggestion;
26+
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.requestPojoTransformNotSupportedComment;
1927
import static software.amazon.awssdk.v2migration.internal.utils.S3TransformUtils.v2S3MethodMatcher;
20-
import static software.amazon.awssdk.v2migration.internal.utils.SdkTypeUtils.fullyQualified;
2128

29+
import java.util.List;
30+
import java.util.Locale;
2231
import org.openrewrite.ExecutionContext;
2332
import org.openrewrite.Recipe;
2433
import org.openrewrite.TreeVisitor;
2534
import org.openrewrite.java.AddImport;
2635
import org.openrewrite.java.JavaIsoVisitor;
2736
import org.openrewrite.java.JavaTemplate;
2837
import org.openrewrite.java.MethodMatcher;
38+
import org.openrewrite.java.RemoveImport;
39+
import org.openrewrite.java.tree.Expression;
2940
import org.openrewrite.java.tree.J;
30-
import org.openrewrite.java.tree.JavaType;
31-
import org.openrewrite.java.tree.TypeUtils;
3241
import software.amazon.awssdk.annotations.SdkInternalApi;
3342

3443
/**
@@ -74,58 +83,99 @@ private static final class Visitor extends JavaIsoVisitor<ExecutionContext> {
7483
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) {
7584

7685
if (isCompleteMpuRequestMultipartUploadSetter(method)) {
77-
method = transformCompleteMpuRequestCompletedPartsArg(method);
78-
return super.visitMethodInvocation(method, executionContext);
86+
return transformCompleteMpuRequestCompletedPartsArg(method);
87+
}
88+
if (isGeneratePresignedUrl(method)) {
89+
return transformGeneratePresignedUrl(method);
7990
}
80-
8191
if (DISABLE_REQUESTER_PAYS.matches(method, false)) {
82-
method = transformSetRequesterPays(method, false);
83-
return super.visitMethodInvocation(method, executionContext);
92+
return transformSetRequesterPays(method, false);
8493
}
8594
if (ENABLE_REQUESTER_PAYS.matches(method, false)) {
86-
method = transformSetRequesterPays(method, true);
87-
return super.visitMethodInvocation(method, executionContext);
95+
return transformSetRequesterPays(method, true);
8896
}
8997
if (IS_REQUESTER_PAYS_ENABLED.matches(method, false)) {
90-
method = transformIsRequesterPays(method);
91-
return super.visitMethodInvocation(method, executionContext);
98+
return transformIsRequesterPays(method);
9299
}
93100
if (GET_OBJECT_AS_STRING.matches(method, false)) {
94-
method = transformGetObjectAsString(method);
95-
return super.visitMethodInvocation(method, executionContext);
101+
return transformGetObjectAsString(method);
96102
}
97103
if (GET_URL.matches(method, false)) {
98-
method = transformGetUrl(method);
99-
return super.visitMethodInvocation(method, executionContext);
104+
return transformGetUrl(method);
100105
}
101106
if (LIST_BUCKETS.matches(method, false)) {
102-
method = transformListBuckets(method);
103-
return super.visitMethodInvocation(method, executionContext);
107+
return transformListBuckets(method);
104108
}
105109
if (RESTORE_OBJECT.matches(method, false)) {
106-
method = transformRestoreObject(method);
107-
return super.visitMethodInvocation(method, executionContext);
110+
return transformRestoreObject(method);
108111
}
109112
if (SET_OBJECT_REDIRECT_LOCATION.matches(method, false)) {
110-
method = transformSetObjectRedirectLocation(method);
111-
return super.visitMethodInvocation(method, executionContext);
113+
return transformSetObjectRedirectLocation(method);
112114
}
113115
if (CHANGE_OBJECT_STORAGE_CLASS.matches(method, false)) {
114-
method = transformChangeObjectStorageClass(method);
115-
return super.visitMethodInvocation(method, executionContext);
116+
return transformChangeObjectStorageClass(method);
116117
}
117118
if (CREATE_BUCKET.matches(method, false)) {
118-
method = transformCreateBucket(method);
119-
return super.visitMethodInvocation(method, executionContext);
119+
return transformCreateBucket(method);
120120
}
121121
return super.visitMethodInvocation(method, executionContext);
122122
}
123123

124-
private boolean isCompleteMpuRequestMultipartUploadSetter(J.MethodInvocation method) {
125-
JavaType.FullyQualified completeMpuRequest =
126-
fullyQualified("software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest.Builder");
127-
return "multipartUpload".equals(method.getSimpleName()) &&
128-
TypeUtils.isAssignableTo(completeMpuRequest, method.getSelect().getType());
124+
private J.MethodInvocation transformGeneratePresignedUrl(J.MethodInvocation method) {
125+
List<Expression> args = method.getArguments();
126+
if (args.size() == 1) {
127+
return method.withComments(requestPojoTransformNotSupportedComment());
128+
}
129+
130+
String httpMethod = determineHttpMethod(args);
131+
132+
if (isUnsupportedHttpMethod(httpMethod)) {
133+
return method.withComments(httpMethodNotSupportedComment(httpMethod));
134+
}
135+
if (httpMethod == null) {
136+
return method.withComments(assignedVariableHttpMethodNotSupportedComment());
137+
}
138+
139+
String v2Method = String.format("S3Presigner.builder().s3Client(#{any()}).build().presign%sObject"
140+
+ "(p -> p.%sObjectRequest(r -> r.bucket(#{any()}).key(#{any()}))"
141+
+ ".signatureDuration(Duration.between(Instant.now(), #{any()}.toInstant()))).url()",
142+
httpMethod, httpMethod.toLowerCase(Locale.ROOT));
143+
144+
removeV1HttpMethodImport();
145+
addInstantImport();
146+
addDurationImport();
147+
addS3PresignerImport();
148+
149+
return JavaTemplate.builder(v2Method).build()
150+
.apply(getCursor(), method.getCoordinates().replace(), method.getSelect(),
151+
args.get(0), args.get(1), args.get(2))
152+
.withComments(presignerSingleInstanceSuggestion());
153+
}
154+
155+
private String determineHttpMethod(List<Expression> args) {
156+
if (args.size() == 3) {
157+
return "Get";
158+
}
159+
Expression argVal = args.get(3);
160+
String httpMethod = argVal.printTrimmed(getCursor());
161+
162+
switch (httpMethod) {
163+
case "HttpMethod.GET":
164+
return "Get";
165+
case "HttpMethod.PUT":
166+
return "Put";
167+
case "HttpMethod.DELETE":
168+
return "Delete";
169+
case "HttpMethod.HEAD":
170+
return "Head";
171+
case "HttpMethod.POST":
172+
return "Post";
173+
case "HttpMethod.PATCH":
174+
return "Patch";
175+
default:
176+
// enum value assigned to variable
177+
return null;
178+
}
129179
}
130180

131181
private J.MethodInvocation transformCompleteMpuRequestCompletedPartsArg(J.MethodInvocation method) {
@@ -251,6 +301,25 @@ private J.MethodInvocation transformSetRequesterPays(J.MethodInvocation method,
251301
return method;
252302
}
253303

304+
private void removeV1HttpMethodImport() {
305+
doAfterVisit(new RemoveImport<>("com.amazonaws.HttpMethod", true));
306+
}
307+
308+
private void addInstantImport() {
309+
String fqcn = "java.time.Instant";
310+
doAfterVisit(new AddImport<>(fqcn, null, false));
311+
}
312+
313+
private void addDurationImport() {
314+
String fqcn = "java.time.Duration";
315+
doAfterVisit(new AddImport<>(fqcn, null, false));
316+
}
317+
318+
private void addS3PresignerImport() {
319+
String fqcn = V2_S3_PKG + "presigner.S3Presigner";
320+
doAfterVisit(new AddImport<>(fqcn, null, false));
321+
}
322+
254323
private void addImport(String pojoName) {
255324
String fqcn = V2_S3_MODEL_PKG + pojoName;
256325
doAfterVisit(new AddImport<>(fqcn, null, false));

v2-migration/src/main/java/software/amazon/awssdk/v2migration/internal/utils/S3TransformUtils.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@
1515

1616
package software.amazon.awssdk.v2migration.internal.utils;
1717

18+
import java.util.Arrays;
1819
import java.util.Collections;
1920
import java.util.List;
21+
import java.util.Locale;
2022
import org.openrewrite.java.MethodMatcher;
2123
import org.openrewrite.java.tree.Comment;
24+
import org.openrewrite.java.tree.J;
2225
import org.openrewrite.java.tree.TextComment;
26+
import org.openrewrite.java.tree.TypeUtils;
2327
import org.openrewrite.marker.Markers;
2428
import software.amazon.awssdk.annotations.SdkInternalApi;
2529

@@ -57,4 +61,43 @@ public static List<Comment> createComments(String comment) {
5761
return Collections.singletonList(
5862
new TextComment(true, "AWS SDK for Java v2 migration: " + comment, "", Markers.EMPTY));
5963
}
64+
65+
public static boolean isCompleteMpuRequestMultipartUploadSetter(J.MethodInvocation method) {
66+
return "multipartUpload".equals(method.getSimpleName())
67+
&& TypeUtils.isAssignableTo(V2_S3_MODEL_PKG + "CompleteMultipartUploadRequest.Builder",
68+
method.getSelect().getType());
69+
}
70+
71+
public static boolean isGeneratePresignedUrl(J.MethodInvocation method) {
72+
return "generatePresignedUrl".equals(method.getSimpleName())
73+
&& TypeUtils.isAssignableTo(V2_S3_CLIENT, method.getSelect().getType());
74+
}
75+
76+
public static boolean isUnsupportedHttpMethod(String httpMethod) {
77+
return Arrays.asList("Head", "Post", "Patch").contains(httpMethod);
78+
}
79+
80+
public static List<Comment> assignedVariableHttpMethodNotSupportedComment() {
81+
String comment = "Transform for S3 generatePresignedUrl() with an assigned variable for HttpMethod is not supported."
82+
+ " Update your code to pass in HttpMethod literal enum, or manually migrate your code.";
83+
return createComments(comment);
84+
}
85+
86+
public static List<Comment> requestPojoTransformNotSupportedComment() {
87+
String comment = "Transforms are not supported for GeneratePresignedUrlRequest, please manually migrate your code.";
88+
return createComments(comment);
89+
}
90+
91+
public static List<Comment> httpMethodNotSupportedComment(String httpMethod) {
92+
String comment = String.format("S3 generatePresignedUrl() with %s HTTP method is not supported in v2.",
93+
httpMethod.toUpperCase(Locale.ROOT));
94+
return createComments(comment);
95+
}
96+
97+
public static List<Comment> presignerSingleInstanceSuggestion() {
98+
String comment = "If generating multiple pre-signed URLs, it is recommended to create a single instance of "
99+
+ "S3Presigner, since creating a presigner can be expensive. If applicable, please manually "
100+
+ "refactor the transformed code.";
101+
return createComments(comment);
102+
}
60103
}

0 commit comments

Comments
 (0)