Skip to content

Commit b341df6

Browse files
author
Maciej
committed
added DaprErrorResponseParser
Signed-off-by: Maciej <[email protected]>
1 parent 5416573 commit b341df6

12 files changed

+182
-66
lines changed

sdk/src/main/java/io/dapr/client/DaprClientBuilder.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import io.grpc.ManagedChannelBuilder;
2323
import org.slf4j.Logger;
2424
import org.slf4j.LoggerFactory;
25-
25+
import io.dapr.exceptions.DaprError;
2626
import java.io.Closeable;
2727

2828
/**
@@ -53,6 +53,11 @@ public class DaprClientBuilder {
5353
*/
5454
private DaprObjectSerializer objectSerializer;
5555

56+
/**
57+
* Response parser used for custom handling of error responses from Dapr.
58+
*/
59+
private DaprErrorResponseParser responseParser;
60+
5661
/**
5762
* Serializer used for state objects in DaprClient.
5863
*/
@@ -92,6 +97,22 @@ public DaprClientBuilder withObjectSerializer(DaprObjectSerializer objectSeriali
9297
return this;
9398
}
9499

100+
/**
101+
* Sets the error parser for objects to received from Dapr.
102+
* See {@link DefaultDaprErrorResponseParser} as a default parser suited for deserializing the response to {@link DaprError}.
103+
*
104+
* @param responseParser Parser for objects received from Dapr.
105+
* @return This instance.
106+
*/
107+
public DaprClientBuilder withCustomErrorResponseParser(DaprErrorResponseParser responseParser) {
108+
if (responseParser == null) {
109+
throw new IllegalArgumentException("Response parser is required");
110+
}
111+
112+
this.responseParser = responseParser;
113+
return this;
114+
}
115+
95116
/**
96117
* Sets the serializer for objects to be persisted.
97118
* See {@link DefaultObjectSerializer} as possible serializer for non-production scenarios.
@@ -183,7 +204,7 @@ private DaprClient buildDaprClientGrpc() {
183204
* @return DaprClient over HTTP.
184205
*/
185206
private DaprClient buildDaprClientHttp() {
186-
return new DaprClientHttp(this.daprHttpBuilder.build(), this.objectSerializer, this.stateSerializer);
207+
return new DaprClientHttp(this.daprHttpBuilder.build(responseParser), this.objectSerializer, this.stateSerializer);
187208
}
188209

189210
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.dapr.client;
2+
3+
import io.dapr.exceptions.DaprException;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
import java.io.IOException;
7+
8+
public interface DaprErrorResponseParser {
9+
DaprException parse(@NotNull okhttp3.Response response) throws IOException;
10+
}

sdk/src/main/java/io/dapr/client/DaprHttp.java

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import io.dapr.config.Properties;
1919
import io.dapr.exceptions.DaprError;
2020
import io.dapr.exceptions.DaprException;
21-
import io.dapr.exceptions.DaprHttpException;
2221
import io.dapr.utils.Version;
2322
import okhttp3.Call;
2423
import okhttp3.Callback;
@@ -73,6 +72,12 @@ public class DaprHttp implements AutoCloseable {
7372
private static final Set<String> ALLOWED_CONTEXT_IN_HEADERS =
7473
Collections.unmodifiableSet(new HashSet<>(Arrays.asList("grpc-trace-bin", "traceparent", "tracestate")));
7574

75+
76+
/**
77+
* Error response parser.
78+
*/
79+
private static DaprErrorResponseParser parser = new DefaultDaprErrorResponseParser();
80+
7681
/**
7782
* HTTP Methods supported.
7883
*/
@@ -163,10 +168,11 @@ public int getStatusCode() {
163168
* @param port Port for calling Dapr. (e.g. 3500)
164169
* @param httpClient RestClient used for all API calls in this new instance.
165170
*/
166-
DaprHttp(String hostname, int port, OkHttpClient httpClient) {
171+
DaprHttp(String hostname, int port, OkHttpClient httpClient, DaprErrorResponseParser parser) {
167172
this.hostname = hostname;
168173
this.port = port;
169174
this.httpClient = httpClient;
175+
this.parser = parser;
170176
}
171177

172178
/**
@@ -324,24 +330,6 @@ private CompletableFuture<Response> doInvokeApi(String method,
324330
return future;
325331
}
326332

327-
/**
328-
* Tries to parse an error from Dapr response body.
329-
*
330-
* @param json Response body from Dapr.
331-
* @return DaprError or null if could not parse.
332-
*/
333-
private static DaprError parseDaprError(byte[] json) {
334-
if ((json == null) || (json.length == 0)) {
335-
return null;
336-
}
337-
338-
try {
339-
return OBJECT_MAPPER.readValue(json, DaprError.class);
340-
} catch (IOException e) {
341-
throw new DaprException("UNKNOWN", new String(json, StandardCharsets.UTF_8));
342-
}
343-
}
344-
345333
private static byte[] getBodyBytesOrEmptyArray(okhttp3.Response response) throws IOException {
346334
ResponseBody body = response.body();
347335
if (body != null) {
@@ -369,24 +357,8 @@ public void onFailure(Call call, IOException e) {
369357
@Override
370358
public void onResponse(@NotNull Call call, @NotNull okhttp3.Response response) throws IOException {
371359
if (!response.isSuccessful()) {
372-
try {
373-
DaprError error = parseDaprError(getBodyBytesOrEmptyArray(response));
374-
if ((error != null) && (error.getErrorCode() != null)) {
375-
if (error.getMessage() != null) {
376-
future.completeExceptionally(new DaprException(error));
377-
} else {
378-
future.completeExceptionally(
379-
new DaprException(error.getErrorCode(), "HTTP status code: " + response.code()));
380-
}
381-
return;
382-
}
383-
384-
future.completeExceptionally(new DaprException("UNKNOWN", "HTTP status code: " + response.code()));
385-
return;
386-
} catch (DaprException e) {
387-
future.completeExceptionally(new DaprHttpException(response));
388-
return;
389-
}
360+
DaprException customException = parser.parse(response);
361+
future.completeExceptionally(customException);
390362
}
391363

392364
Map<String, String> mapHeaders = new HashMap<>();

sdk/src/main/java/io/dapr/client/DaprHttpBuilder.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,32 @@ public class DaprHttpBuilder {
4545
*/
4646
private static final int KEEP_ALIVE_DURATION = 30;
4747

48+
/**
49+
* A default parser for Dapr error responses.
50+
*/
51+
private static final DaprErrorResponseParser DEFAULT_PARSER = new DefaultDaprErrorResponseParser();
52+
53+
4854
/**
4955
* Build an instance of the Http client based on the provided setup.
5056
*
5157
* @return an instance of {@link DaprHttp}
5258
* @throws IllegalStateException if any required field is missing
5359
*/
5460
public DaprHttp build() {
55-
return buildDaprHttp();
61+
return buildDaprHttp(null);
62+
}
63+
64+
public DaprHttp build(DaprErrorResponseParser parser) {
65+
return buildDaprHttp(parser);
5666
}
5767

5868
/**
5969
* Creates an instance of the HTTP Client.
6070
*
6171
* @return Instance of {@link DaprHttp}
6272
*/
63-
private DaprHttp buildDaprHttp() {
73+
private DaprHttp buildDaprHttp(DaprErrorResponseParser parser) {
6474
if (OK_HTTP_CLIENT == null) {
6575
synchronized (LOCK) {
6676
if (OK_HTTP_CLIENT == null) {
@@ -85,6 +95,7 @@ private DaprHttp buildDaprHttp() {
8595
}
8696
}
8797

88-
return new DaprHttp(Properties.SIDECAR_IP.get(), Properties.HTTP_PORT.get(), OK_HTTP_CLIENT);
98+
DaprErrorResponseParser parserToUse = parser == null ? DEFAULT_PARSER : parser;
99+
return new DaprHttp(Properties.SIDECAR_IP.get(), Properties.HTTP_PORT.get(), OK_HTTP_CLIENT, parserToUse);
89100
}
90101
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.dapr.client;
2+
3+
import io.dapr.exceptions.DaprError;
4+
import io.dapr.exceptions.DaprException;
5+
import okhttp3.ResponseBody;
6+
import org.jetbrains.annotations.NotNull;
7+
import java.io.IOException;
8+
import java.nio.charset.StandardCharsets;
9+
10+
import static io.dapr.client.ObjectSerializer.OBJECT_MAPPER;
11+
12+
public class DefaultDaprErrorResponseParser implements DaprErrorResponseParser {
13+
14+
DaprError error;
15+
16+
@Override
17+
public DaprException parse(@NotNull okhttp3.Response response) throws IOException {
18+
byte[] json = getBodyBytesOrEmptyArray(response);
19+
DaprException unknownException = new DaprException("UNKNOWN", "HTTP status code: " + response.code());
20+
21+
if ((json != null) && (json.length != 0)) {
22+
try {
23+
error = OBJECT_MAPPER.readValue(json, DaprError.class);
24+
} catch (IOException e) {
25+
return new DaprException("UNKNOWN", new String(json, StandardCharsets.UTF_8));
26+
}
27+
}
28+
29+
if (error != null) {
30+
return new DaprException(error.getErrorCode(), error.getMessage());
31+
}
32+
return unknownException;
33+
}
34+
35+
private byte[] getBodyBytesOrEmptyArray(okhttp3.Response response) throws IOException {
36+
ResponseBody body = response.body();
37+
if (body != null) {
38+
return body.bytes();
39+
}
40+
41+
return EMPTY_BYTES;
42+
}
43+
44+
/**
45+
* Empty input or output.
46+
*/
47+
private final byte[] EMPTY_BYTES = new byte[0];
48+
}

sdk/src/test/java/io/dapr/client/DaprClientBuilderTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ public class DaprClientBuilderTest {
2525
@Test
2626
public void build() {
2727
DaprObjectSerializer objectSerializer = mock(DaprObjectSerializer.class);
28+
DaprErrorResponseParser errorResponseParser = mock(DaprErrorResponseParser.class);
2829
when(objectSerializer.getContentType()).thenReturn("application/json");
2930
DaprObjectSerializer stateSerializer = mock(DaprObjectSerializer.class);
3031
DaprClientBuilder daprClientBuilder = new DaprClientBuilder();
3132
daprClientBuilder.withObjectSerializer(objectSerializer);
3233
daprClientBuilder.withStateSerializer(stateSerializer);
34+
daprClientBuilder.withCustomErrorResponseParser(errorResponseParser);
3335
DaprClient daprClient = daprClientBuilder.build();
3436
assertNotNull(daprClient);
3537
}

sdk/src/test/java/io/dapr/client/DaprClientHttpTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565

6666
public class DaprClientHttpTest {
6767

68+
private static final DaprErrorResponseParser DEFAULT_PARSER = new DefaultDaprErrorResponseParser();
69+
6870
private static final String STATE_STORE_NAME = "MyStateStore";
6971

7072
private static final String SECRET_STORE_NAME = "MySecretStore";
@@ -86,7 +88,7 @@ public class DaprClientHttpTest {
8688
public void setUp() {
8789
mockInterceptor = new MockInterceptor(Behavior.UNORDERED);
8890
okHttpClient = new OkHttpClient.Builder().addInterceptor(mockInterceptor).build();
89-
daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient);
91+
daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient, DEFAULT_PARSER);
9092
daprClientHttp = new DaprClientProxy(new DaprClientHttp(daprHttp));
9193
daprClientHttpXML = new DaprClientProxy(new DaprClientHttp(daprHttp, new XmlSerializer(), new XmlSerializer()));
9294
}
@@ -95,7 +97,7 @@ public void setUp() {
9597
public void waitForSidecarTimeout() throws Exception {
9698
int port = findFreePort();
9799
System.setProperty(Properties.HTTP_PORT.getName(), Integer.toString(port));
98-
daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), port, okHttpClient);
100+
daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), port, okHttpClient, DEFAULT_PARSER);
99101
DaprClientHttp daprClientHttp = new DaprClientHttp(daprHttp);
100102
assertThrows(RuntimeException.class, () -> daprClientHttp.waitForSidecar(1).block());
101103
}
@@ -113,7 +115,7 @@ public void waitForSidecarTimeoutOK() throws Exception {
113115
}
114116
});
115117
t.start();
116-
daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), port, okHttpClient);
118+
daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), port, okHttpClient, DEFAULT_PARSER);
117119
DaprClientHttp daprClientHttp = new DaprClientHttp(daprHttp);
118120
daprClientHttp.waitForSidecar(10000).block();
119121
}
@@ -125,7 +127,7 @@ public void publishEventInvocation() {
125127
.post("http://127.0.0.1:3000/v1.0/publish/mypubsubname/A")
126128
.respond(EXPECTED_RESULT);
127129
String event = "{ \"message\": \"This is a test\" }";
128-
daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient);
130+
daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3000, okHttpClient, DEFAULT_PARSER);
129131
DaprClientHttp daprClientHttp = new DaprClientHttp(daprHttp);
130132
Mono<Void> mono = daprClientHttp.publishEvent("mypubsubname", "A", event, null);
131133
assertNull(mono.block());

sdk/src/test/java/io/dapr/client/DaprHttpStub.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public ResponseStub(byte[] body, Map<String, String> headers, int statusCode) {
3434
* Instantiates a stub for DaprHttp
3535
*/
3636
public DaprHttpStub() {
37-
super(null, 3000, null);
37+
super(null, 3000, null, null);
3838
}
3939

4040
/**

0 commit comments

Comments
 (0)