Browse Source

Ignore Jakarta Suspended and Context, Support BeanParam (#1942)

Co-authored-by: Marvin Froeder <velo@users.noreply.github.com>
pull/1945/head
Jared Komoroski 2 years ago committed by GitHub
parent
commit
0cb3a0aa5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      jakarta/README.md
  2. 127
      jakarta/src/main/java/feign/jaxrs/JakartaContract.java
  3. 73
      jakarta/src/test/java/feign/jaxrs/JakartaContractTest.java

2
jakarta/README.md

@ -32,3 +32,5 @@ Links the value of the corresponding parameter to a query parameter. When invok @@ -32,3 +32,5 @@ Links the value of the corresponding parameter to a query parameter. When invok
Links the value of the corresponding parameter to a header.
#### `@FormParam`
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.

127
jakarta/src/main/java/feign/jaxrs/JakartaContract.java

@ -20,9 +20,12 @@ import feign.DeclarativeContract; @@ -20,9 +20,12 @@ import feign.DeclarativeContract;
import feign.MethodMetadata;
import feign.Request;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import jakarta.ws.rs.*;
import jakarta.ws.rs.container.Suspended;
import jakarta.ws.rs.core.Context;
public class JakartaContract extends DeclarativeContract {
@ -93,6 +96,12 @@ public class JakartaContract extends DeclarativeContract { @@ -93,6 +96,12 @@ public class JakartaContract extends DeclarativeContract {
super.registerMethodAnnotation(Consumes.class, this::handleConsumesAnnotation);
super.registerMethodAnnotation(Produces.class, this::handleProducesAnnotation);
// parameter with unsupported jax-rs annotations should not be passed as body params.
// this will prevent interfaces from becoming unusable entirely due to single (unsupported)
// endpoints.
// https://github.com/OpenFeign/feign/issues/669
super.registerParameterAnnotation(Suspended.class, (ann, data, i) -> data.ignoreParamater(i));
super.registerParameterAnnotation(Context.class, (ann, data, i) -> data.ignoreParamater(i));
// trying to minimize the diff
registerParamAnnotations();
}
@ -113,37 +122,93 @@ public class JakartaContract extends DeclarativeContract { @@ -113,37 +122,93 @@ public class JakartaContract extends DeclarativeContract {
}
protected void registerParamAnnotations() {
{
registerParameterAnnotation(PathParam.class, (param, data, paramIndex) -> {
final String name = param.value();
checkState(emptyToNull(name) != null, "PathParam.value() was empty on parameter %s",
paramIndex);
nameParam(data, name, paramIndex);
});
registerParameterAnnotation(QueryParam.class, (param, data, paramIndex) -> {
final String name = param.value();
checkState(emptyToNull(name) != null, "QueryParam.value() was empty on parameter %s",
paramIndex);
final String query = addTemplatedParam(name);
data.template().query(name, query);
nameParam(data, name, paramIndex);
});
registerParameterAnnotation(HeaderParam.class, (param, data, paramIndex) -> {
final String name = param.value();
checkState(emptyToNull(name) != null, "HeaderParam.value() was empty on parameter %s",
paramIndex);
final String header = addTemplatedParam(name);
data.template().header(name, header);
nameParam(data, name, paramIndex);
});
registerParameterAnnotation(FormParam.class, (param, data, paramIndex) -> {
final String name = param.value();
checkState(emptyToNull(name) != null, "FormParam.value() was empty on parameter %s",
paramIndex);
data.formParams().add(name);
nameParam(data, name, paramIndex);
});
}
registerParameterAnnotation(PathParam.class, (param, data, paramIndex) -> {
final String name = param.value();
checkState(emptyToNull(name) != null, "PathParam.value() was empty on parameter %s",
paramIndex);
nameParam(data, name, paramIndex);
});
registerParameterAnnotation(QueryParam.class, (param, data, paramIndex) -> {
final String name = param.value();
checkState(emptyToNull(name) != null, "QueryParam.value() was empty on parameter %s",
paramIndex);
final String query = addTemplatedParam(name);
data.template().query(name, query);
nameParam(data, name, paramIndex);
});
registerParameterAnnotation(HeaderParam.class, (param, data, paramIndex) -> {
final String name = param.value();
checkState(emptyToNull(name) != null, "HeaderParam.value() was empty on parameter %s",
paramIndex);
final String header = addTemplatedParam(name);
data.template().header(name, header);
nameParam(data, name, paramIndex);
});
registerParameterAnnotation(FormParam.class, (param, data, paramIndex) -> {
final String name = param.value();
checkState(emptyToNull(name) != null, "FormParam.value() was empty on parameter %s",
paramIndex);
data.formParams().add(name);
nameParam(data, name, paramIndex);
});
// Reflect over the Bean Param looking for supported parameter annotations
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);
}
}
});
}
// Not using override as the super-type's method is deprecated and will be removed.

73
jakarta/src/test/java/feign/jaxrs/JakartaContractTest.java

@ -15,25 +15,23 @@ package feign.jaxrs; @@ -15,25 +15,23 @@ package feign.jaxrs;
import feign.MethodMetadata;
import feign.Response;
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 feign.jaxrs.JakartaContractTest.JakartaInternals.BeanParamInput;
import jakarta.ws.rs.*;
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;
import org.junit.Test;
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 static feign.assertj.FeignAssertions.assertThat;
import static java.util.Arrays.asList;
import static org.assertj.core.data.MapEntry.entry;
/**
* Tests interfaces defined per {@link JakartaContract} are interpreted into expected
@ -41,6 +39,57 @@ import java.util.List; @@ -41,6 +39,57 @@ import java.util.List;
*/
public class JakartaContractTest extends JAXRSContractTestSupport<JakartaContract> {
@Test
public void injectJaxrsInternals() throws Exception {
final MethodMetadata methodMetadata =
parseAndValidateMetadata(JakartaInternals.class, "inject", AsyncResponse.class,
UriInfo.class);
assertThat(methodMetadata.template())
.noRequestBody();
}
@Test
public 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

Loading…
Cancel
Save