Skip to content

Commit b121b80

Browse files
wapkchchao.wang
andauthored
Recipe to migrate @RequestMapping over a FeignClient interface to @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]>
1 parent 5f5b5b7 commit b121b80

File tree

4 files changed

+445
-0
lines changed

4 files changed

+445
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.spring.cloud2022;
17+
18+
import org.openrewrite.ExecutionContext;
19+
import org.openrewrite.Preconditions;
20+
import org.openrewrite.Recipe;
21+
import org.openrewrite.TreeVisitor;
22+
import org.openrewrite.internal.ListUtils;
23+
import org.openrewrite.java.AddOrUpdateAnnotationAttribute;
24+
import org.openrewrite.java.JavaIsoVisitor;
25+
import org.openrewrite.java.RemoveAnnotation;
26+
import org.openrewrite.java.search.UsesType;
27+
import org.openrewrite.java.tree.Expression;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.java.tree.TypeUtils;
30+
31+
public class MigrateRequestMappingOnFeignClient extends Recipe {
32+
33+
private static final String FEIGN_CLIENT = "org.springframework.cloud.openfeign.FeignClient";
34+
35+
private static final String REQUEST_MAPPING = "org.springframework.web.bind.annotation.RequestMapping";
36+
37+
@Override
38+
public String getDisplayName() {
39+
return "Migrate `@RequestMapping` on `FeignClient` to `@FeignClient` path attribute";
40+
}
41+
42+
@Override
43+
public String getDescription() {
44+
return "Support for `@RequestMapping` over a `FeignClient` interface was removed in Spring Cloud OpenFeign 2.2.10.RELEASE.";
45+
}
46+
47+
@Override
48+
public TreeVisitor<?, ExecutionContext> getVisitor() {
49+
return Preconditions.check(Preconditions.and(
50+
new UsesType<>(FEIGN_CLIENT, false),
51+
new UsesType<>(REQUEST_MAPPING, false)),
52+
new JavaIsoVisitor<ExecutionContext>() {
53+
@Override
54+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
55+
J.Annotation requestMapping = classDecl.getLeadingAnnotations().stream()
56+
.filter(a -> TypeUtils.isOfClassType(a.getType(), REQUEST_MAPPING))
57+
.findFirst().orElse(null);
58+
J.Annotation feignClient = classDecl.getLeadingAnnotations().stream()
59+
.filter(a -> TypeUtils.isOfClassType(a.getType(), FEIGN_CLIENT))
60+
.findFirst().orElse(null);
61+
62+
if (requestMapping != null && feignClient != null) {
63+
J.ClassDeclaration cd = classDecl;
64+
if (requestMapping.getArguments() == null || requestMapping.getArguments().isEmpty()) {
65+
cd = removeRequestMapping(cd, ctx);
66+
} else if (requestMapping.getArguments().size() == 1) {
67+
String pathValueFromRequestMapping = getPathValue(requestMapping.getArguments().get(0));
68+
if (pathValueFromRequestMapping != null && !hasPathAttribute(feignClient)) {
69+
cd = removeRequestMapping(cd, ctx);
70+
cd = addAttributeToFeignClient(cd, ctx, pathValueFromRequestMapping);
71+
}
72+
}
73+
return cd;
74+
}
75+
return super.visitClassDeclaration(classDecl, ctx);
76+
}
77+
78+
private boolean hasPathAttribute(J.Annotation annotation) {
79+
if (annotation.getArguments() == null || annotation.getArguments().isEmpty()) {
80+
return false;
81+
}
82+
return annotation.getArguments().stream().anyMatch(arg -> {
83+
if (arg instanceof J.Assignment) {
84+
J.Assignment assignment = (J.Assignment) arg;
85+
if (assignment.getVariable() instanceof J.Identifier) {
86+
J.Identifier variable = (J.Identifier) assignment.getVariable();
87+
return "path".equals(variable.getSimpleName());
88+
}
89+
}
90+
return false;
91+
});
92+
}
93+
94+
private J.ClassDeclaration addAttributeToFeignClient(J.ClassDeclaration cd, ExecutionContext ctx, String path) {
95+
return cd.withLeadingAnnotations(
96+
ListUtils.map(cd.getLeadingAnnotations(), a -> (J.Annotation)
97+
new AddOrUpdateAnnotationAttribute(FEIGN_CLIENT, "path",
98+
path, true, false).getVisitor()
99+
.visit(a, ctx, getCursor().getParentOrThrow())));
100+
}
101+
102+
private J.ClassDeclaration removeRequestMapping(J.ClassDeclaration classDecl, ExecutionContext ctx) {
103+
maybeRemoveImport(REQUEST_MAPPING);
104+
return classDecl.withLeadingAnnotations(ListUtils.map(classDecl.getLeadingAnnotations(),
105+
a -> (J.Annotation) new RemoveAnnotation(REQUEST_MAPPING).getVisitor()
106+
.visit(a, ctx, getCursor().getParentOrThrow())));
107+
}
108+
109+
private String getPathValue(Expression arg) {
110+
if (arg instanceof J.Literal) {
111+
J.Literal literal = (J.Literal) arg;
112+
return (String) literal.getValue();
113+
} else if (arg instanceof J.Assignment) {
114+
J.Assignment assignment = (J.Assignment) arg;
115+
if (assignment.getVariable() instanceof J.Identifier) {
116+
J.Identifier variable = (J.Identifier) assignment.getVariable();
117+
if ("path".equals(variable.getSimpleName()) || "value".equals(variable.getSimpleName())) {
118+
Expression expression = assignment.getAssignment();
119+
if (expression instanceof J.Literal) {
120+
J.Literal value = (J.Literal) expression;
121+
return (String) value.getValue();
122+
}
123+
}
124+
}
125+
}
126+
return null;
127+
}
128+
});
129+
}
130+
131+
}
211 KB
Binary file not shown.

src/main/resources/META-INF/rewrite/spring-cloud-2022.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tags:
2525
recipeList:
2626
- org.openrewrite.java.spring.cloud2022.DependencyUpgrades
2727
- org.openrewrite.java.spring.cloud2022.MigrateCloudSleuthToMicrometerTracing
28+
- org.openrewrite.java.spring.cloud2022.MigrateRequestMappingOnFeignClient
2829

2930
---
3031
type: specs.openrewrite.org/v1beta/recipe

0 commit comments

Comments
 (0)