diff --git a/CHANGELOG.md b/CHANGELOG.md
index 370e93669..88ef78192 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
## Unreleased
+### Added
+
+- The `Problem` (of error responses) is expanded by a hint that gives clearer information about the actual error
+
## [9.1.0](https://github.com/dbmdz/metadata-service/releases/tag/9.1.0) - 2024-04-16
### Added
diff --git a/metasvc-client/pom.xml b/metasvc-client/pom.xml
index 5a37dd7bc..b55ac8c06 100644
--- a/metasvc-client/pom.xml
+++ b/metasvc-client/pom.xml
@@ -6,7 +6,7 @@
io.github.dbmdz.metadata
metasvc
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Client
diff --git a/metasvc-lobid-client/pom.xml b/metasvc-lobid-client/pom.xml
index 825a825c9..499f9e48a 100644
--- a/metasvc-lobid-client/pom.xml
+++ b/metasvc-lobid-client/pom.xml
@@ -7,7 +7,7 @@
io.github.dbmdz.metadata
metasvc
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: lobid.org Client
diff --git a/metasvc-model/pom.xml b/metasvc-model/pom.xml
index 0caad0dae..cc02558f2 100644
--- a/metasvc-model/pom.xml
+++ b/metasvc-model/pom.xml
@@ -6,7 +6,7 @@
io.github.dbmdz.metadata
metasvc
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Model
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/HttpErrorDecoder.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/HttpErrorDecoder.java
index 7571d8c1c..0c4220f6f 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/HttpErrorDecoder.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/HttpErrorDecoder.java
@@ -14,6 +14,7 @@
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.exception.problem.MetasvcProblem;
import de.digitalcollections.model.jackson.DigitalCollectionsObjectMapper;
import java.net.MalformedURLException;
import java.net.URL;
@@ -23,7 +24,6 @@
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 {
@@ -36,7 +36,7 @@ public class HttpErrorDecoder {
}
private static HttpException clientException(
- String methodKey, int statusCode, String requestUrl, Problem problem) {
+ String methodKey, int statusCode, String requestUrl, MetasvcProblem problem) {
switch (statusCode) {
case 401:
return new UnauthorizedException(methodKey, statusCode, requestUrl, problem);
@@ -57,7 +57,7 @@ private static HttpException clientException(
public static HttpException decode(String methodKey, int statusCode, HttpResponse response) {
String requestUrl = null;
- Problem problem = null;
+ MetasvcProblem problem = null;
if (response != null) {
requestUrl =
Optional.ofNullable(response.request())
@@ -77,7 +77,7 @@ public static HttpException decode(String methodKey, int statusCode, HttpRespons
final byte[] body = (byte[]) response.body();
if (body != null && body.length > 0) {
try {
- problem = mapper.readerFor(Problem.class).readValue(body);
+ problem = mapper.readerFor(MetasvcProblem.class).readValue(body);
} catch (Exception e) {
LOGGER.error(
"Got response="
@@ -99,12 +99,12 @@ public static HttpException decode(String methodKey, int statusCode, HttpRespons
}
private static HttpException genericHttpException(
- String methodKey, int statusCode, String requestUrl, Problem problem) {
+ String methodKey, int statusCode, String requestUrl, MetasvcProblem problem) {
return new HttpException(methodKey, statusCode, requestUrl, problem);
}
private static HttpServerException serverException(
- String methodKey, int statusCode, String requestUrl, Problem problem) {
+ String methodKey, int statusCode, String requestUrl, MetasvcProblem problem) {
switch (statusCode) {
case 501:
return new NotImplementedException(methodKey, statusCode, requestUrl, problem);
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/HttpException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/HttpException.java
index 3674a6645..e723fc362 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/HttpException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/HttpException.java
@@ -1,13 +1,13 @@
package de.digitalcollections.model.exception.http;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class HttpException extends RuntimeException {
private final String methodKey;
private final String request;
private final Integer statuscode;
- private final Problem problem;
+ private final MetasvcProblem problem;
public HttpException(String methodKey, Exception ex) {
super(String.format("Got exception for backend call %s.", methodKey), ex);
@@ -25,7 +25,7 @@ public HttpException(String methodKey, int statuscode) {
this.problem = null;
}
- public HttpException(String methodKey, int statuscode, String request, Problem problem) {
+ public HttpException(String methodKey, int statuscode, String request, MetasvcProblem problem) {
super(String.format("Got %d for backend call %s.%n⤷ %s", statuscode, methodKey, request));
this.methodKey = methodKey;
this.request = request;
@@ -45,7 +45,7 @@ public Integer getStatusCode() {
return statuscode;
}
- public Problem getProblem() {
+ public MetasvcProblem getProblem() {
return problem;
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ForbiddenException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ForbiddenException.java
index 021733561..1c3aa2714 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ForbiddenException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ForbiddenException.java
@@ -1,10 +1,10 @@
package de.digitalcollections.model.exception.http.client;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class ForbiddenException extends HttpClientException {
- public ForbiddenException(String methodKey, int status, String request, Problem problem) {
+ public ForbiddenException(String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/HttpClientException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/HttpClientException.java
index 9a2c0ba57..598397513 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/HttpClientException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/HttpClientException.java
@@ -1,11 +1,11 @@
package de.digitalcollections.model.exception.http.client;
import de.digitalcollections.model.exception.http.HttpException;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class HttpClientException extends HttpException {
- public HttpClientException(String methodKey, int status, String request, Problem problem) {
+ public HttpClientException(String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ImATeapotException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ImATeapotException.java
index 84a34868d..a1477159a 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ImATeapotException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ImATeapotException.java
@@ -1,6 +1,6 @@
package de.digitalcollections.model.exception.http.client;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
/**
* HttpStatusCode 418 denoting the api is wrongfully using a teapot for making coffee as specified
@@ -9,7 +9,7 @@
*/
public class ImATeapotException extends HttpClientException {
- public ImATeapotException(String methodKey, int status, String request, Problem problem) {
+ public ImATeapotException(String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ResourceNotFoundException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ResourceNotFoundException.java
index 27e1d0954..0260028e8 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ResourceNotFoundException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/ResourceNotFoundException.java
@@ -1,10 +1,11 @@
package de.digitalcollections.model.exception.http.client;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class ResourceNotFoundException extends HttpClientException {
- public ResourceNotFoundException(String methodKey, int status, String request, Problem problem) {
+ public ResourceNotFoundException(
+ String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnauthorizedException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnauthorizedException.java
index eb2b12e6d..945c02cb1 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnauthorizedException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnauthorizedException.java
@@ -1,10 +1,11 @@
package de.digitalcollections.model.exception.http.client;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class UnauthorizedException extends HttpClientException {
- public UnauthorizedException(String methodKey, int status, String request, Problem problem) {
+ public UnauthorizedException(
+ String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnavailableForLegalReasonsException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnavailableForLegalReasonsException.java
index 033b76d21..21c2ac36b 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnavailableForLegalReasonsException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnavailableForLegalReasonsException.java
@@ -1,11 +1,11 @@
package de.digitalcollections.model.exception.http.client;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class UnavailableForLegalReasonsException extends HttpClientException {
public UnavailableForLegalReasonsException(
- String methodKey, int status, String request, Problem problem) {
+ String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnprocessableEntityException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnprocessableEntityException.java
index 58400ac83..aea0adafd 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnprocessableEntityException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/client/UnprocessableEntityException.java
@@ -1,11 +1,11 @@
package de.digitalcollections.model.exception.http.client;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class UnprocessableEntityException extends HttpClientException {
public UnprocessableEntityException(
- String methodKey, int status, String request, Problem problem) {
+ String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/BadGatewayException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/BadGatewayException.java
index 4b437de71..7afa98d40 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/BadGatewayException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/BadGatewayException.java
@@ -1,10 +1,10 @@
package de.digitalcollections.model.exception.http.server;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class BadGatewayException extends HttpServerException {
- public BadGatewayException(String methodKey, int status, String request, Problem problem) {
+ public BadGatewayException(String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/GatewayTimeOutException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/GatewayTimeOutException.java
index 9796d6713..508362243 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/GatewayTimeOutException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/GatewayTimeOutException.java
@@ -1,10 +1,11 @@
package de.digitalcollections.model.exception.http.server;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class GatewayTimeOutException extends HttpServerException {
- public GatewayTimeOutException(String methodKey, int status, String request, Problem problem) {
+ public GatewayTimeOutException(
+ String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/HttpServerException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/HttpServerException.java
index 850499a3c..77b685b7a 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/HttpServerException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/HttpServerException.java
@@ -1,11 +1,11 @@
package de.digitalcollections.model.exception.http.server;
import de.digitalcollections.model.exception.http.HttpException;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class HttpServerException extends HttpException {
- public HttpServerException(String methodKey, int status, String request, Problem problem) {
+ public HttpServerException(String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/HttpVersionNotSupportedException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/HttpVersionNotSupportedException.java
index 183aef0fd..047840667 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/HttpVersionNotSupportedException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/HttpVersionNotSupportedException.java
@@ -1,11 +1,11 @@
package de.digitalcollections.model.exception.http.server;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class HttpVersionNotSupportedException extends HttpServerException {
public HttpVersionNotSupportedException(
- String methodKey, int status, String request, Problem problem) {
+ String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/NotImplementedException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/NotImplementedException.java
index bbad81ecc..7ff7cc484 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/NotImplementedException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/NotImplementedException.java
@@ -1,10 +1,11 @@
package de.digitalcollections.model.exception.http.server;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class NotImplementedException extends HttpServerException {
- public NotImplementedException(String methodKey, int status, String request, Problem problem) {
+ public NotImplementedException(
+ String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/ServiceUnavailableException.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/ServiceUnavailableException.java
index 35575e0ef..5cf5678e6 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/ServiceUnavailableException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/http/server/ServiceUnavailableException.java
@@ -1,11 +1,11 @@
package de.digitalcollections.model.exception.http.server;
-import org.zalando.problem.Problem;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
public class ServiceUnavailableException extends HttpServerException {
public ServiceUnavailableException(
- String methodKey, int status, String request, Problem problem) {
+ String methodKey, int status, String request, MetasvcProblem problem) {
super(methodKey, status, request, problem);
}
}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/MetasvcProblem.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/MetasvcProblem.java
new file mode 100644
index 000000000..03345443a
--- /dev/null
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/MetasvcProblem.java
@@ -0,0 +1,68 @@
+package de.digitalcollections.model.exception.problem;
+
+import de.digitalcollections.model.validation.ValidationError;
+import java.net.URI;
+import java.util.Date;
+import java.util.List;
+import lombok.Builder;
+import lombok.Singular;
+import org.zalando.problem.AbstractThrowableProblem;
+import org.zalando.problem.StatusType;
+
+public final class MetasvcProblem extends AbstractThrowableProblem {
+
+ private List errors;
+ private Date timestamp;
+ private ProblemHint hint;
+
+ public MetasvcProblem() {
+ super();
+ }
+
+ public MetasvcProblem(
+ URI type, String title, StatusType status, String detail, URI instance, Date timestamp) {
+ super(type, title, status, detail, instance);
+ this.timestamp = timestamp;
+ }
+
+ @Builder(setterPrefix = "with")
+ public MetasvcProblem(
+ URI type,
+ String title,
+ StatusType status,
+ String detail,
+ URI instance,
+ Date timestamp,
+ @Singular List errors,
+ ProblemHint hint) {
+ super(type, title, status, detail, instance);
+ this.timestamp = timestamp;
+ this.errors = errors;
+ this.hint = hint;
+ }
+
+ public MetasvcProblem(
+ URI type,
+ String title,
+ StatusType status,
+ String detail,
+ URI instance,
+ Date timestamp,
+ ProblemHint hint) {
+ super(type, title, status, detail, instance);
+ this.timestamp = timestamp;
+ this.hint = hint;
+ }
+
+ public List getErrors() {
+ return errors;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public ProblemHint getHint() {
+ return hint;
+ }
+}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/ProblemHint.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/ProblemHint.java
new file mode 100644
index 000000000..e0caac6eb
--- /dev/null
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/ProblemHint.java
@@ -0,0 +1,22 @@
+package de.digitalcollections.model.exception.problem;
+
+public enum ProblemHint {
+ NONE("No hint available."),
+ RETRY_RECOMMENDED("The error is caused by a concurrent operation. It might succeed if retried."),
+ REFERENCED_OBJECT_NOT_EXISTS(
+ "An included object is not stored yet. Please save it separately before trying this operation again."),
+ UNIQUE_VIOLATION("One or more of these identifiers exist already."),
+ MANDATORY_CHECK_FAILED(
+ "A data integrity check failed. Please check for missing or incorrect properties."),
+ PROPERTY_MUST_NOT_BE_NULL("Ensure that all necessary/mandatory properties are set.");
+
+ private String description;
+
+ ProblemHint(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/ProblemHinting.java b/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/ProblemHinting.java
new file mode 100644
index 000000000..b83ae46a2
--- /dev/null
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/exception/problem/ProblemHinting.java
@@ -0,0 +1,6 @@
+package de.digitalcollections.model.exception.problem;
+
+public interface ProblemHinting {
+
+ ProblemHint getHint();
+}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/jackson/DigitalCollectionsModelModule.java b/metasvc-model/src/main/java/de/digitalcollections/model/jackson/DigitalCollectionsModelModule.java
index 5ec0b4759..e0681670b 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/jackson/DigitalCollectionsModelModule.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/jackson/DigitalCollectionsModelModule.java
@@ -7,6 +7,7 @@
import com.fasterxml.jackson.databind.util.Converter;
import com.fasterxml.jackson.databind.util.StdConverter;
import de.digitalcollections.model.UniqueObject;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
import de.digitalcollections.model.file.MimeType;
import de.digitalcollections.model.geo.CoordinateLocation;
import de.digitalcollections.model.identifiable.Identifiable;
@@ -55,6 +56,7 @@
import de.digitalcollections.model.identifiable.semantic.Subject;
import de.digitalcollections.model.identifiable.web.Webpage;
import de.digitalcollections.model.jackson.mixin.UniqueObjectMixIn;
+import de.digitalcollections.model.jackson.mixin.exception.problem.MetasvcProblemMixIn;
import de.digitalcollections.model.jackson.mixin.geo.CoordinateLocationMixIn;
import de.digitalcollections.model.jackson.mixin.identifiable.IdentifiableMixIn;
import de.digitalcollections.model.jackson.mixin.identifiable.IdentifierMixIn;
@@ -309,6 +311,7 @@ public void setupModule(SetupContext context) {
context.setMixInAnnotations(LocalizedText.class, LocalizedTextMixIn.class);
context.setMixInAnnotations(LocalizedUrlAliases.class, LocalizedUrlAliasesMixIn.class);
context.setMixInAnnotations(Mark.class, MarkMixIn.class);
+ context.setMixInAnnotations(MetasvcProblem.class, MetasvcProblemMixIn.class);
context.setMixInAnnotations(Node.class, NodeMixIn.class);
context.setMixInAnnotations(Order.class, OrderMixIn.class);
context.setMixInAnnotations(OrderedList.class, OrderedListMixIn.class);
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/jackson/mixin/exception/problem/MetasvcProblemMixIn.java b/metasvc-model/src/main/java/de/digitalcollections/model/jackson/mixin/exception/problem/MetasvcProblemMixIn.java
new file mode 100644
index 000000000..7def413eb
--- /dev/null
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/jackson/mixin/exception/problem/MetasvcProblemMixIn.java
@@ -0,0 +1,7 @@
+package de.digitalcollections.model.jackson.mixin.exception.problem;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+@JsonTypeInfo(use = Id.NONE)
+public interface MetasvcProblemMixIn {}
diff --git a/metasvc-model/src/main/java/de/digitalcollections/model/validation/ValidationException.java b/metasvc-model/src/main/java/de/digitalcollections/model/validation/ValidationException.java
index 3406c7abe..ebdc2b54b 100644
--- a/metasvc-model/src/main/java/de/digitalcollections/model/validation/ValidationException.java
+++ b/metasvc-model/src/main/java/de/digitalcollections/model/validation/ValidationException.java
@@ -1,11 +1,14 @@
package de.digitalcollections.model.validation;
+import de.digitalcollections.model.exception.problem.ProblemHint;
+import de.digitalcollections.model.exception.problem.ProblemHinting;
import java.util.ArrayList;
import java.util.List;
-public class ValidationException extends Exception {
+public class ValidationException extends Exception implements ProblemHinting {
private List errors = new ArrayList<>(1);
+ private ProblemHint hint = ProblemHint.NONE;
public ValidationException(String msg, Exception e) {
super(msg, e);
@@ -15,6 +18,11 @@ public ValidationException(String msg) {
super(msg);
}
+ public ValidationException(String msg, ProblemHint hint) {
+ this(msg);
+ this.hint = hint;
+ }
+
public void addError(ValidationError validationError) {
errors.add(validationError);
}
@@ -22,4 +30,9 @@ public void addError(ValidationError validationError) {
public List getErrors() {
return errors;
}
+
+ @Override
+ public ProblemHint getHint() {
+ return hint;
+ }
}
diff --git a/metasvc-server/backend-api/pom.xml b/metasvc-server/backend-api/pom.xml
index 18ebf0a68..0d1337037 100644
--- a/metasvc-server/backend-api/pom.xml
+++ b/metasvc-server/backend-api/pom.xml
@@ -6,7 +6,7 @@
io.github.dbmdz.metadata
metasvc-server
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Server (Backend API)
diff --git a/metasvc-server/backend-api/src/main/java/io/github/dbmdz/metadata/server/backend/api/repository/exceptions/RepositoryException.java b/metasvc-server/backend-api/src/main/java/io/github/dbmdz/metadata/server/backend/api/repository/exceptions/RepositoryException.java
index 1308ec58a..d00bd205a 100644
--- a/metasvc-server/backend-api/src/main/java/io/github/dbmdz/metadata/server/backend/api/repository/exceptions/RepositoryException.java
+++ b/metasvc-server/backend-api/src/main/java/io/github/dbmdz/metadata/server/backend/api/repository/exceptions/RepositoryException.java
@@ -1,6 +1,11 @@
package io.github.dbmdz.metadata.server.backend.api.repository.exceptions;
-public class RepositoryException extends Exception {
+import de.digitalcollections.model.exception.problem.ProblemHint;
+import de.digitalcollections.model.exception.problem.ProblemHinting;
+
+public class RepositoryException extends Exception implements ProblemHinting {
+
+ private ProblemHint hint = ProblemHint.NONE;
public RepositoryException(String message) {
super(message);
@@ -10,7 +15,22 @@ public RepositoryException(Throwable cause) {
super("An unexpected error occured!", cause);
}
+ public RepositoryException(Throwable cause, ProblemHint hint) {
+ this(cause);
+ this.hint = hint;
+ }
+
public RepositoryException(String message, Throwable cause) {
super(message, cause);
}
+
+ public RepositoryException(String message, Throwable cause, ProblemHint hint) {
+ this(message, cause);
+ this.hint = hint;
+ }
+
+ @Override
+ public ProblemHint getHint() {
+ return hint;
+ }
}
diff --git a/metasvc-server/backend-file/pom.xml b/metasvc-server/backend-file/pom.xml
index 0eac8bcb2..c7814bb27 100644
--- a/metasvc-server/backend-file/pom.xml
+++ b/metasvc-server/backend-file/pom.xml
@@ -5,7 +5,7 @@
io.github.dbmdz.metadata
metasvc-server
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Server (Backend IMPL File)
diff --git a/metasvc-server/backend-inmemory/pom.xml b/metasvc-server/backend-inmemory/pom.xml
index b43048cca..ee4cf6920 100644
--- a/metasvc-server/backend-inmemory/pom.xml
+++ b/metasvc-server/backend-inmemory/pom.xml
@@ -6,7 +6,7 @@
io.github.dbmdz.metadata
metasvc-server
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Server (Backend IMPL InMemory)
diff --git a/metasvc-server/backend-jdbi/pom.xml b/metasvc-server/backend-jdbi/pom.xml
index 18822f750..99b30f208 100644
--- a/metasvc-server/backend-jdbi/pom.xml
+++ b/metasvc-server/backend-jdbi/pom.xml
@@ -8,7 +8,7 @@
io.github.dbmdz.metadata
metasvc-server
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Server (Backend IMPL JDBI PostgreSql)
diff --git a/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/UniqueObjectRepositoryImpl.java b/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/UniqueObjectRepositoryImpl.java
index ccd1649d4..88fb4afeb 100644
--- a/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/UniqueObjectRepositoryImpl.java
+++ b/metasvc-server/backend-jdbi/src/main/java/io/github/dbmdz/metadata/server/backend/impl/jdbi/UniqueObjectRepositoryImpl.java
@@ -1,6 +1,7 @@
package io.github.dbmdz.metadata.server.backend.impl.jdbi;
import de.digitalcollections.model.UniqueObject;
+import de.digitalcollections.model.exception.problem.ProblemHint;
import de.digitalcollections.model.list.filtering.FilterCriterion;
import de.digitalcollections.model.list.filtering.Filtering;
import de.digitalcollections.model.list.paging.PageRequest;
@@ -113,14 +114,37 @@ protected boolean isConstraintViolationException(
* foreign_key_violation: 23503
* unique_violation: 23505
* check_violation: 23514
+ * not_null_violation: 23502
*/
- return List.of("23503", "23505", "23514").contains(sqlexc.getSQLState());
+ return List.of("23503", "23505", "23514", "23502").contains(sqlexc.getSQLState());
}
return throwable.getCause() != null
? isConstraintViolationException(throwable.getCause(), useMessage)
: false;
}
+ protected ProblemHint getHint(Throwable throwable) {
+ if (throwable == null) return ProblemHint.NONE;
+ if (throwable instanceof SQLException sqlexc) {
+ /*
+ * Codes see method `isConstraintViolationException`
+ * serialization_failure: 40001
+ */
+ return switch (sqlexc.getSQLState()) {
+ case "23503" -> ProblemHint.REFERENCED_OBJECT_NOT_EXISTS;
+ case "23505" -> ProblemHint.UNIQUE_VIOLATION;
+ case "23514" -> ProblemHint.MANDATORY_CHECK_FAILED;
+ case "23502" -> ProblemHint.PROPERTY_MUST_NOT_BE_NULL;
+ case "40001" -> ProblemHint.RETRY_RECOMMENDED;
+ default -> ProblemHint.NONE;
+ };
+ } else if (throwable.getCause() != null) {
+ return getHint(throwable.getCause());
+ } else {
+ return ProblemHint.NONE;
+ }
+ }
+
private void execInsertUpdate(
final String sql, U uniqueObject, final Map bindings, boolean withCallback)
throws RepositoryException, ValidationException {
@@ -150,13 +174,14 @@ private void execInsertUpdate(
} catch (StatementException e) {
AtomicReference constraintMessage = new AtomicReference<>();
if (isConstraintViolationException(e, constraintMessage::set)) {
- throw new ValidationException(constraintMessage.get());
+ throw new ValidationException(constraintMessage.get(), getHint(e));
}
+
String detailMessage = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
throw new RepositoryException(
- String.format("The SQL statement is defective: %s", detailMessage), e);
+ String.format("SQL exception: %s", detailMessage), e, getHint(e));
} catch (JdbiException e) {
- throw new RepositoryException(e);
+ throw new RepositoryException(e, getHint(e));
}
}
diff --git a/metasvc-server/backend-lobid/pom.xml b/metasvc-server/backend-lobid/pom.xml
index b02ca47f6..ac27eca29 100644
--- a/metasvc-server/backend-lobid/pom.xml
+++ b/metasvc-server/backend-lobid/pom.xml
@@ -6,7 +6,7 @@
io.github.dbmdz.metadata
metasvc-server
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Server (Backend IMPL External System lobid.org)
diff --git a/metasvc-server/business/pom.xml b/metasvc-server/business/pom.xml
index 8729765d7..4fc6d81ad 100644
--- a/metasvc-server/business/pom.xml
+++ b/metasvc-server/business/pom.xml
@@ -6,7 +6,7 @@
io.github.dbmdz.metadata
metasvc-server
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Server (Business)
diff --git a/metasvc-server/pom.xml b/metasvc-server/pom.xml
index 8c1673715..e86b6092e 100644
--- a/metasvc-server/pom.xml
+++ b/metasvc-server/pom.xml
@@ -6,7 +6,7 @@
io.github.dbmdz.metadata
metasvc
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Server
diff --git a/metasvc-server/webapp/pom.xml b/metasvc-server/webapp/pom.xml
index c14bb42a3..b9af9143c 100644
--- a/metasvc-server/webapp/pom.xml
+++ b/metasvc-server/webapp/pom.xml
@@ -6,7 +6,7 @@
io.github.dbmdz.metadata
metasvc-server
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
Metadata-Service: Repository Server (Webapp)
diff --git a/metasvc-server/webapp/src/main/java/io/github/dbmdz/metadata/server/controller/advice/ExceptionAdvice.java b/metasvc-server/webapp/src/main/java/io/github/dbmdz/metadata/server/controller/advice/ExceptionAdvice.java
index ff862f49f..d5db90874 100644
--- a/metasvc-server/webapp/src/main/java/io/github/dbmdz/metadata/server/controller/advice/ExceptionAdvice.java
+++ b/metasvc-server/webapp/src/main/java/io/github/dbmdz/metadata/server/controller/advice/ExceptionAdvice.java
@@ -1,5 +1,8 @@
package io.github.dbmdz.metadata.server.controller.advice;
+import de.digitalcollections.model.exception.problem.MetasvcProblem;
+import de.digitalcollections.model.exception.problem.ProblemHint;
+import de.digitalcollections.model.exception.problem.ProblemHinting;
import de.digitalcollections.model.validation.ValidationException;
import io.github.dbmdz.metadata.server.business.api.service.exceptions.ConflictException;
import io.github.dbmdz.metadata.server.business.api.service.exceptions.ResourceNotFoundException;
@@ -33,12 +36,6 @@ private static URI getRequestUri(ServletWebRequest servletRequest) {
.toUri();
}
- @ExceptionHandler(UsernameNotFoundException.class)
- public ResponseEntity handleNotFound(
- UsernameNotFoundException e, ServletWebRequest request) {
- return create(Status.NOT_FOUND, e, request);
- }
-
private static Status statusFromExceptionClass(Throwable exc) {
if (exc instanceof ResourceNotFoundException) {
return Status.NOT_FOUND;
@@ -53,11 +50,28 @@ private static Status statusFromExceptionClass(Throwable exc) {
}
}
+ private static ProblemHint hintFromException(Throwable exc) {
+ if (exc == null) return ProblemHint.NONE;
+ if (exc instanceof ProblemHinting hintedExc) {
+ return hintedExc.getHint();
+ } else if (exc.getCause() != null) {
+ return hintFromException(exc.getCause());
+ } else {
+ return ProblemHint.NONE;
+ }
+ }
+
+ @ExceptionHandler(UsernameNotFoundException.class)
+ public ResponseEntity handleNotFound(
+ UsernameNotFoundException e, ServletWebRequest request) {
+ return create(Status.NOT_FOUND, e, request);
+ }
+
@ExceptionHandler(ValidationException.class)
public ResponseEntity handleValidationException(
ValidationException exception, ServletWebRequest request) {
ThrowableProblem problem =
- Problem.builder()
+ MetasvcProblem.builder()
.withType(
UriComponentsBuilder.fromPath("/errors/")
.path(exception.getClass().getSimpleName())
@@ -67,8 +81,9 @@ public ResponseEntity handleValidationException(
.withStatus(statusFromExceptionClass(exception))
.withInstance(getRequestUri(request))
.withDetail(exception.getMessage())
- .with("errors", exception.getErrors())
- .with("timestamp", new Date())
+ .withErrors(exception.getErrors())
+ .withTimestamp(new Date())
+ .withHint(exception.getHint())
.build();
return create(problem, request);
}
@@ -80,7 +95,7 @@ public ResponseEntity handleAllOther(Exception exception, ServletWebReq
cause = cause.getCause();
}
ThrowableProblem problem =
- Problem.builder()
+ MetasvcProblem.builder()
.withType(
UriComponentsBuilder.fromPath("/errors/")
.path(cause.getClass().getSimpleName())
@@ -90,6 +105,8 @@ public ResponseEntity handleAllOther(Exception exception, ServletWebReq
.withStatus(statusFromExceptionClass(cause))
.withDetail(cause.getMessage())
.withInstance(getRequestUri(request))
+ .withHint(hintFromException(exception))
+ .withTimestamp(new Date())
.build();
if (problem.getStatus() == Status.INTERNAL_SERVER_ERROR)
LOGGER.error("Exception stack trace", exception);
diff --git a/pom.xml b/pom.xml
index 38a75d8b3..fb512c419 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
io.github.dbmdz.metadata
metasvc
- 9.1.1-SNAPSHOT
+ 9.2.0-SNAPSHOT
pom
Metadata-Service