forked from loafle/openapi-generator-original
[Java][client] Fix feign classcastexception when getting headers (#16745)
* Avoid ClassCastException when getting headers (the header values are Collection<String> and not List<String>) Delete unused classes * Remove feign10x * Add unit test Refactor to avoid creating the headers map when ApiResponse is not used
This commit is contained in:
parent
494ee489ad
commit
87f9d53c3a
6
.gitignore
vendored
6
.gitignore
vendored
@ -49,8 +49,6 @@ nb-configuration.xml
|
||||
/target
|
||||
/generated-files
|
||||
test-output/
|
||||
nbactions.xml
|
||||
test-output/
|
||||
|
||||
# website
|
||||
website/build/
|
||||
@ -73,7 +71,6 @@ samples/client/petstore/build
|
||||
samples/client/petstore/cpp-qt/PetStore/moc_*
|
||||
samples/client/petstore/cpp-qt/PetStore/*.o
|
||||
samples/client/petstore/cpp-qt/build-*
|
||||
samples/client/petstore/cpp-qt/build-*
|
||||
samples/client/petstore/cpp-qt/PetStore/PetStore
|
||||
samples/client/petstore/cpp-qt/PetStore/Makefile
|
||||
samples/client/petstore/cpp-qt/PetStore/PetStore.pro.user
|
||||
@ -100,9 +97,7 @@ samples/client/petstore/java/jersey2/build/
|
||||
samples/client/petstore/java/okhttp-gson/.gradle/
|
||||
samples/client/petstore/java/okhttp-gson/build/
|
||||
samples/client/petstore/java/feign/build/
|
||||
samples/client/petstore/java/feign10x/build/
|
||||
samples/client/petstore/java/feign/project/
|
||||
samples/client/petstore/java/feign10x/project/
|
||||
samples/client/petstore/java/retrofit/build/
|
||||
samples/client/petstore/java/retrofit2/build/
|
||||
samples/client/petstore/java/retrofit2/hello.txt
|
||||
@ -110,7 +105,6 @@ samples/client/petstore/java/retrofit2rx/build/
|
||||
samples/client/petstore/java/default/build/
|
||||
samples/client/petstore/scala/build/
|
||||
samples/client/petstore/java/resttemplate/hello.txt
|
||||
samples/client/petstore/java/retrofit2/hello.txt
|
||||
samples/client/petstore/java/feign/hello.txt
|
||||
samples/client/petstore/java/jersey2-java6/project/
|
||||
samples/client/petstore/java/jersey2-java8/project/
|
||||
|
@ -22,14 +22,13 @@ public class ApiResponseDecoder extends JacksonDecoder {
|
||||
|
||||
@Override
|
||||
public Object decode(Response response, Type type) throws IOException {
|
||||
Map<String, Collection<String>> responseHeaders = Collections.unmodifiableMap(response.headers());
|
||||
//Detects if the type is an instance of the parameterized class ApiResponse
|
||||
Type responseBodyType;
|
||||
if (type instanceof ParameterizedType && Types.getRawType(type).isAssignableFrom(ApiResponse.class)) {
|
||||
//The ApiResponse class has a single type parameter, the Dto class itself
|
||||
responseBodyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Type responseBodyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Object body = super.decode(response, responseBodyType);
|
||||
return new ApiResponse(response.status(), responseHeaders, body);
|
||||
Map<String, Collection<String>> responseHeaders = Collections.unmodifiableMap(response.headers());
|
||||
return new ApiResponse<>(response.status(), responseHeaders, body);
|
||||
} else {
|
||||
//The response is not encapsulated in the ApiResponse, decode the Dto as normal
|
||||
return super.decode(response, type);
|
||||
|
@ -1,19 +1,19 @@
|
||||
package {{modelPackage}};
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
public class ApiResponse<T>{
|
||||
|
||||
final private int statusCode;
|
||||
final private Map<String, List<String>> headers;
|
||||
final private Map<String, Collection<String>> headers;
|
||||
final private T data;
|
||||
|
||||
/**
|
||||
* @param statusCode The status code of HTTP response
|
||||
* @param headers The headers of HTTP response
|
||||
*/
|
||||
public ApiResponse(int statusCode, Map<String, List<String>> headers) {
|
||||
public ApiResponse(int statusCode, Map<String, Collection<String>> headers) {
|
||||
this(statusCode, headers, null);
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ public class ApiResponse<T>{
|
||||
* @param headers The headers of HTTP response
|
||||
* @param data The object deserialized from response bod
|
||||
*/
|
||||
public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) {
|
||||
public ApiResponse(int statusCode, Map<String, Collection<String>> headers, T data) {
|
||||
this.statusCode = statusCode;
|
||||
this.headers = headers;
|
||||
this.data = data;
|
||||
@ -32,7 +32,7 @@ public class ApiResponse<T>{
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getHeaders() {
|
||||
public Map<String, Collection<String>> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,19 @@
|
||||
package org.openapitools.client.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
public class ApiResponse<T>{
|
||||
|
||||
final private int statusCode;
|
||||
final private Map<String, List<String>> headers;
|
||||
final private Map<String, Collection<String>> headers;
|
||||
final private T data;
|
||||
|
||||
/**
|
||||
* @param statusCode The status code of HTTP response
|
||||
* @param headers The headers of HTTP response
|
||||
*/
|
||||
public ApiResponse(int statusCode, Map<String, List<String>> headers) {
|
||||
public ApiResponse(int statusCode, Map<String, Collection<String>> headers) {
|
||||
this(statusCode, headers, null);
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ public class ApiResponse<T>{
|
||||
* @param headers The headers of HTTP response
|
||||
* @param data The object deserialized from response bod
|
||||
*/
|
||||
public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) {
|
||||
public ApiResponse(int statusCode, Map<String, Collection<String>> headers, T data) {
|
||||
this.statusCode = statusCode;
|
||||
this.headers = headers;
|
||||
this.data = data;
|
||||
@ -32,7 +32,7 @@ public class ApiResponse<T>{
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getHeaders() {
|
||||
public Map<String, Collection<String>> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
@ -22,14 +22,13 @@ public class ApiResponseDecoder extends JacksonDecoder {
|
||||
|
||||
@Override
|
||||
public Object decode(Response response, Type type) throws IOException {
|
||||
Map<String, Collection<String>> responseHeaders = Collections.unmodifiableMap(response.headers());
|
||||
//Detects if the type is an instance of the parameterized class ApiResponse
|
||||
Type responseBodyType;
|
||||
if (type instanceof ParameterizedType && Types.getRawType(type).isAssignableFrom(ApiResponse.class)) {
|
||||
//The ApiResponse class has a single type parameter, the Dto class itself
|
||||
responseBodyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Type responseBodyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Object body = super.decode(response, responseBodyType);
|
||||
return new ApiResponse(response.status(), responseHeaders, body);
|
||||
Map<String, Collection<String>> responseHeaders = Collections.unmodifiableMap(response.headers());
|
||||
return new ApiResponse<>(response.status(), responseHeaders, body);
|
||||
} else {
|
||||
//The response is not encapsulated in the ApiResponse, decode the Dto as normal
|
||||
return super.decode(response, type);
|
||||
|
@ -1,38 +0,0 @@
|
||||
package org.openapitools.client;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import feign.Response;
|
||||
import feign.Types;
|
||||
import feign.jackson.JacksonDecoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.openapitools.client.model.HttpResponse;
|
||||
|
||||
public class JacksonResponseDecoder extends JacksonDecoder {
|
||||
|
||||
public JacksonResponseDecoder(ObjectMapper mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decode(Response response, Type type) throws IOException {
|
||||
Map<String, Collection<String>> responseHeaders = Collections.unmodifiableMap(response.headers());
|
||||
//Detects if the type is an instance of the parameterized class HttpResponse
|
||||
Type responseBodyType;
|
||||
if (Types.getRawType(type).isAssignableFrom(HttpResponse.class)) {
|
||||
//The HttpResponse class has a single type parameter, the Dto class itself
|
||||
responseBodyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Object body = super.decode(response, responseBodyType);
|
||||
return new HttpResponse(responseHeaders, body, response.status());
|
||||
} else {
|
||||
//The response is not encapsulated in the HttpResponse, decode the Dto as normal
|
||||
return super.decode(response, type);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
package org.openapitools.client.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
public class ApiResponse<T>{
|
||||
|
||||
final private int statusCode;
|
||||
final private Map<String, List<String>> headers;
|
||||
final private Map<String, Collection<String>> headers;
|
||||
final private T data;
|
||||
|
||||
/**
|
||||
* @param statusCode The status code of HTTP response
|
||||
* @param headers The headers of HTTP response
|
||||
*/
|
||||
public ApiResponse(int statusCode, Map<String, List<String>> headers) {
|
||||
public ApiResponse(int statusCode, Map<String, Collection<String>> headers) {
|
||||
this(statusCode, headers, null);
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ public class ApiResponse<T>{
|
||||
* @param headers The headers of HTTP response
|
||||
* @param data The object deserialized from response bod
|
||||
*/
|
||||
public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) {
|
||||
public ApiResponse(int statusCode, Map<String, Collection<String>> headers, T data) {
|
||||
this.statusCode = statusCode;
|
||||
this.headers = headers;
|
||||
this.data = data;
|
||||
@ -32,7 +32,7 @@ public class ApiResponse<T>{
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getHeaders() {
|
||||
public Map<String, Collection<String>> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
package org.openapitools.client.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
|
||||
public class HttpResponse<T>{
|
||||
|
||||
private Map<String, Collection<String>> headers;
|
||||
|
||||
private T body;
|
||||
|
||||
private int status;
|
||||
|
||||
public HttpResponse(Map<String, Collection<String>> headers, T body, int status) {
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public T getBody(){
|
||||
return body;
|
||||
}
|
||||
|
||||
public Map<String, Collection<String>> getHeaders(){
|
||||
return headers;
|
||||
}
|
||||
|
||||
public int getStatus(){
|
||||
return status;
|
||||
}
|
||||
}
|
@ -22,14 +22,13 @@ public class ApiResponseDecoder extends JacksonDecoder {
|
||||
|
||||
@Override
|
||||
public Object decode(Response response, Type type) throws IOException {
|
||||
Map<String, Collection<String>> responseHeaders = Collections.unmodifiableMap(response.headers());
|
||||
//Detects if the type is an instance of the parameterized class ApiResponse
|
||||
Type responseBodyType;
|
||||
if (type instanceof ParameterizedType && Types.getRawType(type).isAssignableFrom(ApiResponse.class)) {
|
||||
//The ApiResponse class has a single type parameter, the Dto class itself
|
||||
responseBodyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Type responseBodyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
Object body = super.decode(response, responseBodyType);
|
||||
return new ApiResponse(response.status(), responseHeaders, body);
|
||||
Map<String, Collection<String>> responseHeaders = Collections.unmodifiableMap(response.headers());
|
||||
return new ApiResponse<>(response.status(), responseHeaders, body);
|
||||
} else {
|
||||
//The response is not encapsulated in the ApiResponse, decode the Dto as normal
|
||||
return super.decode(response, type);
|
||||
|
@ -1,19 +1,19 @@
|
||||
package org.openapitools.client.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
public class ApiResponse<T>{
|
||||
|
||||
final private int statusCode;
|
||||
final private Map<String, List<String>> headers;
|
||||
final private Map<String, Collection<String>> headers;
|
||||
final private T data;
|
||||
|
||||
/**
|
||||
* @param statusCode The status code of HTTP response
|
||||
* @param headers The headers of HTTP response
|
||||
*/
|
||||
public ApiResponse(int statusCode, Map<String, List<String>> headers) {
|
||||
public ApiResponse(int statusCode, Map<String, Collection<String>> headers) {
|
||||
this(statusCode, headers, null);
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ public class ApiResponse<T>{
|
||||
* @param headers The headers of HTTP response
|
||||
* @param data The object deserialized from response bod
|
||||
*/
|
||||
public ApiResponse(int statusCode, Map<String, List<String>> headers, T data) {
|
||||
public ApiResponse(int statusCode, Map<String, Collection<String>> headers, T data) {
|
||||
this.statusCode = statusCode;
|
||||
this.headers = headers;
|
||||
this.data = data;
|
||||
@ -32,7 +32,7 @@ public class ApiResponse<T>{
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getHeaders() {
|
||||
public Map<String, Collection<String>> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
package org.openapitools.client;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import feign.Request;
|
||||
import feign.Request.HttpMethod;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openapitools.client.model.ApiResponse;
|
||||
import org.openapitools.client.model.Cat;
|
||||
|
||||
class ApiResponseDecoderTest {
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
ApiResponseDecoder decoder = new ApiResponseDecoder(objectMapper);
|
||||
|
||||
|
||||
@Test
|
||||
void shouldDecodeApiResponseWithHeaders() throws IOException {
|
||||
|
||||
final Cat cat = makeCat();
|
||||
TypeReference<ApiResponse<Cat>> typeReference = new TypeReference<ApiResponse<Cat>>() {
|
||||
};
|
||||
|
||||
ApiResponse<Cat> response = (ApiResponse<Cat>) decoder.decode(Response.builder()
|
||||
.headers(ImmutableMap.of("Location",
|
||||
Collections.singletonList("https://example.com/cats/1")))
|
||||
.body(objectMapper.writeValueAsBytes(cat))
|
||||
.status(201)
|
||||
.request(buildRequest())
|
||||
.build(), typeReference.getType());
|
||||
|
||||
assertEquals(cat.getColor(), response.getData().getColor());
|
||||
assertEquals(cat.isDeclawed(), response.getData().isDeclawed());
|
||||
|
||||
Collection<String> locationValues = response.getHeaders().get("Location");
|
||||
assertEquals(1, locationValues.size());
|
||||
assertEquals("https://example.com/cats/1", locationValues.iterator().next());
|
||||
}
|
||||
|
||||
private Cat makeCat() {
|
||||
final Cat cat = new Cat();
|
||||
cat.color("black");
|
||||
cat.declawed(true);
|
||||
return cat;
|
||||
}
|
||||
|
||||
private Request buildRequest() {
|
||||
return Request.create(HttpMethod.GET, "https://example.com/cats/1", Collections.emptyMap(),
|
||||
null, new RequestTemplate());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user