diff --git a/core/src/test/java/feign/RequestTemplateTest.java b/core/src/test/java/feign/RequestTemplateTest.java index 9b700065e..c8d29a4b2 100644 --- a/core/src/test/java/feign/RequestTemplateTest.java +++ b/core/src/test/java/feign/RequestTemplateTest.java @@ -465,8 +465,8 @@ void fragmentShouldNotBeEncodedInTarget() { @Test void fragmentShouldBeExtractedWhenQueryParamsExist() { RequestTemplate template = - new RequestTemplate().method(HttpMethod.GET).uri("/path?query=queryValue#fragment", - true); + new RequestTemplate().method(HttpMethod.GET).uri("/path?query=queryValue#fragment", + true); assertThat(template.url()).isEqualTo("/path?query=queryValue#fragment"); assertThat(template.queryLine()).isEqualTo("?query=queryValue"); diff --git a/fastjson2/pom.xml b/fastjson2/pom.xml index 68c6d1c9f..3000562f7 100644 --- a/fastjson2/pom.xml +++ b/fastjson2/pom.xml @@ -14,41 +14,39 @@ the License. --> - - 4.0.0 - - - io.github.openfeign - parent - 13.3-SNAPSHOT - - - feign-fastjson2 - Feign Fastjson2 - Feign Fastjson2 - - - ${project.basedir}/.. - - - - - ${project.groupId} - feign-core - - - - com.alibaba.fastjson2 - fastjson2 - - - - ${project.groupId} - feign-core - test-jar - test - - - \ No newline at end of file + + 4.0.0 + + + io.github.openfeign + parent + 13.3-SNAPSHOT + + + feign-fastjson2 + Feign Fastjson2 + Feign Fastjson2 + + + ${project.basedir}/.. + + + + + ${project.groupId} + feign-core + + + + com.alibaba.fastjson2 + fastjson2 + + + + ${project.groupId} + feign-core + test-jar + test + + + diff --git a/jaxrs2/pom.xml b/jaxrs2/pom.xml index d773086f7..497eae850 100644 --- a/jaxrs2/pom.xml +++ b/jaxrs2/pom.xml @@ -105,6 +105,16 @@ + + maven-jar-plugin + + + + test-jar + + + + org.eclipse.transformer org.eclipse.transformer.maven diff --git a/jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java b/jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java index 8730a256b..a9646553a 100644 --- a/jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java +++ b/jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 The Feign Authors + * Copyright 2012-2024 The Feign Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -13,13 +13,17 @@ */ package feign.jaxrs2; -import feign.jaxrs.JAXRSContract; -import javax.ws.rs.*; -import javax.ws.rs.container.Suspended; -import javax.ws.rs.core.Context; -import java.lang.reflect.Field; import static feign.Util.checkState; import static feign.Util.emptyToNull; +import java.lang.reflect.Field; +import javax.ws.rs.BeanParam; +import javax.ws.rs.FormParam; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.container.Suspended; +import javax.ws.rs.core.Context; +import feign.jaxrs.JAXRSContract; /** * Please refer to the Feign diff --git a/jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java b/jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java index 17af624b8..943c0555a 100644 --- a/jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java +++ b/jaxrs2/src/main/java/feign/jaxrs2/JAXRSClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 The Feign Authors + * Copyright 2012-2024 The Feign Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -23,7 +23,12 @@ import java.util.stream.Collectors; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; -import javax.ws.rs.core.*; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Variant; import feign.Client; import feign.Request.Options; diff --git a/jaxrs2/src/test/java/feign/jaxrs2/AbstractJAXRSClientTest.java b/jaxrs2/src/test/java/feign/jaxrs2/AbstractJAXRSClientTest.java new file mode 100644 index 000000000..05fdf37d0 --- /dev/null +++ b/jaxrs2/src/test/java/feign/jaxrs2/AbstractJAXRSClientTest.java @@ -0,0 +1,162 @@ +/* + * Copyright 2012-2024 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 feign.jaxrs2; + +import static feign.Util.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeFalse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Collections; +import org.assertj.core.data.MapEntry; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; +import feign.Headers; +import feign.RequestLine; +import feign.Response; +import feign.Util; +import feign.assertj.MockWebServerAssertions; +import feign.client.AbstractClientTest; +import okhttp3.mockwebserver.MockResponse; + +public abstract class AbstractJAXRSClientTest extends AbstractClientTest { + + @Override + public void patch() throws Exception { + try { + super.patch(); + } catch (final RuntimeException e) { + Assumptions.assumeFalse(false, "JaxRS client do not support PATCH requests"); + } + } + + @Override + public void noResponseBodyForPut() throws Exception { + try { + super.noResponseBodyForPut(); + } catch (final IllegalStateException e) { + Assumptions.assumeFalse(false, "JaxRS client do not support empty bodies on PUT"); + } + } + + @Override + public void noResponseBodyForPatch() { + try { + super.noResponseBodyForPatch(); + } catch (final IllegalStateException e) { + Assumptions.assumeFalse(false, "JaxRS client do not support PATCH requests"); + } + } + + @Override + @Test + public void reasonPhraseIsOptional() throws IOException, InterruptedException { + server.enqueue(new MockResponse().setStatus("HTTP/1.1 " + 200)); + + final TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + final Response response = api.post("foo"); + + assertThat(response.status()).isEqualTo(200); + // jaxrsclient is creating a reason when none is present + // assertThat(response.reason()).isNullOrEmpty(); + } + + @Override + @Test + public void parsesRequestAndResponse() throws IOException, InterruptedException { + server.enqueue(new MockResponse().setBody("foo").addHeader("Foo: Bar")); + + final TestInterface api = newBuilder() + .target(TestInterface.class, "http://localhost:" + server.getPort()); + + final Response response = api.post("foo"); + + assertThat(response.status()).isEqualTo(200); + assertThat(response.reason()).isEqualTo("OK"); + assertThat(response.headers()) + .hasEntrySatisfying("Content-Length", value -> { + assertThat(value).contains("3"); + }).hasEntrySatisfying("Foo", value -> { + assertThat(value).contains("Bar"); + }); + assertThat(response.body().asInputStream()) + .hasSameContentAs(new ByteArrayInputStream("foo".getBytes(UTF_8))); + + /* queries with no values are omitted from the uri. See RFC 6750 */ + MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST") + .hasPath("/?foo=bar&foo=baz&qux") + .hasBody("foo"); + } + + @Test + void contentTypeWithoutCharset2() throws Exception { + server.enqueue(new MockResponse() + .setBody("AAAAAAAA")); + final JaxRSClientTestInterface api = newBuilder() + .target(JaxRSClientTestInterface.class, "http://localhost:" + server.getPort()); + + final Response response = api.getWithContentType(); + // Response length should not be null + assertThat(Util.toString(response.body().asReader(UTF_8))).isEqualTo("AAAAAAAA"); + + MockWebServerAssertions.assertThat(server.takeRequest()) + .hasHeaders( + MapEntry.entry("Accept", Collections.singletonList("text/plain")), + MapEntry.entry("Content-Type", Collections.singletonList("text/plain"))) + .hasMethod("GET"); + } + + /* + * JaxRS does not support gzip and deflate compression out-of-the-box. + */ + @Override + public void canSupportGzip() throws Exception { + assumeFalse(false, "JaxRS client do not support gzip compression"); + } + + @Override + public void canSupportGzipOnError() throws Exception { + assumeFalse(false, "JaxRS client do not support gzip compression"); + } + + @Override + public void canSupportDeflate() throws Exception { + assumeFalse(false, "JaxRS client do not support deflate compression"); + } + + @Override + public void canSupportDeflateOnError() throws Exception { + assumeFalse(false, "JaxRS client do not support deflate compression"); + } + + @Override + public void canExceptCaseInsensitiveHeader() throws Exception { + assumeFalse(false, "JaxRS client do not support gzip compression"); + } + + public interface JaxRSClientTestInterface { + + @RequestLine("GET /") + @Headers({"Accept: text/plain", "Content-Type: text/plain"}) + Response getWithContentType(); + } + + + @Override + public void veryLongResponseNullLength() { + assumeFalse(false, "JaxRS client hang if the response doesn't have a payload"); + } +} diff --git a/jaxrs2/src/test/java/feign/jaxrs2/JAXRSClientTest.java b/jaxrs2/src/test/java/feign/jaxrs2/JAXRSClientTest.java index 17083888b..95cded665 100644 --- a/jaxrs2/src/test/java/feign/jaxrs2/JAXRSClientTest.java +++ b/jaxrs2/src/test/java/feign/jaxrs2/JAXRSClientTest.java @@ -15,125 +15,32 @@ import static feign.Util.UTF_8; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assumptions.assumeFalse; -import java.io.ByteArrayInputStream; -import java.io.IOException; import java.util.Collections; import javax.ws.rs.Consumes; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.Path; -import javax.ws.rs.ProcessingException; import org.assertj.core.data.MapEntry; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import feign.Feign; import feign.Feign.Builder; -import feign.Headers; -import feign.RequestLine; import feign.Response; import feign.Util; import feign.assertj.MockWebServerAssertions; -import feign.client.AbstractClientTest; import feign.jaxrs.JAXRSContract; import okhttp3.mockwebserver.MockResponse; /** * Tests client-specific behavior, such as ensuring Content-Length is sent when specified. */ -public class JAXRSClientTest extends AbstractClientTest { +public class JAXRSClientTest extends AbstractJAXRSClientTest { @Override public Builder newBuilder() { return Feign.builder().client(new JAXRSClient()); } - @Override - public void patch() throws Exception { - try { - super.patch(); - } catch (final ProcessingException e) { - Assumptions.assumeFalse(false, "JaxRS client do not support PATCH requests"); - } - } - - @Override - public void noResponseBodyForPut() throws Exception { - try { - super.noResponseBodyForPut(); - } catch (final IllegalStateException e) { - Assumptions.assumeFalse(false, "JaxRS client do not support empty bodies on PUT"); - } - } - - @Override - public void noResponseBodyForPatch() { - try { - super.noResponseBodyForPatch(); - } catch (final IllegalStateException e) { - Assumptions.assumeFalse(false, "JaxRS client do not support PATCH requests"); - } - } - - @Override - @Test - public void reasonPhraseIsOptional() throws IOException, InterruptedException { - server.enqueue(new MockResponse().setStatus("HTTP/1.1 " + 200)); - - final TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - final Response response = api.post("foo"); - - assertThat(response.status()).isEqualTo(200); - // jaxrsclient is creating a reason when none is present - // assertThat(response.reason()).isNullOrEmpty(); - } - - @Override - @Test - public void parsesRequestAndResponse() throws IOException, InterruptedException { - server.enqueue(new MockResponse().setBody("foo").addHeader("Foo: Bar")); - - final TestInterface api = newBuilder() - .target(TestInterface.class, "http://localhost:" + server.getPort()); - - final Response response = api.post("foo"); - - assertThat(response.status()).isEqualTo(200); - assertThat(response.reason()).isEqualTo("OK"); - assertThat(response.headers()) - .hasEntrySatisfying("Content-Length", value -> { - assertThat(value).contains("3"); - }).hasEntrySatisfying("Foo", value -> { - assertThat(value).contains("Bar"); - }); - assertThat(response.body().asInputStream()) - .hasSameContentAs(new ByteArrayInputStream("foo".getBytes(UTF_8))); - - /* queries with no values are omitted from the uri. See RFC 6750 */ - MockWebServerAssertions.assertThat(server.takeRequest()).hasMethod("POST") - .hasPath("/?foo=bar&foo=baz&qux") - .hasBody("foo"); - } - - @Test - void contentTypeWithoutCharset2() throws Exception { - server.enqueue(new MockResponse() - .setBody("AAAAAAAA")); - final JaxRSClientTestInterface api = newBuilder() - .target(JaxRSClientTestInterface.class, "http://localhost:" + server.getPort()); - final Response response = api.getWithContentType(); - // Response length should not be null - assertThat(Util.toString(response.body().asReader(UTF_8))).isEqualTo("AAAAAAAA"); - - MockWebServerAssertions.assertThat(server.takeRequest()) - .hasHeaders( - MapEntry.entry("Accept", Collections.singletonList("text/plain")), - MapEntry.entry("Content-Type", Collections.singletonList("text/plain"))) - .hasMethod("GET"); - } @Test void consumesMultipleWithContentTypeHeaderAndBody() throws Exception { @@ -153,41 +60,6 @@ void consumesMultipleWithContentTypeHeaderAndBody() throws Exception { .hasMethod("POST"); } - /* - * JaxRS does not support gzip and deflate compression out-of-the-box. - */ - @Override - public void canSupportGzip() throws Exception { - assumeFalse(false, "JaxRS client do not support gzip compression"); - } - - @Override - public void canSupportGzipOnError() throws Exception { - assumeFalse(false, "JaxRS client do not support gzip compression"); - } - - @Override - public void canSupportDeflate() throws Exception { - assumeFalse(false, "JaxRS client do not support deflate compression"); - } - - @Override - public void canSupportDeflateOnError() throws Exception { - assumeFalse(false, "JaxRS client do not support deflate compression"); - } - - @Override - public void canExceptCaseInsensitiveHeader() throws Exception { - assumeFalse(false, "JaxRS client do not support gzip compression"); - } - - public interface JaxRSClientTestInterface { - - @RequestLine("GET /") - @Headers({"Accept: text/plain", "Content-Type: text/plain"}) - Response getWithContentType(); - } - public interface JaxRSClientTestInterfaceWithJaxRsContract { @Path("/") @POST @@ -195,9 +67,4 @@ public interface JaxRSClientTestInterfaceWithJaxRsContract { Response consumesMultipleWithContentTypeHeaderAndBody(@HeaderParam("Content-Type") String contentType, String body); } - - @Override - public void veryLongResponseNullLength() { - assumeFalse(false, "JaxRS client hang if the response doesn't have a payload"); - } } diff --git a/jaxrs3/pom.xml b/jaxrs3/pom.xml index fac192d7e..3683dc085 100644 --- a/jaxrs3/pom.xml +++ b/jaxrs3/pom.xml @@ -29,6 +29,7 @@ 11 + 3.1.6 ${project.basedir}/.. @@ -67,7 +68,6 @@ ${project.groupId} feign-jaxrs test-jar - test javax.ws.rs @@ -75,6 +75,30 @@ + + + ${project.groupId} + feign-jaxrs2 + test-jar + + + com.squareup.okhttp3 + mockwebserver + test + + + org.glassfish.jersey.core + jersey-client + ${jersey.version} + test + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey.version} + test + + diff --git a/jaxrs3/src/main/java/feign/jaxrs3/JAXRS3Contract.java b/jaxrs3/src/main/java/feign/jaxrs3/JAXRS3Contract.java index 60083e07d..333b6ad3e 100644 --- a/jaxrs3/src/main/java/feign/jaxrs3/JAXRS3Contract.java +++ b/jaxrs3/src/main/java/feign/jaxrs3/JAXRS3Contract.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 The Feign Authors + * Copyright 2012-2024 The Feign Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at @@ -13,27 +13,7 @@ */ package feign.jaxrs3; -import static feign.Util.checkState; -import static feign.Util.emptyToNull; -import static feign.Util.removeValues; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Collections; -import feign.MethodMetadata; -import feign.Request; import feign.jaxrs2.JAXRS2Contract; -import jakarta.ws.rs.BeanParam; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.FormParam; -import jakarta.ws.rs.HeaderParam; -import jakarta.ws.rs.HttpMethod; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.container.Suspended; -import jakarta.ws.rs.core.Context; public class JAXRS3Contract extends JAXRS2Contract { } diff --git a/jaxrs3/src/test/java/feign/jaxrs3/JAXRS3ClientTest.java b/jaxrs3/src/test/java/feign/jaxrs3/JAXRS3ClientTest.java new file mode 100644 index 000000000..88cdfb138 --- /dev/null +++ b/jaxrs3/src/test/java/feign/jaxrs3/JAXRS3ClientTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012-2024 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 feign.jaxrs3; + +import feign.Feign; +import feign.Feign.Builder; +import feign.jaxrs2.AbstractJAXRSClientTest; + +/** + * Tests client-specific behavior, such as ensuring Content-Length is sent when specified. + */ +public class JAXRS3ClientTest extends AbstractJAXRSClientTest { + + @Override + public Builder newBuilder() { + return Feign.builder().client(new JAXRS3Client()); + } + +} diff --git a/jaxrs4/README.md b/jaxrs4/README.md new file mode 100644 index 000000000..ddda3b9f8 --- /dev/null +++ b/jaxrs4/README.md @@ -0,0 +1,5 @@ +# Feign Jakarta JaxRS 4 + +Feign Jakarta JaxRS 4 module is dedicated to testing the compatibility of the JaxRS 3 module with JaxRS 4 to ensure seamless integration with the latest version. + +A separated module is required due java version requirement. diff --git a/jaxrs4/pom.xml b/jaxrs4/pom.xml new file mode 100644 index 000000000..a6408d238 --- /dev/null +++ b/jaxrs4/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + + io.github.openfeign + parent + 13.3-SNAPSHOT + + + feign-jaxrs4 + Feign JAXRS 4 + Feign JAXRS 4 + + + 17 + 4.0.0-M1 + ${project.basedir}/.. + + + + + ${project.groupId} + feign-jaxrs3 + + + + jakarta.ws.rs + jakarta.ws.rs-api + 4.0.0 + + + + + ${project.groupId} + feign-gson + test + + + + ${project.groupId} + feign-core + test-jar + test + + + ${project.groupId} + feign-jaxrs + test-jar + + + javax.ws.rs + jsr311-api + + + + + + ${project.groupId} + feign-jaxrs2 + test-jar + + + com.squareup.okhttp3 + mockwebserver + test + + + org.glassfish.jersey.core + jersey-client + ${jersey.version} + test + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey.version} + test + + + + + + + org.moditect + moditect-maven-plugin + + true + + + !jsr311.api; + !feign.jaxrs; + !feign.jaxrs2; + *; + !jsr311.api; + !feign.jaxrs; + !feign.jaxrs2; + *; + true + + + + + + + diff --git a/jaxrs4/src/test/java/feign/jaxrs/JAXRS4ContractTest.java b/jaxrs4/src/test/java/feign/jaxrs/JAXRS4ContractTest.java new file mode 100644 index 000000000..a2eae360a --- /dev/null +++ b/jaxrs4/src/test/java/feign/jaxrs/JAXRS4ContractTest.java @@ -0,0 +1,466 @@ +/* + * Copyright 2012-2024 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 feign.jaxrs; + +import static feign.assertj.FeignAssertions.assertThat; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.data.MapEntry.entry; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.net.URI; +import java.util.List; +import org.junit.jupiter.api.Test; +import feign.MethodMetadata; +import feign.Response; +import feign.jaxrs3.JAXRS3Contract; +import feign.jaxrs.JAXRS4ContractTest.JakartaInternals.BeanParamInput; +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.HttpMethod; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.container.AsyncResponse; +import jakarta.ws.rs.container.Suspended; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.UriInfo; + +/** + * Tests interfaces defined per {@link JAXRS3Contract} are interpreted into expected + * {@link feign .RequestTemplate template} instances. + */ +class JAXRS4ContractTest extends JAXRSContractTestSupport { + + @Test + void injectJaxrsInternals() throws Exception { + final MethodMetadata methodMetadata = + parseAndValidateMetadata(JakartaInternals.class, "inject", AsyncResponse.class, + UriInfo.class); + assertThat(methodMetadata.template()) + .noRequestBody(); + } + + @Test + void injectBeanParam() throws Exception { + final MethodMetadata methodMetadata = + parseAndValidateMetadata(JakartaInternals.class, "beanParameters", BeanParamInput.class); + assertThat(methodMetadata.template()) + .noRequestBody(); + + assertThat(methodMetadata.template()) + .hasHeaders(entry("X-Custom-Header", asList("{X-Custom-Header}"))); + assertThat(methodMetadata.template()) + .hasQueries(entry("query", asList("{query}"))); + assertThat(methodMetadata.formParams()) + .isNotEmpty() + .containsExactly("form"); + + } + + public interface JakartaInternals { + @GET + @Path("/") + void inject(@Suspended AsyncResponse ar, @Context UriInfo info); + + @Path("/{path}") + @POST + void beanParameters(@BeanParam BeanParamInput beanParam); + + public class BeanParamInput { + + @PathParam("path") + String path; + + @QueryParam("query") + String query; + + @FormParam("form") + String form; + + @HeaderParam("X-Custom-Header") + String header; + } + } + + interface Methods { + + @POST + void post(); + + @PUT + void put(); + + @GET + void get(); + + @DELETE + void delete(); + } + + interface CustomMethod { + + @PATCH + Response patch(); + + @Target({ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + @HttpMethod("PATCH") + public @interface PATCH { + } + } + + interface WithQueryParamsInPath { + + @GET + @Path("/") + Response none(); + + @GET + @Path("/?Action=GetUser") + Response one(); + + @GET + @Path("/?Action=GetUser&Version=2010-05-08") + Response two(); + + @GET + @Path("/?Action=GetUser&Version=2010-05-08&limit=1") + Response three(); + + @GET + @Path("/?flag&Action=GetUser&Version=2010-05-08") + Response empty(); + } + + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.TEXT_HTML) + interface ProducesAndConsumes { + + @GET + @Produces("application/xml") + Response produces(); + + @GET + @Produces({"application/xml", "text/plain"}) + Response producesMultiple(); + + @GET + @Produces({}) + Response producesNada(); + + @GET + @Produces({""}) + Response producesEmpty(); + + @POST + @Consumes("application/xml") + Response consumes(); + + @POST + @Consumes({"application/xml", "application/json"}) + Response consumesMultiple(); + + @POST + @Consumes({}) + Response consumesNada(); + + @POST + @Consumes({""}) + Response consumesEmpty(); + + @POST + Response producesAndConsumes(); + } + + interface BodyParams { + + @POST + Response post(List body); + + @POST + Response tooMany(List body, List body2); + } + + @Path("") + interface EmptyPathOnType { + + @GET + Response base(); + + @GET + @Path("/specific") + Response get(); + } + + @Path("/base") + interface PathOnType { + + @GET + Response base(); + + @GET + @Path("/specific") + Response get(); + + @GET + @Path("") + Response emptyPath(); + + @GET + @Path("/{param}") + Response emptyPathParam(@PathParam(value = "") String empty); + + @GET + @Path("/{ param }") + Response pathParamWithSpaces(@PathParam("param") String path); + + @GET + @Path("regex/{param:.+}") + Response pathParamWithRegex(@PathParam("param") String path); + + @GET + @Path("regex/{param1:[0-9]*}/{ param2 : .+}") + Response pathParamWithMultipleRegex( + @PathParam("param1") String param1, + @PathParam("param2") String param2); + } + + @Path("/{baseparam: [0-9]+}") + interface ComplexPathOnType { + + @GET + @Path("regex/{param1:[0-9]*}/{ param2 : .+}") + Response pathParamWithMultipleRegex( + @PathParam("param1") String param1, + @PathParam("param2") String param2); + } + + interface WithURIParam { + + @GET + @Path("/{1}/{2}") + Response uriParam(@PathParam("1") String one, URI endpoint, @PathParam("2") String two); + } + + interface WithPathAndQueryParams { + + @GET + @Path("/domains/{domainId}/records") + Response recordsByNameAndType( + @PathParam("domainId") int id, + @QueryParam("name") String nameFilter, + @QueryParam("type") String typeFilter); + + @GET + Response empty(@QueryParam("") String empty); + } + + interface FormParams { + + @POST + void login( + @FormParam("customer_name") String customer, + @FormParam("user_name") String user, + @FormParam("password") String password); + + @GET + Response emptyFormParam(@FormParam("") String empty); + } + + interface HeaderParams { + + @POST + void logout(@HeaderParam("Auth-Token") String token); + + @GET + Response emptyHeaderParam(@HeaderParam("") String empty); + } + + @Path("base") + interface PathsWithoutAnySlashes { + + @GET + @Path("specific") + Response get(); + } + + @Path("/base") + interface PathsWithSomeSlashes { + + @GET + @Path("specific") + Response get(); + } + + @Path("base") + interface PathsWithSomeOtherSlashes { + + @GET + @Path("/specific") + Response get(); + } + + @Path("/") + interface ClassRootPath { + @GET + @Path("/specific") + Response get(); + } + + @Path("/base/") + interface ClassPathWithTrailingSlash { + @GET + @Path("/specific") + Response get(); + } + + @Path("/base/") + interface MethodWithFirstPathThenGetWithoutLeadingSlash { + @Path("specific") + @GET + Response get(); + } + + interface MixedAnnotations { + + @GET + @Path("/api/stuff?multiple=stuff") + @Produces("application/json") + Response getWithHeaders( + @HeaderParam("Accept") String accept, + @QueryParam("multiple") String multiple, + @QueryParam("another") String another); + } + + @Override + protected JAXRS3Contract createContract() { + return new JAXRS3Contract(); + } + + @Override + protected MethodMetadata parseAndValidateMetadata( + Class targetType, + String method, + Class... parameterTypes) + throws NoSuchMethodException { + return contract.parseAndValidateMetadata( + targetType, targetType.getMethod(method, parameterTypes)); + } + + @Override + protected Class methodsClass() { + return Methods.class; + } + + @Override + protected Class customMethodClass() { + return CustomMethod.class; + } + + @Override + protected Class withQueryParamsInPathClass() { + return WithQueryParamsInPath.class; + } + + @Override + protected Class producesAndConsumesClass() { + return ProducesAndConsumes.class; + } + + @Override + protected Class bodyParamsClass() { + return BodyParams.class; + } + + @Override + protected Class emptyPathOnTypeClass() { + return EmptyPathOnType.class; + } + + @Override + protected Class pathOnTypeClass() { + return PathOnType.class; + } + + @Override + protected Class complexPathOnTypeClass() { + return ComplexPathOnType.class; + } + + @Override + protected Class withURIParamClass() { + return WithURIParam.class; + } + + @Override + protected Class withPathAndQueryParamsClass() { + return WithPathAndQueryParams.class; + } + + @Override + protected Class formParamsClass() { + return FormParams.class; + } + + @Override + protected Class headerParamsClass() { + return HeaderParams.class; + } + + @Override + protected Class pathsWithoutAnySlashesClass() { + return PathsWithoutAnySlashes.class; + } + + @Override + protected Class pathsWithSomeSlashesClass() { + return PathsWithSomeSlashes.class; + } + + @Override + protected Class pathsWithSomeOtherSlashesClass() { + return PathsWithSomeOtherSlashes.class; + } + + @Override + protected Class classRootPathClass() { + return ClassRootPath.class; + } + + @Override + protected Class classPathWithTrailingSlashClass() { + return ClassPathWithTrailingSlash.class; + } + + @Override + protected Class methodWithFirstPathThenGetWithoutLeadingSlashClass() { + return MethodWithFirstPathThenGetWithoutLeadingSlash.class; + } + + @Override + protected Class mixedAnnotationsClass() { + return MixedAnnotations.class; + } +} diff --git a/jaxrs4/src/test/java/feign/jaxrs4/JAXRS4ClientTest.java b/jaxrs4/src/test/java/feign/jaxrs4/JAXRS4ClientTest.java new file mode 100644 index 000000000..8e7a556db --- /dev/null +++ b/jaxrs4/src/test/java/feign/jaxrs4/JAXRS4ClientTest.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012-2024 The Feign Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 feign.jaxrs4; + +import feign.Feign; +import feign.Feign.Builder; +import feign.jaxrs2.AbstractJAXRSClientTest; +import feign.jaxrs3.JAXRS3Client; + +/** + * Tests client-specific behavior, such as ensuring Content-Length is sent when specified. + */ +public class JAXRS4ClientTest extends AbstractJAXRSClientTest { + + @Override + public Builder newBuilder() { + return Feign.builder().client(new JAXRS3Client()); + } + +} diff --git a/pom.xml b/pom.xml index 032ce78b4..d34a490aa 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,7 @@ jaxrs jaxrs2 jaxrs3 + jaxrs4 java11 jakarta json @@ -257,6 +258,14 @@ ${project.version} + + ${project.groupId} + feign-jaxrs2 + ${project.version} + test-jar + test + + ${project.groupId} feign-jaxrs2