Repository navigation
Add the @Present constraint for JsonNullable - #208
Sekator778 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
3 issues found across 11 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/main/java/org/openapitools/jackson/nullable/jakarta/Present.java">
<violation number="1" location="src/main/java/org/openapitools/jackson/nullable/jakarta/Present.java:37">
P3: The default `message()` is a plain literal, so the default violation message cannot be overridden globally through `ValidationMessages.properties` (the standard Bean Validation mechanism). Users must spell out `message = "{...}"` at every use site to change or localize it. Publish a message key instead, e.g. `default "{org.openapitools.jackson.nullable.jakarta.Present.message}"`, with a matching `META-INF/ValidationMessages.properties` entry, following how `jakarta.validation.constraints.*` define their defaults. Applies to the javax variant too.</violation>
</file>
<file name="README.md">
<violation number="1" location="README.md:124">
P3: The 'Without the payload...' sentence is a long run-on that stacks the pass-through case, the throw case, and the no-extractor case in one clause chain, which makes the most important caveat in this section hard to follow. Split it into separate sentences: '...an undefined property and a `null` reference pass unchecked. A defined value (an explicit `null` included) makes Hibernate Validator throw an `UnexpectedTypeException` (HV000030). Without the extractor, a plain `@Present` simply checks presence.'</violation>
</file>
<file name="src/it/javax-classpath/pom.xml">
<violation number="1" location="src/it/javax-classpath/pom.xml:82">
P3: This IT targets Java 8 (`<java.version>8</java.version>` with `<release>8</release>`), but `java --class-path` is only recognized by the JDK 9+ launcher (JEP 293 long-form options; a JDK 8 java exits with "Unrecognized option: --class-path"). The sibling `jakarta-jar-classpath` IT pairs the same `--class-path` invocation with `java.version=9`, which is consistent. Since the javax flavor exists specifically to cover the Java 8 runtime, run this under a JDK 8 `java` and the verify phase fails. Use `-classpath` (valid on every JDK) so the Java 8 scenario is actually runnable; the current CI matrix (17/21/25) masks this.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| @Constraint(validatedBy = { PresentValidator.class }) | ||
| public @interface Present { | ||
|
|
||
| String message() default "must be present"; |
There was a problem hiding this comment.
P3: The default message() is a plain literal, so the default violation message cannot be overridden globally through ValidationMessages.properties (the standard Bean Validation mechanism). Users must spell out message = "{...}" at every use site to change or localize it. Publish a message key instead, e.g. default "{org.openapitools.jackson.nullable.jakarta.Present.message}", with a matching META-INF/ValidationMessages.properties entry, following how jakarta.validation.constraints.* define their defaults. Applies to the javax variant too.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/main/java/org/openapitools/jackson/nullable/jakarta/Present.java, line 37:
<comment>The default `message()` is a plain literal, so the default violation message cannot be overridden globally through `ValidationMessages.properties` (the standard Bean Validation mechanism). Users must spell out `message = "{...}"` at every use site to change or localize it. Publish a message key instead, e.g. `default "{org.openapitools.jackson.nullable.jakarta.Present.message}"`, with a matching `META-INF/ValidationMessages.properties` entry, following how `jakarta.validation.constraints.*` define their defaults. Applies to the javax variant too.</comment>
<file context>
@@ -0,0 +1,53 @@
+@Constraint(validatedBy = { PresentValidator.class })
+public @interface Present {
+
+ String message() default "must be present";
+
+ Class<?>[] groups() default { };
</file context>
| public JsonNullable<String> color = JsonNullable.undefined(); | ||
| ``` | ||
| The annotation comes in two packages with the same name, `org.openapitools.jackson.nullable.jakarta` for `jakarta.validation` and `org.openapitools.jackson.nullable.javax` for `javax.validation`. | ||
| Without the payload, while the module's `ValueExtractor` is registered (the default on the class path), an undefined property and a `null` reference pass unchecked and a defined value, an explicit `null` included, makes Hibernate Validator throw an `UnexpectedTypeException` (HV000030); without the extractor a plain `@Present` simply checks presence. This was measured on Hibernate Validator 6.2.5, 7.0.5, 8.0.2, 9.0.1 and 9.1.4. |
There was a problem hiding this comment.
P3: The 'Without the payload...' sentence is a long run-on that stacks the pass-through case, the throw case, and the no-extractor case in one clause chain, which makes the most important caveat in this section hard to follow. Split it into separate sentences: '...an undefined property and a null reference pass unchecked. A defined value (an explicit null included) makes Hibernate Validator throw an UnexpectedTypeException (HV000030). Without the extractor, a plain @Present simply checks presence.'
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At README.md, line 124:
<comment>The 'Without the payload...' sentence is a long run-on that stacks the pass-through case, the throw case, and the no-extractor case in one clause chain, which makes the most important caveat in this section hard to follow. Split it into separate sentences: '...an undefined property and a `null` reference pass unchecked. A defined value (an explicit `null` included) makes Hibernate Validator throw an `UnexpectedTypeException` (HV000030). Without the extractor, a plain `@Present` simply checks presence.'</comment>
<file context>
@@ -108,6 +108,22 @@ Set<ConstraintViolation<Pet>> validationResult = validator.validate(myPet);
+public JsonNullable<String> color = JsonNullable.undefined();
+```
+The annotation comes in two packages with the same name, `org.openapitools.jackson.nullable.jakarta` for `jakarta.validation` and `org.openapitools.jackson.nullable.javax` for `javax.validation`.
+Without the payload, while the module's `ValueExtractor` is registered (the default on the class path), an undefined property and a `null` reference pass unchecked and a defined value, an explicit `null` included, makes Hibernate Validator throw an `UnexpectedTypeException` (HV000030); without the extractor a plain `@Present` simply checks presence. This was measured on Hibernate Validator 6.2.5, 7.0.5, 8.0.2, 9.0.1 and 9.1.4.
+In a composed constraint that carries `@Present(payload = Unwrapping.Skip.class)` as a meta-annotation the payload is ignored and an undefined property passes; give the payload where the composed annotation is used.
+
</file context>
| Without the payload, while the module's `ValueExtractor` is registered (the default on the class path), an undefined property and a `null` reference pass unchecked and a defined value, an explicit `null` included, makes Hibernate Validator throw an `UnexpectedTypeException` (HV000030); without the extractor a plain `@Present` simply checks presence. This was measured on Hibernate Validator 6.2.5, 7.0.5, 8.0.2, 9.0.1 and 9.1.4. | |
| Without the payload, while the module's `ValueExtractor` is registered (the default on the class path), an undefined property and a `null` reference pass unchecked. A defined value (an explicit `null` included) makes Hibernate Validator throw an `UnexpectedTypeException` (HV000030). Without the extractor a plain `@Present` simply checks presence. This was measured on Hibernate Validator 6.2.5, 7.0.5, 8.0.2, 9.0.1 and 9.1.4. |
| </goals> | ||
| <configuration> | ||
| <executable>java</executable> | ||
| <commandlineArgs>--class-path ${project.build.directory}/test-1.0-SNAPSHOT.jar${path.separator}${project.build.directory}/lib/* org.openapitools.it.Main</commandlineArgs> |
There was a problem hiding this comment.
P3: This IT targets Java 8 (<java.version>8</java.version> with <release>8</release>), but java --class-path is only recognized by the JDK 9+ launcher (JEP 293 long-form options; a JDK 8 java exits with "Unrecognized option: --class-path"). The sibling jakarta-jar-classpath IT pairs the same --class-path invocation with java.version=9, which is consistent. Since the javax flavor exists specifically to cover the Java 8 runtime, run this under a JDK 8 java and the verify phase fails. Use -classpath (valid on every JDK) so the Java 8 scenario is actually runnable; the current CI matrix (17/21/25) masks this.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/it/javax-classpath/pom.xml, line 82:
<comment>This IT targets Java 8 (`<java.version>8</java.version>` with `<release>8</release>`), but `java --class-path` is only recognized by the JDK 9+ launcher (JEP 293 long-form options; a JDK 8 java exits with "Unrecognized option: --class-path"). The sibling `jakarta-jar-classpath` IT pairs the same `--class-path` invocation with `java.version=9`, which is consistent. Since the javax flavor exists specifically to cover the Java 8 runtime, run this under a JDK 8 `java` and the verify phase fails. Use `-classpath` (valid on every JDK) so the Java 8 scenario is actually runnable; the current CI matrix (17/21/25) masks this.</comment>
<file context>
@@ -0,0 +1,90 @@
+ </goals>
+ <configuration>
+ <executable>java</executable>
+ <commandlineArgs>--class-path ${project.build.directory}/test-1.0-SNAPSHOT.jar${path.separator}${project.build.directory}/lib/* org.openapitools.it.Main</commandlineArgs>
+ </configuration>
+ </execution>
</file context>
| <commandlineArgs>--class-path ${project.build.directory}/test-1.0-SNAPSHOT.jar${path.separator}${project.build.directory}/lib/* org.openapitools.it.Main</commandlineArgs> | |
| + <commandlineArgs>-classpath ${project.build.directory}/test-1.0-SNAPSHOT.jar${path.separator}${project.build.directory}/lib/* org.openapitools.it.Main</commandlineArgs> |
Thorsrud22
left a comment
There was a problem hiding this comment.
Ran clean verify -P integration-test on JDK 17 and 21 with this merged onto master: 381 tests and all four ITs pass, and the new javax-classpath IT covers field, getter, constructor, the null reference and the default message. The opt-in payload is the only option, since the spec requires payload() to default to an empty array, and the README is clear about what happens without it.
One optional gap: the new exports in both module-info files aren't exercised by the module-path ITs (jakarta-jar-modulepath, jakarta-jlink don't use @Present). One @Present check in jakarta-jar-modulepath would cover Hibernate Validator instantiating PresentValidator from the module. Fine as a follow-up. LGTM.
Adds
@Presentfor #176: theJsonNullablemust be set, so an undefined one is invalid and an explicit null is valid. It comes forjakarta.validationandjavax.validationunder the same name in two packages, and needspayload = Unwrapping.Skip.classwhere it is used, because the value extractor unwraps by default. The README says what happens without it.@jpfinne the template's packages are
org.openapitools.jackson.nullable.jakartaand.javax.Closes #176
Summary by cubic
Adds a
@Presentconstraint forJsonNullablethat requires the property to be set:JsonNullable.undefined()and anullreference are invalid, while an explicitnull(JsonNullable.of(null)) is valid. This is the opposite of@NotNullon aJsonNullable. Closes #176.The annotation ships in both
org.openapitools.jackson.nullable.jakartaandorg.openapitools.jackson.nullable.javax, which are now exported in the module-info files. It supports fields, getters, constructor parameters, groups, and repetition, with a default message of "must be present".Usage
@Present(payload = Unwrapping.Skip.class)at the use site. The module's value extractor unwrapsJsonNullableby default, so without the payload an undefined property passes unchecked and a defined value throwsUnexpectedTypeException.Written for commit 7fa531d. Summary will update on new commits.