Browse Source

Add Optional BeanParam support for JAXRS2Contract (#1935)

* Add Optional BeanParam support for JAXRS2Contract

* Fix Code Style

* Commit Build Code Format Changes

* Roll Back Removal of braces

* Make sure jaxrs1 dependencies not avaiable on jaxrs2

* Update JAXRS2ContractWithBeanParamSupportTest.java

* Always Use Enable BeanParam Support

* Roll Back Test Changes

---------

Co-authored-by: Marvin Froeder <velo@users.noreply.github.com>
pull/1945/head
Jared Komoroski 2 years ago committed by GitHub
parent
commit
4455fc2255
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      jaxrs/src/main/java/feign/jaxrs/JAXRSContract.java
  2. 2
      jaxrs2/README.md
  3. 18
      jaxrs2/pom.xml
  4. 69
      jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java
  5. 92
      jaxrs2/src/test/java/feign/jaxrs2/JAXRS2ContractWithBeanParamSupportTest.java

3
jaxrs/src/main/java/feign/jaxrs/JAXRSContract.java

@ -151,7 +151,8 @@ public class JAXRSContract extends DeclarativeContract {
} }
// Not using override as the super-type's method is deprecated and will be removed. // Not using override as the super-type's method is deprecated and will be removed.
private String addTemplatedParam(String name) { // Protected so JAXRS2Contract can make use of this
protected String addTemplatedParam(String name) {
return String.format("{%s}", name); return String.format("{%s}", name);
} }
} }

2
jaxrs2/README.md

@ -35,3 +35,5 @@ Links the value of the corresponding parameter to a query parameter. When invok
Links the value of the corresponding parameter to a header. Links the value of the corresponding parameter to a header.
#### `@FormParam` #### `@FormParam`
Links the value of the corresponding parameter to a key passed to `Encoder.Text<Map<String, Object>>.encode()`. Links the value of the corresponding parameter to a key passed to `Encoder.Text<Map<String, Object>>.encode()`.
#### `@BeanParm`
Aggregates the above supported parameter annotations under a single value object.

18
jaxrs2/pom.xml

@ -42,6 +42,12 @@
<artifactId>feign-core</artifactId> <artifactId>feign-core</artifactId>
</dependency> </dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>feign-jaxrs</artifactId> <artifactId>feign-jaxrs</artifactId>
@ -53,12 +59,6 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1.1</version>
</dependency>
<!-- for example --> <!-- for example -->
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
@ -77,6 +77,12 @@
<artifactId>feign-jaxrs</artifactId> <artifactId>feign-jaxrs</artifactId>
<type>test-jar</type> <type>test-jar</type>
<scope>test</scope> <scope>test</scope>
<exclusions>
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>

69
jaxrs2/src/main/java/feign/jaxrs2/JAXRS2Contract.java

@ -13,10 +13,13 @@
*/ */
package feign.jaxrs2; package feign.jaxrs2;
import javax.ws.rs.BeanParam; import feign.jaxrs.JAXRSContract;
import javax.ws.rs.*;
import javax.ws.rs.container.Suspended; import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import feign.jaxrs.JAXRSContract; import java.lang.reflect.Field;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
/** /**
* Please refer to the <a href="https://github.com/Netflix/feign/tree/master/feign-jaxrs2">Feign * Please refer to the <a href="https://github.com/Netflix/feign/tree/master/feign-jaxrs2">Feign
@ -31,7 +34,67 @@ public final class JAXRS2Contract extends JAXRSContract {
// https://github.com/OpenFeign/feign/issues/669 // https://github.com/OpenFeign/feign/issues/669
super.registerParameterAnnotation(Suspended.class, (ann, data, i) -> data.ignoreParamater(i)); super.registerParameterAnnotation(Suspended.class, (ann, data, i) -> data.ignoreParamater(i));
super.registerParameterAnnotation(Context.class, (ann, data, i) -> data.ignoreParamater(i)); super.registerParameterAnnotation(Context.class, (ann, data, i) -> data.ignoreParamater(i));
super.registerParameterAnnotation(BeanParam.class, (ann, data, i) -> data.ignoreParamater(i));
} }
@Override
protected void registerParamAnnotations() {
super.registerParamAnnotations();
registerParameterAnnotation(BeanParam.class, (param, data, paramIndex) -> {
final Field[] aggregatedParams = data.method()
.getParameters()[paramIndex]
.getType()
.getDeclaredFields();
for (Field aggregatedParam : aggregatedParams) {
if (aggregatedParam.isAnnotationPresent(PathParam.class)) {
final String name = aggregatedParam.getAnnotation(PathParam.class).value();
checkState(
emptyToNull(name) != null,
"BeanParam parameter %s contains PathParam with empty .value() on field %s",
paramIndex,
aggregatedParam.getName());
nameParam(data, name, paramIndex);
}
if (aggregatedParam.isAnnotationPresent(QueryParam.class)) {
final String name = aggregatedParam.getAnnotation(QueryParam.class).value();
checkState(
emptyToNull(name) != null,
"BeanParam parameter %s contains QueryParam with empty .value() on field %s",
paramIndex,
aggregatedParam.getName());
final String query = addTemplatedParam(name);
data.template().query(name, query);
nameParam(data, name, paramIndex);
}
if (aggregatedParam.isAnnotationPresent(HeaderParam.class)) {
final String name = aggregatedParam.getAnnotation(HeaderParam.class).value();
checkState(
emptyToNull(name) != null,
"BeanParam parameter %s contains HeaderParam with empty .value() on field %s",
paramIndex,
aggregatedParam.getName());
final String header = addTemplatedParam(name);
data.template().header(name, header);
nameParam(data, name, paramIndex);
}
if (aggregatedParam.isAnnotationPresent(FormParam.class)) {
final String name = aggregatedParam.getAnnotation(FormParam.class).value();
checkState(
emptyToNull(name) != null,
"BeanParam parameter %s contains FormParam with empty .value() on field %s",
paramIndex,
aggregatedParam.getName());
data.formParams().add(name);
nameParam(data, name, paramIndex);
}
}
});
}
} }

92
jaxrs2/src/test/java/feign/jaxrs2/JAXRS2ContractWithBeanParamSupportTest.java

@ -0,0 +1,92 @@
/*
* Copyright 2012-2023 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 feign.MethodMetadata;
import feign.jaxrs.JAXRSContract;
import feign.jaxrs.JAXRSContractTest;
import feign.jaxrs2.JAXRS2ContractWithBeanParamSupportTest.Jaxrs2Internals.BeanParamInput;
import org.junit.Test;
import javax.ws.rs.*;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import static feign.assertj.FeignAssertions.assertThat;
import static java.util.Arrays.asList;
import static org.assertj.core.data.MapEntry.entry;
/**
* Tests interfaces defined per {@link JAXRS2Contract} are interpreted into expected
* {@link feign .RequestTemplate template} instances.
*/
public class JAXRS2ContractWithBeanParamSupportTest extends JAXRSContractTest {
@Override
protected JAXRSContract createContract() {
return new JAXRS2Contract();
}
@Test
public void injectJaxrsInternals() throws Exception {
final MethodMetadata methodMetadata =
parseAndValidateMetadata(Jaxrs2Internals.class, "inject", AsyncResponse.class,
UriInfo.class);
assertThat(methodMetadata.template())
.noRequestBody();
}
@Test
public void injectBeanParam() throws Exception {
final MethodMetadata methodMetadata =
parseAndValidateMetadata(Jaxrs2Internals.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 Jaxrs2Internals {
@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;
}
}
}
Loading…
Cancel
Save