diff --git a/src/main/java/org/openrewrite/java/spring/cloud2022/MigrateRequestMappingOnFeignClient.java b/src/main/java/org/openrewrite/java/spring/cloud2022/MigrateRequestMappingOnFeignClient.java new file mode 100644 index 000000000..5a8101328 --- /dev/null +++ b/src/main/java/org/openrewrite/java/spring/cloud2022/MigrateRequestMappingOnFeignClient.java @@ -0,0 +1,131 @@ +/* + * Copyright 2025 the original author or authors. + *
+ * 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 + *
+ * https://docs.moderne.io/licensing/moderne-source-available-license + *
+ * 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
+ * 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
+ *
+ * https://docs.moderne.io/licensing/moderne-source-available-license
+ *
+ * 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.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.InMemoryExecutionContext;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class MigrateRequestMappingOnFeignClientTest implements RewriteTest {
+
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec
+ .recipe(new MigrateRequestMappingOnFeignClient())
+ .parser(JavaParser.fromJavaVersion()
+ .classpathFromResources(new InMemoryExecutionContext(),
+ "spring-web", "spring-cloud-openfeign-core"));
+ }
+
+ @Test
+ @DocumentExample
+ void migrateRequestMappingAnnotation() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+ import org.springframework.web.bind.annotation.GetMapping;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ @RequestMapping(path = "/posts")
+ public interface MyServiceClient {
+
+ @GetMapping(value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """,
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+ import org.springframework.web.bind.annotation.GetMapping;
+
+ @FeignClient(path = "/posts", name = "myService", url = "http://localhost:8080")
+ public interface MyServiceClient {
+
+ @GetMapping(value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void requestMappingWithDefaultAttributeName() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+ import org.springframework.web.bind.annotation.GetMapping;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ @RequestMapping("/posts")
+ public interface MyServiceClient {
+
+ @GetMapping(value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """,
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+ import org.springframework.web.bind.annotation.GetMapping;
+
+ @FeignClient(path = "/posts", name = "myService", url = "http://localhost:8080")
+ public interface MyServiceClient {
+
+ @GetMapping(value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void requestMappingWithValueAttributeName() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+ import org.springframework.web.bind.annotation.GetMapping;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ @RequestMapping(value = "/posts")
+ public interface MyServiceClient {
+
+ @GetMapping(value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """,
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+ import org.springframework.web.bind.annotation.GetMapping;
+
+ @FeignClient(path = "/posts", name = "myService", url = "http://localhost:8080")
+ public interface MyServiceClient {
+
+ @GetMapping(value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void withRequestMappingAnnotationOnMethod() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ @RequestMapping(path = "/posts")
+ public interface MyServiceClient {
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """,
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+
+ @FeignClient(path = "/posts", name = "myService", url = "http://localhost:8080")
+ public interface MyServiceClient {
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void withNonRequestMappingOnFeignClientButWithOneOnMethod() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ public interface MyServiceClient {
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void requestMappingAnnotationWithMultipleAttributes() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ @RequestMapping(headers = "X-My-Header=MyValue", path = "/posts")
+ public interface MyServiceClient {
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void requestMappingAnnotationWithNonAttributes() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ @RequestMapping
+ public interface MyServiceClient {
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """,
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ public interface MyServiceClient {
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void requestMappingAnnotationWithOnlyHeadersAttribute() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080")
+ @RequestMapping(headers = "X-My-Header=MyValue")
+ public interface MyServiceClient {
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void feignClientAnnotationAlreadyHasPathAttribute() {
+ rewriteRun(
+ //language=java
+ java(
+ """
+ import org.springframework.cloud.openfeign.FeignClient;
+ import org.springframework.web.bind.annotation.RequestMapping;
+ import org.springframework.web.bind.annotation.RequestMethod;
+ import org.springframework.web.bind.annotation.PathVariable;
+
+ @FeignClient(name = "myService", url = "http://localhost:8080", path = "/api/v1")
+ @RequestMapping(path = "/posts")
+ public interface MyServiceClient {
+
+ @RequestMapping(method = RequestMethod.GET, value = "/{postId}", produces = "application/json")
+ String getPostById(@PathVariable("postId") Long postId);
+ }
+ """
+ )
+ );
+ }
+
+}