Fix HawkbitClient multipart data error handling (#2259)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -23,15 +23,19 @@ import java.net.URL;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.Contract;
|
import feign.Contract;
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
|
import feign.FeignException;
|
||||||
|
import feign.Request;
|
||||||
import feign.RequestInterceptor;
|
import feign.RequestInterceptor;
|
||||||
import feign.RequestTemplate;
|
import feign.RequestTemplate;
|
||||||
import feign.codec.Decoder;
|
import feign.codec.Decoder;
|
||||||
@@ -139,6 +143,16 @@ public class HawkbitClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> T service0(final Class<T> serviceType, final Tenant tenant, final Controller controller) {
|
||||||
|
return Feign.builder().client(client)
|
||||||
|
.encoder(encoder)
|
||||||
|
.decoder(decoder)
|
||||||
|
.errorDecoder(errorDecoder)
|
||||||
|
.contract(contract)
|
||||||
|
.requestInterceptor(requestInterceptorFn.apply(tenant, controller))
|
||||||
|
.target(serviceType, controller == null ? hawkBitServer.getMgmtUrl() : hawkBitServer.getDdiUrl());
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> T proxy(final Class<T> serviceType, final T service, final Tenant tenant, final Controller controller) {
|
private <T> T proxy(final Class<T> serviceType, final T service, final Tenant tenant, final Controller controller) {
|
||||||
final ObjectMapper objectMapper = new ObjectMapper();
|
final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
@@ -146,7 +160,7 @@ public class HawkbitClient {
|
|||||||
try {
|
try {
|
||||||
final Class<?>[] parameterTypes = method.getParameterTypes();
|
final Class<?>[] parameterTypes = method.getParameterTypes();
|
||||||
if (method.getDeclaringClass() == serviceType && List.of(parameterTypes).contains(MultipartFile.class)) {
|
if (method.getDeclaringClass() == serviceType && List.of(parameterTypes).contains(MultipartFile.class)) {
|
||||||
return processMultipartFormData(method, args, tenant, controller, parameterTypes, objectMapper);
|
return callMultipartFormDataRequest(method, args, tenant, controller, parameterTypes, objectMapper);
|
||||||
} else {
|
} else {
|
||||||
return method.invoke(service, args);
|
return method.invoke(service, args);
|
||||||
}
|
}
|
||||||
@@ -156,9 +170,7 @@ public class HawkbitClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String CRLF = "\r\n";
|
private Object callMultipartFormDataRequest(
|
||||||
|
|
||||||
private Object processMultipartFormData(
|
|
||||||
final Method method, final Object[] args,
|
final Method method, final Object[] args,
|
||||||
final Tenant tenant, final Controller controller,
|
final Tenant tenant, final Controller controller,
|
||||||
final Class<?>[] parameterTypes, final ObjectMapper objectMapper) throws IOException {
|
final Class<?>[] parameterTypes, final ObjectMapper objectMapper) throws IOException {
|
||||||
@@ -207,19 +219,59 @@ public class HawkbitClient {
|
|||||||
}
|
}
|
||||||
out.write(("--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));
|
out.write(("--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int responseCode = conn.getResponseCode();
|
||||||
|
if (responseCode < 200 || responseCode >= 300) {
|
||||||
|
throw toFeignException(responseCode, conn, Request.create(
|
||||||
|
Request.HttpMethod.POST, conn.getURL().toString(),
|
||||||
|
conn.getHeaderFields().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)),
|
||||||
|
(Request.Body)null, null));
|
||||||
|
}
|
||||||
|
|
||||||
return method.getReturnType() == ResponseEntity.class
|
return method.getReturnType() == ResponseEntity.class
|
||||||
? new ResponseEntity<>(
|
? new ResponseEntity<>(
|
||||||
deserialize(
|
deserialize(
|
||||||
conn.getInputStream(),
|
conn.getInputStream(),
|
||||||
(Class<?>) ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0],
|
(Class<?>) ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0],
|
||||||
objectMapper),
|
objectMapper),
|
||||||
HttpStatusCode.valueOf(conn.getResponseCode()))
|
HttpStatusCode.valueOf(responseCode))
|
||||||
: deserialize(conn.getInputStream(), method.getReturnType(), objectMapper);
|
: deserialize(conn.getInputStream(), method.getReturnType(), objectMapper);
|
||||||
}
|
}
|
||||||
private Object deserialize(final InputStream is, final Class<?> type, final ObjectMapper objectMapper) throws IOException {
|
|
||||||
|
private static FeignException toFeignException(final int responseCode, final HttpURLConnection conn, final Request request) throws IOException {
|
||||||
|
if (responseCode >= 500 && responseCode < 600) {
|
||||||
|
// server error
|
||||||
|
return switch (responseCode) {
|
||||||
|
case 500 -> new FeignException.InternalServerError(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 501 -> new FeignException.NotImplemented(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 502 -> new FeignException.BadGateway(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 503 -> new FeignException.ServiceUnavailable(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 504 -> new FeignException.GatewayTimeout(conn.getResponseMessage(), request, null, null);
|
||||||
|
default -> new FeignException.FeignServerException(responseCode, conn.getResponseMessage(), request, null, null);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return switch (responseCode) {
|
||||||
|
case 400 -> new FeignException.BadRequest(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 401 -> new FeignException.Unauthorized(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 403 -> new FeignException.Forbidden(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 404 -> new FeignException.NotFound(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 405 -> new FeignException.MethodNotAllowed(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 406 -> new FeignException.NotAcceptable(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 409 -> new FeignException.Conflict(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 410 -> new FeignException.Gone(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 415 -> new FeignException.UnsupportedMediaType(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 429 -> new FeignException.TooManyRequests(conn.getResponseMessage(), request, null, null);
|
||||||
|
case 422 -> new FeignException.UnprocessableEntity(conn.getResponseMessage(), request, null, null);
|
||||||
|
default -> new FeignException.FeignClientException(responseCode, conn.getResponseMessage(), request, null, null);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object deserialize(final InputStream is, final Class<?> type, final ObjectMapper objectMapper) throws IOException {
|
||||||
return type == void.class || type == Void.class ? null : objectMapper.readValue(is, type);
|
return type == void.class || type == Void.class ? null : objectMapper.readValue(is, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String CRLF = "\r\n";
|
||||||
private void writeMultipartFile(
|
private void writeMultipartFile(
|
||||||
final MultipartFile multipartFile, final OutputStream out, final String boundary, final Annotation[] parametersAnnotations)
|
final MultipartFile multipartFile, final OutputStream out, final String boundary, final Annotation[] parametersAnnotations)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
@@ -238,7 +290,6 @@ public class HawkbitClient {
|
|||||||
out.write(CRLF.getBytes(StandardCharsets.UTF_8));
|
out.write(CRLF.getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeSimpleFormData(
|
private void writeSimpleFormData(
|
||||||
final Object arg, final OutputStream out, final String boundary, final Annotation[] parameterAnnotations) throws IOException {
|
final Object arg, final OutputStream out, final String boundary, final Annotation[] parameterAnnotations) throws IOException {
|
||||||
if (arg != null) {
|
if (arg != null) {
|
||||||
@@ -253,7 +304,7 @@ public class HawkbitClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T extends Annotation> T getAnnotation(final Class<T> annotationClass, final Annotation[] annotations) {
|
private static <T extends Annotation> T getAnnotation(final Class<T> annotationClass, final Annotation[] annotations) {
|
||||||
for (final Annotation annotation : annotations) {
|
for (final Annotation annotation : annotations) {
|
||||||
if (annotation.annotationType().equals(annotationClass)) {
|
if (annotation.annotationType().equals(annotationClass)) {
|
||||||
return (T) annotation;
|
return (T) annotation;
|
||||||
@@ -261,14 +312,4 @@ public class HawkbitClient {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T service0(final Class<T> serviceType, final Tenant tenant, final Controller controller) {
|
|
||||||
return Feign.builder().client(client)
|
|
||||||
.encoder(encoder)
|
|
||||||
.decoder(decoder)
|
|
||||||
.errorDecoder(errorDecoder)
|
|
||||||
.contract(contract)
|
|
||||||
.requestInterceptor(requestInterceptorFn.apply(tenant, controller))
|
|
||||||
.target(serviceType, controller == null ? hawkBitServer.getMgmtUrl() : hawkBitServer.getDdiUrl());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user