Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Java][Feign] Add http status to feign result #10583

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,8 @@ public void processOpts() {
modelDocTemplateFiles.remove("model_doc.mustache");
apiDocTemplateFiles.remove("api_doc.mustache");
//Templates to decode response headers
supportingFiles.add(new SupportingFile("model/HttpResponse.mustache", modelsFolder, "HttpResponse.java"));
supportingFiles.add(new SupportingFile("JacksonResponseDecoder.mustache", invokerFolder, "JacksonResponseDecoder.java"));
supportingFiles.add(new SupportingFile("model/ApiResponse.mustache", modelsFolder, "ApiResponse.java"));
supportingFiles.add(new SupportingFile("ApiResponseDecoder.mustache", invokerFolder, "ApiResponseDecoder.java"));
}

if (!(FEIGN.equals(getLibrary()) || RESTTEMPLATE.equals(getLibrary()) || RETROFIT_2.equals(getLibrary()) || GOOGLE_API_CLIENT.equals(getLibrary()) || REST_ASSURED.equals(getLibrary()) || WEBCLIENT.equals(getLibrary()) || MICROPROFILE.equals(getLibrary()))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import feign.slf4j.Slf4jLogger;
import {{invokerPackage}}.auth.HttpBasicAuth;
import {{invokerPackage}}.auth.HttpBearerAuth;
import {{invokerPackage}}.auth.ApiKeyAuth;
import {{invokerPackage}}.JacksonResponseDecoder;
import {{invokerPackage}}.ApiResponseDecoder;

{{#hasOAuthMethods}}
import {{invokerPackage}}.auth.ApiErrorDecoder;
Expand Down Expand Up @@ -64,7 +64,7 @@ public class ApiClient {
feignBuilder = Feign.builder()
.client(new OkHttpClient())
.encoder(new FormEncoder(new JacksonEncoder(objectMapper)))
.decoder(new JacksonResponseDecoder(objectMapper))
.decoder(new ApiResponseDecoder(objectMapper))
{{#hasOAuthMethods}}
.errorDecoder(new ApiErrorDecoder())
.retryer(new Retryer.Default(0, 0, 2))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,26 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Map;

import {{modelPackage}}.HttpResponse;
import {{modelPackage}}.ApiResponse;

public class JacksonResponseDecoder extends JacksonDecoder {
public class ApiResponseDecoder extends JacksonDecoder {

public JacksonResponseDecoder(ObjectMapper mapper) {
public ApiResponseDecoder(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
//Detects if the type is an instance of the parameterized class ApiResponse
Type responseBodyType;
if (Types.getRawType(type).isAssignableFrom(HttpResponse.class)) {
//The HttpResponse class has a single type parameter, the Dto class itself
if (Types.getRawType(type).isAssignableFrom(ApiResponse.class)) {
//The ApiResponse 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);
return new ApiResponse(response.status(), responseHeaders, body);
} else {
//The response is not encapsulated in the HttpResponse, decode the Dto as normal
//The response is not encapsulated in the ApiResponse, decode the Dto as normal
return super.decode(response, type);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {{invokerPackage}}.EncodingUtils;
{{#legacyDates}}
import {{invokerPackage}}.ParamExpander;
{{/legacyDates}}
import {{modelPackage}}.HttpResponse;
import {{modelPackage}}.ApiResponse;

{{#imports}}import {{import}};
{{/imports}}
Expand Down Expand Up @@ -59,7 +59,7 @@ public interface {{classname}} extends ApiClient.Api {
* @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}}){{/required}}
{{/allParams}}
{{#returnType}}
* @return A HttpResponse that wraps the response boyd and the http headers.
* @return A ApiResponse that wraps the response boyd and the http headers.
{{/returnType}}
{{#externalDocs}}
* {{description}}
Expand All @@ -79,7 +79,7 @@ public interface {{classname}} extends ApiClient.Api {
"{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
{{/-last}}{{/headerParams}}
})
HttpResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{nickname}}WithHttpInfo({{#allParams}}{{^isBodyParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isBodyParam}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}});
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{nickname}}WithHttpInfo({{#allParams}}{{^isBodyParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isBodyParam}}{{{dataType}}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}});


{{#hasQueryParams}}
Expand Down Expand Up @@ -164,7 +164,7 @@ public interface {{classname}} extends ApiClient.Api {
"{{baseName}}: {{=<% %>=}}{<%paramName%>}<%={{ }}=%>"{{^-last}},
{{/-last}}{{/headerParams}}
})
HttpResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{nickname}}WithHttpInfo({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) Map<String, Object> queryParams);
ApiResponse<{{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{nickname}}WithHttpInfo({{#allParams}}{{^isQueryParam}}{{^isBodyParam}}{{^legacyDates}}@Param("{{paramName}}") {{/legacyDates}}{{#legacyDates}}@Param(value="{{paramName}}", expander=ParamExpander.class) {{/legacyDates}}{{/isBodyParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}}{{/allParams}}@QueryMap(encoded=true) Map<String, Object> queryParams);


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import feign.RetryableException;
import feign.codec.ErrorDecoder;

/**
* Error decoder that makes the HTTP 401 and 403 Retryable. Sometimes the 401 or 402 may indicate an expired token
* Error decoder that makes the HTTP 401 and 403 Retryable. Sometimes the 401 or 403 may indicate an expired token
* All the other HTTP status are handled by the {@link feign.codec.ErrorDecoder.Default} decoder
*/
public class ApiErrorDecoder implements ErrorDecoder {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package {{modelPackage}};

import java.util.Map;
import java.util.List;

public class ApiResponse<T>{

final private int statusCode;
final private Map<String, List<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) {
this(statusCode, headers, null);
}

/**
* @param statusCode The status code of HTTP response
* @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) {
this.statusCode = statusCode;
this.headers = headers;
this.data = data;
}

public int getStatusCode() {
return statusCode;
}

public Map<String, List<String>> getHeaders() {
return headers;
}

public T getData() {
return data;
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pom.xml
settings.gradle
src/main/AndroidManifest.xml
src/main/java/org/openapitools/client/ApiClient.java
src/main/java/org/openapitools/client/ApiResponseDecoder.java
src/main/java/org/openapitools/client/CustomInstantDeserializer.java
src/main/java/org/openapitools/client/EncodingUtils.java
src/main/java/org/openapitools/client/JacksonResponseDecoder.java
src/main/java/org/openapitools/client/ParamExpander.java
src/main/java/org/openapitools/client/RFC3339DateFormat.java
src/main/java/org/openapitools/client/ServerConfiguration.java
Expand Down Expand Up @@ -46,6 +46,7 @@ src/main/java/org/openapitools/client/model/AdditionalPropertiesNumber.java
src/main/java/org/openapitools/client/model/AdditionalPropertiesObject.java
src/main/java/org/openapitools/client/model/AdditionalPropertiesString.java
src/main/java/org/openapitools/client/model/Animal.java
src/main/java/org/openapitools/client/model/ApiResponse.java
src/main/java/org/openapitools/client/model/ArrayOfArrayOfNumberOnly.java
src/main/java/org/openapitools/client/model/ArrayOfNumberOnly.java
src/main/java/org/openapitools/client/model/ArrayTest.java
Expand All @@ -65,7 +66,6 @@ src/main/java/org/openapitools/client/model/EnumTest.java
src/main/java/org/openapitools/client/model/FileSchemaTestClass.java
src/main/java/org/openapitools/client/model/FormatTest.java
src/main/java/org/openapitools/client/model/HasOnlyReadOnly.java
src/main/java/org/openapitools/client/model/HttpResponse.java
src/main/java/org/openapitools/client/model/MapTest.java
src/main/java/org/openapitools/client/model/MixedPropertiesAndAdditionalPropertiesClass.java
src/main/java/org/openapitools/client/model/Model200Response.java
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import org.openapitools.client.auth.HttpBasicAuth;
import org.openapitools.client.auth.HttpBearerAuth;
import org.openapitools.client.auth.ApiKeyAuth;
import org.openapitools.client.JacksonResponseDecoder;
import org.openapitools.client.ApiResponseDecoder;

import org.openapitools.client.auth.ApiErrorDecoder;
import org.openapitools.client.auth.OAuth;
Expand All @@ -49,7 +49,7 @@ public ApiClient() {
feignBuilder = Feign.builder()
.client(new OkHttpClient())
.encoder(new FormEncoder(new JacksonEncoder(objectMapper)))
.decoder(new JacksonResponseDecoder(objectMapper))
.decoder(new ApiResponseDecoder(objectMapper))
.errorDecoder(new ApiErrorDecoder())
.retryer(new Retryer.Default(0, 0, 2))
.logger(new Slf4jLogger());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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.ApiResponse;

public class ApiResponseDecoder extends JacksonDecoder {

public ApiResponseDecoder(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 ApiResponse
Type responseBodyType;
if (Types.getRawType(type).isAssignableFrom(ApiResponse.class)) {
//The ApiResponse class has a single type parameter, the Dto class itself
responseBodyType = ((ParameterizedType) type).getActualTypeArguments()[0];
Object body = super.decode(response, responseBodyType);
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Object decode(Response response, Type type) throws IOException {
//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);
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import org.openapitools.client.ApiClient;
import org.openapitools.client.EncodingUtils;
import org.openapitools.client.model.HttpResponse;
import org.openapitools.client.model.ApiResponse;

import org.openapitools.client.model.Client;

Expand Down Expand Up @@ -34,14 +34,14 @@ public interface AnotherFakeApi extends ApiClient.Api {
* Similar to <code>call123testSpecialTags</code> but it also returns the http response headers .
* To test special tags and operation ID starting with number
* @param body client model (required)
* @return A HttpResponse that wraps the response boyd and the http headers.
* @return A ApiResponse that wraps the response boyd and the http headers.
*/
@RequestLine("PATCH /another-fake/dummy")
@Headers({
"Content-Type: application/json",
"Accept: application/json",
})
HttpResponse<Client> call123testSpecialTagsWithHttpInfo(Client body);
ApiResponse<Client> call123testSpecialTagsWithHttpInfo(Client body);


}
Loading