Skip to content

Commit dd1a687

Browse files
authored
Support code transformation for credentials related classes. (#5147)
* Support code transformation for credentials related classes. Add NewClassToStaticFactory reciipe to convert objects creating using constructor to static factory method Refactor NewClassToBuilderPattern to support client classes * Fix tests
1 parent 5e99856 commit dd1a687

File tree

20 files changed

+897
-120
lines changed

20 files changed

+897
-120
lines changed
Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
package software.amazon.awssdk.migration.internal.recipe;
1717

18+
import static software.amazon.awssdk.migration.internal.utils.SdkTypeUtils.isEligibleToConvertToBuilder;
19+
1820
import java.util.Collections;
1921
import org.openrewrite.ExecutionContext;
2022
import org.openrewrite.Recipe;
@@ -31,23 +33,24 @@
3133
import software.amazon.awssdk.annotations.SdkInternalApi;
3234
import software.amazon.awssdk.migration.internal.utils.NamingUtils;
3335
import software.amazon.awssdk.migration.internal.utils.SdkTypeUtils;
34-
import software.amazon.awssdk.migration.recipe.NewV1ModelClassToV2;
36+
import software.amazon.awssdk.migration.recipe.NewClassToBuilderPattern;
3537

3638
/**
37-
* Internal recipe that converts V1 model creation to the builder pattern.
39+
* Internal recipe that converts new class creation to the builder pattern.
3840
*
39-
* @see NewV1ModelClassToV2
41+
* @see NewClassToBuilderPattern
4042
*/
4143
@SdkInternalApi
42-
public class NewV1ClassToBuilder extends Recipe {
44+
public class NewClassToBuilder extends Recipe {
4345
@Override
4446
public String getDisplayName() {
4547
return "Transform 'new' expressions to builders";
4648
}
4749

4850
@Override
4951
public String getDescription() {
50-
return "Transforms 'new' expression for V1 model objects to the equivalent builder()..build() expression in V2.";
52+
return "Transforms 'new' expression for generated model, client objects and client config related objects to the "
53+
+ "equivalent builder()..build() expression in V2.";
5154
}
5255

5356
@Override
@@ -96,21 +99,25 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext execu
9699
public J visitNewClass(J.NewClass newClass, ExecutionContext executionContext) {
97100
newClass = super.visitNewClass(newClass, executionContext).cast();
98101

99-
JavaType classType = newClass.getType();
100-
if (!SdkTypeUtils.isV1ModelClass(classType)) {
102+
if (!(newClass.getType() instanceof JavaType.FullyQualified)) {
103+
return newClass;
104+
}
105+
106+
JavaType.FullyQualified classType = (JavaType.FullyQualified) newClass.getType();
107+
108+
if (!isEligibleToConvertToBuilder(classType)) {
101109
return newClass;
102110
}
103111

104-
JavaType.FullyQualified v2Type = SdkTypeUtils.asV2Type((JavaType.FullyQualified) classType);
105-
JavaType.FullyQualified v2TypeBuilder = SdkTypeUtils.v2ModelBuilder(v2Type);
112+
JavaType.FullyQualified builderType = SdkTypeUtils.v2Builder(classType);
106113

107114
J.Identifier modelId = new J.Identifier(
108115
Tree.randomId(),
109116
Space.EMPTY,
110117
Markers.EMPTY,
111118
Collections.emptyList(),
112-
v2Type.getClassName(),
113-
v2Type,
119+
classType.getClassName(),
120+
classType,
114121
null
115122
);
116123

@@ -127,9 +134,9 @@ public J visitNewClass(J.NewClass newClass, ExecutionContext executionContext) {
127134
JavaType.Method methodType = new JavaType.Method(
128135
null,
129136
0L,
130-
v2Type,
137+
classType,
131138
"builder",
132-
v2TypeBuilder,
139+
builderType,
133140
Collections.emptyList(),
134141
Collections.emptyList(),
135142
Collections.emptyList(),
@@ -145,7 +152,28 @@ public J visitNewClass(J.NewClass newClass, ExecutionContext executionContext) {
145152
builderMethod,
146153
JContainer.empty(),
147154
methodType
155+
);
156+
157+
J.Identifier buildName = new J.Identifier(
158+
Tree.randomId(),
159+
Space.EMPTY,
160+
Markers.EMPTY,
161+
Collections.emptyList(),
162+
"build",
163+
null,
164+
null
165+
);
148166

167+
JavaType.Method buildMethodType = new JavaType.Method(
168+
null,
169+
0L,
170+
builderType,
171+
"build",
172+
classType,
173+
Collections.emptyList(),
174+
Collections.emptyList(),
175+
Collections.emptyList(),
176+
Collections.emptyList()
149177
);
150178

151179
J.MethodInvocation buildInvoke = new J.MethodInvocation(
@@ -154,27 +182,9 @@ public J visitNewClass(J.NewClass newClass, ExecutionContext executionContext) {
154182
Markers.EMPTY,
155183
JRightPadded.build(builderInvoke),
156184
null,
157-
new J.Identifier(
158-
Tree.randomId(),
159-
Space.EMPTY,
160-
Markers.EMPTY,
161-
Collections.emptyList(),
162-
"build",
163-
v2Type,
164-
null
165-
),
185+
buildName,
166186
JContainer.empty(),
167-
new JavaType.Method(
168-
null,
169-
0L,
170-
v2TypeBuilder,
171-
"build",
172-
v2Type,
173-
Collections.emptyList(),
174-
Collections.emptyList(),
175-
Collections.emptyList(),
176-
Collections.emptyList()
177-
)
187+
buildMethodType
178188
);
179189

180190
return buildInvoke;

migration-tool/src/main/java/software/amazon/awssdk/migration/internal/recipe/V1SetterToV2.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
package software.amazon.awssdk.migration.internal.recipe;
1717

18+
import static software.amazon.awssdk.migration.internal.utils.SdkTypeUtils.isV2ClientClass;
19+
import static software.amazon.awssdk.migration.internal.utils.SdkTypeUtils.isV2ModelClass;
20+
1821
import org.openrewrite.ExecutionContext;
1922
import org.openrewrite.Recipe;
2023
import org.openrewrite.TreeVisitor;
@@ -25,13 +28,12 @@
2528
import org.openrewrite.java.tree.TypeUtils;
2629
import software.amazon.awssdk.annotations.SdkInternalApi;
2730
import software.amazon.awssdk.migration.internal.utils.NamingUtils;
28-
import software.amazon.awssdk.migration.internal.utils.SdkTypeUtils;
29-
import software.amazon.awssdk.migration.recipe.NewV1ModelClassToV2;
31+
import software.amazon.awssdk.migration.recipe.NewClassToBuilderPattern;
3032

3133
/**
3234
* Internal recipe that renames fluent V1 setters (withers), to V2 equivalents.
3335
*
34-
* @see NewV1ModelClassToV2
36+
* @see NewClassToBuilderPattern
3537
*/
3638
@SdkInternalApi
3739
public class V1SetterToV2 extends Recipe {
@@ -42,7 +44,7 @@ public String getDisplayName() {
4244

4345
@Override
4446
public String getDescription() {
45-
return "Transforms a setter on a V1 model object to the equivalent in V2.";
47+
return "Transforms V1 setter to fluent setter in V2.";
4648
}
4749

4850
@Override
@@ -67,7 +69,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu
6769
return method;
6870
}
6971

70-
if (SdkTypeUtils.isV2ModelBuilder(selectType) && !SdkTypeUtils.isV2ModelBuilder(method.getType())) {
72+
if (shouldChangeSetter(method, selectType)) {
7173
String methodName = method.getSimpleName();
7274

7375
if (NamingUtils.isWither(methodName)) {
@@ -97,5 +99,9 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu
9799

98100
return method;
99101
}
102+
103+
private static boolean shouldChangeSetter(J.MethodInvocation method, JavaType selectType) {
104+
return isV2ModelClass(selectType) || isV2ClientClass(selectType);
105+
}
100106
}
101107
}

migration-tool/src/main/java/software/amazon/awssdk/migration/internal/utils/SdkTypeUtils.java

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,63 @@
1515

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

18+
import java.util.Arrays;
19+
import java.util.HashSet;
20+
import java.util.Map;
21+
import java.util.Set;
1822
import java.util.regex.Pattern;
1923
import org.openrewrite.java.tree.JavaType;
2024
import org.openrewrite.java.tree.TypeUtils;
2125
import software.amazon.awssdk.annotations.SdkInternalApi;
22-
import software.amazon.awssdk.utils.StringUtils;
26+
import software.amazon.awssdk.utils.ImmutableMap;
2327

2428
/**
2529
* Type creation and checking utilities.
2630
*/
2731
@SdkInternalApi
2832
public final class SdkTypeUtils {
33+
/**
34+
* V2 core classes with a static factory method
35+
*/
36+
public static final Map<String, Integer> V2_CORE_CLASSES_WITH_STATIC_FACTORY =
37+
ImmutableMap.<String, Integer>builder()
38+
.put("software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider", 0)
39+
.put("software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider", 0)
40+
.put("software.amazon.awssdk.auth.credentials.AwsBasicCredentials", 2)
41+
.put("software.amazon.awssdk.auth.credentials.AwsSessionCredentials", 3)
42+
.put("software.amazon.awssdk.auth.credentials.StaticCredentialsProvider", 1)
43+
.build();
44+
2945
private static final Pattern V1_SERVICE_CLASS_PATTERN =
3046
Pattern.compile("com\\.amazonaws\\.services\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+");
3147
private static final Pattern V1_SERVICE_MODEL_CLASS_PATTERN =
3248
Pattern.compile("com\\.amazonaws\\.services\\.[a-zA-Z0-9]+\\.model\\.[a-zA-Z0-9]+");
49+
50+
private static final Pattern V1_SERVICE_CLIENT_CLASS_PATTERN =
51+
Pattern.compile("com\\.amazonaws\\.services\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+");
3352
private static final Pattern V2_MODEL_BUILDER_PATTERN =
3453
Pattern.compile("software\\.amazon\\.awssdk\\.services\\.[a-zA-Z0-9]+\\.model\\.[a-zA-Z0-9]+\\.Builder");
3554
private static final Pattern V2_MODEL_CLASS_PATTERN = Pattern.compile(
3655
"software\\.amazon\\.awssdk\\.services\\.[a-zA-Z0-9]+\\.model\\..[a-zA-Z0-9]+");
56+
private static final Pattern V2_CLIENT_CLASS_PATTERN = Pattern.compile(
57+
"software\\.amazon\\.awssdk\\.services\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+");
58+
59+
/**
60+
* V2 core classes with a builder
61+
*/
62+
private static final Set<String> V2_CORE_CLASSES_WITH_BUILDER =
63+
new HashSet<>(Arrays.asList("software.amazon.awssdk.core.client.ClientOverrideConfiguration",
64+
"software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider",
65+
"software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider",
66+
"software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider",
67+
"software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider",
68+
"software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider",
69+
"software.amazon.awssdk.services.sts.auth.StsGetSessionTokenCredentialsProvider",
70+
"software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithWebIdentityCredentialsProvider",
71+
"software.amazon.awssdk.auth.credentials.ProcessCredentialsProvider"));
72+
73+
private static final Pattern V2_CLIENT_BUILDER_PATTERN = Pattern.compile(
74+
"software\\.amazon\\.awssdk\\.services\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+Builder");
3775

3876
private SdkTypeUtils() {
3977
}
@@ -48,6 +86,12 @@ public static boolean isV1ModelClass(JavaType type) {
4886
&& type.isAssignableFrom(V1_SERVICE_MODEL_CLASS_PATTERN);
4987
}
5088

89+
public static boolean isV1ClientClass(JavaType type) {
90+
return type != null
91+
&& type instanceof JavaType.FullyQualified
92+
&& type.isAssignableFrom(V1_SERVICE_CLIENT_CLASS_PATTERN);
93+
}
94+
5195
public static boolean isV2ModelBuilder(JavaType type) {
5296
return type != null
5397
&& type.isAssignableFrom(V2_MODEL_BUILDER_PATTERN);
@@ -58,26 +102,39 @@ public static boolean isV2ModelClass(JavaType type) {
58102
&& type.isAssignableFrom(V2_MODEL_CLASS_PATTERN);
59103
}
60104

61-
public static JavaType.FullyQualified asV2Type(JavaType.FullyQualified type) {
62-
if (!isV1ModelClass(type)) {
63-
throw new IllegalArgumentException(String.format("%s is not a V1 SDK model type", type));
64-
}
65-
66-
String className = type.getClassName();
67-
String packageName = type.getPackageName();
105+
public static boolean isV2ClientClass(JavaType type) {
106+
return type != null
107+
&& type.isAssignableFrom(V2_CLIENT_CLASS_PATTERN);
108+
}
68109

69-
packageName = StringUtils.replaceOnce(packageName, "com.amazonaws", "software.amazon.awssdk");
110+
public static boolean isV2ClientBuilder(JavaType type) {
111+
return type != null
112+
&& type.isAssignableFrom(V2_CLIENT_BUILDER_PATTERN);
113+
}
70114

71-
return TypeUtils.asFullyQualified(JavaType.buildType(String.format("%s.%s", packageName, className)));
115+
public static boolean isEligibleToConvertToBuilder(JavaType.FullyQualified type) {
116+
return isV2ModelClass(type) || isV2ClientClass(type) || isV2CoreClassesWithBuilder(type.getFullyQualifiedName());
72117
}
73118

74-
public static JavaType.FullyQualified v2ModelBuilder(JavaType.FullyQualified type) {
75-
if (!isV2ModelClass(type)) {
76-
throw new IllegalArgumentException(String.format("%s is not a V2 model class", type));
77-
}
119+
public static boolean isEligibleToConvertToStaticFactory(JavaType.FullyQualified type) {
120+
return type != null && V2_CORE_CLASSES_WITH_STATIC_FACTORY.containsKey(type.getFullyQualifiedName());
121+
}
78122

79-
String fqcn = String.format("%s.%s", type.getFullyQualifiedName(), "Builder");
123+
private static boolean isV2CoreClassesWithBuilder(String fqcn) {
124+
return V2_CORE_CLASSES_WITH_BUILDER.contains(fqcn);
125+
}
80126

127+
public static JavaType.FullyQualified v2Builder(JavaType.FullyQualified type) {
128+
if (!isEligibleToConvertToBuilder(type)) {
129+
throw new IllegalArgumentException(String.format("%s cannot be converted to builder", type));
130+
}
131+
String fqcn;
132+
if (isV2ModelClass(type)) {
133+
fqcn = String.format("%s.%s", type.getFullyQualifiedName(), "Builder");
134+
} else {
135+
fqcn = String.format("%s%s", type.getFullyQualifiedName(), "Builder");
136+
}
137+
81138
return TypeUtils.asFullyQualified(JavaType.buildType(fqcn));
82139
}
83140
}

migration-tool/src/main/java/software/amazon/awssdk/migration/recipe/ChangeSdkType.java

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package software.amazon.awssdk.migration.recipe;
1717

1818
import static software.amazon.awssdk.migration.internal.utils.NamingConversionUtils.getV2Equivalent;
19+
import static software.amazon.awssdk.migration.internal.utils.SdkTypeUtils.isV1ClientClass;
20+
import static software.amazon.awssdk.migration.internal.utils.SdkTypeUtils.isV1ModelClass;
1921

2022
import java.util.HashMap;
2123
import java.util.HashSet;
@@ -93,8 +95,19 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct
9395

9496
@Override
9597
public J visitImport(J.Import anImport, ExecutionContext ctx) {
96-
String currentFqcn = currentFqcn(anImport);
97-
if (isV1Import(currentFqcn)) {
98+
JavaType.FullyQualified fullyQualified =
99+
Optional.ofNullable(anImport.getQualid())
100+
.map(J.FieldAccess::getType)
101+
.map(TypeUtils::asFullyQualified)
102+
.orElse(null);
103+
104+
if (fullyQualified == null) {
105+
return anImport;
106+
}
107+
108+
String currentFqcn = fullyQualified.getFullyQualifiedName();
109+
110+
if (isV1ModelClass(fullyQualified) || isV1ClientClass(fullyQualified)) {
98111
JavaType.ShallowClass originalType = JavaType.ShallowClass.build(currentFqcn);
99112
String v2Equivalent = getV2Equivalent(currentFqcn);
100113

@@ -464,19 +477,4 @@ private static JavaType.FullyQualified getTopLevelClassName(JavaType.FullyQualif
464477
classType = classType.getOwningClass();
465478
}
466479
}
467-
468-
private static boolean isV1Import(String currentFqcn) {
469-
if (currentFqcn != null) {
470-
return currentFqcn.startsWith(V1_PATTERN);
471-
}
472-
473-
return false;
474-
}
475-
476-
private static String currentFqcn(J.Import anImport) {
477-
JavaType.FullyQualified currentFqcn =
478-
TypeUtils.asFullyQualified(Optional.ofNullable(anImport.getQualid()).map(J.FieldAccess::getType).orElse(null));
479-
String curFqn = currentFqcn != null ? currentFqcn.getFullyQualifiedName() : null;
480-
return curFqn;
481-
}
482480
}

0 commit comments

Comments
 (0)