Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Problem handling #83

Merged
merged 8 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
### Added

- More typedeclarations
- Improved error responses: the body contains a `Problem` (JSON) with details about the exception;
using the metasvc-client the `Problem`-object can be retrieved from the exception by calling `getProblem()`

## [9.0.0](https://github.com/dbmdz/metadata-service/releases/tag/9.0.0) – 2024-03-14

Expand Down
2 changes: 1 addition & 1 deletion metasvc-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.github.dbmdz.metadata</groupId>
<artifactId>metasvc</artifactId>
<version>9.0.1-SNAPSHOT</version>
<version>9.1.0-SNAPSHOT</version>
</parent>

<name>Metadata-Service: Repository Client</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,8 @@ protected PageResponse<T> doGetRequestForPagedObjectList(
String requestUrl, PageRequest pageRequest) throws TechnicalException {
if (!requestUrl.contains("?")) {
requestUrl = requestUrl + "?";
} else {
if (!requestUrl.endsWith("&")) {
requestUrl = requestUrl + "&";
}
} else if (!requestUrl.endsWith("&")) {
requestUrl = requestUrl + "&";
}
String findParams = getFindParamsAsString(pageRequest);
requestUrl = requestUrl + findParams;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,10 @@
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CudamiRestClient<T extends UniqueObject> extends BaseRestClient<T> {

public static final String API_VERSION_PREFIX = "/v6";
private static final Logger LOGGER = LoggerFactory.getLogger(CudamiRestClient.class);

public CudamiRestClient(
HttpClient http,
Expand Down
2 changes: 1 addition & 1 deletion metasvc-lobid-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>io.github.dbmdz.metadata</groupId>
<artifactId>metasvc</artifactId>
<version>9.0.1-SNAPSHOT</version>
<version>9.1.0-SNAPSHOT</version>
</parent>

<name>Metadata-Service: lobid.org Client</name>
Expand Down
12 changes: 11 additions & 1 deletion metasvc-model/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.github.dbmdz.metadata</groupId>
<artifactId>metasvc</artifactId>
<version>9.0.1-SNAPSHOT</version>
<version>9.1.0-SNAPSHOT</version>
</parent>

<name>Metadata-Service: Model</name>
Expand Down Expand Up @@ -102,5 +102,15 @@
<artifactId>wdtk-datamodel</artifactId>
<version>0.14.7</version>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>problem</artifactId>
<version>0.26.0</version>
</dependency>
<dependency>
<groupId>org.zalando</groupId>
<artifactId>jackson-datatype-problem</artifactId>
<version>0.26.0</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.digitalcollections.model.exception.http;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.digitalcollections.model.exception.http.client.ForbiddenException;
import de.digitalcollections.model.exception.http.client.HttpClientException;
import de.digitalcollections.model.exception.http.client.ImATeapotException;
Expand All @@ -13,74 +14,110 @@
import de.digitalcollections.model.exception.http.server.HttpVersionNotSupportedException;
import de.digitalcollections.model.exception.http.server.NotImplementedException;
import de.digitalcollections.model.exception.http.server.ServiceUnavailableException;
import de.digitalcollections.model.jackson.DigitalCollectionsObjectMapper;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zalando.problem.Problem;
import org.zalando.problem.jackson.ProblemModule;

public class HttpErrorDecoder {

private static final Logger LOGGER = LoggerFactory.getLogger(HttpErrorDecoder.class);
private static final ObjectMapper mapper;

static {
mapper = new DigitalCollectionsObjectMapper().registerModule(new ProblemModule());
}

private static HttpException clientException(
String methodKey, int statusCode, String requestUrl) {
String methodKey, int statusCode, String requestUrl, Problem problem) {
switch (statusCode) {
case 401:
return new UnauthorizedException(methodKey, statusCode, requestUrl);
return new UnauthorizedException(methodKey, statusCode, requestUrl, problem);
case 403:
return new ForbiddenException(methodKey, statusCode, requestUrl);
return new ForbiddenException(methodKey, statusCode, requestUrl, problem);
case 404:
return new ResourceNotFoundException(methodKey, statusCode, requestUrl);
return new ResourceNotFoundException(methodKey, statusCode, requestUrl, problem);
case 418:
return new ImATeapotException(methodKey, statusCode, requestUrl);
return new ImATeapotException(methodKey, statusCode, requestUrl, problem);
case 422:
return new UnprocessableEntityException(methodKey, statusCode, requestUrl);
return new UnprocessableEntityException(methodKey, statusCode, requestUrl, problem);
case 451:
return new UnavailableForLegalReasonsException(methodKey, statusCode, requestUrl);
return new UnavailableForLegalReasonsException(methodKey, statusCode, requestUrl, problem);
default:
return new HttpClientException(methodKey, statusCode, requestUrl);
return new HttpClientException(methodKey, statusCode, requestUrl, problem);
}
}

public static HttpException decode(String methodKey, int statusCode, HttpResponse response) {
String requestUrl = null;
try {
if (response != null) {
requestUrl = response.request().uri().toURL().toString();
Problem problem = null;
if (response != null) {
requestUrl =
Optional.ofNullable(response.request())
.map(HttpRequest::uri)
.map(
uri -> {
try {
return uri.toURL();
} catch (MalformedURLException e) {
LOGGER.warn("Invalid request URL for: " + uri.toString());
return null;
}
})
.map(URL::toString)
.orElse(null);

final byte[] body = (byte[]) response.body();
if (body != null && body.length > 0) {
try {
problem = mapper.readerFor(Problem.class).readValue(body);
} catch (Exception e) {
LOGGER.error(
"Got response="
+ new String(body, StandardCharsets.UTF_8)
+ " but cannot construct problem: "
+ e,
e);
}
}
} catch (MalformedURLException ex) {
LOGGER.warn("Invalid request Url for: " + response.request().uri());
}

if (400 <= statusCode && statusCode < 500) {
return clientException(methodKey, statusCode, requestUrl);
return clientException(methodKey, statusCode, requestUrl, problem);
} else if (500 <= statusCode && statusCode < 600) {
return serverException(methodKey, statusCode, requestUrl);
return serverException(methodKey, statusCode, requestUrl, problem);
} else {
return genericHttpException(methodKey, statusCode, requestUrl);
return genericHttpException(methodKey, statusCode, requestUrl, problem);
}
}

private static HttpException genericHttpException(
String methodKey, int statusCode, String requestUrl) {
return new HttpException(methodKey, statusCode, requestUrl);
String methodKey, int statusCode, String requestUrl, Problem problem) {
return new HttpException(methodKey, statusCode, requestUrl, problem);
}

private static HttpServerException serverException(
String methodKey, int statusCode, String requestUrl) {
String methodKey, int statusCode, String requestUrl, Problem problem) {
switch (statusCode) {
case 501:
return new NotImplementedException(methodKey, statusCode, requestUrl);
return new NotImplementedException(methodKey, statusCode, requestUrl, problem);
case 502:
return new BadGatewayException(methodKey, statusCode, requestUrl);
return new BadGatewayException(methodKey, statusCode, requestUrl, problem);
case 503:
return new ServiceUnavailableException(methodKey, statusCode, requestUrl);
return new ServiceUnavailableException(methodKey, statusCode, requestUrl, problem);
case 504:
return new GatewayTimeOutException(methodKey, statusCode, requestUrl);
return new GatewayTimeOutException(methodKey, statusCode, requestUrl, problem);
case 505:
return new HttpVersionNotSupportedException(methodKey, statusCode, requestUrl);
return new HttpVersionNotSupportedException(methodKey, statusCode, requestUrl, problem);
default:
return new HttpServerException(methodKey, statusCode, requestUrl);
return new HttpServerException(methodKey, statusCode, requestUrl, problem);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
package de.digitalcollections.model.exception.http;

import org.zalando.problem.Problem;

public class HttpException extends RuntimeException {

private final String methodKey;
private final String request;
private final Integer statuscode;
private final Problem problem;

public HttpException(String methodKey, Exception ex) {
super(String.format("Got exception for backend call %s.", methodKey), ex);
this.methodKey = methodKey;
this.request = null;
this.statuscode = null;
this.problem = null;
}

public HttpException(String methodKey, int statuscode) {
super(String.format("Got status code %d for backend call %s.", statuscode, methodKey));
this.methodKey = methodKey;
this.request = null;
this.statuscode = statuscode;
this.problem = null;
}

public HttpException(String methodKey, int statuscode, String request) {
public HttpException(String methodKey, int statuscode, String request, Problem problem) {
super(String.format("Got %d for backend call %s.%n⤷ %s", statuscode, methodKey, request));
this.methodKey = methodKey;
this.request = request;
this.statuscode = statuscode;
this.problem = problem;
}

public String getMethodKey() {
Expand All @@ -38,4 +44,8 @@ public String getRequest() {
public Integer getStatusCode() {
return statuscode;
}

public Problem getProblem() {
return problem;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package de.digitalcollections.model.exception.http.client;

import org.zalando.problem.Problem;

public class ForbiddenException extends HttpClientException {

public ForbiddenException(String methodKey, int status, String request) {
super(methodKey, status, request);
public ForbiddenException(String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package de.digitalcollections.model.exception.http.client;

import de.digitalcollections.model.exception.http.HttpException;
import org.zalando.problem.Problem;

public class HttpClientException extends HttpException {

public HttpClientException(String methodKey, int status, String request) {
super(methodKey, status, request);
public HttpClientException(String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package de.digitalcollections.model.exception.http.client;

import org.zalando.problem.Problem;

/**
* HttpStatusCode 418 denoting the api is wrongfully using a teapot for making coffee as specified
* in the Hyper Text Coffee Pot Control Protocol (see <a
* href="https://tools.ietf.org/html/rfc2324">RFC 2324</a> for details).
*/
public class ImATeapotException extends HttpClientException {

public ImATeapotException(String methodKey, int status, String request) {
super(methodKey, status, request);
public ImATeapotException(String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package de.digitalcollections.model.exception.http.client;

import org.zalando.problem.Problem;

public class ResourceNotFoundException extends HttpClientException {

public ResourceNotFoundException(String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}

public ResourceNotFoundException(String methodKey, int status, String request) {
super(methodKey, status, request);
this(methodKey, status, request, null);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package de.digitalcollections.model.exception.http.client;

import org.zalando.problem.Problem;

public class UnauthorizedException extends HttpClientException {

public UnauthorizedException(String methodKey, int status, String request) {
super(methodKey, status, request);
public UnauthorizedException(String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package de.digitalcollections.model.exception.http.client;

import org.zalando.problem.Problem;

public class UnavailableForLegalReasonsException extends HttpClientException {

public UnavailableForLegalReasonsException(String methodKey, int status, String request) {
super(methodKey, status, request);
public UnavailableForLegalReasonsException(
String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package de.digitalcollections.model.exception.http.client;

import org.zalando.problem.Problem;

public class UnprocessableEntityException extends HttpClientException {

public UnprocessableEntityException(String methodKey, int status, String request) {
super(methodKey, status, request);
public UnprocessableEntityException(
String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package de.digitalcollections.model.exception.http.server;

import org.zalando.problem.Problem;

public class BadGatewayException extends HttpServerException {

public BadGatewayException(String methodKey, int status, String request) {
super(methodKey, status, request);
public BadGatewayException(String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package de.digitalcollections.model.exception.http.server;

import org.zalando.problem.Problem;

public class GatewayTimeOutException extends HttpServerException {

public GatewayTimeOutException(String methodKey, int status, String request) {
super(methodKey, status, request);
public GatewayTimeOutException(String methodKey, int status, String request, Problem problem) {
super(methodKey, status, request, problem);
}
}
Loading
Loading