Skip to content

Commit

Permalink
Merge branch 'release/0.11' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
bannmann committed Dec 10, 2023
2 parents 90a74a1 + 3468e99 commit 1cffb4e
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 59 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<groupId>dev.bannmann.restflow</groupId>
<artifactId>restflow</artifactId>
<version>0.10</version>
<version>0.11</version>

<name>${project.groupId}:${project.artifactId}</name>
<description>Fluent API for Java 11 HTTP Client with JSON-B support</description>
Expand Down
25 changes: 19 additions & 6 deletions src/main/java/dev/bannmann/restflow/AbstractRequester.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,23 @@ private HttpResponse<B> failOrPassThrough(HttpResponse<B> response)

protected abstract void verifyNoErrors(HttpResponse<B> response);

protected abstract R extractValue(HttpResponse<B> response);
private R extractValue(HttpResponse<B> httpResponse)
{
try
{
return doExtractValue(httpResponse);
}
catch (RuntimeException e)
{
String message = String.format("Could not process response to %s %s:\n%s",
request.method(),
request.uri(),
httpResponse.body());
throw new ResponseBodyException(message, e, httpResponse, diagnosticsData, callerFrames);
}
}

protected abstract R doExtractValue(HttpResponse<B> response);

protected boolean isFailure(int responseStatus)
{
Expand All @@ -105,7 +121,7 @@ protected boolean isSuccess(int responseStatus)
return responseStatus >= 200 && responseStatus < 300;
}

protected <T> RequestStatusException createException(HttpResponse<T> response)
protected <T> ResponseStatusException createException(HttpResponse<T> response)
{
int status = response.statusCode();
String body = getStringBody(response);
Expand All @@ -115,12 +131,9 @@ protected <T> RequestStatusException createException(HttpResponse<T> response)
request.method(),
request.uri());

return RequestStatusException.builder()
return ResponseStatusException.builder()
.message(message)
.request(request)
.response(response)
.status(status)
.body(body)
.diagnosticsData(diagnosticsData)
.build();
}
Expand Down
49 changes: 49 additions & 0 deletions src/main/java/dev/bannmann/restflow/InvalidResponseException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package dev.bannmann.restflow;

import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;

import lombok.Getter;

/**
* Thrown when the response is invalid. Refer to subclasses for specific cases.
*/
@Getter
public abstract class InvalidResponseException extends RequestException
{
private final transient HttpResponse<?> response;

protected InvalidResponseException(
String message,
Throwable cause,
HttpResponse<?> response,
Map<String, Object> diagnosticsData,
List<StackWalker.StackFrame> callerFrames)
{
super(message, cause, diagnosticsData, callerFrames);
this.response = response;
}

@Override
public HttpRequest getRequest()
{
return response.request();
}

public int getStatusCode()
{
return response.statusCode();
}

public String getRawBody()
{
Object body = response.body();
if (body == null)
{
return null;
}
return body.toString();
}
}
2 changes: 1 addition & 1 deletion src/main/java/dev/bannmann/restflow/OptionalRequester.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected void verifyNoErrors(HttpResponse<B> response)
}

@Override
protected Optional<R> extractValue(HttpResponse<B> response)
protected Optional<R> doExtractValue(HttpResponse<B> response)
{
if (response.statusCode() == HttpStatus.NOT_FOUND)
{
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/dev/bannmann/restflow/RegularRequester.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected void verifyNoErrors(HttpResponse<B> response)
}

@Override
protected R extractValue(HttpResponse<B> response)
protected R doExtractValue(HttpResponse<B> response)
{
return spec.getResponseBodyConfig()
.getResponseConverter()
Expand Down
8 changes: 3 additions & 5 deletions src/main/java/dev/bannmann/restflow/RequestException.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,24 @@
import lombok.Getter;

/**
* Thrown when the request could not be completed for some reason. Refer to {@link RequestStatusException} and
* {@link RequestFailureException} for specific cases.
* Thrown when the request could not be completed for some reason. Refer to subclasses for specific cases.
*/
@Getter
public abstract class RequestException extends RuntimeException
{
private final transient HttpRequest request;
private final transient Map<String, Object> diagnosticsData;
private final transient List<StackWalker.StackFrame> callerFrames;

protected RequestException(
HttpRequest request,
String message,
Throwable cause,
Map<String, Object> diagnosticsData,
List<StackWalker.StackFrame> callerFrames)
{
super(message, cause);
this.request = request;
this.diagnosticsData = diagnosticsData;
this.callerFrames = callerFrames;
}

public abstract HttpRequest getRequest();
}
10 changes: 7 additions & 3 deletions src/main/java/dev/bannmann/restflow/RequestFailureException.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@
import java.util.List;
import java.util.Map;

import lombok.Getter;

/**
* Thrown when the request failed without a response.
*
* @see RequestStatusException
*/
@Getter
public class RequestFailureException extends RequestException
{
private final HttpRequest request;

public RequestFailureException(
HttpRequest request,
String message,
Throwable cause,
Map<String, Object> diagnosticsData,
List<StackWalker.StackFrame> callerFrames)
{
super(request, message, cause, diagnosticsData, callerFrames);
super(message, cause, diagnosticsData, callerFrames);
this.request = request;
}
}
40 changes: 0 additions & 40 deletions src/main/java/dev/bannmann/restflow/RequestStatusException.java

This file was deleted.

24 changes: 24 additions & 0 deletions src/main/java/dev/bannmann/restflow/ResponseBodyException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.bannmann.restflow;

import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;

import lombok.Builder;

/**
* Thrown when the response body could not be processed.
*/
public class ResponseBodyException extends InvalidResponseException
{
@Builder
public ResponseBodyException(
String message,
Throwable cause,
HttpResponse<?> response,
Map<String, Object> diagnosticsData,
List<StackWalker.StackFrame> callerFrames)
{
super(message, cause, response, diagnosticsData, callerFrames);
}
}
24 changes: 24 additions & 0 deletions src/main/java/dev/bannmann/restflow/ResponseStatusException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.bannmann.restflow;

import java.net.http.HttpResponse;
import java.util.List;
import java.util.Map;

import lombok.Builder;

/**
* Thrown when the response to a request has a non-2xx status code.
*/
public class ResponseStatusException extends InvalidResponseException
{
@Builder
public ResponseStatusException(
String message,
Throwable cause,
HttpResponse<?> response,
Map<String, Object> diagnosticsData,
List<StackWalker.StackFrame> callerFrames)
{
super(message, cause, response, diagnosticsData, callerFrames);
}
}
22 changes: 20 additions & 2 deletions src/test/java/dev/bannmann/restflow/TestBasicRestClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import java.util.function.Function;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.bind.JsonbBuilder;
import javax.json.stream.JsonParsingException;

import lombok.AllArgsConstructor;
import lombok.Data;
Expand Down Expand Up @@ -185,6 +187,22 @@ public void testTryFetchServerError()
assertThrowsInternalServerError(responseFuture, "POST");
}

@Test(timeOut = METHOD_TIMEOUT)
public void testExecuteWithMalformedJsonResponse()
{
mockedServer.when(TestData.Requests.Incoming.POST)
.respond(TestData.Responses.HTML_ERROR_PAGE_WITH_SUCCESS_STATUS);

CompletableFuture<Optional<JsonObject>> responseFuture = makeClient().make(TestData.Requests.Outgoing.POST)
.returningJsonObject()
.tryFetch();

assertThatThrownBy(responseFuture::get).isExactlyInstanceOf(ExecutionException.class)
.extracting(Throwable::getCause, as(InstanceOfAssertFactories.THROWABLE))
.isExactlyInstanceOf(ResponseBodyException.class)
.hasRootCauseExactlyInstanceOf(JsonParsingException.class);
}

private void assertThrowsInternalServerError(CompletableFuture<?> responseFuture, String method)
{
assertThrowsRequestStatusException(responseFuture,
Expand All @@ -197,13 +215,13 @@ private void assertThrowsInternalServerError(CompletableFuture<?> responseFuture
private void assertThrowsRequestStatusException(
CompletableFuture<?> responseFuture, int status, String path, String body, String method)
{
String message = String.format("Got status %d with message '%s' for %s %s%s",
var message = String.format("Got status %d with message '%s' for %s %s%s",
status,
body,
method,
TestData.BASE_URL,
path);
RequestStatusException expectedCause = RequestStatusException.builder()
var expectedCause = ResponseStatusException.builder()
.message(message)
.build();

Expand Down
8 changes: 8 additions & 0 deletions src/test/java/dev/bannmann/restflow/TestData.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public class Body
public final String
INTERNAL_SERVER_ERROR_BODY
= "Detected a slight field variance in the thera-magnetic caesium portal housing.";

public final String
HTML_UNEXPECTED_ERROR
= "<html><body>Sorry, an unexpected error occurred. Please try again later.</body></html>";
}

public final org.mockserver.model.HttpResponse NO_CONTENT = response().withStatusCode(204);
Expand All @@ -85,6 +89,10 @@ public class Body
public final org.mockserver.model.HttpResponse HELLO_WORLD_ARRAY = response().withStatusCode(200)
.withBody(Body.HELLO_WORLD_JSON_ARRAY);

public final org.mockserver.model.HttpResponse HTML_ERROR_PAGE_WITH_SUCCESS_STATUS = response().withStatusCode(
200)
.withBody(Body.HTML_UNEXPECTED_ERROR);

public final org.mockserver.model.HttpResponse SERVER_BUSY = response().withStatusCode(429)
.withBody("Please try again later")
.withDelay(TimeUnit.MILLISECONDS, 500);
Expand Down

0 comments on commit 1cffb4e

Please sign in to comment.