-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathResponseHandler.java
More file actions
130 lines (120 loc) · 4.02 KB
/
ResponseHandler.java
File metadata and controls
130 lines (120 loc) · 4.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package com.veem.client;
import static java.util.Optional.ofNullable;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.veem.exceptions.VeemBadRequestException;
import com.veem.exceptions.VeemConflictException;
import com.veem.exceptions.VeemErrorResponse;
import com.veem.exceptions.VeemException;
import com.veem.exceptions.VeemForbiddenException;
import com.veem.exceptions.VeemInternalException;
import com.veem.exceptions.VeemNotFoundException;
import com.veem.exceptions.VeemSdkException;
import com.veem.exceptions.VeemUnauthorizedException;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import retrofit2.Response;
import java.io.IOException;
import java.util.function.Function;
/**
* Retrofit2 response handler for internal Veem requests.
*/
@Slf4j
class ResponseHandler
{
interface Request<T>
{
T call() throws IOException;
}
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* Handle success and failure responses from internal Veem request.
*
* @param request A method reference to the API request execution.
* @param <T> The type of the response body
* @return The response body POJO
*/
static <T> T handleResponse(final Request<Response<T>> request) throws VeemException
{
val response = handleException(request);
if (response.isSuccessful())
{
return response.body();
}
else
{
throw convertErrorResponse(response);
}
}
private static <T> T handleException(final Request<T> request) throws VeemSdkException
{
try
{
return request.call();
}
catch (IOException e)
{
throw new VeemSdkException(e, e.getMessage());
}
}
/**
* Convert the {@link Response} of a failed Retrofit2 request to an instance of {@link VeemException}.
* If {@link Response#errorBody()} is a {@link VeemErrorResponse}, it will be converted back to its original
* {@link VeemException}.
*
* @param response The {@link Response} from the failed request
* @return An instance of a {@link VeemException} subtype.
*/
private static VeemException convertErrorResponse(final Response response)
{
String errorBody = null;
try
{
errorBody = response.errorBody() == null ? "{}" : response.errorBody().string();
return getExceptionContructor(response.code()).apply(MAPPER.readValue(errorBody, VeemErrorResponse.class));
}
catch (IOException e)
{
log.error(String.format("Failed to deserialize error response for %d error", response.code()), e);
return new VeemSdkException(e, extractError(response, errorBody));
}
}
private static Function<VeemErrorResponse, VeemException> getExceptionContructor(final int statusCode)
{
switch (statusCode)
{
case 400:
return VeemBadRequestException::new;
case 401:
return VeemUnauthorizedException::new;
case 403:
return VeemForbiddenException::new;
case 404:
return VeemNotFoundException::new;
case 409:
return VeemConflictException::new;
case 500:
default:
return VeemInternalException::new;
}
}
private static String extractError(final Response<?> response, final String errorBody)
{
String error;
try
{
val errorResponse = MAPPER.readTree(errorBody);
error = ofNullable(errorResponse)
.map(er -> er.get("message"))
.map(JsonNode::asText)
.orElseThrow(RuntimeException::new);
log.error(error);
}
catch (Exception e)
{
log.error("Unable to parse error response body", e);
error = response.message();
}
return error;
}
}