Skip to content

Commit

Permalink
Recipe to migrate @RequestMapping over a FeignClient interface to…
Browse files Browse the repository at this point in the history
… `@FeignClient` path attribute (#661)

* Recipe to migrate `@RequestMapping` over a `FeignClient` interface to ``@FeignClient` path attribute

* Remove processing for `@RequestMapping` with multiple attributes

* Recipe to migrate `@RequestMapping` over a `FeignClient` interface to ``@FeignClient` path attribute

* Resolve comments

* Revert gradle.properties update

---------

Co-authored-by: chao.wang <[email protected]>
  • Loading branch information
wapkch and chao.wang authored Jan 10, 2025
1 parent 5f5b5b7 commit b121b80
Show file tree
Hide file tree
Showing 4 changed files with 445 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://docs.moderne.io/licensing/moderne-source-available-license
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.spring.cloud2022;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.java.AddOrUpdateAnnotationAttribute;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.RemoveAnnotation;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.TypeUtils;

public class MigrateRequestMappingOnFeignClient extends Recipe {

private static final String FEIGN_CLIENT = "org.springframework.cloud.openfeign.FeignClient";

private static final String REQUEST_MAPPING = "org.springframework.web.bind.annotation.RequestMapping";

@Override
public String getDisplayName() {
return "Migrate `@RequestMapping` on `FeignClient` to `@FeignClient` path attribute";
}

@Override
public String getDescription() {
return "Support for `@RequestMapping` over a `FeignClient` interface was removed in Spring Cloud OpenFeign 2.2.10.RELEASE.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(Preconditions.and(
new UsesType<>(FEIGN_CLIENT, false),
new UsesType<>(REQUEST_MAPPING, false)),
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
J.Annotation requestMapping = classDecl.getLeadingAnnotations().stream()
.filter(a -> TypeUtils.isOfClassType(a.getType(), REQUEST_MAPPING))
.findFirst().orElse(null);
J.Annotation feignClient = classDecl.getLeadingAnnotations().stream()
.filter(a -> TypeUtils.isOfClassType(a.getType(), FEIGN_CLIENT))
.findFirst().orElse(null);

if (requestMapping != null && feignClient != null) {
J.ClassDeclaration cd = classDecl;
if (requestMapping.getArguments() == null || requestMapping.getArguments().isEmpty()) {
cd = removeRequestMapping(cd, ctx);
} else if (requestMapping.getArguments().size() == 1) {
String pathValueFromRequestMapping = getPathValue(requestMapping.getArguments().get(0));
if (pathValueFromRequestMapping != null && !hasPathAttribute(feignClient)) {
cd = removeRequestMapping(cd, ctx);
cd = addAttributeToFeignClient(cd, ctx, pathValueFromRequestMapping);
}
}
return cd;
}
return super.visitClassDeclaration(classDecl, ctx);
}

private boolean hasPathAttribute(J.Annotation annotation) {
if (annotation.getArguments() == null || annotation.getArguments().isEmpty()) {
return false;
}
return annotation.getArguments().stream().anyMatch(arg -> {
if (arg instanceof J.Assignment) {
J.Assignment assignment = (J.Assignment) arg;
if (assignment.getVariable() instanceof J.Identifier) {
J.Identifier variable = (J.Identifier) assignment.getVariable();
return "path".equals(variable.getSimpleName());
}
}
return false;
});
}

private J.ClassDeclaration addAttributeToFeignClient(J.ClassDeclaration cd, ExecutionContext ctx, String path) {
return cd.withLeadingAnnotations(
ListUtils.map(cd.getLeadingAnnotations(), a -> (J.Annotation)
new AddOrUpdateAnnotationAttribute(FEIGN_CLIENT, "path",
path, true, false).getVisitor()
.visit(a, ctx, getCursor().getParentOrThrow())));
}

private J.ClassDeclaration removeRequestMapping(J.ClassDeclaration classDecl, ExecutionContext ctx) {
maybeRemoveImport(REQUEST_MAPPING);
return classDecl.withLeadingAnnotations(ListUtils.map(classDecl.getLeadingAnnotations(),
a -> (J.Annotation) new RemoveAnnotation(REQUEST_MAPPING).getVisitor()
.visit(a, ctx, getCursor().getParentOrThrow())));
}

private String getPathValue(Expression arg) {
if (arg instanceof J.Literal) {
J.Literal literal = (J.Literal) arg;
return (String) literal.getValue();
} else if (arg instanceof J.Assignment) {
J.Assignment assignment = (J.Assignment) arg;
if (assignment.getVariable() instanceof J.Identifier) {
J.Identifier variable = (J.Identifier) assignment.getVariable();
if ("path".equals(variable.getSimpleName()) || "value".equals(variable.getSimpleName())) {
Expression expression = assignment.getAssignment();
if (expression instanceof J.Literal) {
J.Literal value = (J.Literal) expression;
return (String) value.getValue();
}
}
}
}
return null;
}
});
}

}
Binary file not shown.
1 change: 1 addition & 0 deletions src/main/resources/META-INF/rewrite/spring-cloud-2022.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ tags:
recipeList:
- org.openrewrite.java.spring.cloud2022.DependencyUpgrades
- org.openrewrite.java.spring.cloud2022.MigrateCloudSleuthToMicrometerTracing
- org.openrewrite.java.spring.cloud2022.MigrateRequestMappingOnFeignClient

---
type: specs.openrewrite.org/v1beta/recipe
Expand Down
Loading

0 comments on commit b121b80

Please sign in to comment.