diff --git a/eng/versioning/external_dependencies.txt b/eng/versioning/external_dependencies.txt index a8fe028ca1af7..398a653808ddc 100644 --- a/eng/versioning/external_dependencies.txt +++ b/eng/versioning/external_dependencies.txt @@ -16,6 +16,7 @@ com.fasterxml.jackson.core:jackson-core;2.17.2 com.fasterxml.jackson.core:jackson-databind;2.17.2 com.fasterxml.jackson.dataformat:jackson-dataformat-avro;2.17.2 com.fasterxml.jackson.dataformat:jackson-dataformat-xml;2.17.2 +com.fasterxml.jackson.dataformat:jackson-dataformat-smile;2.17.2 com.fasterxml.jackson.datatype:jackson-datatype-jsr310;2.17.2 com.fasterxml.jackson.module:jackson-module-afterburner;2.17.2 com.fasterxml.jackson.module:jackson-module-blackbird;2.17.2 @@ -98,6 +99,7 @@ io.lettuce:lettuce-core;6.4.0.RELEASE org.redisson:redisson;3.36.0 testdep_net.bytebuddy:byte-buddy;1.15.5 testdep_net.bytebuddy:byte-buddy-agent;1.15.5 +org.openrewrite.recipe:rewrite-recipe-bom;3.0.2 ## Spring boot dependency versions org.springframework.boot:spring-boot-maven-plugin;2.7.18 diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index c1184dbef8b25..6b4449f29e556 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -170,6 +170,7 @@ com.azure:azure-monitor-ingestion;1.2.7;1.3.0-beta.1 com.azure:azure-monitor-ingestion-perf;1.0.0-beta.1;1.0.0-beta.1 com.azure:azure-monitor-query;1.5.4;1.6.0-beta.1 com.azure:azure-monitor-query-perf;1.0.0-beta.1;1.0.0-beta.1 +com.azure:azure-openrewrite;1.0.0-beta.1;1.0.0-beta.1 com.azure:azure-perf-test-parent;1.0.0-beta.1;1.0.0-beta.1 com.azure:azure-quantum-jobs;1.0.0-beta.1;1.0.0-beta.2 com.azure:azure-search-documents;11.7.4;11.8.0-beta.5 @@ -485,6 +486,7 @@ io.clientcore:http-okhttp3;1.0.0-beta.1;1.0.0-beta.1 io.clientcore:http-stress;1.0.0-beta.1;1.0.0-beta.1 io.clientcore:optional-dependency-tests;1.0.0-beta.1;1.0.0-beta.1 + # Unreleased dependencies: Copy the entry from above, prepend "unreleased_" and remove the current # version. Unreleased dependencies are only valid for dependency versions. # Format; diff --git a/sdk/tools/azure-openrewrite/CHANGELOG.md b/sdk/tools/azure-openrewrite/CHANGELOG.md new file mode 100644 index 0000000000000..8b33f0fedccc1 --- /dev/null +++ b/sdk/tools/azure-openrewrite/CHANGELOG.md @@ -0,0 +1,11 @@ +# Release History + +## 1.0.0-beta.1 (Unreleased) + +### Features Added + +### Breaking Changes + +### Bugs Fixed + +### Other Changes \ No newline at end of file diff --git a/sdk/tools/azure-openrewrite/MIGRATION.md b/sdk/tools/azure-openrewrite/MIGRATION.md new file mode 100644 index 0000000000000..24410baff7dac --- /dev/null +++ b/sdk/tools/azure-openrewrite/MIGRATION.md @@ -0,0 +1,89 @@ +# Azure Code Migration with OpenRewrite + +This library contains integrations for OpenRewrite with Maven for code migrating Azure SDK libraries +such as `azure-core` to `azure-core-v2`. + +## Setup + +### Prerequisites +The following tools are required to build and execute this project: +- Java (version 8 or higher) +- Maven + +### Recipe Configuration + +The migration recipe is defined in the `azure-openrewrite` module as detailed below: + +```yaml +### Recipe Configuration for OpenRewrite +type: specs.openrewrite.org/v1beta/recipe +name: com.azure.openrewrite.migrateToVNext +displayName: Migrate from azure-core to azure-core-v2 +description: This recipe migrates the azure sdk libraries from azure-core to azure-core-v2 and clientcore libraries. +recipeList: + ... +``` +You can find the full recipe configuration in the `rewrite.yml` file [here](https://github.com/Azure/azsdk-java-rewrite-recipes/blob/main/rewrite-java-core/src/main/resources/META-INF/rewrite/rewrite.yml). + + +## Usage +### Maven Plugin Configuration +The OpenRewrite Maven plugin is configured in the `rewrite-java-core` module to run the migration recipe on the sample project +as follows: +```xml + + org.openrewrite.maven + rewrite-maven-plugin + 5.7.1 + + + com.azure.openrewrite.migrateToVNext + + + + + com.azure + azure-openrewrite + 1.0.0 + + + +``` +The plugin configuration is defined in the `pom.xml` file [here](https://github.com/Azure/azsdk-java-rewrite-recipes/blob/main/rewrite-sample/pom.xml). + +## Execution +The `rewrite-sample` module is configured to use the `openrewrite-maven-plugin` to run the OpenRewrite recipe on the sample project. +The `rewrite-sample` module contains the modules `azure-ai-translation-text-v1` and `azure-ai-translation-text-v2` +to demonstrate the migration of code from `azure-core` to `azure-core-v2`. + +**Note:** To execute the below commands, ensure that you are within the `rewrite-sample` directory. + +### Dry Run +To run the OpenRewrite recipe in dry-run mode, execute the following command: +```shell +mvn rewrite:dryRun +``` +If the above command is not recognised, execute the full version of the command: + +```shell +mvn org.openrewrite.maven:rewrite-maven-plugin:dryRun +``` + +This will generate a file `rewrite.patch` in `rewrite-sample/target/rewrite` directory. + +### Run (apply changes) +To actually apply the changes to the sample project, execute the following command: +```shell +mvn rewrite:run +``` +If the above command is not recognised, execute the full version of the command: + +```shell +mvn org.openrewrite.maven:rewrite-maven-plugin:run +``` + +## Testing +To run the unit tests for the OpenRewrite recipe, execute the following command: +```shell +mvn:test +``` diff --git a/sdk/tools/azure-openrewrite/README.MD b/sdk/tools/azure-openrewrite/README.MD new file mode 100644 index 0000000000000..41f504fb329bc --- /dev/null +++ b/sdk/tools/azure-openrewrite/README.MD @@ -0,0 +1,7 @@ +# Azure OpenRewrite Plugin library for Java +## Getting started +## Key concepts +## Examples +## Troubleshooting +## Next steps +## Contributing diff --git a/sdk/tools/azure-openrewrite/pom.xml b/sdk/tools/azure-openrewrite/pom.xml new file mode 100644 index 0000000000000..a773fb3c617c4 --- /dev/null +++ b/sdk/tools/azure-openrewrite/pom.xml @@ -0,0 +1,131 @@ + + + 4.0.0 + + com.azure + azure-openrewrite + 1.0.0-beta.1 + + Microsoft Azure OpenRewrite Plugin library for Java + This module contains OpenRewrite recipe for migrating to next generation Microsoft Azure client libraries. + + + + + org.openrewrite.recipe + rewrite-recipe-bom + 3.0.2 + pom + import + + + + + + + + org.openrewrite + rewrite-java + compile + + + + + org.openrewrite + rewrite-java-8 + test + + + + + org.openrewrite + rewrite-maven + compile + + + com.azure + azure-core + 1.54.1 + test + + + org.openrewrite + rewrite-test + test + + + org.junit.jupiter + junit-jupiter-api + 5.11.4 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.11.4 + test + + + + org.assertj + assertj-core + 3.26.0 + test + + + + com.fasterxml.jackson.core + jackson-core + 2.17.2 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-smile + 2.17.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.17.2 + + + org.projectlombok + lombok + 1.18.34 + provided + + + + com.azure + azure-ai-translation-text + 1.1.0 + test + + + + com.azure + azure-storage-blob + 12.29.0 + test + + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.15.0 + + + + diff --git a/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/AddTryCatchToMethodCallRecipe.java b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/AddTryCatchToMethodCallRecipe.java new file mode 100644 index 0000000000000..f0414f56fc994 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/AddTryCatchToMethodCallRecipe.java @@ -0,0 +1,324 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.openrewrite.Cursor; +import org.openrewrite.ExecutionContext; +import org.openrewrite.NlsRewrite; +import org.openrewrite.Option; +import org.openrewrite.Recipe; +import org.openrewrite.Tree; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.internal.lang.NonNull; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.MethodMatcher; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.MethodCall; +import org.openrewrite.java.tree.Statement; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.regex.Pattern; + +/** + * Add try-catch to method recipe places all calls to methods matching the provided method + * pattern in a try-catch block based off the template provided. + * Recipe will attempt to use the JavaParser first, but will also run on code that cannot be + * parsed by Open Rewrites Java parser. + * Recipe does not check if the method throws the supplied exception, only that the method is + * in a suitable try-catch block. + * If the template is not syntactically correct, the recipe will not make any changes. + */ + +@Value +@EqualsAndHashCode(callSuper = false) +public class AddTryCatchToMethodCallRecipe extends Recipe { + + @Option(displayName = "Method pattern", + description = "A method pattern used to find matching method declaration.", + example = "*..* hello(..)") + @NonNull + String methodPattern; + + @Option(displayName = "Catch template", + description = "The code snippet to be executed in the catch block", + example = "catch (IOException e) { e.printStackTrace(); }") + @NonNull + String catchTemplateString; + + @Option(displayName = "Exclude owner of method", + description = "When enabled, the owner (class) from which the method originates from will not be matched during search.", + required = false) + @NonNull + boolean excludeOwner; + + @Option(displayName = "Fully qualified exception name", + description = "The fully qualified type name for the caught exception", + example = "java.io.IOException") + @NonNull + String fullyQualifiedExceptionName; + + /** + * All recipes must be serializable. This is verified by RewriteTest.rewriteRun() in your tests. + * Json creator allows your recipes to be used from a yaml file. + */ + @JsonCreator + public AddTryCatchToMethodCallRecipe(@NonNull @JsonProperty("methodPattern") String methodPattern, + @NonNull @JsonProperty("catchTemplateString") String catchTemplateString, + @NonNull @JsonProperty("fullyQualifiedExceptionName") String fullyQualifiedExceptionName, + @Nullable @JsonProperty("excludeOwner") boolean excludeOwner) { + this.methodPattern = methodPattern; + this.excludeOwner = excludeOwner; + this.catchTemplateString = catchTemplateString; + this.fullyQualifiedExceptionName = fullyQualifiedExceptionName; + } + + + @Override + public @NlsRewrite.DisplayName String getDisplayName() { + return "Add try-catch to method"; + } + + @Override + public @NlsRewrite.Description String getDescription() { + return "Surrounds calls to the target method in a custom try-catch block."; + } + + /** + * Method to return the visitor that performs the checks and changes + * + * @return Returns the visitor that performs the checks and changes + */ + @Override + public TreeVisitor getVisitor() { + return new AddTryCatchVisitor(); + } + + /** + * Visitor that performs the checks and changes + */ + private class AddTryCatchVisitor extends JavaIsoVisitor { + + private final MethodMatcher methodMatcher = new MethodMatcher(methodPattern, true); + + /** + * Overridden visitBlock method performs the changes to methods filtered by visitMethodCall. + */ + @Override + public J.Block visitBlock(J.Block block, ExecutionContext context) { + J.Block body = super.visitBlock(block, context); + + // Get the method that needs to be changed + MethodCall method = getCursor().pollMessage("METHOD"); + if (method == null) { + return body; + } + + //Get the parents of the method + Tree parent = getCursor().pollMessage("PARENT"); + // Get the first statement parent of method + Statement parentStatement = getCursor().pollMessage("STATEMENT"); + + JavaTemplate tryCatchTemplate = JavaTemplate.builder("try { int a = null; a = 3; } " + catchTemplateString) + .imports(fullyQualifiedExceptionName) + .build(); + + // Create an empty block to apply the try-catch template based off the cursor values from the main body + // This should create the correct formatting. + J.Block b = J.Block.createEmptyBlock(); + b = tryCatchTemplate.apply(new Cursor(getCursor(),b), b.getCoordinates().firstStatement()); + int parentIndex; + + // Extract the try-catch block and dummy elements + J.Try _try = (J.Try) b.getStatements().get(0); + J.VariableDeclarations dummyVarDec = (J.VariableDeclarations) _try.getBody().getStatements().get(0); + J.Assignment dummyAssignment = (J.Assignment) _try.getBody().getStatements().get(1); + + if (_try.getCatches().isEmpty()) { + return body; + } + // The original list of statements to alter + List bodyStatements = body.getStatements(); + + // Method is the first element on its line. + if (parent == null) { + // Cast method as a statement and update the indentation (prefix) + + Statement methodStatement = method.withPrefix(dummyVarDec.getPrefix()); + parentIndex = body.getStatements().indexOf(methodStatement); + // Make it the only statement in the try block + _try = _try.withBody(_try.getBody().withStatements(ListUtils.insert( + new ArrayList<>(), methodStatement, 0 ))); + + // Update the statements + bodyStatements.set(parentIndex, _try); + } + else if (parent instanceof J.Assignment) { + parentIndex = body.getStatements().indexOf(parent); + J.Assignment new_assignment = ((J.Assignment) parent).withPrefix(dummyAssignment.getPrefix()); + + _try = _try.withBody(_try.getBody().withStatements(ListUtils.insert( + new ArrayList<>(), new_assignment, 0 ))); + + bodyStatements.set(parentIndex, _try); + } + else if (parentStatement instanceof J.VariableDeclarations) { + + J.VariableDeclarations parentVd = (J.VariableDeclarations) parentStatement; + parentIndex = body.getStatements().indexOf(parentVd); + + if (parentVd.getVariables().size() != 1) { + // Recipe can only handle a variable declaration with a single named variable at this time + // Could be changed. + return body; + } + + J.VariableDeclarations.NamedVariable namedVariable = parentVd.getVariables().get(0); + Expression expression = namedVariable.getInitializer(); + + assert expression != null; + // Repurpose the dummyAssignment variable + dummyAssignment = dummyAssignment.withVariable(namedVariable.getName().unwrap()); + dummyAssignment = dummyAssignment.withAssignment(expression); + + _try = _try.withBody(_try.getBody().withStatements(ListUtils.insert( + new ArrayList<>(), dummyAssignment, 0 ))); + + // Make the original declaration initialise with '= null' + namedVariable = namedVariable.withInitializer(dummyVarDec.getVariables().get(0).getInitializer()); + + parentVd = parentVd.withVariables(ListUtils.insert( + new ArrayList<>(), namedVariable, 0 )); + + // Replace the old VariableDeclarations + bodyStatements.set(parentIndex, parentVd); + // Add the try below it + bodyStatements.add(parentIndex +1, _try); + + } + else { // Wrap method call in try catch for all other cases + int i = 0; + int index = -1; + for (Statement statement : body.getStatements()) { + if (statement.print().contains(method.print())) { + index = i; + } + i ++; + } + String tryCatchBlock = String.format( + "try { %s; } %s", body.getStatements().get(index), catchTemplateString + ); + JavaTemplate tryCatchNewTemplate = JavaTemplate.builder(tryCatchBlock) + .imports(fullyQualifiedExceptionName) + .build(); + + // Create an empty block to apply the try-catch template based off the cursor values from the main body + // This should create the correct formatting. + J.Block newBlock = J.Block.createEmptyBlock(); + newBlock = tryCatchNewTemplate.apply(new Cursor(getCursor(),newBlock), newBlock.getCoordinates().firstStatement()); + + bodyStatements.remove(index); + for (Statement statement : newBlock.getStatements()) { + bodyStatements.add(index, statement); + } + } + + // Update the body block with the new set of statements and return. + body = body.withStatements(bodyStatements); + // Add the import if needed + maybeAddImport(fullyQualifiedExceptionName,false); + return body; + } + public boolean containsSubstringWithParenthesis(String input, String substring) { + String regex = substring + "\\s*\\("; + return input.matches(".*" + regex + ".*"); + } + + + /** + * Method to find method calls that need to be wrapped + */ + private @Nullable M visitMethodCall(M methodCall, Supplier visitSuper) { + if (!methodMatcher.matches(methodCall)) { + if (!excludeOwner) { + // Make no changes + return visitSuper.get(); + } + if (!containsSubstringWithParenthesis(methodCall.toString(), methodPattern.replaceAll("\\(.*?\\)", ""). + replaceAll("\\*", "").trim().split(" ")[1])){ + // Make no changes + return visitSuper.get(); + } + } + // If match found, check that it is not already handled by a try block + try { + // Get the first upstream try block. Will throw exception if there are none + J.Try _try = getCursor().dropParentUntil(it -> it instanceof J.Try).getValue(); + + // Get the first enclosing block + J.Block block = getCursor().dropParentUntil(it -> it instanceof J.Block).getValue(); + + // Check to see if this try block is the parent of the enclosing block + if (_try.getBody().equals(block)) { + // Check if the correct exception is caught + boolean isCaught = _try.getCatches().stream().anyMatch( + _catch -> Objects.requireNonNull(_catch.getParameter().getType()) + .isAssignableFrom(Pattern.compile(fullyQualifiedExceptionName))); + + // Make no changes if exception already caught + if (isCaught) { + return visitSuper.get(); + } + } + } catch (IllegalStateException ignored) {} + // If the method matches and exception is not caught set messages for block + getCursor().putMessageOnFirstEnclosing(J.Block.class, "METHOD", methodCall); + + Tree parent = getCursor().getParentTreeCursor().getValue(); + if (! (parent instanceof J.Block)) { + // If the method is part of a nested statement flag the direct tree parent + getCursor().putMessageOnFirstEnclosing(J.Block.class, "PARENT", parent); + try { + // And the first parent that is a statement + Statement statement = getCursor().dropParentUntil(it -> it instanceof Statement).getValue(); + getCursor().putMessageOnFirstEnclosing(J.Block.class, "STATEMENT", statement); + } catch (IllegalStateException ignored) {} + } + + return visitSuper.get(); + } + + /** + * The Suppliers that traverse the LST and redirect all types of method calls through visitMethodCall. + */ + + @Override + public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext context) { + return visitMethodCall(newClass, () -> super.visitNewClass(newClass, context)); + } + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext context) { + return visitMethodCall(method, () -> super.visitMethodInvocation(method, context)); + } + + @Override + public J.MemberReference visitMemberReference(J.MemberReference memberRef, ExecutionContext context) { + return visitMethodCall(memberRef, () -> super.visitMemberReference(memberRef, context)); + } + + } // end catchUncheckedVisitor + +} diff --git a/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/RemoveFixedDelayRecipe.java b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/RemoveFixedDelayRecipe.java new file mode 100644 index 0000000000000..8009eca8f1672 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/RemoveFixedDelayRecipe.java @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; + +/** + * RemoveFixedDelayRecipe removes any leftover imports for FixedDelayOptions + * and any variables declared with the FixedDelayOptions type. + * -------------------------------------------------- + * Removes code such as: + * import com.azure.core.http.policy.FixedDelayOptions; + * ... + * FixedDelayOptions s; + * -------------------------------------------------- + */ +public class RemoveFixedDelayRecipe extends Recipe { + /** + * Method to return a simple short description of RemoveFixedDelayRecipe + * @return A simple short description/name of the recipe + */ + @Override + public String getDisplayName() { + return "Removes imports and variable declarations for FixedDelayOptions"; + } + /** + * Method to return a description of RemoveFixedDelayRecipe + * @return A short description of the recipe + */ + @Override + public String getDescription() { + return "This recipe removes any leftover imports and variables using FixedDelayOptions."; + } + /** + * Method to return the visitor that visits the usages of FixedDelayOptions + * @return A TreeVisitor to visit the usages of FixedDelayOptions + */ + @Override + public TreeVisitor getVisitor() { + return new FixedDelayVisitor(); + } + /** + * Visitor to remove FixedDelayOptions + */ + private static class FixedDelayVisitor extends JavaIsoVisitor { + /** + * Method to remove unnecessary import for FixedDelay + */ + @Override + public J.Import visitImport(J.Import _import, ExecutionContext executionContext) { + J.Import visitedImport = super.visitImport(_import, executionContext); + if (visitedImport.getQualid() != null){ + if (visitedImport.getQualid().getSimpleName().contains("FixedDelay")){ + return null; + } + } + return visitedImport; + } + /** + * Method to remove unnecessary variable declarations for FixedDelay + */ + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext executionContext) { + J.VariableDeclarations visitedVar = super.visitVariableDeclarations(multiVariable, executionContext); + if (visitedVar.getTypeExpression() == null) { + return visitedVar; + } + if (visitedVar.getTypeExpression().toString().contains("FixedDelayOptions")) { + // Return null to remove the block + return null; + } + return visitedVar; + } + } +} diff --git a/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/TypeReferenceRecipe.java b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/TypeReferenceRecipe.java new file mode 100644 index 0000000000000..a927f8626026e --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/TypeReferenceRecipe.java @@ -0,0 +1,236 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite; + +import org.openrewrite.Cursor; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeTree; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Recipe to convert TypeReference to ParameterizedType and remove TypeReference import statements. + * -------------------------------------------------- + * Before applying this recipe: + * import com.azure.core.util.serializer.TypeReference; + * ... + * result = binaryDataResponse.getValue().toObject(new TypeReference>() { }); + * -------------------------------------------------- + * After applying this recipe: + * import java.lang.reflect.ParameterizedType; + * import java.lang.reflect.Type; + * ... + result = binaryDataResponse.getValue().toObject(new ParameterizedType() { + @Override + public Type getRawType() { + return List.class; + } + + @Override + public Type[] getActualTypeArguments() { + return new Type[]{TranslatedTextItem.class}; + } + + @Override + public Type getOwnerType() { + return null; + } + }); + * -------------------------------------------------- + */ +public class TypeReferenceRecipe extends Recipe { + + @Override + public String getDisplayName() { + return "Convert TypeReference to ParameterizedType and remove imports"; + } + + @Override + public String getDescription() { + return "This recipe converts TypeReference<> to ParameterizedType and removes the import statement for TypeReference."; + } + + @Override + public TreeVisitor getVisitor() { + return new ConvertTypeReferenceVisitor(); + } + + private static class ConvertTypeReferenceVisitor extends JavaIsoVisitor { + /** + * Method to visit instantiation of TypeReference and replace it with ParameterizedType + * instantiation including override methods. + * */ + @Override + public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) { + J.NewClass visitedNewClass = super.visitNewClass(newClass, ctx); + + // Check if the TypeReference reference has already been transformed + if (visitedNewClass.getBody() == null){return visitedNewClass;} + boolean alreadyTransformed = visitedNewClass.getBody().getStatements().stream() + .filter(statement -> statement instanceof J.MethodDeclaration) + .map(J.MethodDeclaration.class::cast) + .anyMatch(methodDeclaration -> methodDeclaration.getName().getSimpleName().equals("getRawType")); + if (visitedNewClass.getClazz() == null){return visitedNewClass;} + if (!alreadyTransformed && visitedNewClass.getClazz().toString().contains("TypeReference")) { + // Extract type from generic type in TypeReference declaration + String genType = null; + String type = null; + String type2 = null; + List fullType = null; + + + if(visitedNewClass.getClazz().getType() != null){ + try { + fullType = ((JavaType.Parameterized) visitedNewClass.getClazz().getType()).getTypeParameters(); + if (fullType.get(0) instanceof JavaType.Parameterized) { // Check if using parameterized type + type = ((JavaType.Class)((JavaType.Parameterized) fullType.get(0)).getTypeParameters().get(0)).getClassName(); + if (((JavaType.Parameterized) fullType.get(0)).getTypeParameters().size() > 1) { + type2 = ((JavaType.Class)((JavaType.Parameterized) fullType.get(0)).getTypeParameters().get(1)).getClassName(); + } + genType = ((JavaType.Parameterized) fullType.get(0)).getClassName(); + } else { + genType = ((JavaType.Class)fullType.get(0)).getClassName(); + } + } catch (ClassCastException e) { // OpenRewrite has a bug where parameterized new-classes contained in method arguments can't be parsed, so extract type information using String manipulation instead + genType = visitedNewClass.getClazz().toString().split("<")[1]; + if (visitedNewClass.getClazz().toString().contains(",")) { + type = visitedNewClass.getClazz().toString().split("<")[2].replace(">", "").split(",")[0]; + type2 = visitedNewClass.getClazz().toString().split("<")[2].replace(">", "").split(",")[1]; + }else { + type = visitedNewClass.getClazz().toString().split("<")[2].replace(">", ""); + } + } + } + JavaTemplate methodRawTypeTemplate = JavaTemplate.builder("@Override public Type getRawType() { return " + extractTypeArgument(visitedNewClass.toString()) + ".class; }").build(); + if (genType!=null){ + methodRawTypeTemplate = JavaTemplate.builder("@Override public Type getRawType() { return " + genType + ".class; }").build(); + } + JavaTemplate methodActualTypeTemplate = JavaTemplate.builder("@Override public Type[] getActualTypeArguments() { return new Type[] {}; }").build(); + if (type != null){ + methodActualTypeTemplate = JavaTemplate.builder("@Override public Type[] getActualTypeArguments() { return new Type[] { " + type + ".class }; }").build(); + } + JavaTemplate methodOwnerTypeTemplate = JavaTemplate.builder("@Override public Type getOwnerType() { return null; }").build(); + if (type2 != null){ // If TypeReference uses Map or any other class with the same structure like Foo + methodActualTypeTemplate = JavaTemplate.builder("@Override public Type[] getActualTypeArguments() { return new Type[] { " + type + ".class," + type2 + ".class }; }").build(); + } + // Apply Templates (add methods to body) + + visitedNewClass = visitedNewClass.withBody(methodRawTypeTemplate.apply(new Cursor(getCursor(), visitedNewClass.getBody()), + visitedNewClass.getBody().getCoordinates().lastStatement())); + visitedNewClass = visitedNewClass.withBody(methodActualTypeTemplate.apply(new Cursor(getCursor(), visitedNewClass.getBody()), + visitedNewClass.getBody().getCoordinates().lastStatement())); + visitedNewClass = visitedNewClass.withBody(methodOwnerTypeTemplate.apply(new Cursor(getCursor(), visitedNewClass.getBody()), + visitedNewClass.getBody().getCoordinates().lastStatement())); + + visitedNewClass = visitedNewClass.withClazz(TypeTree.build(" ParameterizedType")); // Replace TypeReference with Type + + } + return visitedNewClass; + } + + private final Set importSet = new HashSet<>(); + + @Override + public J.Import visitImport(J.Import importStmt, ExecutionContext ctx) { + String importQualid = importStmt.getQualid().toString(); + + // Add the import to the set and check if it already exists + boolean isNewImport = importSet.add(importQualid); + + // If the import is for ParameterizedType, and it's already in the set, skip it + if (importQualid.trim().equals("java.lang.reflect.ParameterizedType") && !isNewImport) { + return null; + } + + // Remove the import statement for TypeReference and add import for ParameterizedType + if (importQualid.equals("com.azure.core.util.serializer.TypeReference")) { + importSet.add("java.lang.reflect.ParameterizedType"); + return importStmt.withQualid(TypeTree.build(" java.lang.reflect.ParameterizedType")); + } + + // Change BinaryData import to a new package + if (importQualid.equals("com.azure.core.util.BinaryData")) { + importSet.add("io.clientcore.core.util.binarydata.BinaryData"); + return importStmt.withQualid(TypeTree.build(" io.clientcore.core.util.binarydata.BinaryData")); + } + + // Return other imports normally + return importStmt; + } + /** + * Method to visit variable declaration for TypeReference and make sure it is converted to java.lang.reflect.Type + */ + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext executionContext) { + J.VariableDeclarations visitedDeclarations = super.visitVariableDeclarations(multiVariable, executionContext); + if (visitedDeclarations.toString().contains("TypeReference") + && visitedDeclarations.toString().contains("ParameterizedType")) { + visitedDeclarations = visitedDeclarations.withTypeExpression(TypeTree.build(" Type")); + return visitedDeclarations; + } + return visitedDeclarations; + } + + /** + * Method to visit BinaryData type and change it to the new version + */ + @Override + public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { + J.FieldAccess fa = super.visitFieldAccess(fieldAccess, ctx); + String fullyQualified = fa.getTarget() + "." + fa.getSimpleName(); + if (fullyQualified.equals("com.azure.core.util.BinaryData")) { + return TypeTree.build(" io.clientcore.core.util.binarydata.BinaryData"); + } + return fa; + } + + /** + * Method to add import for java.lang.reflect.Type if needed + */ + @Override + public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext executionContext) { + J.CompilationUnit visitedCompilationUnit = super.visitCompilationUnit(cu, executionContext); + J.Import newImport = null; + boolean addTypeImport = false; + if (visitedCompilationUnit.getImports().isEmpty()){return visitedCompilationUnit;} + for (J.Import i : visitedCompilationUnit.getImports()) { + newImport = i; + if (i.toString().contains("java.lang.reflect.Type")){ + return visitedCompilationUnit; + } + if (i.toString().contains("java.lang.reflect.ParameterizedType")){ + addTypeImport = true; + } + } + if (addTypeImport) { + newImport = newImport.withQualid(TypeTree.build(" java.lang.reflect.Type")); + List imports = visitedCompilationUnit.getImports(); + imports.add(newImport); + return visitedCompilationUnit.withImports(imports); + } + return visitedCompilationUnit; + } + + public String extractTypeArgument(String text) { + // Find the start and end of the type argument + int startIndex = text.indexOf('<'); + int endIndex = text.indexOf('>', startIndex); + + if (startIndex == -1 || endIndex == -1) { + return null; // If '<' or '>' not found, return null + } + + // Extract the substring between the angle brackets + return text.substring(startIndex + 1, endIndex).trim(); + } + } + +} diff --git a/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/ContextRecipe.java b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/ContextRecipe.java new file mode 100644 index 0000000000000..44c7ce408ef36 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/ContextRecipe.java @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.clientcore; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeTree; + +/** + * ContextRecipe changes all instances of Context.NONE (from azure core v1) to Context.none() (from azure core v2). + * This recipe also updates the import statements for the aforementioned class. + * -------------------------------------------------- + * Before applying this recipe: + * import com.azure.core.util.Context; + * com.azure.core.http.rest.RequestOptions; + * ... + * public void context(){ print(Context.NONE); } + * -------------------------------------------------- + * After applying this recipe: + * import io.clientcore.core.util.Context; + * io.clientcore.core.http.models.RequestOptions + * ... + * public void context(){ print(Context.none()); } + * -------------------------------------------------- + */ +public class ContextRecipe extends Recipe { + /** + * Method to return a simple short description of ContextRecipe + * @return A simple short description/name of the recipe + */ + @Override + public String getDisplayName() { + return "Change static field 'Context.NONE' to Method 'Context.none()'"; + } + /** + * Method to return a description of ContextRecipe + * @return A short description of the recipe + */ + @Override + public String getDescription() { + return "This recipe changes any calls to Context.NONE to Context.none().\n" + + "It also changes the import statement of com.azure.core.util.Context to io.clientcore.core.util.Context."; + } + /** + * Method to return the visitor that visits the Context.NONE identifier + * @return A TreeVisitor to visit the NONE identifier and change it to none() + */ + @Override + public TreeVisitor getVisitor() { + return new ChangeStaticFieldToMethodVisitor(); + } + /** + * Visitor to change NONE identifier to none() + */ + private static class ChangeStaticFieldToMethodVisitor extends JavaIsoVisitor { + /** + * Method to change com.azure.core.util.Context to io.clientcore.core.util.Context + */ + @Override + public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { + J.FieldAccess visitedFieldAccess = super.visitFieldAccess(fieldAccess, ctx); + String fullyQualified = visitedFieldAccess.getTarget() + "." + visitedFieldAccess.getSimpleName(); + if (fullyQualified.equals("com.azure.core.http.rest.RequestOptions")) { + return TypeTree.build(" io.clientcore.core.http.models.RequestOptions"); + } + if (fullyQualified.equals("com.azure.core.util.Context")) { + return TypeTree.build(" io.clientcore.core.util.Context"); + } + if (fullyQualified.equals("Context.NONE")){ + return TypeTree.build("Context.none()"); + } + return visitedFieldAccess; + } + } +} diff --git a/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/HttpLogOptionsRecipe.java b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/HttpLogOptionsRecipe.java new file mode 100644 index 0000000000000..068653848998d --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/HttpLogOptionsRecipe.java @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.clientcore; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeTree; + +/** + * HttpLogOptionsRecipe change usage of the com.azure.core.http.policy.HttpLogDetailLevel while also changing + * the com.azure.core.http.policy.HttpLogOptions Type. + * The import statements are also updated. + * Changes: + * com.azure.core.http.policy.HttpLogDetailLevel -> io.clientcore.core.http.models.HttpLogOptions.HttpLogDetailLevel + * com.azure.core.http.policy.HttpLogOptions -> io.clientcore.core.http.models.HttpLogOptions + * -------------------------------------------------- + * Before applying this recipe: + * import com.azure.core.http.policy.HttpLogDetailLevel; + * import com.azure.core.http.policy.HttpLogOptions; + * ... + * public void logOptions(){ print(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS)); } + * -------------------------------------------------- + * After applying this recipe: + * import io.clientcore.core.http.models.HttpLogOptions; + * ... + * public void logOptions(){ print(new HttpLogOptions().setLogLevel(HttpLogOptions.HttpLogDetailLevel.BODY_AND_HEADERS)); } + * -------------------------------------------------- + */ +public class HttpLogOptionsRecipe extends Recipe { + /** + * Method to return a simple short description of HttpLogOptionsRecipe + * @return A simple short description/name of the recipe + */ + @Override + public String getDisplayName() { + return "Migrate the HttpLogOptions and HttpLogDetail usages"; + } + /** + * Method to return a description of HttpLogOptionsRecipe + * @return A short description of the recipe + */ + @Override + public String getDescription() { + return "This recipe changes any usages of HttpLogOptions from azure core v1 to its respective type from azure core v2.\n" + + "It also migrates any usages of HttpLogDetailLevel to azure core v2."; + } + /** + * Method to return the visitor that visits the usages of HttpLogOptions and HttpLogDetailLevel + * @return A TreeVisitor to visit the usages of HttpLogOptions and HttpLogDetailLevel + */ + @Override + public TreeVisitor getVisitor() { + return new HttpLogOptionsVisitor(); + } + /** + * Visitor to change HttpLogOptions type and change usage of HttpLogDetailLevel + */ + private static class HttpLogOptionsVisitor extends JavaIsoVisitor { + /** + * Method to change usage of the HttpLogDetailLevel while also changing the HttpLogOptions Type + */ + @Override + public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { + J.FieldAccess visitedFieldAccess = super.visitFieldAccess(fieldAccess, ctx); + String fullyQualified = visitedFieldAccess.getTarget() + "." + visitedFieldAccess.getSimpleName(); + if (fullyQualified.equals("com.azure.core.http.policy.HttpLogOptions")) { + return TypeTree.build(" io.clientcore.core.http.models.HttpLogOptions"); + } + if (fullyQualified.equals("com.azure.core.http.policy.HttpLogDetailLevel") && + visitedFieldAccess.getSimpleName().equals("HttpLogDetailLevel") && + visitedFieldAccess.print().contains("com.azure")){ + return TypeTree.build(" io.clientcore.core.http.models.HttpLogOptions.HttpLogDetailLevel"); + } + return visitedFieldAccess; + } + /** + * Method to remove unnecessary import fo HttpLogDetailLevel as the class is already included in client-core HttpLogOptions class + */ + @Override + public J.Import visitImport(J.Import _import, ExecutionContext executionContext) { + J.Import visitedImport = super.visitImport(_import, executionContext); + if (visitedImport.getQualid().getSimpleName().contains("HttpLogDetailLevel")){ + return null; + } + return visitedImport; + } + } +} diff --git a/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/ResponseRecipe.java b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/ResponseRecipe.java new file mode 100644 index 0000000000000..a1ca8aa50ce6f --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/ResponseRecipe.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.clientcore; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeTree; + +/** + * ResponseRecipe changes all instances of Response (from azure core v1) to Response (from azure core v2). + * This recipe also updates the import statements for the aforementioned class. + * -------------------------------------------------- + * Before applying this recipe: + * import com.azure.core.http.rest.Response; + * -------------------------------------------------- + * After applying this recipe: + * import io.clientcore.core.http.models.Response; + * -------------------------------------------------- + */ +public class ResponseRecipe extends Recipe { + /** + * Method to return a simple short description of ResponseRecipe + * @return A simple short description/name of the recipe + */ + @Override + public String getDisplayName() { + return "Update Response type to v2 version"; + } + /** + * Method to return a description of ResponseRecipe + * @return A short description of the recipe + */ + @Override + public String getDescription() { + return "This recipe changes com.azure.core.http.rest.Response to io.clientcore.core.http.models.Response."; + } + /** + * Method to return the visitor that visits the Response class + * @return A TreeVisitor to visit the Response class and update it + */ + @Override + public TreeVisitor getVisitor() { + return new UpdateResponseVisitor(); + } + /** + * Visitor to update Response + */ + private static class UpdateResponseVisitor extends JavaIsoVisitor { + /** + * Method to change com.azure.core.http.rest.Response to io.clientcore.core.http.models.Response + */ + @Override + public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { + J.FieldAccess visitedFieldAccess = super.visitFieldAccess(fieldAccess, ctx); + String fullyQualified = visitedFieldAccess.getTarget() + "." + visitedFieldAccess.getSimpleName(); + if (fullyQualified.equals("com.azure.core.http.rest.Response")) { + return TypeTree.build(" io.clientcore.core.http.models.Response"); + } + return visitedFieldAccess; + } + } +} diff --git a/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/RetryOptionsConstructorRecipe.java b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/RetryOptionsConstructorRecipe.java new file mode 100644 index 0000000000000..079b4663d75a8 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/main/java/com/azure/openrewrite/clientcore/RetryOptionsConstructorRecipe.java @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.clientcore; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeTree; + +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; +/** + * RetryOptionsRecipe changes RetryOptions constructor to HttpRetryOptions constructor. + * It also removes any references to FixedDelay and ExponentialDelay and changes + * com.azure.core.http.policy.RetryOptions to io.clientcore.core.http.models.HttpRetryOptions + * -------------------------------------------------- + * Before applying this recipe: + * import com.azure.core.http.policy.RetryOptions; + * ... + * new RetryOptions(new FixedDelayOptions(3, Duration.ofMillis(50))) + * -------------------------------------------------- + * After applying this recipe: + * import io.clientcore.core.http.models.HttpRetryOptions; + * ... + * new HttpRetryOptions(3, Duration.ofMillis(50)) + * -------------------------------------------------- + */ +public class RetryOptionsConstructorRecipe extends Recipe { + /** + * Method to return a simple short description of RetryOptionsRecipe + * @return A simple short description/name of the recipe + */ + @Override + public String getDisplayName() { + return "Change RetryOptions constructor"; + } + /** + * Method to return a description of RetryOptionsRecipe + * @return A short description of the recipe + */ + @Override + public String getDescription() { + return "This recipe changes the constructor for RetryOptions to HttpRetryOptions.\n" + + "This includes removing any references to FixedDelay and ExponentialDelay and changing\n" + + " * com.azure.core.http.policy.RetryOptions to io.clientcore.core.http.models.HttpRetryOptions."; + } + /** + * Method to return the visitor that changes RetryOptions constructor to HttpRetryOptions constructor + * @return A TreeVisitor to change RetryOptions constructor to HttpRetryOptions constructor + */ + @Override + public TreeVisitor getVisitor() { + return new RetryVisitor(); + } + /** + * Visitor to change RetryOptions constructor to HttpRetryOptions constructor + */ + private static class RetryVisitor extends JavaIsoVisitor { + + private final Map> variableToArgsMap = new HashMap<>(); + + /** + * Method to visit variable declaration for FixedDelay or ExponentialDelay + */ + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations variableDeclarations, ExecutionContext executionContext) { + J.VariableDeclarations vd = super.visitVariableDeclarations(variableDeclarations, executionContext); + for (J.VariableDeclarations.NamedVariable variable : vd.getVariables()) { + J.NewClass newClass = null; + try { + newClass = (J.NewClass) variable.getInitializer(); + } catch (Exception e) { + return vd; + } + if (newClass != null) { + if (newClass.getType() != null) { + String className = newClass.getType().toString(); + if (className.contains("FixedDelayOptions") || className.contains("ExponentialDelayOptions")) { + List args = new ArrayList<>(newClass.getArguments()); + variableToArgsMap.put(variable.getSimpleName(), args); + } + } + } + } + return vd; + } + + /** + * Method to visit constructor for RetryOptions + */ + @Override + public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext executionContext) { + J.NewClass visitedNewClass = super.visitNewClass(newClass, executionContext); + if (visitedNewClass.toString().contains("new HttpRetryOptions")) { + if (visitedNewClass.getArguments().size() == 1) { + Expression constructorArg = visitedNewClass.getArguments().get(0); + if (constructorArg instanceof J.Identifier) { + String variableName = ((J.Identifier) constructorArg).getSimpleName(); + List args = variableToArgsMap.get(variableName); + if (args != null) { + return visitedNewClass.withArguments(args); + } + } else if (constructorArg instanceof J.NewClass) { + J.NewClass newArg = (J.NewClass) constructorArg; + List args = new ArrayList<>(newArg.getArguments()); + return visitedNewClass.withArguments(args); + } + } + } + return visitedNewClass; + } + + /** + * Method to change RetryOptions to HttpRetryOptions + */ + @Override + public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { + J.Identifier visitedIdentifier = super.visitIdentifier(identifier, ctx); + if (visitedIdentifier.getSimpleName().equals("RetryOptions")) { + return visitedIdentifier.withSimpleName("HttpRetryOptions"); + } + return visitedIdentifier; + } + + /** + * Method to change import to HttpRetryOptions + */ + @Override + public J.FieldAccess visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { + J.FieldAccess visitedFieldAccess = super.visitFieldAccess(fieldAccess, ctx); + String fullyQualified = visitedFieldAccess.getTarget() + "." + visitedFieldAccess.getSimpleName(); + if (fullyQualified.equals("com.azure.core.http.policy.HttpRetryOptions")) { + return TypeTree.build(" io.clientcore.core.http.models.HttpRetryOptions"); + } + return visitedFieldAccess; + } + + /** + * Method to change usages of retryOptions builder method to httpRetryOptions + */ + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) { + J.MethodInvocation visitedMethodInv = super.visitMethodInvocation(method, executionContext); + if (visitedMethodInv.getSimpleName().equals("retryOptions")) { + return visitedMethodInv.withName(visitedMethodInv.getName().withSimpleName("httpRetryOptions")); + } + return visitedMethodInv; + } + } +} diff --git a/sdk/tools/azure-openrewrite/src/main/resources/META-INF/rewrite/rewrite.yml b/sdk/tools/azure-openrewrite/src/main/resources/META-INF/rewrite/rewrite.yml new file mode 100644 index 0000000000000..0d18f42a84624 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/main/resources/META-INF/rewrite/rewrite.yml @@ -0,0 +1,258 @@ +type: specs.openrewrite.org/v1beta/recipe +name: com.azure.openrewrite.migrateToVNext +displayName: Migrate from azure-core to azure-core-v2 +description: This recipe migrates the azure sdk libraries from azure-core to azure-core-v2 and clientcore libraries. +recipeList: + # ---------------------------------- + # + # Java-Based LST Replacement Recipes + # + # ---------------------------------- + + # Recipe that changes all instances of com.azure.core.http.HttpHeaderName + # to io.clientcore.core.http.models.HttpHeaderName + # Before: + # import com.azure.core.http.HttpHeaderName; + # After: + # import io.clientcore.core.http.models.HttpHeaderName; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.http.HttpHeaderName + newFullyQualifiedTypeName: io.clientcore.core.http.models.HttpHeaderName + + # Recipe that changes all instances of com.azure.core.util.logging.ClientLogger + # to io.clientcore.core.util.ClientLogger + # Before: + # import com.azure.core.util.logging.ClientLogger; + # After: + # import io.clientcore.core.util.ClientLogger; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.util.logging.ClientLogger + newFullyQualifiedTypeName: io.clientcore.core.util.ClientLogger + + # Recipe that changes all instances of com.azure.core.util.CoreUtils + # to com.azure.core.v2.util.CoreUtils + # Before: + # import com.azure.core.util.CoreUtils; + # After: + # import com.azure.core.v2.util.CoreUtils; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.util.CoreUtils + newFullyQualifiedTypeName: com.azure.core.v2.util.CoreUtils + + # Recipe that changes all instances of com.azure.core.http.policy.KeyCredentialPolicy + # to io.clientcore.core.http.pipeline.KeyCredentialPolicy + # Before: + # import com.azure.core.http.policy.KeyCredentialPolicy; + # After: + # import io.clientcore.core.http.pipeline.KeyCredentialPolicy; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.http.policy.KeyCredentialPolicy + newFullyQualifiedTypeName: io.clientcore.core.http.pipeline.KeyCredentialPolicy + + # Recipe that changes all instances of com.azure.core.credential.KeyCredential + # to io.clientcore.core.credential.KeyCredential + # Before: + # import com.azure.core.credential.KeyCredential; + # After: + # import io.clientcore.core.credential.KeyCredential; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.credential.KeyCredential + newFullyQualifiedTypeName: io.clientcore.core.credential.KeyCredential + + # Recipe that changes all instances of com.azure.core.client.traits.KeyCredentialTrait + # to io.clientcore.core.models.traits.KeyCredentialTrait + # Before: + # import com.azure.core.client.traits.KeyCredentialTrait; + # After: + # import io.clientcore.core.models.traits.KeyCredentialTrait; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.client.traits.KeyCredentialTrait + newFullyQualifiedTypeName: io.clientcore.core.models.traits.KeyCredentialTrait + + # NOTE: + # A copy of the client-core jar file must be in the resources\classpath + # directory to reliably parse from clientcore library + + # -------------------------------------------------------------------- + # Recipes to migrate TextTranslationClientBuilder HttpTrait implemented + # methods and parameters + # -------------------------------------------------------------------- + # Change Types from the azure-core to clientcore versions + # Before: + # import com.azure.core.http.HttpClient; + # After: + # import io.clientcore.core.http.client.HttpClient; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.http.HttpClient + newFullyQualifiedTypeName: io.clientcore.core.http.client.HttpClient + ignoreDefinition: false + # Before: + # import com.azure.core.http.HttpPipeline; + # After: + # import io.clientcore.core.http.pipeline.HttpPipeline; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.http.HttpPipeline + newFullyQualifiedTypeName: io.clientcore.core.http.pipeline.HttpPipeline + ignoreDefinition: false + # Before: + # import com.azure.core.http.policy.HttpPipelinePolicy; + # After: + # import io.clientcore.core.http.pipeline.HttpPipelinePolicy; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.http.policy.HttpPipelinePolicy + newFullyQualifiedTypeName: io.clientcore.core.http.pipeline.HttpPipelinePolicy + ignoreDefinition: false + + # Rename methods + # Alternatively, can target HttpTrait directly + + # Before: + # textTranslationClient.retryOptions(new RetryOptions()) + # After: + # textTranslationClient.httpRetryOptions(new RetryOptions()) + - org.openrewrite.java.ChangeMethodName: + methodPattern: com.azure.ai.translation.text.TextTranslationClientBuilder retryOptions(..) + newMethodName: httpRetryOptions + matchOverrides: true + # Before: + # textTranslationClient.pipeline(pipeline) + # After: + # textTranslationClient.httpPipeline(pipeline) + - org.openrewrite.java.ChangeMethodName: + methodPattern: com.azure.ai.translation.text.TextTranslationClientBuilder pipeline(..) + newMethodName: httpPipeline + matchOverrides: true + # Before: + # textTranslationClient.addPolicy(customPolicy) + # After: + # textTranslationClient.addHttpPipelinePolicy(customPolicy) + - org.openrewrite.java.ChangeMethodName: + methodPattern: com.azure.ai.translation.text.TextTranslationClientBuilder addPolicy(..) + newMethodName: addHttpPipelinePolicy + matchOverrides: true + # End TextTranslationClientBuilder - HttpTrait implementation recipes + + # Recipe to change com.azure.core.credential package name to io.clientcore.core.credential + # Before: + # import com.azure.core.credential.*; + # After: + # import io.clientcore.core.credential.*; + - org.openrewrite.java.ChangePackage: + oldPackageName: com.azure.core.credential + newPackageName: io.clientcore.core.credential + + # Recipes that update all instances of azure-core exceptions where + # use is equivalent and type/name change is sufficient/safe. + # Update to azure-core-v2 version + # Before: + # import com.azure.core.exception.ClientAuthenticationException; + # After: + # import com.azure.core.v2.exception.ClientAuthenticationException; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.exception.ClientAuthenticationException + newFullyQualifiedTypeName: com.azure.core.v2.exception.ClientAuthenticationException + # Before: + # import com.azure.core.exception.ResourceModifiedException; + # After: + # import com.azure.core.v2.exception.ResourceModifiedException; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.exception.ResourceModifiedException + newFullyQualifiedTypeName: com.azure.core.v2.exception.ResourceModifiedException + # Before: + # import com.azure.core.exception.ResourceNotFoundException; + # After: + # import com.azure.core.v2.exception.ResourceNotFoundException; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.exception.ResourceNotFoundException + newFullyQualifiedTypeName: com.azure.core.v2.exception.ResourceNotFoundException + # Update to clientcore version + # Before: + # import com.azure.core.exception.HttpResponseException; + # After: + # import io.clientcore.core.http.exception.HttpResponseException; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.exception.HttpResponseException + newFullyQualifiedTypeName: io.clientcore.core.http.exception.HttpResponseException + # End azure-core-exception change type Recipes + + # Recipe that changes all instances of com.azure.core.util.Configuration + # to io.clientcore.core.util.configuration.Configuration + # Before: + # import com.azure.core.util.Configuration; + # After: + # import io.clientcore.core.util.configuration.Configuration; + - org.openrewrite.java.ChangeType: + oldFullyQualifiedTypeName: com.azure.core.util.Configuration + newFullyQualifiedTypeName: io.clientcore.core.util.configuration.Configuration + + # azure-core v2 BinaryData.toObject throws an IOException. This recipe encloses all calls in + # a try-catch block. + # Before: + # List result = binaryDataResponse.getValue().toObject( /* Any Args */); + # After: + # List result = null; + # try { + # result = binaryDataResponse.getValue().toObject( /* Any Args */); + # } catch (IOException e) { + # e.printStackTrace(); + # } + # + # NOTE: Tests are failing, so I'm disabling this recipe for now. + # WARNING: This recipe MUST be run before com.azure.openrewrite.TypeReferenceRecipe. + #- com.azure.openrewrite.AddTryCatchToMethodCallRecipe: + # methodPattern: com.azure.core.util.BinaryData toObject(..) + # catchTemplateString: catch (IOException e) { e.printStackTrace(); } + # fullyQualifiedExceptionName: java.io.IOException + # excludeOwner: true + + # azure-core v2 TextTranslationClient.translate throws an IOException. This recipe encloses all calls in + # a try-catch block. + # Before: + # List result = textTranslationClient.translate( /* Any Args */); + # After: + # List result = null; + # try { + # result = textTranslationClient.translate( /* Any Args */); + # } catch (IOException e) { + # throw new RuntimeException(e); + # } + # NOTE: Tests are failing, so I'm disabling this recipe for now. + #- com.azure.openrewrite.AddTryCatchToMethodCallRecipe: + # methodPattern: com.azure.ai.translation.text.TextTranslationClient translate(..) + # catchTemplateString: catch (IOException e) { throw new RuntimeException(e); } + # fullyQualifiedExceptionName: java.io.IOException + # excludeOwner: true + + - com.azure.openrewrite.clientcore.ResponseRecipe + - com.azure.openrewrite.clientcore.ContextRecipe + - com.azure.openrewrite.clientcore.RetryOptionsConstructorRecipe + - com.azure.openrewrite.TypeReferenceRecipe + - com.azure.openrewrite.clientcore.HttpLogOptionsRecipe + - com.azure.openrewrite.RemoveFixedDelayRecipe + + # Recipe to re-order imports to match standard conventions + - org.openrewrite.java.OrderImports + + # ---------------------------------- + # + # Text-Based Replacement Recipes + # + # ---------------------------------- + + # Recipe to replace HttpLogDetailLevel call with HttpLogOptions.HttpLogDetailLevel + # Before: + # HttpLogDetailLevel.BODY_AND_HEADERS + # After: + # HttpLogOptions.HttpLogDetailLevel.BODY_AND_HEADERS + - org.openrewrite.text.FindAndReplace: + find: '(? inputTextItems = Arrays.asList(new InputTextItem(\"hello world\"));\n" + + " List result = textTranslationClient.translate(Arrays.asList(\"es\"), inputTextItems);\n" + + " }\n" + + "}"; + + @Language("java") String after = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "import com.azure.ai.translation.text.models.InputTextItem;\n" + + "import com.azure.ai.translation.text.models.TranslatedTextItem;\n" + + "\n" + + "import java.io.IOException;\n" + + "import java.util.Arrays;\n" + + "import java.util.List;\n" + + "\n" + + "public class UserClass {\n" + + " public void myMethod() {\n" + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder().buildClient();\n" + + " List inputTextItems = Arrays.asList(new InputTextItem(\"hello world\"));\n" + + " List result = null;\n" + + " try {\n" + + " result = textTranslationClient.translate(Arrays.asList(\"es\"), inputTextItems);\n" + + " } catch (IOException e) {\n" + + " throw new RuntimeException(e);\n" + + " }\n" + + " }\n" + + "}"; + + rewriteRun( + java(before,after) + ); + } + + @Test + void testTextTranslationClientTranslateWithResponse() { + @Language("java") String before = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "import com.azure.ai.translation.text.models.InputTextItem;\n" + + "import com.azure.ai.translation.text.models.TranslatedTextItem;\n" + + "import com.azure.core.http.rest.RequestOptions;\n" + + "import com.azure.core.http.rest.Response;\n" + + "import com.azure.core.util.BinaryData;\n" + + "import com.azure.core.util.Context;\n" + + "import com.azure.core.util.serializer.TypeReference;\n" + + "\n" + + "import java.util.Arrays;\n" + + "import java.util.List;\n" + + "\n" + + "public class UserClass {\n" + + " void myMethod() {\n" + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder().buildClient();\n" + + "\n" + + " List inputTextItems = Arrays.asList(new InputTextItem(\"hello world\"));\n" + + " List targetLanguages = Arrays.asList(\"es\");\n" + + " BinaryData requestBody = BinaryData.fromObject(inputTextItems);\n" + + " RequestOptions requestOptions = new RequestOptions().setContext(Context.NONE);\n" + + "\n" + + " Response binaryDataResponse = textTranslationClient.translateWithResponse(targetLanguages, requestBody, requestOptions);\n" + + " List result = binaryDataResponse.getValue().toObject(new TypeReference>() { });\n" + + // " \n" + + " }\n" + + "}"; + + @Language("java") String after = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "import com.azure.ai.translation.text.models.InputTextItem;\n" + + "import com.azure.ai.translation.text.models.TranslatedTextItem;\n" + + "import io.clientcore.core.http.models.RequestOptions;\n" + + "import io.clientcore.core.http.models.Response;\n" + + "import io.clientcore.core.util.Context;\n" + + "import io.clientcore.core.util.binarydata.BinaryData;\n" + + "\n" + + "import java.io.IOException;\n" + + "import java.lang.reflect.ParameterizedType;\n" + + "import java.lang.reflect.Type;\n" + + "import java.util.Arrays;\n" + + "import java.util.List;\n" + + "\n" + + "public class UserClass {\n" + + " void myMethod() {\n" + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder().buildClient();\n" + + "\n" + + " List inputTextItems = Arrays.asList(new InputTextItem(\"hello world\"));\n" + + " List targetLanguages = Arrays.asList(\"es\");\n" + + " BinaryData requestBody = BinaryData.fromObject(inputTextItems);\n" + + " RequestOptions requestOptions = new RequestOptions().setContext(Context.none());\n" + + "\n" + + " Response binaryDataResponse = textTranslationClient.translateWithResponse(targetLanguages, requestBody, requestOptions);\n" + + " List result = null;\n" + + " try {\n" + + " result = binaryDataResponse.getValue().toObject(new ParameterizedType() {\n" + + " @Override\n" + + " public Type getRawType() {\n" + + " return List.class;\n" + + " }\n" + + "\n" + + " @Override\n" + + " public Type[] getActualTypeArguments() {\n" + + " return new Type[]{TranslatedTextItem.class};\n" + + " }\n" + + "\n" + + " @Override\n" + + " public Type getOwnerType() {\n" + + " return null;\n" + + " } });\n" + + " } catch (IOException e) {\n" + + " e.printStackTrace();\n" + + " }\n" + + " }\n" + + "}"; + + rewriteRun( + spec -> spec.typeValidationOptions(TypeValidation.none()), + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/AddTryCatchToMethodCallUnitTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/AddTryCatchToMethodCallUnitTest.java new file mode 100644 index 0000000000000..111481f718e1a --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/AddTryCatchToMethodCallUnitTest.java @@ -0,0 +1,292 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + + +import static org.openrewrite.java.Assertions.java; + +/** + * Add try-catch recipe wraps all method calls that match the supplied method pattern + * in a try-catch block with the provided catch code snippet. + * This test class tests the recipe alone. + */ +public class AddTryCatchToMethodCallUnitTest implements RewriteTest { + /** + * This method defines recipes used for testing. + * @param spec stores settings for testing environment. + */ + @Override + public void defaults(RecipeSpec spec) { + spec.recipes(new com.azure.openrewrite.AddTryCatchToMethodCallRecipe("UserClass myMethod(..)", + "catch (IOException e) { e.printStackTrace(); }", + "java.io.IOException",false), + new com.azure.openrewrite.AddTryCatchToMethodCallRecipe("CatchAndThrow myMethod(..)", + "catch (IOException e) { throw new RuntimeException(e); }", + "java.io.IOException",false) + ); + } + + @Test + void testAddTryCatchVoidMethod() { + @Language("java") String before = + "public class UserClass {\n" + + " \n" + + " private void myMethod() {\n" + + " int a = 1 + 1;\n" + + " }\n" + + " \n" + + " private void anotherMethod(){\n" + + " int b = 2 + 2;\n" + + " myMethod();\n" + + " int c = 3;\n" + + " }\n" + + "}\n"; + + @Language("java") String after = "import java.io.IOException;\n" + + "\n" + + "public class UserClass {\n" + + " \n" + + " private void myMethod() {\n" + + " int a = 1 + 1;\n" + + " }\n" + + " \n" + + " private void anotherMethod(){\n" + + " int b = 2 + 2;\n" + + " try {\n" + + " myMethod();\n" + + " } catch (IOException e) {\n" + + " e.printStackTrace();\n" + + " }\n" + + " int c = 3;\n" + + " }\n" + + "}\n"; + + rewriteRun( + java(before,after) + ); + } + + @Test + void testAddTryCatchMethodInVarDeclaration() { + @Language("java") String before = + "public class UserClass {\n" + + " \n" + + " private int myMethod() {\n" + + " return 2;\n" + + " }\n" + + " \n" + + " private void anotherMethod(){\n" + + " int b = myMethod();\n" + + " }\n" + + "}\n"; + + @Language("java") String after = "import java.io.IOException;\n" + + "\n" + + "public class UserClass {\n" + + " \n" + + " private int myMethod() {\n" + + " return 2;\n" + + " }\n" + + " \n" + + " private void anotherMethod(){\n" + + " int b = null;\n" + + " try {\n" + + " b = myMethod();\n" + + " } catch (IOException e) {\n" + + " e.printStackTrace();\n" + + " }\n" + + " }\n" + + "}\n"; + + rewriteRun( + java(before,after) + ); + } + + @Test + void testAddTryCatchMethodInAssignment() { + @Language("java") String before = + "public class UserClass {\n" + + " \n" + + " private int myMethod() {\n" + + " return 2;\n" + + " }\n" + + " \n" + + " private void anotherMethod(){\n" + + " int b;\n" + + " b = myMethod();\n" + + " int a = b;\n" + + " }\n" + + "}\n"; + + @Language("java") String after = "import java.io.IOException;\n" + + "\n" + + "public class UserClass {\n" + + " \n" + + " private int myMethod() {\n" + + " return 2;\n" + + " }\n" + + " \n" + + " private void anotherMethod(){\n" + + " int b;\n" + + " try {\n" + + " b = myMethod();\n" + + " } catch (IOException e) {\n" + + " e.printStackTrace();\n" + + " }\n" + + " int a = b;\n" + + " }\n"+ + "}\n"; + + rewriteRun( + java(before,after) + ); + } + + @Test + void testAddTryCatchMethodIsFromInstance() { + @Language("java") String before = + "public class UserClass {\n" + + " public UserClass(){}\n" + + " String s = \"Hello\";\n" + + " \n" + + " public String myMethod(int a, String b) {\n" + + " return s;\n" + + " }\n" + + "}\n" + + "\n" + + "class UserClass2 {" + + " \n" + + " public void myMethod2() {\n" + + " UserClass c = new UserClass();\n" + + " String s2 = c.myMethod(3, \"hello\");\n" + + " }\n" + + "}\n"; + + @Language("java") String after = "import java.io.IOException;\n" + + "\n" + + "public class UserClass {\n" + + " public UserClass(){}\n" + + " String s = \"Hello\";\n" + + " \n" + + " public String myMethod(int a, String b) {\n" + + " return s;\n" + + " }\n" + + "}\n" + + "\n" + + "class UserClass2 {" + + " \n" + + " public void myMethod2() {\n" + + " UserClass c = new UserClass();\n" + + " String s2 = null;\n" + + " try {\n" + + " s2 = c.myMethod(3, \"hello\");\n" + + " } catch (IOException e) {\n" + + " e.printStackTrace();\n" + + " }\n" + + " }\n" + + "}\n"; + + rewriteRun( + java(before,after) + ); + } + + @Test + void testAddTryCatchMethodIsInNestedCall() { + @Language("java") String before = + "public class CatchAndThrow {\n" + + " public CatchAndThrow(){}\n" + + " String s = \"Hello\";\n" + + " \n" + + " public String myMethod() {\n" + + " return s;\n" + + " }\n" + + "}\n" + + "\n" + + "class UserClass2 {\n" + + " CatchAndThrow c = new CatchAndThrow();\n" + + "}\n" + + "class UserClass3 {\n" + + " UserClass2 c2 = new UserClass2();\n" + + " public void myMethod3() {\n" + + " String s = c2.c.myMethod();\n" + + " }\n" + + "}"; + + @Language("java") String after = "import java.io.IOException;\n" + + "\n" + + "public class CatchAndThrow {\n" + + " public CatchAndThrow(){}\n" + + " String s = \"Hello\";\n" + + " \n" + + " public String myMethod() {\n" + + " return s;\n" + + " }\n" + + "}\n" + + "\n" + + "class UserClass2 {\n" + + " CatchAndThrow c = new CatchAndThrow();\n" + + "}\n" + + "class UserClass3 {\n" + + " UserClass2 c2 = new UserClass2();\n" + + " public void myMethod3() {\n" + + " String s = null;\n" + + " try {\n" + + " s = c2.c.myMethod();\n" + + " } catch (IOException e) {\n" + + " throw new RuntimeException(e);\n" + + //" e.printStackTrace();\n" + + " }\n" + + " }\n" + + "}"; + + rewriteRun( + java(before,after) + ); + } + @Test + void testAddTryCatchThrows() { + @Language("java") String before = + "public class CatchAndThrow {\n" + + " \n" + + " private void myMethod() {\n" + + " int a = 1 + 1;\n" + + " }\n" + + " \n" + + " private void anotherMethod(){\n" + + " int b = 2 + 2;\n" + + " myMethod();\n" + + " int c = 3;\n" + + " }\n" + + "}\n"; + + @Language("java") String after = "import java.io.IOException;\n" + + "\n" + + "public class CatchAndThrow {\n" + + " \n" + + " private void myMethod() {\n" + + " int a = 1 + 1;\n" + + " }\n" + + " \n" + + " private void anotherMethod(){\n" + + " int b = 2 + 2;\n" + + " try {\n" + + " myMethod();\n" + + " } catch (IOException e) {\n" + + " throw new RuntimeException(e);\n" + + " }\n" + + " int c = 3;\n" + + " }\n" + + "}\n"; + + rewriteRun( + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/BinaryDataTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/BinaryDataTest.java new file mode 100644 index 0000000000000..9294139486274 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/BinaryDataTest.java @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.TypeValidation; + +public class BinaryDataTest implements RewriteTest { + /** + * BinaryDataTest tests the recipe that changes + * com.azure.core.util.BinaryData to io.clientcore.core.util.binarydata.BinaryData. + * This recipe also tests to ensure that the TypeReference is correctly changed + * when used in conjunction with BinaryData. + */ + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + // Added due to bug in OpenRewrite parser when parsing azure TypeReference instantiation + spec.typeValidationOptions(TypeValidation.none()); + } + + /* Test to make sure BinaryData import is changed */ + @Test + public void testBinaryDataImportChanged() { + @Language("java") String before = "import com.azure.core.util.BinaryData;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n BinaryData b = BinaryData.fromObject(null);"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.util.binarydata.BinaryData;"; + after += "\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n BinaryData b = BinaryData.fromObject(null);"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + + /* Test to make sure BinaryData type is changed */ + @Test + public void testBinaryDataTypeChanged() { + @Language("java") String before = ""; + before += "public class Testing {\n public Testing(){\n com.azure.core.util.BinaryData b = com.azure.core.util.BinaryData.fromObject(null);\n }\n}"; + + @Language("java") String after = ""; + after += "public class Testing {\n public Testing(){\n io.clientcore.core.util.binarydata.BinaryData b = io.clientcore.core.util.binarydata.BinaryData.fromObject(null);\n }\n}"; + rewriteRun( + java(before,after) + ); + } + + /* Test to make sure BinaryData type and import is changed and TypeReference is changed */ + @Test + @Disabled("This test is failing and is temporarily disabled") + void testBinaryDataTypeReferenceChange() { + @Language("java") String before = ""; + before += "\nimport java.lang.reflect.ParameterizedType;"; + before += "\nimport java.lang.reflect.Type;"; + before += "\nimport java.util.List;"; + before += "\nimport com.azure.core.util.serializer.TypeReference;"; + before += "\nimport com.azure.core.util.BinaryData;"; + before += "\npublic class Testing {"; + before += "\n private static final TypeReference> TESTING_TYPE = new TypeReference>() {\n };"; + before += "\n private static final BinaryData b = BinaryData.fromObject(null);"; + before += "\n public static void main(String[] args) {"; + before += "\n System.out.println(b.toObject(TESTING_TYPE));"; + before += "\n }"; + before += "\n}"; + + + @Language("java") String after = + "import io.clientcore.core.util.binarydata.BinaryData;\n\n" + + "import java.io.IOException;\n" + + "import java.lang.reflect.ParameterizedType;\n" + + "import java.lang.reflect.Type;\n" + + "import java.util.List;\n\n"+ + "public class Testing {\n" + + " private static final Type TESTING_TYPE = new ParameterizedType() {\n" + + " @Override\n" + + " public Type getRawType() {\n" + + " return List.class;\n" + + " }\n\n" + + " @Override\n" + + " public Type[] getActualTypeArguments() {\n" + + " return new Type[]{String.class};\n" + + " }\n\n" + + " @Override\n" + + " public Type getOwnerType() {\n" + + " return null;\n" + + " }\n"; + after += " };"; + after += "\n private static final BinaryData b = BinaryData.fromObject(null);"; + after += "\n public static void main(String[] args) {"; + after += "\n try {"; + after += "\n System.out.println(b.toObject(TESTING_TYPE));"; + after += "\n } catch (IOException e) {"; + after += "\n e.printStackTrace();"; + after += "\n }"; + after += "\n }\n"; + after += "}\n"; + + rewriteRun( + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ClientLoggerTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ClientLoggerTest.java new file mode 100644 index 0000000000000..e21a5086d4ef0 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ClientLoggerTest.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +public class ClientLoggerTest implements RewriteTest { + /** + * ClientLoggerTest tests the recipe that changes + * com.azure.core.util.logging.ClientLogger to io.clientcore.core.util.ClientLogger. + */ + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /* Test to make sure ClientLogger import is changed */ + @Test + public void testClientLoggerWithImport() { + @Language("java") String before = "import com.azure.core.util.logging.ClientLogger;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n ClientLogger c = new ClientLogger(Testing.class);"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.util.ClientLogger;"; + after += "\n\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n ClientLogger c = new ClientLogger(Testing.class);"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } + + /* Test to make sure ClientLogger type is changed */ + @Test + public void testClientLoggerWithFullyQualifiedName() { + @Language("java") String before = "public class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.util.logging.ClientLogger c = new com.azure.core.util.logging.ClientLogger(Testing.class);"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "public class Testing {"; + after += "\n public Testing(){"; + after += "\n io.clientcore.core.util.ClientLogger c = new io.clientcore.core.util.ClientLogger(Testing.class);"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } + +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ContextTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ContextTest.java new file mode 100644 index 0000000000000..bbb4eef762851 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ContextTest.java @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +/** + * ContextTest is used to test out the recipe that converts code to use the + * new clientcore Context class. + */ +public class ContextTest implements RewriteTest { + + /** + * This method sets which recipe should be used for testing + * @param spec stores settings for testing environment; e.g. which recipes to use for testing + */ + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /** + * This test method is used to make sure that the Context class is updated + */ + @Test + void testImportReplaceContext() { + @Language("java") String before = "import com.azure.core.util.Context;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){}"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.util.Context;"; + after += "\npublic class Testing {"; + after += "\n public Testing(){}"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/CoreUtilsTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/CoreUtilsTest.java new file mode 100644 index 0000000000000..b2f674fafaa0e --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/CoreUtilsTest.java @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +public class CoreUtilsTest implements RewriteTest { + /** + * Test migrations from + * com.azure.core.util.CoreUtils to com.azure.core.v2.util.CoreUtils + */ + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /* Tests that CoreUtil import is changed */ + @Test + public void testConfigurationImportChanged() { + @Language("java") String before = "import com.azure.core.util.CoreUtils;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n CoreUtils cu = new CoreUtils();"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import com.azure.core.v2.util.CoreUtils;"; + after += "\n\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n CoreUtils cu = new CoreUtils();"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + + /* Tests that CoreUtils type is changed */ + @Test + public void testConfigurationTypeChanged() { + @Language("java") String before = ""; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.util.CoreUtils cu = new com.azure.core.util.CoreUtils();"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = ""; + after += "public class Testing {"; + after += "\n public Testing(){"; + after += "\n com.azure.core.v2.util.CoreUtils cu = new com.azure.core.v2.util.CoreUtils();"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + +} + diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/CredentialTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/CredentialTest.java new file mode 100644 index 0000000000000..e38951b687348 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/CredentialTest.java @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +/** + * CredentialTest is used to test out the recipe that changes the package name com.azure.core.credential + * to io.clientcore.core.credential. + */ +public class CredentialTest implements RewriteTest { + + /** + * This method sets which recipe should be used for testing + * @param spec stores settings for testing environment; e.g. which recipes to use for testing + */ + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /** + * This test method is used to make sure that the package com.azure.core.credential is changed + */ + @Test + void testCredentialPackageNameChange() { + @Language("java") String before = "import com.azure.core.credential.KeyCredential;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n KeyCredential kc = new KeyCredential(\"\");"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.credential.KeyCredential;"; + after += "\n\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n KeyCredential kc = new KeyCredential(\"\");"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + + /** + * This test method is used to make sure that the KeyCredential type is changed + */ + @Test + void testKeyCredentialChangeNoImport() { + @Language("java") String before = "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.credential.KeyCredential kc = new com.azure.core.credential.KeyCredential(\"\");"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n io.clientcore.core.credential.KeyCredential kc = new io.clientcore.core.credential.KeyCredential(\"\");"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + + /** + * This test method is used to make sure that KeyCredentialPolicy type and import is changed + */ + @Test + void testKeyCredentialPolicyTypeAndImportChange() { + @Language("java") String before = "import com.azure.core.http.policy.KeyCredentialPolicy;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.http.policy.KeyCredentialPolicy kc = new KeyCredentialPolicy(\"key\", null);"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.http.pipeline.KeyCredentialPolicy;"; + after += "\n\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n io.clientcore.core.http.pipeline.KeyCredentialPolicy kc = new KeyCredentialPolicy(\"key\", null);"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + + /** + * This test method is used to make sure that KeyCredentialTrait type and import is changed + */ + @Test + void testKeyCredentialTraitTypeAndImportChange() { + @Language("java") String before = "import com.azure.core.client.traits.KeyCredentialTrait;"; + before += "\npublic class Testing implements KeyCredentialTrait{"; + before += "\n public Testing(){"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.models.traits.KeyCredentialTrait;"; + after += "\n\npublic class Testing implements KeyCredentialTrait {"; + after += "\n public Testing(){"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ExceptionTypesTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ExceptionTypesTest.java new file mode 100644 index 0000000000000..c84682dd7c9bd --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ExceptionTypesTest.java @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +public class ExceptionTypesTest implements RewriteTest { + /** + * ExceptionTypesTest tests exception migrations from azure-core v1 + * to azure-core-v2 and client-core. + * Recipes used: ChangeType + * From: + * com.azure.core.exception + * ClientAuthenticationException + * HttpResponseException + * ResourceModifiedException + * ResourceNotFoundException + * To: + * com.azure.core.v2.exception + * ClientAuthenticationException + * ResourceModifiedException + * ResourceNotFoundException + * io.clientcore.core.http.exception + * HttpResponseException + * + */ + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /* Testing ChangeType recipes */ + @Test + public void testClientAuthenticationExceptionChanged() { + @Language("java") String before = "import com.azure.core.exception.ClientAuthenticationException;"; + before += "\npublic class Testing {"; + before += "\n public void testMethod() {"; + before += "\n try {"; + before += "\n // Some code that may throw ClientAuthenticationException"; + before += "\n } catch (ClientAuthenticationException e) {"; + before += "\n // Handle exception"; + before += "\n }"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import com.azure.core.v2.exception.ClientAuthenticationException;"; + after += "\n\npublic class Testing {"; + after += "\n public void testMethod() {"; + after += "\n try {"; + after += "\n // Some code that may throw ClientAuthenticationException"; + after += "\n } catch (ClientAuthenticationException e) {"; + after += "\n // Handle exception"; + after += "\n }"; + after += "\n }"; + after += "\n}"; + + rewriteRun( + java(before,after) + ); + } + + + @Test + public void testHttpResponseExceptionChanged() { + @Language("java") String before = "import com.azure.core.exception.HttpResponseException;"; + before += "\npublic class Testing {"; + before += "\n public void testMethod() {"; + before += "\n try {"; + before += "\n // Some code that may throw HttpResponseException"; + before += "\n } catch (HttpResponseException e) {"; + before += "\n // Handle exception"; + before += "\n }"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.http.exception.HttpResponseException;"; + after += "\n\npublic class Testing {"; + after += "\n public void testMethod() {"; + after += "\n try {"; + after += "\n // Some code that may throw HttpResponseException"; + after += "\n } catch (HttpResponseException e) {"; + after += "\n // Handle exception"; + after += "\n }"; + after += "\n }"; + after += "\n}"; + + rewriteRun( + java(before,after) + ); + } + + + @Test + public void testResourceModifiedExceptionChanged() { + @Language("java") String before = "import com.azure.core.exception.ResourceModifiedException;"; + before += "\npublic class Testing {"; + before += "\n public void testMethod() {"; + before += "\n try {"; + before += "\n // Some code that may throw ResourceModifiedException"; + before += "\n } catch (ResourceModifiedException e) {"; + before += "\n // Handle exception"; + before += "\n }"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import com.azure.core.v2.exception.ResourceModifiedException;"; + after += "\n\npublic class Testing {"; + after += "\n public void testMethod() {"; + after += "\n try {"; + after += "\n // Some code that may throw ResourceModifiedException"; + after += "\n } catch (ResourceModifiedException e) {"; + after += "\n // Handle exception"; + after += "\n }"; + after += "\n }"; + after += "\n}"; + + rewriteRun( + java(before,after) + ); + } + + + @Test + public void testResourceNotFoundExceptionChanged() { + @Language("java") String before = "import com.azure.core.exception.ResourceNotFoundException;"; + before += "\npublic class Testing {"; + before += "\n public void testMethod() {"; + before += "\n try {"; + before += "\n // Some code that may throw ResourceNotFoundException"; + before += "\n } catch (ResourceNotFoundException e) {"; + before += "\n // Handle exception"; + before += "\n }"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import com.azure.core.v2.exception.ResourceNotFoundException;"; + after += "\n\npublic class Testing {"; + after += "\n public void testMethod() {"; + after += "\n try {"; + after += "\n // Some code that may throw ResourceNotFoundException"; + after += "\n } catch (ResourceNotFoundException e) {"; + after += "\n // Handle exception"; + after += "\n }"; + after += "\n }"; + after += "\n}"; + + rewriteRun( + java(before,after) + ); + } + + /** + * Will fail and need updating if all azure-core v1 exceptions are migrated, + * or if all exceptions are migrated to the same directory. + */ + @Test + public void testBundledImportsChanged() { + @Language("java") String before = "import com.azure.core.exception.*;"; + before += "\npublic class Testing {"; + before += "\n public void testMethod() {"; + before += "\n try {"; + before += "\n // Some code that may throw ClientAuthenticationException"; + before += "\n throw new ClientAuthenticationException(null,null);"; + before += "\n } catch (ClientAuthenticationException e) {"; + before += "\n // Handle exception"; + before += "\n }"; + before += "\n try {"; + before += "\n // Some code that may throw HttpResponseException"; + before += "\n throw new HttpResponseException(null,null);"; + before += "\n } catch (HttpResponseException e) {"; + before += "\n // Handle exception"; + before += "\n }"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import com.azure.core.exception.*;" + + "\nimport com.azure.core.v2.exception.ClientAuthenticationException;" + + "\nimport io.clientcore.core.http.exception.HttpResponseException;"; + after += "\n\npublic class Testing {"; + after += "\n public void testMethod() {"; + after += "\n try {"; + after += "\n // Some code that may throw ClientAuthenticationException"; + after += "\n throw new ClientAuthenticationException(null,null);"; + after += "\n } catch (ClientAuthenticationException e) {"; + after += "\n // Handle exception"; + after += "\n }"; + after += "\n try {"; + after += "\n // Some code that may throw HttpResponseException"; + after += "\n throw new HttpResponseException(null,null);"; + after += "\n } catch (HttpResponseException e) {"; + after += "\n // Handle exception"; + after += "\n }"; + after += "\n }"; + after += "\n}"; + + rewriteRun( + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/HttpHeaderNameTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/HttpHeaderNameTest.java new file mode 100644 index 0000000000000..9c45314e09882 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/HttpHeaderNameTest.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +public class HttpHeaderNameTest implements RewriteTest { + /** + * HttpHeaderNameTest tests the recipe that changes + * com.azure.core.http.HttpHeaderName to io.clientcore.core.http.models.HttpHeaderName. + */ + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /* Test to make sure HttpHeaderName type is changed */ + @Test + public void testHeaderNameTypeChanged() { + @Language("java") String before = ""; + before += "public class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.http.HttpHeaderName h = new com.azure.core.http.HttpHeaderName();"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = ""; + after += "public class Testing {"; + after += "\n public Testing(){"; + after += "\n io.clientcore.core.http.models.HttpHeaderName h = new io.clientcore.core.http.models.HttpHeaderName();"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + + /* Test to make sure HttpHeaderName import is changed */ + @Test + public void testHeaderNameImportChanged() { + @Language("java") String before = "import com.azure.core.http.HttpHeaderName;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n HttpHeaderName h = new HttpHeaderName();"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.http.models.HttpHeaderName;"; + after += "\n\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n HttpHeaderName h = new HttpHeaderName();"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/HttpLogOptionsTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/HttpLogOptionsTest.java new file mode 100644 index 0000000000000..80030f24622bc --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/HttpLogOptionsTest.java @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +public class HttpLogOptionsTest implements RewriteTest { + /** + * HttpLogOptionsTest tests the recipe that changes + * com.azure.core.http.policy.HttpLogDetailLevel to io.clientcore.core.http.models.HttpLogOptions.HttpLogDetailLevel + * and com.azure.core.http.policy.HttpLogOptions to io.clientcore.core.http.models.HttpLogOptions + * + */ + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /* Test to make sure HttpLogOptions and HttpLogDetailLevel imports are changed*/ + @Test + public void testHttpLogOptionsLogLevelImportsChanged() { + @Language("java") String before = "import com.azure.core.http.policy.HttpLogOptions;"; + before += "\nimport com.azure.core.http.policy.HttpLogDetailLevel;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n HttpLogOptions h = new HttpLogOptions();h.setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS);"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.http.models.HttpLogOptions;"; + after += "\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n HttpLogOptions h = new HttpLogOptions();h.setLogLevel(HttpLogOptions.HttpLogDetailLevel.BODY_AND_HEADERS);"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } + + + /* Test to make sure HttpLogOptions and HttpLogDetailLevel type is changed*/ + @Test + public void testHttpLogOptionsLogLevelTypesChanged() { + @Language("java") String before = "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.http.policy.HttpLogOptions h = new com.azure.core.http.policy.HttpLogOptions();h.setLogLevel(com.azure.core.http.policy.HttpLogDetailLevel.BODY_AND_HEADERS);"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n io.clientcore.core.http.models.HttpLogOptions h = new io.clientcore.core.http.models.HttpLogOptions();h.setLogLevel( io.clientcore.core.http.models.HttpLogOptions.HttpLogDetailLevel.BODY_AND_HEADERS);"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } + +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/RequestOptionsTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/RequestOptionsTest.java new file mode 100644 index 0000000000000..f4bbbb4a33dee --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/RequestOptionsTest.java @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +/** + * RequestOptionsTest is used to test out the recipe that converts com.azure.core.http.rest.RequestOptions + * to io.clientcore.core.http.models.RequestOptions. + */ +public class RequestOptionsTest implements RewriteTest { + + /** + * This method sets which recipe should be used for testing + * @param spec stores settings for testing environment; e.g. which recipes to use for testing + */ + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /** + * This test method is used to make sure that the class import for RequestOptions is updated + */ + @Test + void testChangeRequestImportWithImport() { + @Language("java") String before = "import com.azure.core.http.rest.RequestOptions;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n RequestOptions r = new RequestOptions();"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.http.models.RequestOptions;"; + after += "\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n RequestOptions r = new RequestOptions();"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } + + /** + * This test method is used to make sure that the class type for RequestOptions is updated + */ + @Test + void testChangeRequestImportWithFullyQualifiedName() { + @Language("java") String before = "public class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.http.rest.RequestOptions r = new com.azure.core.http.rest.RequestOptions();"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "public class Testing {"; + after += "\n public Testing(){"; + after += "\n io.clientcore.core.http.models.RequestOptions r = new io.clientcore.core.http.models.RequestOptions();"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ResponseTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ResponseTest.java new file mode 100644 index 0000000000000..c5eaa3f6cdd1c --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/ResponseTest.java @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +/** + * ResponseTest is used to test out the recipe that changes + * references to com.azure.core.http.rest.response to + * io.clientcore.core.http.models.response. + */ +public class ResponseTest implements RewriteTest { + + /** + * This method sets which recipe should be used for testing + * @param spec stores settings for testing environment; e.g. which recipes to use for testing + */ + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /** + * This test method is used to make sure that the Response import is updated to the new version + */ + @Test + void testUpdateResponseTypeWithImport() { + @Language("java") String before = "import com.azure.core.http.rest.Response;\n"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n Response str = null;"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.http.models.Response;\n"; + after += "\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n Response str = null;"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } + /** + * This test method is used to make sure that the Response type is updated to the new version + */ + @Test + void testUpdateResponseTypeWithFullyQualifiedName() { + @Language("java") String before = "public class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.http.rest.Response str = null;"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "public class Testing {"; + after += "\n public Testing(){"; + after += "\n io.clientcore.core.http.models.Response str = null;"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/RetryOptionsConstructorTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/RetryOptionsConstructorTest.java new file mode 100644 index 0000000000000..dece38898a387 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/RetryOptionsConstructorTest.java @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +/** + * RetryOptionsTest is used to test out the recipe that removes usage of + * FixedDelay and ExponentialDelay from the RetryOptions constructor and updates + * it to use the new azure-core-v2 HttpRetryOptions class. + */ +public class RetryOptionsConstructorTest implements RewriteTest { + + /** + * This method sets which recipe should be used for testing + * @param spec stores settings for testing environment; e.g. which recipes to use for testing + */ + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /** + * This test method is used to make sure that RetryOptions is updated to the new constructor and class + */ + @Test + void testChangeRetryOptionsType() { + @Language("java") String before = "import com.azure.core.http.policy.RetryOptions;import java.time.Duration;import com.azure.core.http.policy.FixedDelayOptions;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n RetryOptions r = new RetryOptions(new FixedDelayOptions(3, Duration.ofMillis(50)));"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.http.models.HttpRetryOptions;\n\nimport java.time.Duration;\n"; + after += "\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n HttpRetryOptions r = new HttpRetryOptions(3, Duration.ofMillis(50));"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } + + /** + * This test method is used to make sure that RetryOptions is updated to the new constructor and class + * if the FixedDelayOptions is passed as a variable and not a direct instantiation in the constructor of + * the RetryOptions. + */ + @Test + void testChangeRetryOptionsTypeNoArgInit() { + @Language("java") String before = "import com.azure.core.http.policy.RetryOptions;import java.time.Duration;import com.azure.core.http.policy.FixedDelayOptions;"; + before += "\npublic class Testing {"; + before += "\n FixedDelayOptions f = new FixedDelayOptions(3, Duration.ofMillis(50));"; + before += "\n public Testing(){"; + before += "\n RetryOptions r = new RetryOptions(f);"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.http.models.HttpRetryOptions;\n\nimport java.time.Duration;\n"; + after += "\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n HttpRetryOptions r = new HttpRetryOptions(3, Duration.ofMillis(50));"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/TextTranslationClientBuilderHttpTraitTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/TextTranslationClientBuilderHttpTraitTest.java new file mode 100644 index 0000000000000..dddb0582339d3 --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/TextTranslationClientBuilderHttpTraitTest.java @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +/** + * HttpTraitTest tests interface migration from com.azure.core.client.traits.HttpTrait + * to io.clientcore.core.models.traits.HttpTrait. + * Tests simple method renaming with declarative recipe. + */ + +public class TextTranslationClientBuilderHttpTraitTest implements RewriteTest { + /** + * This method defines recipes used for testing. + * @param spec stores settings for testing environment. + */ + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /** + * Test simple declarative rename of: + * retryOptions to httpRetryOptions + * pipeline to httpPipeline + * addPolicy to addHttpPipelinePolicy + * and complex rename of clientOptions to httpRedirectOptions + */ + @Test + void testMethodsRenamedSuccessful() { + @Language("java") String before = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "\n" + + "public class UserClass {\n" + + "\n" + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder()\n" + + " .pipeline(null)\n" + + " .addPolicy(null)\n" + + " .retryOptions(null)\n" + + " .buildClient();\n" + + "\n" + + "}\n"; + + + @Language("java") String after = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "\n" + + "public class UserClass {\n" + + "\n" + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder()\n" + + " .httpPipeline(null)\n" + + " .addHttpPipelinePolicy(null)\n" + + " .httpRetryOptions(null)\n" + + " .buildClient();\n" + + "\n" + + "}\n"; + + rewriteRun( + java(before,after) + ); + } + + + @Test + void testUninitializedParamsAndImportsChanged() { + @Language("java") String before = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "import com.azure.core.http.HttpClient;\n" + + "import com.azure.core.http.HttpPipeline;\n" + + "import com.azure.core.http.policy.HttpLogOptions;\n" + + "import com.azure.core.http.policy.HttpPipelinePolicy;\n" + + "import com.azure.core.http.policy.RetryOptions;\n" + + //"import com.azure.core.util.ClientOptions;" + + "\n" + + "public class UserClass{\n" + + " \n" + + " HttpClient client;\n" + + " HttpPipeline pipeline;\n" + + " HttpPipelinePolicy httpPipelinePolicy;\n" + + " RetryOptions retryOptions;\n" + + " HttpLogOptions logOptions;\n" + + //" ClientOptions clientOptions;\n" + + " \n" + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder()\n" + + " .httpClient(client)\n" + + " .pipeline(pipeline)\n" + + " .addPolicy(httpPipelinePolicy)\n" + + " .retryOptions(retryOptions)\n" + + " .httpLogOptions(logOptions)\n" + + //" .clientOptions(clientOptions)\n" + + " .buildClient();\n" + + "}"; + + + @Language("java") String after = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "import io.clientcore.core.http.client.HttpClient;\n" + + "import io.clientcore.core.http.models.HttpLogOptions;\n" + + "import io.clientcore.core.http.models.HttpRetryOptions;\n" + + "import io.clientcore.core.http.pipeline.HttpPipeline;\n" + + "import io.clientcore.core.http.pipeline.HttpPipelinePolicy;\n" + + "\n" + + "public class UserClass {\n" + + " \n" + + " HttpClient client;\n" + + " HttpPipeline pipeline;\n" + + " HttpPipelinePolicy httpPipelinePolicy;\n" + + " HttpRetryOptions retryOptions;\n" + + " HttpLogOptions logOptions;\n" + + " \n" + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder()\n" + + " .httpClient(client)\n" + + " .httpPipeline(pipeline)\n" + + " .addHttpPipelinePolicy(httpPipelinePolicy)\n" + + " .httpRetryOptions(retryOptions)\n" + + " .httpLogOptions(logOptions)\n" + + " .buildClient();\n" + + "}"; + + rewriteRun( + java(before,after) + ); + } + + + @Test + void testLikeSampleImplementationChanged() { + @Language("java") String before = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "import com.azure.core.credential.AzureKeyCredential;\n" + + "import com.azure.core.http.policy.FixedDelayOptions;\n" + + "import com.azure.core.http.policy.HttpLogDetailLevel;\n" + + "import com.azure.core.http.policy.HttpLogOptions;\n" + + "import com.azure.core.http.policy.RetryOptions;\n" + + "import java.time.Duration;\n" + + "\n" + + "public class UserClass {\n" + + "\n" + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder()\n" + + " .credential(new AzureKeyCredential(\"\"))\n" + + " .endpoint(\"\")\n" + + " .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS))\n" + + " .retryOptions(new RetryOptions(new FixedDelayOptions(3, Duration.ofMillis(50))))\n" + + " .buildClient();" + + "\n" + + "}\n"; + + + @Language("java") String after = "import com.azure.ai.translation.text.TextTranslationClient;\n" + + "import com.azure.ai.translation.text.TextTranslationClientBuilder;\n" + + "import io.clientcore.core.credential.AzureKeyCredential;\n" + + "import io.clientcore.core.http.models.HttpLogOptions;\n" + + "import io.clientcore.core.http.models.HttpRetryOptions;\n\n" + + "import java.time.Duration;\n" + + "\n" + + "public class UserClass {\n" + + "\n" + + + " TextTranslationClient textTranslationClient = new TextTranslationClientBuilder()\n" + + " .credential(new AzureKeyCredential(\"\"))\n" + + " .endpoint(\"\")\n" + + // Copied from azure-ai-translation-text-v2 TextTranslationSample + // Not affected at all + " .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogOptions.HttpLogDetailLevel.BODY_AND_HEADERS))\n" + + " .httpRetryOptions(new HttpRetryOptions(3, Duration.ofMillis(50)))\n" + + " .buildClient();" + + "\n" + + "}\n"; + + rewriteRun( + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/TypeReferenceTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/TypeReferenceTest.java new file mode 100644 index 0000000000000..4f19caa4bcabb --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/TypeReferenceTest.java @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.TypeValidation; + +/** + * TypeReferenceTest is used to test out the recipe that changes the usage of TypeReference (azure core v1) + * to ParameterizedType (azure core v2) + */ +public class TypeReferenceTest implements RewriteTest { + + /** + * This method sets which recipe should be used for testing + * @param spec stores settings for testing environment; e.g. which recipes to use for testing + */ + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + // Added due to bug in OpenRewrite parser when parsing azure TypeReference instantiation + spec.typeValidationOptions(TypeValidation.none()); + } + + /** + * This test method is used to make sure that TypeReference is correctly + * changed to ParameterizedType when using List generic type + */ + @Test + void testTypeReferenceVariableDeclarationChangeList() { + @Language("java") String before = ""; + before += "\nimport java.lang.reflect.ParameterizedType;"; + before += "\nimport java.lang.reflect.Type;"; + before += "\nimport java.util.List;"; + before += "\nimport com.azure.core.util.serializer.TypeReference;"; + before += "\npublic class Testing {"; + before += "\n private static final TypeReference> TESTING_TYPE = new TypeReference>() {\n };"; + before += "\n}"; + + + @Language("java") String after = "import java.lang.reflect.ParameterizedType;\n" + + "import java.lang.reflect.Type;\n" + + "import java.util.List;\n\n"+ + "public class Testing {\n" + + " private static final Type TESTING_TYPE = new ParameterizedType() {\n" + + " @Override\n" + + " public Type getRawType() {\n" + + " return List.class;\n" + + " }\n\n" + + " @Override\n" + + " public Type[] getActualTypeArguments() {\n" + + " return new Type[]{String.class};\n" + + " }\n\n" + + " @Override\n" + + " public Type getOwnerType() {\n" + + " return null;\n" + + " }\n" + + " };\n" + + "}\n"; + + rewriteRun( + + java(before,after) + ); + } + /** + * This test method is used to make sure that TypeReference is correctly + * changed to ParameterizedType when using Map generic type + */ + @Test + void testTypeReferenceVariableDeclarationChangeMap() { + @Language("java") String before = ""; + before += "\nimport java.lang.reflect.ParameterizedType;"; + before += "\nimport java.lang.reflect.Type;"; + before += "\nimport java.util.Map;"; + before += "\nimport com.azure.core.util.serializer.TypeReference;"; + before += "\npublic class Testing {"; + before += "\n private static final TypeReference> TESTING_TYPE = new TypeReference>() {\n };"; + before += "\n}"; + + + @Language("java") String after = "import java.lang.reflect.ParameterizedType;\n" + + "import java.lang.reflect.Type;\n" + + "import java.util.Map;\n\n" + + "public class Testing {\n" + + " private static final Type TESTING_TYPE = new ParameterizedType() {\n" + + " @Override\n" + + " public Type getRawType() {\n" + + " return Map.class;\n" + + " }\n\n" + + " @Override\n" + + " public Type[] getActualTypeArguments() {\n" + + " return new Type[]{String.class, Integer.class};\n" + + " }\n\n" + + " @Override\n" + + " public Type getOwnerType() {\n" + + " return null;\n" + + " }\n" + + " };\n" + + "}\n"; + + rewriteRun( + java(before,after) + ); + } + /** + * This test method is used to make sure that TypeReference is correctly + * changed to ParameterizedType when using non-generic type + */ + @Test + void testTypeReferenceVariableDeclarationChangeNonGeneric() { + @Language("java") String before = ""; + before += "\nimport java.lang.reflect.ParameterizedType;"; + before += "\nimport java.lang.reflect.Type;"; + before += "\nimport com.azure.core.util.serializer.TypeReference;"; + before += "\npublic class Testing {"; + before += "\n private static final TypeReference TESTING_TYPE = new TypeReference() {\n };"; + before += "\n}"; + + + @Language("java") String after = "import java.lang.reflect.ParameterizedType;\n" + + "import java.lang.reflect.Type;\n\n" + + "public class Testing {\n" + + " private static final Type TESTING_TYPE = new ParameterizedType() {\n" + + " @Override\n" + + " public Type getRawType() {\n" + + " return String.class;\n" + + " }\n\n" + + " @Override\n" + + " public Type[] getActualTypeArguments() {\n" + + " return new Type[]{};\n" + + " }\n\n" + + " @Override\n" + + " public Type getOwnerType() {\n" + + " return null;\n" + + " }\n" + + " };\n" + + "}\n"; + + rewriteRun( + java(before,after) + ); + } +} diff --git a/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/UtilConfigurationTest.java b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/UtilConfigurationTest.java new file mode 100644 index 0000000000000..465eb7b3b563a --- /dev/null +++ b/sdk/tools/azure-openrewrite/src/test/java/com/azure/openrewrite/recipe/UtilConfigurationTest.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.openrewrite.recipe; + +import org.intellij.lang.annotations.Language; +import org.junit.jupiter.api.Test; +import static org.openrewrite.java.Assertions.java; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +public class UtilConfigurationTest implements RewriteTest { + /** + * UtilConfigurationTest tests util.Configuration migrations from + * com.azure.core.util to io.clientcore.core.util.configuration. + */ + + @Override + public void defaults(RecipeSpec spec) { + spec.recipeFromResource("/META-INF/rewrite/rewrite.yml", + "com.azure.openrewrite.migrateToVNext"); + } + + /* Testing ChangeType recipe for changing import */ + @Test + public void testConfigurationWithImport() { + @Language("java") String before = "import com.azure.core.util.Configuration;"; + before += "\npublic class Testing {"; + before += "\n public Testing(){"; + before += "\n Configuration c = new Configuration();"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "import io.clientcore.core.util.configuration.Configuration;"; + after += "\n\npublic class Testing {"; + after += "\n public Testing(){"; + after += "\n Configuration c = new Configuration();"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } + + /* Testing ChangeType recipe for changing type */ + @Test + public void testConfigurationWithFullyQualifiedName() { + @Language("java") String before = "public class Testing {"; + before += "\n public Testing(){"; + before += "\n com.azure.core.util.Configuration c = new com.azure.core.util.Configuration();"; + before += "\n }"; + before += "\n}"; + + @Language("java") String after = "public class Testing {"; + after += "\n public Testing(){"; + after += "\n io.clientcore.core.util.configuration.Configuration c = new io.clientcore.core.util.configuration.Configuration();"; + after += "\n }"; + after += "\n}"; + rewriteRun( + java(before, after) + ); + } + +} diff --git a/sdk/tools/ci.yml b/sdk/tools/ci.yml index 27a6413bd6605..bde31426cce99 100644 --- a/sdk/tools/ci.yml +++ b/sdk/tools/ci.yml @@ -6,10 +6,12 @@ trigger: include: - /sdk/tools/azure-sdk-archetype/ - /sdk/tools/azure-sdk-build-tool/ + - /sdk/tools/azure-openrewrite/ exclude: - sdk/tools/pom.xml - sdk/tools/azure-sdk-build-tool/pom.xml - sdk/tools/azure-sdk-archetype/pom.xml + - /sdk/tools/azure-openrewrite/pom.xml pr: branches: @@ -22,10 +24,12 @@ pr: include: - /sdk/tools/azure-sdk-archetype/ - /sdk/tools/azure-sdk-build-tool/ + - /sdk/tools/azure-openrewrite/ exclude: - sdk/tools/pom.xml - sdk/tools/azure-sdk-build-tool/pom.xml - sdk/tools/azure-sdk-archetype/pom.xml + - /sdk/tools/azure-openrewrite/pom.xml extends: template: /eng/pipelines/templates/stages/archetype-sdk-client.yml diff --git a/sdk/tools/pom.xml b/sdk/tools/pom.xml index 58a31f84a0aad..70ff59fb1c41e 100644 --- a/sdk/tools/pom.xml +++ b/sdk/tools/pom.xml @@ -11,5 +11,6 @@ azure-sdk-archetype azure-sdk-build-tool + azure-openrewrite