diff --git a/belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/it/AbstractJacksonSerializationTest.java b/belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/it/AbstractJacksonSerializationTest.java
index f6cb5c51..f2dc1015 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/it/AbstractJacksonSerializationTest.java
+++ b/belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/it/AbstractJacksonSerializationTest.java
@@ -80,7 +80,7 @@ void retryAfterProblem() throws IOException {
@Test
void badRequestProblemReplacedSsin() throws IOException {
BadRequestProblem problem = new BadRequestProblem(
- InputValidationIssues.replacedSsin(InEnum.BODY, "parent[1].ssin", "12345678901", "23456789012"));
+ InputValidationIssues.replacedSsin(InEnum.BODY, "/parent/1/ssin", "12345678901", "23456789012"));
assertSerializationRoundtrip(problem);
}
@@ -182,7 +182,7 @@ void legacyInvalidParamProblem() throws IOException {
+ " \"detail\": \"The input message is incorrect\",\n"
+ " \"invalidParams\": [ {\n"
+ " \"in\": \"body\",\n"
- + " \"name\": \"sector\",\n"
+ + " \"name\": \"/sector\",\n"
+ " \"reason\": \"must be less than or equal to 999\",\n"
+ " \"value\": 9999,\n"
+ " \"issueType\": \"schemaViolation\"\n"
@@ -239,7 +239,7 @@ void issueWithStatusAndInstance() throws IOException {
@Test
void issueWithNullValue() throws IOException {
BadRequestProblem problem = new BadRequestProblem(
- new InputValidationIssue(InEnum.BODY, "id", null));
+ new InputValidationIssue(InEnum.BODY, "/id", null));
String json = writeProblem(problem);
assertThat(json).doesNotContain("null");
assertSerializationRoundtrip(problem);
@@ -249,7 +249,7 @@ void issueWithNullValue() throws IOException {
void issueWithNullInputValue() throws IOException {
ProblemConfig.setExtInputsArrayEnabled(true);
BadRequestProblem problem = new BadRequestProblem(new InputValidationIssue()
- .inputs(Input.body("a", null), Input.body("b", null)));
+ .inputs(Input.body("/a", null), Input.body("/b", null)));
String json = writeProblem(problem);
assertThat(json).doesNotContain("null");
assertSerializationRoundtrip(problem);
diff --git a/belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/it/AbstractRestProblemIT.java b/belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/it/AbstractRestProblemIT.java
index f30aae1f..ff2d22a3 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/it/AbstractRestProblemIT.java
+++ b/belgif-rest-problem-it/belgif-rest-problem-it-common/src/main/java/io/github/belgif/rest/problem/it/AbstractRestProblemIT.java
@@ -332,11 +332,11 @@ public void constraintViolationBody() {
.statusCode(400)
.body("type", equalTo("urn:problem-type:belgif:badRequest"))
.body("issues[0].in", equalTo("body"))
- .body("issues[0].name", equalTo("email"))
+ .body("issues[0].name", equalTo("/email"))
.body("issues[0].value", equalTo("mymail.com"))
.body("issues[0].detail", equalTo("must be a well-formed email address"))
.body("issues[1].in", equalTo("body"))
- .body("issues[1].name", equalTo("name"))
+ .body("issues[1].name", equalTo("/name"))
.body("issues[1].value", nullValue())
.body("issues[1].detail", equalTo("must not be blank"));
}
@@ -352,11 +352,11 @@ public void constraintViolationBodyNested() {
.statusCode(400)
.body("type", equalTo("urn:problem-type:belgif:badRequest"))
.body("issues[0].in", equalTo("body"))
- .body("issues[0].name", equalTo("nested.email"))
+ .body("issues[0].name", equalTo("/nested/email"))
.body("issues[0].value", equalTo("mymail.com"))
.body("issues[0].detail", equalTo("must be a well-formed email address"))
.body("issues[1].in", equalTo("body"))
- .body("issues[1].name", equalTo("nested.name"))
+ .body("issues[1].name", equalTo("/nested/name"))
.body("issues[1].value", nullValue())
.body("issues[1].detail", equalTo("must not be blank"));
}
@@ -372,11 +372,11 @@ public void constraintViolationBodyInheritance() {
.statusCode(400)
.body("type", equalTo("urn:problem-type:belgif:badRequest"))
.body("issues[0].in", equalTo("body"))
- .body("issues[0].name", equalTo("email"))
+ .body("issues[0].name", equalTo("/email"))
.body("issues[0].value", equalTo("mymail.com"))
.body("issues[0].detail", equalTo("must be a well-formed email address"))
.body("issues[1].in", equalTo("body"))
- .body("issues[1].name", equalTo("name"))
+ .body("issues[1].name", equalTo("/name"))
.body("issues[1].value", nullValue())
.body("issues[1].detail", equalTo("must not be blank"));
}
@@ -389,7 +389,7 @@ public void jacksonMismatchedInputException() {
.statusCode(400)
.body("type", equalTo("urn:problem-type:belgif:badRequest"))
.body("issues[0].in", equalTo("body"))
- .body("issues[0].name", equalTo("id"))
+ .body("issues[0].name", equalTo("/id"))
.body("issues[0].detail", equalTo("must not be null"));
}
@@ -542,7 +542,7 @@ public void invalidJsonNested() {
.body("issues[0].title", equalTo("Input value is invalid with respect to the schema"))
.body("issues[0].detail", equalTo("JSON syntax error"))
.body("issues[0].in", equalTo("body"))
- .body("issues[0].name", equalTo("nested"));
+ .body("issues[0].name", equalTo("/nested"));
}
@Test
@@ -558,7 +558,7 @@ public void invalidJsonType() {
.body("issues[0].title", equalTo("Input value is invalid with respect to the schema"))
.body("issues[0].detail", equalTo("not a valid `int` value"))
.body("issues[0].in", equalTo("body"))
- .body("issues[0].name", equalTo("age"))
+ .body("issues[0].name", equalTo("/age"))
.body("issues[0].value", equalTo("twenty-two"));
}
diff --git a/belgif-rest-problem-it/belgif-rest-problem-jackson2-latest-it/pom.xml b/belgif-rest-problem-it/belgif-rest-problem-jackson2-latest-it/pom.xml
index edafaffe..978265c4 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-jackson2-latest-it/pom.xml
+++ b/belgif-rest-problem-it/belgif-rest-problem-jackson2-latest-it/pom.xml
@@ -25,6 +25,12 @@
2.17.0
provided
+
+ org.slf4j
+ slf4j-api
+ 2.0.18
+ provided
+
org.junit.jupiter
junit-jupiter
diff --git a/belgif-rest-problem-it/belgif-rest-problem-jackson2-minimal-it/pom.xml b/belgif-rest-problem-it/belgif-rest-problem-jackson2-minimal-it/pom.xml
index dd9b195d..d7c564fd 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-jackson2-minimal-it/pom.xml
+++ b/belgif-rest-problem-it/belgif-rest-problem-jackson2-minimal-it/pom.xml
@@ -25,6 +25,12 @@
${version.jackson.minimal}
provided
+
+ org.slf4j
+ slf4j-api
+ 2.0.18
+ provided
+
org.junit.jupiter
junit-jupiter
diff --git a/belgif-rest-problem-it/belgif-rest-problem-jackson3-it/pom.xml b/belgif-rest-problem-it/belgif-rest-problem-jackson3-it/pom.xml
index 63f4e637..6b657c2f 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-jackson3-it/pom.xml
+++ b/belgif-rest-problem-it/belgif-rest-problem-jackson3-it/pom.xml
@@ -25,6 +25,12 @@
${version.jackson3.minimal}
provided
+
+ org.slf4j
+ slf4j-api
+ 2.0.18
+ provided
+
org.junit.jupiter
junit-jupiter
diff --git a/belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/LocalDateConverter.java b/belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/LocalDateConverter.java
index ca9a72da..d89f0dc4 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/LocalDateConverter.java
+++ b/belgif-rest-problem-it/belgif-rest-problem-jakarta-ee-it/src/main/java/io/github/belgif/rest/problem/LocalDateConverter.java
@@ -10,6 +10,7 @@
import jakarta.ws.rs.ext.Provider;
import io.github.belgif.rest.problem.api.InEnum;
+import io.github.belgif.rest.problem.api.InputValidationIssue;
import io.github.belgif.rest.problem.ee.server.jaxrs.AbstractInputParamConverterProvider;
@Provider
@@ -31,7 +32,9 @@ protected LocalDate fromString(InEnum in, String name, String value) {
try {
return LocalDate.parse(value, LOCAL_DATE_FORMATTER);
} catch (DateTimeParseException e) {
- throw new BadRequestProblem(schemaViolation(in, name, value, "date has invalid format"));
+ throw new BadRequestProblem(
+ schemaViolation(in, InputValidationIssue.transformName(in, name), value,
+ "date has invalid format"));
}
}
diff --git a/belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/LocalDateConverter.java b/belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/LocalDateConverter.java
index dafb618e..b0bb9ef7 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/LocalDateConverter.java
+++ b/belgif-rest-problem-it/belgif-rest-problem-java-ee-it/src/main/java/io/github/belgif/rest/problem/LocalDateConverter.java
@@ -10,6 +10,7 @@
import javax.ws.rs.ext.Provider;
import io.github.belgif.rest.problem.api.InEnum;
+import io.github.belgif.rest.problem.api.InputValidationIssue;
import io.github.belgif.rest.problem.ee.server.jaxrs.AbstractInputParamConverterProvider;
@Provider
@@ -31,7 +32,9 @@ protected LocalDate fromString(InEnum in, String name, String value) {
try {
return LocalDate.parse(value, LOCAL_DATE_FORMATTER);
} catch (DateTimeParseException e) {
- throw new BadRequestProblem(schemaViolation(in, name, value, "date has invalid format"));
+ throw new BadRequestProblem(
+ schemaViolation(in, InputValidationIssue.transformName(in, name), value,
+ "date has invalid format"));
}
}
diff --git a/belgif-rest-problem-it/belgif-rest-problem-quarkus-it/src/main/java/io/github/belgif/rest/problem/quarkus/it/LocalDateConverter.java b/belgif-rest-problem-it/belgif-rest-problem-quarkus-it/src/main/java/io/github/belgif/rest/problem/quarkus/it/LocalDateConverter.java
index 3b56b2fe..1db1d9ba 100644
--- a/belgif-rest-problem-it/belgif-rest-problem-quarkus-it/src/main/java/io/github/belgif/rest/problem/quarkus/it/LocalDateConverter.java
+++ b/belgif-rest-problem-it/belgif-rest-problem-quarkus-it/src/main/java/io/github/belgif/rest/problem/quarkus/it/LocalDateConverter.java
@@ -11,6 +11,7 @@
import io.github.belgif.rest.problem.BadRequestProblem;
import io.github.belgif.rest.problem.api.InEnum;
+import io.github.belgif.rest.problem.api.InputValidationIssue;
import io.github.belgif.rest.problem.ee.server.jaxrs.AbstractInputParamConverterProvider;
@Provider
@@ -32,7 +33,9 @@ protected LocalDate fromString(InEnum in, String name, String value) {
try {
return LocalDate.parse(value, LOCAL_DATE_FORMATTER);
} catch (DateTimeParseException e) {
- throw new BadRequestProblem(schemaViolation(in, name, value, "date has invalid format"));
+ throw new BadRequestProblem(
+ schemaViolation(in, InputValidationIssue.transformName(in, name), value,
+ "date has invalid format"));
}
}
diff --git a/belgif-rest-problem-java-ee-core/src/main/java/io/github/belgif/rest/problem/ee/core/jaxrs/ProblemConfigurator.java b/belgif-rest-problem-java-ee-core/src/main/java/io/github/belgif/rest/problem/ee/core/jaxrs/ProblemConfigurator.java
index 9f87b6cf..4a484488 100644
--- a/belgif-rest-problem-java-ee-core/src/main/java/io/github/belgif/rest/problem/ee/core/jaxrs/ProblemConfigurator.java
+++ b/belgif-rest-problem-java-ee-core/src/main/java/io/github/belgif/rest/problem/ee/core/jaxrs/ProblemConfigurator.java
@@ -25,6 +25,7 @@ public void contextInitialized(ServletContextEvent sce) {
setBooleanConfig(sce, ProblemConfig.PROPERTY_STACK_TRACE_ENABLED, ProblemConfig::setStackTraceEnabled);
setBooleanConfig(sce, ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED, ProblemConfig::setExtIssueTypesEnabled);
setBooleanConfig(sce, ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED, ProblemConfig::setExtInputsArrayEnabled);
+ setBooleanConfig(sce, ProblemConfig.PROPERTY_JSON_POINTER_ENABLED, ProblemConfig::setJsonPointerEnabled);
}
private void setBooleanConfig(ServletContextEvent sce, String key, Consumer configSetter) {
diff --git a/belgif-rest-problem-java-ee-core/src/test/java/io/github/belgif/rest/problem/ee/core/jaxrs/ProblemConfiguratorTest.java b/belgif-rest-problem-java-ee-core/src/test/java/io/github/belgif/rest/problem/ee/core/jaxrs/ProblemConfiguratorTest.java
index 808830b5..a0dcbef3 100644
--- a/belgif-rest-problem-java-ee-core/src/test/java/io/github/belgif/rest/problem/ee/core/jaxrs/ProblemConfiguratorTest.java
+++ b/belgif-rest-problem-java-ee-core/src/test/java/io/github/belgif/rest/problem/ee/core/jaxrs/ProblemConfiguratorTest.java
@@ -37,15 +37,18 @@ void notConfigured() {
boolean stackTraceEnabledBefore = ProblemConfig.isStackTraceEnabled();
boolean extIssueTypesEnabledBefore = ProblemConfig.isExtIssueTypesEnabled();
boolean extInputsArrayEnabledBefore = ProblemConfig.isExtInputsArrayEnabled();
+ boolean jsonPointerEnabledBefore = ProblemConfig.isJsonPointerEnabled();
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_I18N_ENABLED)).thenReturn(null);
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_STACK_TRACE_ENABLED)).thenReturn(null);
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED)).thenReturn(null);
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED)).thenReturn(null);
+ when(servletContext.getInitParameter(ProblemConfig.PROPERTY_JSON_POINTER_ENABLED)).thenReturn(null);
configurator.contextInitialized(new ServletContextEvent(servletContext));
assertThat(ProblemConfig.isI18nEnabled()).isEqualTo(i18nEnabledBefore);
assertThat(ProblemConfig.isStackTraceEnabled()).isEqualTo(stackTraceEnabledBefore);
assertThat(ProblemConfig.isExtIssueTypesEnabled()).isEqualTo(extIssueTypesEnabledBefore);
assertThat(ProblemConfig.isExtInputsArrayEnabled()).isEqualTo(extInputsArrayEnabledBefore);
+ assertThat(ProblemConfig.isJsonPointerEnabled()).isEqualTo(jsonPointerEnabledBefore);
}
@Test
@@ -54,11 +57,13 @@ void enabledViaInitParam() {
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_STACK_TRACE_ENABLED)).thenReturn("true");
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED)).thenReturn("true");
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED)).thenReturn("true");
+ when(servletContext.getInitParameter(ProblemConfig.PROPERTY_JSON_POINTER_ENABLED)).thenReturn("true");
configurator.contextInitialized(new ServletContextEvent(servletContext));
assertThat(ProblemConfig.isI18nEnabled()).isTrue();
assertThat(ProblemConfig.isStackTraceEnabled()).isTrue();
assertThat(ProblemConfig.isExtIssueTypesEnabled()).isTrue();
assertThat(ProblemConfig.isExtInputsArrayEnabled()).isTrue();
+ assertThat(ProblemConfig.isJsonPointerEnabled()).isTrue();
}
@Test
@@ -67,11 +72,13 @@ void disabledViaInitParam() {
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_STACK_TRACE_ENABLED)).thenReturn("false");
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED)).thenReturn("false");
when(servletContext.getInitParameter(ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED)).thenReturn("false");
+ when(servletContext.getInitParameter(ProblemConfig.PROPERTY_JSON_POINTER_ENABLED)).thenReturn("false");
configurator.contextInitialized(new ServletContextEvent(servletContext));
assertThat(ProblemConfig.isI18nEnabled()).isFalse();
assertThat(ProblemConfig.isStackTraceEnabled()).isFalse();
assertThat(ProblemConfig.isExtIssueTypesEnabled()).isFalse();
assertThat(ProblemConfig.isExtInputsArrayEnabled()).isFalse();
+ assertThat(ProblemConfig.isJsonPointerEnabled()).isFalse();
}
@Test
@@ -79,12 +86,14 @@ void disabledViaInitParam() {
@SetSystemProperty(key = ProblemConfig.PROPERTY_STACK_TRACE_ENABLED, value = "true")
@SetSystemProperty(key = ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED, value = "true")
@SetSystemProperty(key = ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED, value = "true")
+ @SetSystemProperty(key = ProblemConfig.PROPERTY_JSON_POINTER_ENABLED, value = "true")
void enabledViaSystemProperties() {
configurator.contextInitialized(new ServletContextEvent(servletContext));
assertThat(ProblemConfig.isI18nEnabled()).isTrue();
assertThat(ProblemConfig.isStackTraceEnabled()).isTrue();
assertThat(ProblemConfig.isExtIssueTypesEnabled()).isTrue();
assertThat(ProblemConfig.isExtInputsArrayEnabled()).isTrue();
+ assertThat(ProblemConfig.isJsonPointerEnabled()).isTrue();
verifyNoInteractions(servletContext);
}
@@ -93,12 +102,14 @@ void enabledViaSystemProperties() {
@SetSystemProperty(key = ProblemConfig.PROPERTY_STACK_TRACE_ENABLED, value = "false")
@SetSystemProperty(key = ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED, value = "false")
@SetSystemProperty(key = ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED, value = "false")
+ @SetSystemProperty(key = ProblemConfig.PROPERTY_JSON_POINTER_ENABLED, value = "false")
void disabledViaSystemProperty() {
configurator.contextInitialized(new ServletContextEvent(servletContext));
assertThat(ProblemConfig.isI18nEnabled()).isFalse();
assertThat(ProblemConfig.isStackTraceEnabled()).isFalse();
assertThat(ProblemConfig.isExtIssueTypesEnabled()).isFalse();
assertThat(ProblemConfig.isExtInputsArrayEnabled()).isFalse();
+ assertThat(ProblemConfig.isJsonPointerEnabled()).isFalse();
verifyNoInteractions(servletContext);
}
@@ -107,12 +118,14 @@ void disabledViaSystemProperty() {
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_STACK_TRACE_ENABLED, value = "true")
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED, value = "true")
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED, value = "true")
+ @SetEnvironmentVariable(key = ProblemConfig.PROPERTY_JSON_POINTER_ENABLED, value = "true")
void enabledViaEnvironmentVariable() {
configurator.contextInitialized(new ServletContextEvent(servletContext));
assertThat(ProblemConfig.isI18nEnabled()).isTrue();
assertThat(ProblemConfig.isStackTraceEnabled()).isTrue();
assertThat(ProblemConfig.isExtIssueTypesEnabled()).isTrue();
assertThat(ProblemConfig.isExtInputsArrayEnabled()).isTrue();
+ assertThat(ProblemConfig.isJsonPointerEnabled()).isTrue();
verifyNoInteractions(servletContext);
}
@@ -121,12 +134,14 @@ void enabledViaEnvironmentVariable() {
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_STACK_TRACE_ENABLED, value = "false")
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED, value = "false")
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED, value = "false")
+ @SetEnvironmentVariable(key = ProblemConfig.PROPERTY_JSON_POINTER_ENABLED, value = "false")
void disabledViaEnvironmentVariable() {
configurator.contextInitialized(new ServletContextEvent(servletContext));
assertThat(ProblemConfig.isI18nEnabled()).isFalse();
assertThat(ProblemConfig.isStackTraceEnabled()).isFalse();
assertThat(ProblemConfig.isExtIssueTypesEnabled()).isFalse();
assertThat(ProblemConfig.isExtInputsArrayEnabled()).isFalse();
+ assertThat(ProblemConfig.isJsonPointerEnabled()).isFalse();
verifyNoInteractions(servletContext);
}
@@ -135,16 +150,19 @@ void disabledViaEnvironmentVariable() {
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_STACK_TRACE_ENABLED, value = "false")
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED, value = "false")
@SetEnvironmentVariable(key = ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED, value = "false")
+ @SetEnvironmentVariable(key = ProblemConfig.PROPERTY_JSON_POINTER_ENABLED, value = "false")
@SetSystemProperty(key = ProblemConfig.PROPERTY_I18N_ENABLED, value = "true")
@SetSystemProperty(key = ProblemConfig.PROPERTY_STACK_TRACE_ENABLED, value = "true")
@SetSystemProperty(key = ProblemConfig.PROPERTY_EXT_ISSUE_TYPES_ENABLED, value = "true")
@SetSystemProperty(key = ProblemConfig.PROPERTY_EXT_INPUTS_ARRAY_ENABLED, value = "true")
+ @SetSystemProperty(key = ProblemConfig.PROPERTY_JSON_POINTER_ENABLED, value = "true")
void systemPropertyHasPrecedenceOverEnvironmentVariable() {
configurator.contextInitialized(new ServletContextEvent(servletContext));
assertThat(ProblemConfig.isI18nEnabled()).isTrue();
assertThat(ProblemConfig.isStackTraceEnabled()).isTrue();
assertThat(ProblemConfig.isExtIssueTypesEnabled()).isTrue();
assertThat(ProblemConfig.isExtInputsArrayEnabled()).isTrue();
+ assertThat(ProblemConfig.isJsonPointerEnabled()).isTrue();
verifyNoInteractions(servletContext);
}
diff --git a/belgif-rest-problem-java-ee-server/src/main/java/io/github/belgif/rest/problem/ee/server/internal/ConstraintViolationUtil.java b/belgif-rest-problem-java-ee-server/src/main/java/io/github/belgif/rest/problem/ee/server/internal/ConstraintViolationUtil.java
index 362eec01..9092bd2d 100644
--- a/belgif-rest-problem-java-ee-server/src/main/java/io/github/belgif/rest/problem/ee/server/internal/ConstraintViolationUtil.java
+++ b/belgif-rest-problem-java-ee-server/src/main/java/io/github/belgif/rest/problem/ee/server/internal/ConstraintViolationUtil.java
@@ -15,10 +15,13 @@
import javax.validation.Path.ParameterNode;
import javax.ws.rs.BeanParam;
+import com.fasterxml.jackson.core.JsonPointer;
+
import io.github.belgif.rest.problem.api.InEnum;
import io.github.belgif.rest.problem.api.Input;
import io.github.belgif.rest.problem.api.InputValidationIssue;
import io.github.belgif.rest.problem.api.InputValidationIssues;
+import io.github.belgif.rest.problem.config.ProblemConfig;
import io.github.belgif.rest.problem.internal.AnnotationUtil;
/**
@@ -59,7 +62,10 @@ public static InputValidationIssue convertToInputValidationIssue(ConstraintViola
private static Input
+
+ org.slf4j
+ slf4j-api
+ 2.0.18
+ provided
+
org.junit.jupiter
junit-jupiter
diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/Input.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/Input.java
index cf60474c..119e5fcb 100644
--- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/Input.java
+++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/Input.java
@@ -33,7 +33,7 @@ public Input() {
public Input(InEnum in, String name, V value) {
this.in = in;
- this.name = name;
+ this.name = InputValidationIssue.convertName(in, name);
this.value = value;
}
@@ -43,6 +43,7 @@ public InEnum getIn() {
public void setIn(InEnum in) {
this.in = in;
+ setName(name);
}
public String getName() {
@@ -50,7 +51,7 @@ public String getName() {
}
public void setName(String name) {
- this.name = name;
+ this.name = InputValidationIssue.convertName(in, name);
}
public V getValue() {
diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/InputValidationIssue.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/InputValidationIssue.java
index 3ca02753..7e6b8be9 100644
--- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/InputValidationIssue.java
+++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/InputValidationIssue.java
@@ -13,6 +13,9 @@
import java.util.Objects;
import java.util.stream.Collectors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonInclude;
@@ -41,12 +44,17 @@ public class InputValidationIssue {
public static final Comparator BY_NAME = Comparator.comparing(InputValidationIssue::getName);
+ private static final Logger LOGGER = LoggerFactory.getLogger(InputValidationIssue.class);
+
private static final String INPUTS_AND_IN_NAME_VALUE_ARE_MUTUALLY_EXCLUSIVE =
"inputs[] and in/name/value are mutually exclusive";
private static final String INPUTS_SETTER_ONE_ITEM =
"inputs[] can not be set with a single item, use in(in, name, value) instead";
+ // e.g: /, field, /field, /field/0, /field/0/nested
+ private static final String JSON_POINTER_BASIC_REGEX = "\\/+[a-zA-Z0-9-]*+(\\/[a-zA-Z0-9-]++)*+";
+
private URI type;
private URI href;
private String title;
@@ -73,13 +81,13 @@ public InputValidationIssue(URI type, URI href, String title) {
public InputValidationIssue(InEnum in, String name, Object value) {
this.in = in;
- this.name = name;
+ this.name = convertName(in, name);
this.value = value;
}
public InputValidationIssue(InEnum in, String name) {
this.in = in;
- this.name = name;
+ this.name = convertName(in, name);
}
public URI getType() {
@@ -121,6 +129,7 @@ public InEnum getIn() {
public void setIn(InEnum in) {
verifyNoInputs(in);
this.in = in;
+ this.name = convertName(in, name);
}
public String getName() {
@@ -129,7 +138,7 @@ public String getName() {
public void setName(String name) {
verifyNoInputs(name);
- this.name = name;
+ this.name = convertName(in, name);
}
public Object getValue() {
@@ -450,4 +459,68 @@ public String toString() {
'}';
}
+ private static boolean nameMatchesJsonPointerFormat(String name) {
+ return name == null || (name.matches(JSON_POINTER_BASIC_REGEX)
+ && !name.matches(".*\\/\\d+\\/\\d+\\/*+") // not two indexes following each other (e.g: person/1/2)
+ && !name.matches("\\/+\\d++(\\/[a-zA-Z0-9-.]++)*+")); // not starting with an index (e.g: /1/person)
+ }
+
+ /**
+ *
+ * @param in the issue place in the query
+ * @param nameJsonPath the name in JsonPath syntax
+ * @return the name converted to JsonPointer syntax
+ */
+ public static String transformName(InEnum in, String nameJsonPath) {
+
+ if (nameJsonPath == null || nameJsonPath.trim().isEmpty()) {
+ return null;
+ } else if (!ProblemConfig.isJsonPointerEnabled() || in != InEnum.BODY) {
+ return nameJsonPath;
+ } else {
+ // replace all indexes "[X]" by "/X" and replace all "." by "/"
+ String convertedName = replaceSquareBrackets(nameJsonPath).replace(".", "/");
+ return convertedName.charAt(0) != '/' ? "/" + convertedName : convertedName;
+ }
+ }
+
+ /**
+ *
+ * @param in the issue place in the query
+ * @param name the name in JsonPath syntax
+ * @return the name converted (if necessary) to JsonPointer syntax
+ */
+ protected static String convertName(InEnum in, String name) {
+ if (in == InEnum.BODY && ProblemConfig.isJsonPointerEnabled() && !nameMatchesJsonPointerFormat(name)) {
+ LOGGER.warn(
+ "Your application does not use the JsonPointer syntax for issue in the body although it is "
+ + "enabled [in: %s, name: %s]. Auto-conversion will be applied. ",
+ in, name);
+ return transformName(in, name);
+ }
+
+ return name;
+ }
+
+ public static String getNameFromProperties(InEnum in, List propertiesName) {
+
+ if (in != InEnum.BODY && propertiesName != null && propertiesName.size() > 1) {
+ throw new IllegalArgumentException(
+ "This method should only be used with several properties for issues located in the body");
+ }
+
+ if (propertiesName == null || propertiesName.isEmpty()) {
+ return null;
+ }
+
+ String name = ProblemConfig.isJsonPointerEnabled() && in == InEnum.BODY ? propertiesName.stream()
+ .map(InputValidationIssue::replaceSquareBrackets).collect(Collectors.joining("/"))
+ : String.join(".", propertiesName);
+ return in == InEnum.BODY && ProblemConfig.isJsonPointerEnabled() ? "/" + name : name;
+ }
+
+ private static String replaceSquareBrackets(String propertyName) {
+ // replace all indexes "[X]" by "/X"
+ return propertyName.replaceAll("\\[(\\d++)\\]", "/$1");
+ }
}
diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/InputValidationIssues.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/InputValidationIssues.java
index b3d6a3c5..9ae7c8a6 100644
--- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/InputValidationIssues.java
+++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/api/InputValidationIssues.java
@@ -139,8 +139,10 @@ public static InputValidationIssue referencedResourceNotFound(InEnum in, String
* @param The type of the reference
*/
public static InputValidationIssue referencedResourceNotFound(InEnum in, String name, T value, List source) {
- String nameWithIndex = name + "[" + source.indexOf(value) + "]";
- return referencedResourceNotFound(in, nameWithIndex, value);
+ String indexFormat = ProblemConfig.isJsonPointerEnabled() && in == InEnum.BODY ? ("/" + source.indexOf(value))
+ : ("[" + source.indexOf(value) + "]");
+ String nameWithIndex = name + indexFormat;
+ return referencedResourceNotFound(in, InputValidationIssue.transformName(in, nameWithIndex), value);
}
public static InputValidationIssue rejectedInput(InEnum in, String name, Object value) {
diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/config/ProblemConfig.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/config/ProblemConfig.java
index 8ca23093..1e7b5ebd 100644
--- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/config/ProblemConfig.java
+++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/config/ProblemConfig.java
@@ -17,6 +17,9 @@ public class ProblemConfig {
public static final String PROPERTY_EXT_INPUTS_ARRAY_ENABLED =
"io.github.belgif.rest.problem.ext.inputs-array-enabled";
+ public static final String PROPERTY_JSON_POINTER_ENABLED =
+ "io.github.belgif.rest.problem.json-pointer-enabled";
+
private static final boolean DEFAULT_I18N_ENABLED = true;
private static final boolean DEFAULT_STACK_TRACE_ENABLED = false;
@@ -24,6 +27,7 @@ public class ProblemConfig {
private static final boolean DEFAULT_EXT_ISSUE_TYPES_ENABLED = false;
private static final boolean DEFAULT_EXT_INPUTS_ARRAY_ENABLED = false;
+ private static final boolean DEFAULT_JSON_POINTER_ENABLED = true;
private static boolean i18nEnabled = DEFAULT_I18N_ENABLED;
@@ -33,6 +37,8 @@ public class ProblemConfig {
private static boolean extInputsArrayEnabled = DEFAULT_EXT_INPUTS_ARRAY_ENABLED;
+ private static boolean jsonPointerEnabled = DEFAULT_JSON_POINTER_ENABLED;
+
private static final ThreadLocal LOCAL_EXT_ISSUE_TYPES_ENABLED = new InheritableThreadLocal() {
@Override
protected Boolean initialValue() {
@@ -96,6 +102,14 @@ public static void setLocalExtInputsArrayEnabled(boolean extInputsArrayEnabled)
LOCAL_EXT_INPUTS_ARRAY_ENABLED.set(extInputsArrayEnabled);
}
+ public static boolean isJsonPointerEnabled() {
+ return jsonPointerEnabled;
+ }
+
+ public static void setJsonPointerEnabled(boolean jsonPointerEnabled) {
+ ProblemConfig.jsonPointerEnabled = jsonPointerEnabled;
+ }
+
public static void clearLocal() {
LOCAL_EXT_ISSUE_TYPES_ENABLED.remove();
LOCAL_EXT_INPUTS_ARRAY_ENABLED.remove();
@@ -106,6 +120,7 @@ public static void reset() {
stackTraceEnabled = DEFAULT_STACK_TRACE_ENABLED;
extIssueTypesEnabled = DEFAULT_EXT_ISSUE_TYPES_ENABLED;
extInputsArrayEnabled = DEFAULT_EXT_INPUTS_ARRAY_ENABLED;
+ jsonPointerEnabled = DEFAULT_JSON_POINTER_ENABLED;
}
}
diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/internal/Jackson2Util.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/internal/Jackson2Util.java
index 4ee30822..02e97557 100644
--- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/internal/Jackson2Util.java
+++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/internal/Jackson2Util.java
@@ -2,6 +2,7 @@
import static io.github.belgif.rest.problem.api.InputValidationIssues.*;
+import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -12,6 +13,7 @@
import io.github.belgif.rest.problem.BadRequestProblem;
import io.github.belgif.rest.problem.api.InEnum;
+import io.github.belgif.rest.problem.api.InputValidationIssue;
import io.github.belgif.rest.problem.api.InputValidationIssues;
/**
@@ -59,21 +61,22 @@ public static BadRequestProblem toBadRequestProblem(JsonMappingException e) {
}
private static String getName(List path) {
+
if (path.isEmpty()) {
return null;
}
- StringBuilder builder = new StringBuilder();
+ List properties = new ArrayList<>();
+
for (Reference reference : path) {
if (reference.getFrom() instanceof List) {
- builder.append("[").append(reference.getIndex()).append("]");
+ // append the index to the property name
+ properties.set(properties.size() - 1,
+ properties.get(properties.size() - 1) + "[" + reference.getIndex() + "]");
} else {
- if (builder.length() > 0) {
- builder.append(".");
- }
- builder.append(reference.getFieldName());
+ properties.add(reference.getFieldName());
}
}
- return builder.toString();
+ return InputValidationIssue.getNameFromProperties(InEnum.BODY, properties);
}
@SuppressWarnings("java:S1872")
diff --git a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/internal/Jackson3Util.java b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/internal/Jackson3Util.java
index b7942490..09f7655a 100644
--- a/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/internal/Jackson3Util.java
+++ b/belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/internal/Jackson3Util.java
@@ -2,10 +2,12 @@
import static io.github.belgif.rest.problem.api.InputValidationIssues.*;
+import java.util.ArrayList;
import java.util.List;
import io.github.belgif.rest.problem.BadRequestProblem;
import io.github.belgif.rest.problem.api.InEnum;
+import io.github.belgif.rest.problem.api.InputValidationIssue;
import io.github.belgif.rest.problem.api.InputValidationIssues;
import tools.jackson.core.JacksonException.Reference;
import tools.jackson.core.exc.StreamReadException;
@@ -43,21 +45,22 @@ public static BadRequestProblem toBadRequestProblem(DatabindException e) {
}
private static String getName(List path) {
+
if (path.isEmpty()) {
return null;
}
- StringBuilder name = new StringBuilder();
+
+ List properties = new ArrayList<>();
+
for (Reference reference : path) {
if (reference.from() instanceof List) {
- name.append("[").append(reference.getIndex()).append("]");
+ // append the index to the property name
+ properties.set(properties.size() - 1,
+ properties.get(properties.size() - 1) + "[" + reference.getIndex() + "]");
} else {
- if (name.length() > 0) {
- name.append(".");
- }
- name.append(reference.getPropertyName());
+ properties.add(reference.getPropertyName());
}
}
- return name.toString();
+ return InputValidationIssue.getNameFromProperties(InEnum.BODY, properties);
}
-
}
diff --git a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputTest.java b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputTest.java
index da9dd644..d58dd986 100644
--- a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputTest.java
+++ b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputTest.java
@@ -2,10 +2,18 @@
import static org.assertj.core.api.Assertions.*;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import io.github.belgif.rest.problem.config.ProblemConfig;
+
class InputTest {
+ @BeforeEach
+ void resetProblemConfig() {
+ ProblemConfig.reset();
+ }
+
@Test
void construct() {
Input input = new Input<>();
@@ -21,10 +29,10 @@ void construct() {
@Test
void body() {
- Input bodyInput = Input.body("bodyName", "bodyValue");
+ Input bodyInput = Input.body("/bodyName", "bodyValue");
assertThat(bodyInput.getIn()).isEqualTo(InEnum.BODY);
- assertThat(bodyInput.getName()).isEqualTo("bodyName");
+ assertThat(bodyInput.getName()).isEqualTo("/bodyName");
assertThat(bodyInput.getValue()).isEqualTo("bodyValue");
}
@@ -57,9 +65,9 @@ void header() {
@Test
void equalsHashCodeToString() {
- Input input = new Input<>(InEnum.BODY, "name", "value");
- Input equal = new Input<>(InEnum.BODY, "name", "value");
- Input other = new Input<>(InEnum.BODY, "anotherName", "anotherValue");
+ Input input = new Input<>(InEnum.BODY, "/name", "value");
+ Input equal = new Input<>(InEnum.BODY, "/name", "value");
+ Input other = new Input<>(InEnum.BODY, "/anotherName", "anotherValue");
assertThat(input).isEqualTo(input);
assertThat(input).hasSameHashCodeAs(input);
diff --git a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputValidationIssueTest.java b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputValidationIssueTest.java
index 96d1e10c..d54e8d4e 100644
--- a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputValidationIssueTest.java
+++ b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputValidationIssueTest.java
@@ -6,12 +6,16 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.assertj.core.api.ThrowableAssert;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
import io.github.belgif.rest.problem.config.ProblemConfig;
import io.github.belgif.rest.problem.i18n.Context;
@@ -317,10 +321,252 @@ void equalsHashCodeToString() {
assertThat(issue).isNotEqualTo("other type");
}
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void transformNameInBody(InEnum in) {
+ assertThat(InputValidationIssue.transformName(in, null)).isNull();
+ assertThat(InputValidationIssue.transformName(in, "")).isNull();
+ assertThat(InputValidationIssue.transformName(in, " ")).isNull();
+ assertThat(InputValidationIssue.transformName(in, "field")).isEqualTo(in == InEnum.BODY ? "/field" : "field");
+ assertThat(InputValidationIssue.transformName(in, "field[0]"))
+ .isEqualTo(in == InEnum.BODY ? "/field/0" : "field[0]");
+ assertThat(InputValidationIssue.transformName(in, "field[0].nested"))
+ .isEqualTo(in == InEnum.BODY ? "/field/0/nested" : "field[0].nested");
+ assertThat(InputValidationIssue.transformName(in, "field/0"))
+ .isEqualTo(in == InEnum.BODY ? "/field/0" : "field/0");
+ assertThat(InputValidationIssue.transformName(in, "field/0/nested"))
+ .isEqualTo(in == InEnum.BODY ? "/field/0/nested" : "field/0/nested");
+ assertThat(InputValidationIssue.transformName(in, "/field/0")).isEqualTo("/field/0");
+ assertThat(InputValidationIssue.transformName(in, "/field/0/nested")).isEqualTo("/field/0/nested");
+ assertThat(InputValidationIssue.transformName(in, "/field")).isEqualTo("/field");
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void transformNameInBodyJsonPointerDisabled(InEnum in) {
+ ProblemConfig.setJsonPointerEnabled(false);
+
+ assertThat(InputValidationIssue.transformName(in, null)).isNull();
+ assertThat(InputValidationIssue.transformName(in, "")).isNull();
+ assertThat(InputValidationIssue.transformName(in, " ")).isNull();
+ assertThat(InputValidationIssue.transformName(in, "field")).isEqualTo("field");
+ assertThat(InputValidationIssue.transformName(in, "field[0]")).isEqualTo("field[0]");
+ assertThat(InputValidationIssue.transformName(in, "field[0].nested")).isEqualTo("field[0].nested");
+ assertThat(InputValidationIssue.transformName(in, "field/0")).isEqualTo("field/0");
+ assertThat(InputValidationIssue.transformName(in, "field/0/nested")).isEqualTo("field/0/nested");
+ assertThat(InputValidationIssue.transformName(in, "/field/0")).isEqualTo("/field/0");
+ assertThat(InputValidationIssue.transformName(in, "/field/0/nested")).isEqualTo("/field/0/nested");
+ assertThat(InputValidationIssue.transformName(in, "/field")).isEqualTo("/field");
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void getNameFromProperties(InEnum in) {
+ List properties = null;
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isNull();
+
+ properties = new ArrayList<>();
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isNull();
+
+ properties.add("field");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties))
+ .isEqualTo(in == InEnum.BODY ? "/field" : "field");
+
+ properties.set(0, "field[0]");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties))
+ .isEqualTo(in == InEnum.BODY ? "/field/0" : "field[0]");
+
+ if (in == InEnum.BODY) {
+ properties.add("nested");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isEqualTo("/field/0/nested");
+
+ properties.add("nestedAgain[1]");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties))
+ .isEqualTo("/field/0/nested/nestedAgain/1");
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void getNameFromPropertiesWithJsonPointerDisabled(InEnum in) {
+
+ ProblemConfig.setJsonPointerEnabled(false);
+
+ List properties = null;
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isNull();
+
+ properties = new ArrayList<>();
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isNull();
+
+ properties.add("field");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isEqualTo("field");
+
+ properties.set(0, "field[0]");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isEqualTo("field[0]");
+
+ if (in == InEnum.BODY) {
+ properties.add("nested");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isEqualTo("field[0].nested");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties)).isEqualTo("field[0].nested");
+
+ properties.add("nestedAgain[1]");
+ assertThat(InputValidationIssue.getNameFromProperties(in, properties))
+ .isEqualTo("field[0].nested.nestedAgain[1]");
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = InEnum.class, names = { "BODY" }, mode = EnumSource.Mode.EXCLUDE)
+ void getNameFromPropertiesIllegalArgument() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> InputValidationIssue.getNameFromProperties(InEnum.QUERY,
+ Arrays.asList("field", "nested")))
+ .withMessageContaining("located in the body");
+ assertThatIllegalArgumentException()
+ .isThrownBy(
+ () -> InputValidationIssue.getNameFromProperties(InEnum.PATH, Arrays.asList("field", "nested")))
+ .withMessageContaining("located in the body");
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> InputValidationIssue.getNameFromProperties(InEnum.HEADER,
+ Arrays.asList("field", "nested")))
+ .withMessageContaining("located in the body");
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void nameMatchingJsonPointerFormatInConstructor(InEnum in) {
+ List names = Arrays.asList("/field", "/field/0", "/field/0/nested/2/nestedAgain");
+
+ for (String name : names) {
+ InputValidationIssue input = new InputValidationIssue(in, name);
+ assertThat(input.getName()).isEqualTo(name);
+ input = new InputValidationIssue(in, name, "value");
+ assertThat(input.getName()).isEqualTo(name);
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void nameNotMatchingJsonPointerFormatInConstructorAutoConversion(InEnum in) {
+ Map names = new HashMap<>();
+ names.put("field", "/field");
+ names.put("", null);
+ names.put("field[0]", "/field/0");
+ names.put("field[0].nested[2].nestedAgain", "/field/0/nested/2/nestedAgain");
+ names.put("field/0", "/field/0");
+ names.put("field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain");
+
+ for (Map.Entry name : names.entrySet()) {
+ InputValidationIssue input = new InputValidationIssue(in, name.getKey());
+ assertThat(input.getName()).isEqualTo(in == InEnum.BODY ? name.getValue() : name.getKey());
+ input = new InputValidationIssue(in, name.getKey(), "value");
+ assertThat(input.getName()).isEqualTo(in == InEnum.BODY ? name.getValue() : name.getKey());
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void nameNotConvertedInConstructorWithJsonPointerDisabled(InEnum in) {
+ ProblemConfig.setJsonPointerEnabled(false);
+
+ List names = Arrays.asList("field", "/field", "", null, "field[0]", "/field/0", "field/0",
+ "field[0].nested[2].nestedAgain", "field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain");
+
+ for (String name : names) {
+ InputValidationIssue input = new InputValidationIssue(in, name);
+ assertThat(input.getName()).isEqualTo(name);
+ input = new InputValidationIssue(in, name, "value");
+ assertThat(input.getName()).isEqualTo(name);
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void setName(InEnum in) {
+ Map names = new HashMap<>();
+ names.put("field", "/field");
+ names.put("/field", "/field");
+ names.put("", null);
+ names.put("field[0]", "/field/0");
+ names.put("field[0].nested[2].nestedAgain", "/field/0/nested/2/nestedAgain");
+ names.put("field/0", "/field/0");
+ names.put("/field/0", "/field/0");
+ names.put("field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain");
+ names.put("/field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain");
+
+ for (Map.Entry name : names.entrySet()) {
+ InputValidationIssue input = new InputValidationIssue(null, name.getKey(), null);
+ input.setName(name.getKey());
+ assertThat(input.getName()).isEqualTo(name.getKey());
+ input = new InputValidationIssue(in, null, null);
+ input.setName(name.getKey());
+ assertThat(input.getName()).isEqualTo(in == InEnum.BODY ? name.getValue() : name.getKey());
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void setNameJsonPointerDisabled(InEnum in) {
+ ProblemConfig.setJsonPointerEnabled(false);
+
+ List names = Arrays.asList("field", "/field", "", "field[0]", "/field/0",
+ "field[0].nested[2].nestedAgain", "/field/0/nested/2/nestedAgain", "field/0",
+ "field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain",
+ "/field/0/nested/2/nestedAgain");
+
+ for (String name : names) {
+ InputValidationIssue input = new InputValidationIssue(null, name, null);
+ input.setName(name);
+ assertThat(input.getName()).isEqualTo(name);
+ input = new InputValidationIssue(in, null, null);
+ input.setName(name);
+ assertThat(input.getName()).isEqualTo(name);
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void setInput(InEnum in) {
+ Map names = new HashMap<>();
+ names.put("field", "/field");
+ names.put("/field", "/field");
+ names.put("", null);
+ names.put("field[0]", "/field/0");
+ names.put("field[0].nested[2].nestedAgain", "/field/0/nested/2/nestedAgain");
+ names.put("field/0", "/field/0");
+ names.put("/field/0", "/field/0");
+ names.put("field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain");
+ names.put("/field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain");
+
+ for (Map.Entry name : names.entrySet()) {
+ InputValidationIssue input = new InputValidationIssue(null, name.getKey(), null);
+ assertThat(input.getName()).isEqualTo(name.getKey());
+ input.setIn(in);
+ assertThat(input.getName()).isEqualTo(in == InEnum.BODY ? name.getValue() : name.getKey());
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(InEnum.class)
+ void setInputJsonPointerDisabled(InEnum in) {
+ ProblemConfig.setJsonPointerEnabled(false);
+
+ List names = Arrays.asList("field", "/field", "", "field[0]", "/field/0",
+ "field[0].nested[2].nestedAgain", "/field/0/nested/2/nestedAgain", "field/0",
+ "field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain", "/field/0/nested/2/nestedAgain",
+ "/field/0/nested/2/nestedAgain");
+
+ for (String name : names) {
+ InputValidationIssue input = new InputValidationIssue(null, name, null);
+ assertThat(input.getName()).isEqualTo(name);
+ input.setIn(in);
+ assertThat(input.getName()).isEqualTo(name);
+ }
+ }
+
private void assertMutuallyExclusiveException(ThrowableAssert.ThrowingCallable throwingCallable) {
assertThatIllegalArgumentException()
.isThrownBy(throwingCallable)
.withMessageContaining(MUTUALLY_EXCLUSIVE_EXC);
}
-
}
diff --git a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputValidationIssuesTest.java b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputValidationIssuesTest.java
index bb42165a..23348661 100644
--- a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputValidationIssuesTest.java
+++ b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/api/InputValidationIssuesTest.java
@@ -58,13 +58,13 @@ void resetProblemConfig() {
@Test
void schemaViolation() {
InputValidationIssue issue =
- InputValidationIssues.schemaViolation(InEnum.BODY, "test", "value", "detail");
+ InputValidationIssues.schemaViolation(InEnum.BODY, "/test", "value", "detail");
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getHref())
.hasToString("https://www.belgif.be/specification/rest/api-guide/issues/schemaViolation.html");
assertThat(issue.getTitle()).isEqualTo("Input value is invalid with respect to the schema");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo("value");
assertThat(issue.getDetail()).isEqualTo("detail");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -73,28 +73,28 @@ void schemaViolation() {
@Test
void unknownInput() {
InputValidationIssue issue =
- InputValidationIssues.unknownInput(InEnum.BODY, "oops", "value");
+ InputValidationIssues.unknownInput(InEnum.BODY, "/oops", "value");
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:unknownInput");
assertThat(issue.getHref())
.hasToString("https://www.belgif.be/specification/rest/api-guide/issues/unknownInput.html");
assertThat(issue.getTitle()).isEqualTo("Unknown input");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("oops");
+ assertThat(issue.getName()).isEqualTo("/oops");
assertThat(issue.getValue()).isEqualTo("value");
- assertThat(issue.getDetail()).isEqualTo("Input oops is unknown");
+ assertThat(issue.getDetail()).isEqualTo("Input /oops is unknown");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
}
@Test
void invalidInput() {
InputValidationIssue issue =
- InputValidationIssues.invalidInput(InEnum.BODY, "oops", "value", "detail");
+ InputValidationIssues.invalidInput(InEnum.BODY, "/oops", "value", "detail");
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:invalidInput");
assertThat(issue.getHref())
.hasToString("https://www.belgif.be/specification/rest/api-guide/issues/invalidInput.html");
assertThat(issue.getTitle()).isEqualTo("Invalid input");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("oops");
+ assertThat(issue.getName()).isEqualTo("/oops");
assertThat(issue.getValue()).isEqualTo("value");
assertThat(issue.getDetail()).isEqualTo("detail");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -104,7 +104,7 @@ void invalidInput() {
@MethodSource("toggleExtIssueTypes")
void invalidStructure(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.invalidStructure(InEnum.BODY, "test", "value", "detail");
+ InputValidationIssues.invalidStructure(InEnum.BODY, "/test", "value", "detail");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:invalidStructure");
assertThat(issue.getHref())
@@ -117,7 +117,7 @@ void invalidStructure(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo("value");
assertThat(issue.getDetail()).isEqualTo("detail");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -127,7 +127,7 @@ void invalidStructure(boolean extIssueTypes) {
@MethodSource("toggleExtIssueTypes")
void outOfRangeMinMax(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.outOfRange(InEnum.BODY, "test", 6, 1, 5);
+ InputValidationIssues.outOfRange(InEnum.BODY, "/test", 6, 1, 5);
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:outOfRange");
assertThat(issue.getHref())
@@ -140,9 +140,9 @@ void outOfRangeMinMax(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo(6);
- assertThat(issue.getDetail()).isEqualTo("Input value test = 6 is out of range [1, 5]");
+ assertThat(issue.getDetail()).isEqualTo("Input value /test = 6 is out of range [1, 5]");
assertThat(issue.getAdditionalProperties()).containsOnly(entry("minimum", "1"), entry("maximum", "5"));
assertThat(issue.getInputs()).isEmpty();
}
@@ -151,7 +151,7 @@ void outOfRangeMinMax(boolean extIssueTypes) {
@MethodSource("toggleExtIssueTypes")
void outOfRangeMin(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.outOfRange(InEnum.BODY, "test", 0, 1, null);
+ InputValidationIssues.outOfRange(InEnum.BODY, "/test", 0, 1, null);
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:outOfRange");
assertThat(issue.getHref())
@@ -164,9 +164,9 @@ void outOfRangeMin(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo(0);
- assertThat(issue.getDetail()).isEqualTo("Input value test = 0 should be at least 1");
+ assertThat(issue.getDetail()).isEqualTo("Input value /test = 0 should be at least 1");
assertThat(issue.getAdditionalProperties()).containsExactly(entry("minimum", "1"));
assertThat(issue.getInputs()).isEmpty();
}
@@ -175,7 +175,7 @@ void outOfRangeMin(boolean extIssueTypes) {
@MethodSource("toggleExtIssueTypes")
void outOfRangeMax(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.outOfRange(InEnum.BODY, "test", 6, null, 5);
+ InputValidationIssues.outOfRange(InEnum.BODY, "/test", 6, null, 5);
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:outOfRange");
assertThat(issue.getHref())
@@ -188,9 +188,9 @@ void outOfRangeMax(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo(6);
- assertThat(issue.getDetail()).isEqualTo("Input value test = 6 should not exceed 5");
+ assertThat(issue.getDetail()).isEqualTo("Input value /test = 6 should not exceed 5");
assertThat(issue.getAdditionalProperties()).containsExactly(entry("maximum", "5"));
assertThat(issue.getInputs()).isEmpty();
}
@@ -205,30 +205,31 @@ void outOfRangeMinAndMaxNull() {
@Test
void referencedResourceNotFound() {
InputValidationIssue issue =
- InputValidationIssues.referencedResourceNotFound(InEnum.BODY, "test", "value");
+ InputValidationIssues.referencedResourceNotFound(InEnum.BODY, "/test", "value");
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:referencedResourceNotFound");
assertThat(issue.getHref())
.hasToString(
"https://www.belgif.be/specification/rest/api-guide/issues/referencedResourceNotFound.html");
assertThat(issue.getTitle()).isEqualTo("Referenced resource not found");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo("value");
- assertThat(issue.getDetail()).isEqualTo("Referenced resource test = 'value' does not exist");
+ assertThat(issue.getDetail()).isEqualTo("Referenced resource /test = 'value' does not exist");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
}
@Test
void referencedResourceNotFoundDifferentResourceAndParameterName() {
InputValidationIssue issue =
- InputValidationIssues.referencedResourceNotFound(InEnum.BODY, "partners", "organization", "0123456789");
+ InputValidationIssues.referencedResourceNotFound(InEnum.BODY, "/partners", "organization",
+ "0123456789");
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:referencedResourceNotFound");
assertThat(issue.getHref())
.hasToString(
"https://www.belgif.be/specification/rest/api-guide/issues/referencedResourceNotFound.html");
assertThat(issue.getTitle()).isEqualTo("Referenced resource not found");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("partners");
+ assertThat(issue.getName()).isEqualTo("/partners");
assertThat(issue.getValue()).isEqualTo("0123456789");
assertThat(issue.getDetail()).isEqualTo("Referenced resource organization = '0123456789' does not exist");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -237,7 +238,7 @@ void referencedResourceNotFoundDifferentResourceAndParameterName() {
@Test
void referencedResourceFromCollectionParameterNotFound() {
InputValidationIssue issue =
- InputValidationIssues.referencedResourceNotFound(InEnum.BODY, "partners", 123,
+ InputValidationIssues.referencedResourceNotFound(InEnum.BODY, "/partners", 123,
Arrays.asList(1, 123, 3));
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:referencedResourceNotFound");
assertThat(issue.getHref())
@@ -245,17 +246,23 @@ void referencedResourceFromCollectionParameterNotFound() {
"https://www.belgif.be/specification/rest/api-guide/issues/referencedResourceNotFound.html");
assertThat(issue.getTitle()).isEqualTo("Referenced resource not found");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("partners[1]");
+ assertThat(issue.getName()).isEqualTo("/partners/1");
assertThat(issue.getValue()).isEqualTo(123);
- assertThat(issue.getDetail()).isEqualTo("Referenced resource partners[1] = '123' does not exist");
+ assertThat(issue.getDetail()).isEqualTo("Referenced resource /partners/1 = '123' does not exist");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
+
+ ProblemConfig.setJsonPointerEnabled(false);
+ issue = InputValidationIssues.referencedResourceNotFound(InEnum.BODY, "partners", 123,
+ Arrays.asList(1, 123, 3));
+ assertThat(issue.getName()).isEqualTo("partners[1]");
+ assertThat(issue.getDetail()).isEqualTo("Referenced resource partners[1] = '123' does not exist");
}
@ParameterizedTest
@MethodSource("toggleExtIssueTypes")
void rejectedInput(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.rejectedInput(InEnum.BODY, "test", "value");
+ InputValidationIssues.rejectedInput(InEnum.BODY, "/test", "value");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:rejectedInput");
assertThat(issue.getHref())
@@ -268,9 +275,9 @@ void rejectedInput(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo("value");
- assertThat(issue.getDetail()).isEqualTo("Input test is not allowed in this context");
+ assertThat(issue.getDetail()).isEqualTo("Input /test is not allowed in this context");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
}
@@ -278,7 +285,7 @@ void rejectedInput(boolean extIssueTypes) {
@MethodSource("toggleExtIssueTypes")
void requiredInput(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.requiredInput(InEnum.BODY, "test");
+ InputValidationIssues.requiredInput(InEnum.BODY, "/test");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:requiredInput");
assertThat(issue.getHref())
@@ -291,9 +298,9 @@ void requiredInput(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isNull();
- assertThat(issue.getDetail()).isEqualTo("Input test is required in this context");
+ assertThat(issue.getDetail()).isEqualTo("Input /test is required in this context");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
}
@@ -330,11 +337,11 @@ void requiredInputsIfPresent(boolean extIssueTypes, boolean extInputsArray) {
@Test
void replacedSsin() {
InputValidationIssue issue =
- InputValidationIssues.replacedSsin(InEnum.BODY, "ssin", "00000000196", "00000000295");
+ InputValidationIssues.replacedSsin(InEnum.BODY, "/ssin", "00000000196", "00000000295");
assertThat(issue.getType()).hasToString("urn:problem-type:cbss:input-validation:replacedSsin");
assertThat(issue.getTitle()).isEqualTo("SSIN has been replaced, use new SSIN");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000196");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000196 has been replaced by 00000000295");
assertThat(issue.getAdditionalProperties()).containsExactly(entry("replacedBy", "00000000295"));
@@ -344,11 +351,11 @@ void replacedSsin() {
@Test
void replacedSsinInput() {
InputValidationIssue issue =
- InputValidationIssues.replacedSsin(Input.body("ssin", "00000000196"), "00000000295");
+ InputValidationIssues.replacedSsin(Input.body("/ssin", "00000000196"), "00000000295");
assertThat(issue.getType()).hasToString("urn:problem-type:cbss:input-validation:replacedSsin");
assertThat(issue.getTitle()).isEqualTo("SSIN has been replaced, use new SSIN");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000196");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000196 has been replaced by 00000000295");
assertThat(issue.getAdditionalProperties()).containsExactly(entry("replacedBy", "00000000295"));
@@ -358,12 +365,12 @@ void replacedSsinInput() {
@Test
void replacedSsinWithReplacedByHref() {
InputValidationIssue issue =
- InputValidationIssues.replacedSsin(InEnum.BODY, "ssin", "00000000196", "00000000295",
+ InputValidationIssues.replacedSsin(InEnum.BODY, "/ssin", "00000000196", "00000000295",
URI.create("https://api.company.com/v1/employees?ssin=00000000295"));
assertThat(issue.getType()).hasToString("urn:problem-type:cbss:input-validation:replacedSsin");
assertThat(issue.getTitle()).isEqualTo("SSIN has been replaced, use new SSIN");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000196");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000196 has been replaced by 00000000295");
assertThat(issue.getAdditionalProperties()).containsExactly(
@@ -374,11 +381,11 @@ void replacedSsinWithReplacedByHref() {
@Test
void canceledSsin() {
- InputValidationIssue issue = InputValidationIssues.canceledSsin(InEnum.BODY, "ssin", "00000000196");
+ InputValidationIssue issue = InputValidationIssues.canceledSsin(InEnum.BODY, "/ssin", "00000000196");
assertThat(issue.getType()).hasToString("urn:problem-type:cbss:input-validation:canceledSsin");
assertThat(issue.getTitle()).isEqualTo("SSIN has been canceled");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000196");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000196 has been canceled");
assertThat(issue).extracting("href", "inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -386,11 +393,11 @@ void canceledSsin() {
@Test
void canceledSsinInput() {
- InputValidationIssue issue = InputValidationIssues.canceledSsin(Input.body("ssin", "00000000196"));
+ InputValidationIssue issue = InputValidationIssues.canceledSsin(Input.body("/ssin", "00000000196"));
assertThat(issue.getType()).hasToString("urn:problem-type:cbss:input-validation:canceledSsin");
assertThat(issue.getTitle()).isEqualTo("SSIN has been canceled");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000196");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000196 has been canceled");
assertThat(issue).extracting("href", "inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -399,7 +406,7 @@ void canceledSsinInput() {
@ParameterizedTest
@MethodSource("toggleExtIssueTypes")
void invalidSsin(boolean extIssueTypes) {
- InputValidationIssue issue = InputValidationIssues.invalidSsin(InEnum.BODY, "ssin", "00000000195");
+ InputValidationIssue issue = InputValidationIssues.invalidSsin(InEnum.BODY, "/ssin", "00000000195");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:invalidStructure");
assertThat(issue.getHref())
@@ -412,7 +419,7 @@ void invalidSsin(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000195");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000195 is invalid");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -421,7 +428,7 @@ void invalidSsin(boolean extIssueTypes) {
@ParameterizedTest
@MethodSource("toggleExtIssueTypes")
void invalidSsinInput(boolean extIssueTypes) {
- InputValidationIssue issue = InputValidationIssues.invalidSsin(Input.body("ssin", "00000000195"));
+ InputValidationIssue issue = InputValidationIssues.invalidSsin(Input.body("/ssin", "00000000195"));
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:invalidStructure");
assertThat(issue.getHref())
@@ -434,7 +441,7 @@ void invalidSsinInput(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000195");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000195 is invalid");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -442,13 +449,13 @@ void invalidSsinInput(boolean extIssueTypes) {
@Test
void unknownSsin() {
- InputValidationIssue issue = InputValidationIssues.unknownSsin(InEnum.BODY, "ssin", "00000000196");
+ InputValidationIssue issue = InputValidationIssues.unknownSsin(InEnum.BODY, "/ssin", "00000000196");
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:referencedResourceNotFound");
assertThat(issue.getHref()).hasToString(
"https://www.belgif.be/specification/rest/api-guide/issues/referencedResourceNotFound.html");
assertThat(issue.getTitle()).isEqualTo("Referenced resource not found");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000196");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000196 does not exist");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -456,13 +463,13 @@ void unknownSsin() {
@Test
void unknownSsinInput() {
- InputValidationIssue issue = InputValidationIssues.unknownSsin(Input.body("ssin", "00000000196"));
+ InputValidationIssue issue = InputValidationIssues.unknownSsin(Input.body("/ssin", "00000000196"));
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:referencedResourceNotFound");
assertThat(issue.getHref()).hasToString(
"https://www.belgif.be/specification/rest/api-guide/issues/referencedResourceNotFound.html");
assertThat(issue.getTitle()).isEqualTo("Referenced resource not found");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("ssin");
+ assertThat(issue.getName()).isEqualTo("/ssin");
assertThat(issue.getValue()).isEqualTo("00000000196");
assertThat(issue.getDetail()).isEqualTo("SSIN 00000000196 does not exist");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -472,7 +479,7 @@ void unknownSsinInput() {
@MethodSource("toggleExtIssueTypes")
void invalidPeriod(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.invalidPeriod(InEnum.BODY, "period", "value");
+ InputValidationIssues.invalidPeriod(InEnum.BODY, "/period", "value");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:invalidPeriod");
assertThat(issue.getHref())
@@ -485,7 +492,7 @@ void invalidPeriod(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("period");
+ assertThat(issue.getName()).isEqualTo("/period");
assertThat(issue.getValue()).isEqualTo("value");
assertThat(issue.getDetail()).isEqualTo("endDate should not precede startDate");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -551,7 +558,7 @@ void invalidPeriodOffsetDateTime(boolean extIssueTypes, boolean extInputsArray)
@MethodSource("toggleExtIssueTypes")
void invalidIncompleteDate(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.invalidIncompleteDate(InEnum.BODY, "test", "2024-00-01");
+ InputValidationIssues.invalidIncompleteDate(InEnum.BODY, "/test", "2024-00-01");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:invalidStructure");
assertThat(issue.getHref())
@@ -564,7 +571,7 @@ void invalidIncompleteDate(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo("2024-00-01");
assertThat(issue.getDetail()).isEqualTo("Incomplete date 2024-00-01 is invalid");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -574,7 +581,7 @@ void invalidIncompleteDate(boolean extIssueTypes) {
@MethodSource("toggleExtIssueTypes")
void invalidYearMonth(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.invalidYearMonth(InEnum.BODY, "test", "2024-13");
+ InputValidationIssues.invalidYearMonth(InEnum.BODY, "/test", "2024-13");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:invalidStructure");
assertThat(issue.getHref())
@@ -587,7 +594,7 @@ void invalidYearMonth(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo("2024-13");
assertThat(issue.getDetail()).isEqualTo("Year month 2024-13 is invalid");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -597,7 +604,7 @@ void invalidYearMonth(boolean extIssueTypes) {
@MethodSource("toggleExtIssueTypes")
void invalidEnterpriseNumber(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.invalidEnterpriseNumber(InEnum.BODY, "test", "0000000001");
+ InputValidationIssues.invalidEnterpriseNumber(InEnum.BODY, "/test", "0000000001");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:invalidStructure");
assertThat(issue.getHref())
@@ -610,7 +617,7 @@ void invalidEnterpriseNumber(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo("0000000001");
assertThat(issue.getDetail()).isEqualTo("Enterprise number 0000000001 is invalid");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -620,7 +627,7 @@ void invalidEnterpriseNumber(boolean extIssueTypes) {
@MethodSource("toggleExtIssueTypes")
void invalidEstablishmentUnitNumber(boolean extIssueTypes) {
InputValidationIssue issue =
- InputValidationIssues.invalidEstablishmentUnitNumber(InEnum.BODY, "test", "0000000001");
+ InputValidationIssues.invalidEstablishmentUnitNumber(InEnum.BODY, "/test", "0000000001");
if (extIssueTypes) {
assertThat(issue.getType()).hasToString("urn:problem-type:belgif-ext:input-validation:invalidStructure");
assertThat(issue.getHref())
@@ -633,7 +640,7 @@ void invalidEstablishmentUnitNumber(boolean extIssueTypes) {
assertThat(issue.getTitle()).isEqualTo("Invalid input");
}
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("test");
+ assertThat(issue.getName()).isEqualTo("/test");
assertThat(issue.getValue()).isEqualTo("0000000001");
assertThat(issue.getDetail()).isEqualTo("Establishment unit number 0000000001 is invalid");
assertThat(issue).extracting("inputs", "additionalProperties").allMatch(this::isEmpty);
@@ -782,9 +789,9 @@ void getHrefConcurrently() throws Exception {
List> results = new ArrayList<>(threads);
for (int i = 0; i < threads; i++) {
results.add(executorService.submit(() -> {
- assertThat(InputValidationIssues.schemaViolation(InEnum.BODY, "name", "value", "detail").getHref())
+ assertThat(InputValidationIssues.schemaViolation(InEnum.BODY, "/name", "value", "detail").getHref())
.hasToString("https://www.belgif.be/specification/rest/api-guide/issues/schemaViolation.html");
- assertThat(InputValidationIssues.invalidInput(InEnum.BODY, "name", "value", "detail").getHref())
+ assertThat(InputValidationIssues.invalidInput(InEnum.BODY, "/name", "value", "detail").getHref())
.hasToString("https://www.belgif.be/specification/rest/api-guide/issues/invalidInput.html");
return null;
}));
diff --git a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/internal/Jackson2UtilTest.java b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/internal/Jackson2UtilTest.java
index 5bd62a86..d41cd950 100644
--- a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/internal/Jackson2UtilTest.java
+++ b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/internal/Jackson2UtilTest.java
@@ -4,6 +4,7 @@
import java.util.List;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonCreator;
@@ -18,11 +19,33 @@
import io.github.belgif.rest.problem.BadRequestProblem;
import io.github.belgif.rest.problem.api.InEnum;
import io.github.belgif.rest.problem.api.InputValidationIssue;
+import io.github.belgif.rest.problem.config.ProblemConfig;
class Jackson2UtilTest {
+ @BeforeEach
+ void resetProblemConfif() {
+ ProblemConfig.reset();
+ }
+
@Test
void mismatchedInput() {
+ assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(() -> {
+ new ObjectMapper().readValue("{}", Model.class);
+ }).satisfies(e -> {
+ BadRequestProblem problem = Jackson2Util.toBadRequestProblem(e);
+ InputValidationIssue issue = problem.getIssues().get(0);
+ assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
+ assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
+ assertThat(issue.getName()).isEqualTo("/id");
+ assertThat(issue.getValue()).isNull();
+ assertThat(issue.getDetail()).isEqualTo("must not be null");
+ });
+ }
+
+ @Test
+ void mismatchedInputWithJsonPointerDisabled() {
+ ProblemConfig.setJsonPointerEnabled(false);
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(() -> {
new ObjectMapper().readValue("{}", Model.class);
}).satisfies(e -> {
@@ -45,7 +68,7 @@ void mismatchedInputType() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("nbr");
+ assertThat(issue.getName()).isEqualTo("/nbr");
assertThat(issue.getValue()).isEqualTo("twenty-two");
assertThat(issue.getDetail()).isEqualTo("not a valid `int` value");
});
@@ -60,7 +83,7 @@ void valueInstantiationException() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("size");
+ assertThat(issue.getName()).isEqualTo("/size");
assertThat(issue.getValue()).isNull();
assertThat(issue.getDetail()).isEqualTo("Unexpected value 'XXL'");
});
@@ -75,7 +98,7 @@ void invalidFormatException() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("size2");
+ assertThat(issue.getName()).isEqualTo("/size2");
assertThat(issue.getValue()).isEqualTo("XXL");
assertThat(issue.getDetail()).isEqualTo("not one of the values accepted for enumeration: [S, L, M]");
});
@@ -90,7 +113,7 @@ void jsonParseExceptionWrappedJsonMappingException() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("model");
+ assertThat(issue.getName()).isEqualTo("/model");
assertThat(issue.getValue()).isNull();
assertThat(issue.getDetail()).isEqualTo("JSON syntax error");
});
@@ -135,7 +158,7 @@ void mismatchedInputNested() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("model.id");
+ assertThat(issue.getName()).isEqualTo("/model/id");
assertThat(issue.getValue()).isNull();
assertThat(issue.getDetail()).isEqualTo("must not be null");
});
@@ -143,6 +166,22 @@ void mismatchedInputNested() {
@Test
void mismatchedInputNestedWithArray() {
+ assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(() -> {
+ new ObjectMapper().readValue("{\"models\": [{}]}", NestedWithArray.class);
+ }).satisfies(e -> {
+ BadRequestProblem problem = Jackson2Util.toBadRequestProblem(e);
+ InputValidationIssue issue = problem.getIssues().get(0);
+ assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
+ assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
+ assertThat(issue.getName()).isEqualTo("/models/0/id");
+ assertThat(issue.getValue()).isNull();
+ assertThat(issue.getDetail()).isEqualTo("must not be null");
+ });
+ }
+
+ @Test
+ void mismatchedInputNestedWithArrayWithJsonPointerDisabled() {
+ ProblemConfig.setJsonPointerEnabled(false);
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(() -> {
new ObjectMapper().readValue("{\"models\": [{}]}", NestedWithArray.class);
}).satisfies(e -> {
@@ -165,7 +204,7 @@ void mismatchedInputFormatError() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("id");
+ assertThat(issue.getName()).isEqualTo("/id");
assertThat(issue.getValue()).isEqualTo("one two three");
assertThat(issue.getDetail()).isEqualTo(
"not a valid `int` value");
diff --git a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/internal/Jackson3UtilTest.java b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/internal/Jackson3UtilTest.java
index 09cbf729..f93f8c9d 100644
--- a/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/internal/Jackson3UtilTest.java
+++ b/belgif-rest-problem/src/test/java/io/github/belgif/rest/problem/internal/Jackson3UtilTest.java
@@ -4,6 +4,7 @@
import java.util.List;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonCreator;
@@ -12,6 +13,7 @@
import io.github.belgif.rest.problem.BadRequestProblem;
import io.github.belgif.rest.problem.api.InEnum;
import io.github.belgif.rest.problem.api.InputValidationIssue;
+import io.github.belgif.rest.problem.config.ProblemConfig;
import tools.jackson.core.exc.StreamReadException;
import tools.jackson.databind.DatabindException;
import tools.jackson.databind.ObjectMapper;
@@ -22,8 +24,29 @@
class Jackson3UtilTest {
+ @BeforeEach
+ void resetProblemConfif() {
+ ProblemConfig.reset();
+ }
+
@Test
void mismatchedInput() {
+ assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(() -> {
+ new JsonMapper().readValue("{}", Model.class);
+ }).satisfies(e -> {
+ BadRequestProblem problem = Jackson3Util.toBadRequestProblem(e);
+ InputValidationIssue issue = problem.getIssues().get(0);
+ assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
+ assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
+ assertThat(issue.getName()).isEqualTo("/id");
+ assertThat(issue.getValue()).isNull();
+ assertThat(issue.getDetail()).isEqualTo("must not be null");
+ });
+ }
+
+ @Test
+ void mismatchedInputWithJsonPointerDisabled() {
+ ProblemConfig.setJsonPointerEnabled(false);
assertThatExceptionOfType(MismatchedInputException.class).isThrownBy(() -> {
new JsonMapper().readValue("{}", Model.class);
}).satisfies(e -> {
@@ -46,7 +69,7 @@ void mismatchedInputType() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("nbr");
+ assertThat(issue.getName()).isEqualTo("/nbr");
assertThat(issue.getValue()).isEqualTo("twenty-two");
assertThat(issue.getDetail()).isEqualTo("not a valid `int` value");
});
@@ -61,7 +84,7 @@ void valueInstantiationException() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("size");
+ assertThat(issue.getName()).isEqualTo("/size");
assertThat(issue.getValue()).isNull();
assertThat(issue.getDetail()).isEqualTo("Unexpected value 'XXL'");
});
@@ -76,7 +99,7 @@ void invalidFormatException() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("size2");
+ assertThat(issue.getName()).isEqualTo("/size2");
assertThat(issue.getValue()).isEqualTo("XXL");
assertThat(issue.getDetail()).isEqualTo("not one of the values accepted for enumeration: [S, L, M]");
});
@@ -91,7 +114,7 @@ void streamReadException() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("model");
+ assertThat(issue.getName()).isEqualTo("/model");
assertThat(issue.getValue()).isNull();
assertThat(issue.getDetail()).isEqualTo("JSON syntax error");
});
@@ -136,7 +159,7 @@ void mismatchedInputNested() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("model.id");
+ assertThat(issue.getName()).isEqualTo("/model/id");
assertThat(issue.getValue()).isNull();
assertThat(issue.getDetail()).isEqualTo("must not be null");
});
@@ -144,6 +167,22 @@ void mismatchedInputNested() {
@Test
void DatabindExceptionWithArray() {
+ assertThatExceptionOfType(DatabindException.class).isThrownBy(() -> {
+ new ObjectMapper().readValue("{\"models\": [{}]}", NestedWithArray.class);
+ }).satisfies(e -> {
+ BadRequestProblem problem = Jackson3Util.toBadRequestProblem(e);
+ InputValidationIssue issue = problem.getIssues().get(0);
+ assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
+ assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
+ assertThat(issue.getName()).isEqualTo("/models/0/id");
+ assertThat(issue.getValue()).isNull();
+ assertThat(issue.getDetail()).isEqualTo("must not be null");
+ });
+ }
+
+ @Test
+ void DatabindExceptionWithArrayJsonPointerDisabled() {
+ ProblemConfig.setJsonPointerEnabled(false);
assertThatExceptionOfType(DatabindException.class).isThrownBy(() -> {
new ObjectMapper().readValue("{\"models\": [{}]}", NestedWithArray.class);
}).satisfies(e -> {
@@ -166,7 +205,7 @@ void mismatchedInputFormatError() {
InputValidationIssue issue = problem.getIssues().get(0);
assertThat(issue.getType()).hasToString("urn:problem-type:belgif:input-validation:schemaViolation");
assertThat(issue.getIn()).isEqualTo(InEnum.BODY);
- assertThat(issue.getName()).isEqualTo("id");
+ assertThat(issue.getName()).isEqualTo("/id");
assertThat(issue.getValue()).isEqualTo("one two three");
assertThat(issue.getDetail()).isEqualTo("not a valid `int` value");
});
diff --git a/src/main/asciidoc/index.adoc b/src/main/asciidoc/index.adoc
index baa784d8..7e389608 100644
--- a/src/main/asciidoc/index.adoc
+++ b/src/main/asciidoc/index.adoc
@@ -822,6 +822,50 @@ Scan additional packages for @ProblemType annotations.
This configuration property only applies to *Spring Boot*.
On Jakarta EE and Quarkus, all problem types are automatically picked up by CDI.
+[[io.github.belgif.rest.problem.json-pointer-enabled]]
+==== io.github.belgif.rest.problem.json-pointer-enabled
+
+Enable using JsonPointer syntax for the 'name' property for a schema violation issue in the request body.
+Default `true`.
+
+[source,json]
+----
+{
+ "title": "Bad Request",
+ "status": 400,type:
+ "urn:problem-type:belgif:badRequest",href:"https://www.belgif.be/specification/rest/api-guide/problems/badRequest.html",
+ "issues": [
+ {
+ "type": "urn:problem-type:belgif:input-validation:schemaViolation",
+ "in": "body",
+ "name": "/person/0/ssin",
+ "detail": "An SSIN should be 11 digits long",
+ "value": "1234"
+ }
+ ]
+}
+----
+
+If set to false, the JsonPath syntax is used.
+
+[source,json]
+----
+{
+ "title": "Bad Request",
+ "status": 400,type:
+ "urn:problem-type:belgif:badRequest",href:"https://www.belgif.be/specification/rest/api-guide/problems/badRequest.html",
+ "issues": [
+ {
+ "type": "urn:problem-type:belgif:input-validation:schemaViolation",
+ "in": "body",
+ "name": "person[0].ssin",
+ "detail": "An SSIN should be 11 digits long",
+ "value": "1234"
+ }
+ ]
+}
+----
+
[[implementation-details]]
== Implementation details
diff --git a/src/main/asciidoc/release-notes.adoc b/src/main/asciidoc/release-notes.adoc
index 48eedcfc..54427474 100644
--- a/src/main/asciidoc/release-notes.adoc
+++ b/src/main/asciidoc/release-notes.adoc
@@ -12,6 +12,12 @@
// tag::recent-versions[]
+== Version 0.23
+
+*belgif-rest-problem*:
+
+* Use <> by default for schema violation issues in request body.
+
== Version 0.22
*belgif-rest-problem*: