-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow adding diagnostics data to exceptions
- Loading branch information
Showing
11 changed files
with
246 additions
and
227 deletions.
There are no files selected for viewing
118 changes: 118 additions & 0 deletions
118
src/main/java/com/github/bannmann/restflow/AbstractRequester.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package com.github.bannmann.restflow; | ||
|
||
import java.net.http.HttpRequest; | ||
import java.net.http.HttpResponse; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ConcurrentMap; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
import dev.failsafe.Failsafe; | ||
import dev.failsafe.Policy; | ||
|
||
@RequiredArgsConstructor | ||
abstract class AbstractRequester<B, R> implements Requester<R> | ||
{ | ||
protected final HttpRequest request; | ||
protected final ClientConfig clientConfig; | ||
protected final ConcurrentMap<String, Object> diagnosticsData = new ConcurrentHashMap<>(); | ||
|
||
@Override | ||
public final CompletableFuture<R> start() | ||
{ | ||
var diagnosticsDataSupplier = clientConfig.getDiagnosticsDataSupplier(); | ||
if (diagnosticsDataSupplier != null) | ||
{ | ||
Map<String, Object> values = diagnosticsDataSupplier.get(); | ||
diagnosticsData.putAll(values); | ||
} | ||
|
||
return send().thenApply(this::extractValue); | ||
} | ||
|
||
private CompletableFuture<HttpResponse<B>> send() | ||
{ | ||
List<Policy<HttpResponse<?>>> policies = clientConfig.getPolicies(); | ||
if (!policies.isEmpty()) | ||
{ | ||
return Failsafe.with(policies) | ||
.getStageAsync(context -> sendOnce()); | ||
} | ||
|
||
return sendOnce(); | ||
} | ||
|
||
private CompletableFuture<HttpResponse<B>> sendOnce() | ||
{ | ||
return clientConfig.getHttpClient() | ||
.sendAsync(request, getBodyHandler()) | ||
.handle(this::addDetailsForLowLevelExceptions) | ||
.thenApply(this::failOrPassThrough); | ||
} | ||
|
||
protected abstract HttpResponse.BodyHandler<B> getBodyHandler(); | ||
|
||
private <T> T addDetailsForLowLevelExceptions(T result, Throwable throwable) | ||
{ | ||
if (throwable != null) | ||
{ | ||
String message = String.format("Request to URL %s failed", request.uri()); | ||
throw new RequestFailureException(request, message, throwable, diagnosticsData); | ||
} | ||
return result; | ||
} | ||
|
||
private HttpResponse<B> failOrPassThrough(HttpResponse<B> response) | ||
{ | ||
verifyNoErrors(response); | ||
return response; | ||
} | ||
|
||
protected abstract void verifyNoErrors(HttpResponse<B> response); | ||
|
||
protected abstract R extractValue(HttpResponse<B> response); | ||
|
||
protected boolean isFailure(int responseStatus) | ||
{ | ||
return !isSuccess(responseStatus); | ||
} | ||
|
||
protected boolean isSuccess(int responseStatus) | ||
{ | ||
return responseStatus >= 200 && responseStatus < 300; | ||
} | ||
|
||
protected <T> RequestStatusException createException(HttpResponse<T> response) | ||
{ | ||
int status = response.statusCode(); | ||
String body = getStringBody(response); | ||
String message = String.format("Got status %d with message '%s' for %s %s", | ||
status, | ||
body, | ||
request.method(), | ||
request.uri()); | ||
|
||
return RequestStatusException.builder() | ||
.message(message) | ||
.request(request) | ||
.response(response) | ||
.status(status) | ||
.body(body) | ||
.diagnosticsData(diagnosticsData) | ||
.build(); | ||
} | ||
|
||
private <T> String getStringBody(HttpResponse<T> response) | ||
{ | ||
T body = response.body(); | ||
if (body != null) | ||
{ | ||
return body.toString() | ||
.trim(); | ||
} | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
src/main/java/com/github/bannmann/restflow/OptionalRequester.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.github.bannmann.restflow; | ||
|
||
import java.net.http.HttpResponse; | ||
import java.util.Optional; | ||
|
||
import com.github.mizool.core.rest.errorhandling.HttpStatus; | ||
|
||
final class OptionalRequester<B, R> extends AbstractRequester<B, Optional<R>> | ||
{ | ||
public static <B, R> Requester<Optional<R>> forSpec(RequestSpecification<B, R> spec) | ||
{ | ||
return new OptionalRequester<>(spec); | ||
} | ||
|
||
private final RequestSpecification<B, R> spec; | ||
|
||
private OptionalRequester(RequestSpecification<B, R> spec) | ||
{ | ||
super(spec.createFinalRequest(), spec.getClientConfig()); | ||
this.spec = spec; | ||
} | ||
|
||
@Override | ||
protected HttpResponse.BodyHandler<B> getBodyHandler() | ||
{ | ||
return spec.getResponseBodyConfig() | ||
.getBodyHandler(); | ||
} | ||
|
||
@Override | ||
protected void verifyNoErrors(HttpResponse<B> response) | ||
{ | ||
int responseStatus = response.statusCode(); | ||
if (isFailure(responseStatus) && responseStatus != HttpStatus.NOT_FOUND) | ||
{ | ||
throw createException(response); | ||
} | ||
} | ||
|
||
@Override | ||
protected Optional<R> extractValue(HttpResponse<B> response) | ||
{ | ||
if (response.statusCode() == HttpStatus.NOT_FOUND) | ||
{ | ||
return Optional.empty(); | ||
} | ||
|
||
B body = response.body(); | ||
R result = spec.getResponseBodyConfig() | ||
.getResponseConverter() | ||
.apply(body); | ||
return Optional.of(result); | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
src/main/java/com/github/bannmann/restflow/RegularRequester.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.github.bannmann.restflow; | ||
|
||
import java.net.http.HttpResponse; | ||
|
||
final class RegularRequester<B, R> extends AbstractRequester<B, R> | ||
{ | ||
public static <B, R> Requester<R> forSpec(RequestSpecification<B, R> spec) | ||
{ | ||
return new RegularRequester<>(spec); | ||
} | ||
|
||
private final RequestSpecification<B, R> spec; | ||
|
||
private RegularRequester(RequestSpecification<B, R> spec) | ||
{ | ||
super(spec.createFinalRequest(), spec.getClientConfig()); | ||
this.spec = spec; | ||
} | ||
|
||
@Override | ||
protected HttpResponse.BodyHandler<B> getBodyHandler() | ||
{ | ||
return spec.getResponseBodyConfig() | ||
.getBodyHandler(); | ||
} | ||
|
||
@Override | ||
protected void verifyNoErrors(HttpResponse<B> response) | ||
{ | ||
int responseStatus = response.statusCode(); | ||
if (isFailure(responseStatus)) | ||
{ | ||
throw createException(response); | ||
} | ||
} | ||
|
||
@Override | ||
protected R extractValue(HttpResponse<B> response) | ||
{ | ||
return spec.getResponseBodyConfig() | ||
.getResponseConverter() | ||
.apply(response.body()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.