Repository navigation
Conversation
merge master
|
@jpfinne is this ready for review? If not, what remains to be done? I'd like to get this fixed ASAP, so if needed I can create a PR myself |
@anthonyvdotbe I've simplified the nullable annotations to easily distinguish between model and api. We are close to the release date... Contact wing328 to accelerate the merge |
There was a problem hiding this comment.
All reported issues were addressed across 17 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
@jpfinne I see the AI bot suggested changes, so I'll let you resolve those first |
@anthonyvdotbe done |
anthonyvdotbe
left a comment
There was a problem hiding this comment.
In general, I'd prefer a more focused PR that just fixes this particular issue and leaves the refactoring for another PR.
KannaKim
left a comment
There was a problem hiding this comment.
noticed a possible edge case in nullableAnnotation_apiArgument.mustache.
For an optional parameter explicitly declared as nullable:
parameters:
- name: filter
in: query
required: false
schema:
type: string
nullable: truegenerating a Spring HTTP Interface with openApiNullable=true produces:
@RequestParam(value = "filter", required = false) String filter
instead of:
@RequestParam(value = "filter", required = false) @Nullable String filter
This appears to come from:
{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}
WhenisNullable=true, the inverted section suppresses the annotation. Interestingly, setting openApiNullable=false does generate @Nullable.
It may be worth adding a test covering isNullable=true with openApiNullable=true.
There was a problem hiding this comment.
1 issue found across 26 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="samples/openapi3/server/petstore/springboot-4-jspecify-openapiNullable/src/main/java/org/openapitools/api/RequiredAndNullableApi.java">
<violation number="1" location="samples/openapi3/server/petstore/springboot-4-jspecify-openapiNullable/src/main/java/org/openapitools/api/RequiredAndNullableApi.java:65">
P1: The generated GET endpoint's `filter` query param is optional (required=false) and nullable per the spec, but is emitted as `String filter` with no `@Nullable`. In the new base `nullableAnnotation_apiArgument.mustache`, when `openApiNullable=true` the annotation is gated on `{{^isNullable}}`, so a param that is explicitly nullable gets no `@Nullable` at all. This contradicts the PR's aim (nullable API arguments should be annotated so Spring handles null) and leaves the newly imported `org.jspecify.annotations.Nullable` unused. Emit `@Nullable` for nullable params too (e.g. also when `isNullable`, or drop the `{{^isNullable}}` guard and gate only on `{{^required}}`/`{{^useOptional}}`).</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| value = RequiredAndNullableApi.PATH_REQUIRED_AND_NULLABLE_GET | ||
| ) | ||
| default ResponseEntity<Void> requiredAndNullableGet( | ||
| @Parameter(name = "filter", description = "", in = ParameterIn.QUERY) @Valid @RequestParam(value = "filter", required = false) String filter |
There was a problem hiding this comment.
P1: The generated GET endpoint's filter query param is optional (required=false) and nullable per the spec, but is emitted as String filter with no @Nullable. In the new base nullableAnnotation_apiArgument.mustache, when openApiNullable=true the annotation is gated on {{^isNullable}}, so a param that is explicitly nullable gets no @Nullable at all. This contradicts the PR's aim (nullable API arguments should be annotated so Spring handles null) and leaves the newly imported org.jspecify.annotations.Nullable unused. Emit @Nullable for nullable params too (e.g. also when isNullable, or drop the {{^isNullable}} guard and gate only on {{^required}}/{{^useOptional}}).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/openapi3/server/petstore/springboot-4-jspecify-openapiNullable/src/main/java/org/openapitools/api/RequiredAndNullableApi.java, line 65:
<comment>The generated GET endpoint's `filter` query param is optional (required=false) and nullable per the spec, but is emitted as `String filter` with no `@Nullable`. In the new base `nullableAnnotation_apiArgument.mustache`, when `openApiNullable=true` the annotation is gated on `{{^isNullable}}`, so a param that is explicitly nullable gets no `@Nullable` at all. This contradicts the PR's aim (nullable API arguments should be annotated so Spring handles null) and leaves the newly imported `org.jspecify.annotations.Nullable` unused. Emit `@Nullable` for nullable params too (e.g. also when `isNullable`, or drop the `{{^isNullable}}` guard and gate only on `{{^required}}`/`{{^useOptional}}`).</comment>
<file context>
@@ -42,6 +43,32 @@ default Optional<NativeWebRequest> getRequest() {
+ value = RequiredAndNullableApi.PATH_REQUIRED_AND_NULLABLE_GET
+ )
+ default ResponseEntity<Void> requiredAndNullableGet(
+ @Parameter(name = "filter", description = "", in = ParameterIn.QUERY) @Valid @RequestParam(value = "filter", required = false) String filter
+ ) {
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
</file context>
There was a problem hiding this comment.
2 issues found across 40 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="modules/openapi-generator/src/test/resources/3_0/java/jspecify.yaml">
<violation number="1" location="modules/openapi-generator/src/test/resources/3_0/java/jspecify.yaml:53">
P3: Operations are now tagged `nble`, but the top-level `tags:` list still declares `requiredAndNullable` (now unused) and has no `nble` entry. Update the top-level tag list to match the renamed operations so the fixture does not carry a stale tag declaration.</violation>
</file>
<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java">
<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java:7239">
P3: In testJspecify the nbleApi.fileContains(") String mandatory", ") @Nullable String optional") block is now byte-for-byte identical in the SPRING_HTTP_INTERFACE branch and the else branch, so the if/else reads as if it varies the NbleApi assertions when only the fooApiAssert color value differs. Hoist the identical nbleApi assertion above the if/else and keep only the differing color assertion inside each branch.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| /nble: | ||
| get: | ||
| tags: | ||
| - nble |
There was a problem hiding this comment.
P3: Operations are now tagged nble, but the top-level tags: list still declares requiredAndNullable (now unused) and has no nble entry. Update the top-level tag list to match the renamed operations so the fixture does not carry a stale tag declaration.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/resources/3_0/java/jspecify.yaml, line 53:
<comment>Operations are now tagged `nble`, but the top-level `tags:` list still declares `requiredAndNullable` (now unused) and has no `nble` entry. Update the top-level tag list to match the renamed operations so the fixture does not carry a stale tag declaration.</comment>
<file context>
@@ -47,23 +47,29 @@ paths:
get:
tags:
- - requiredAndNullable
+ - nble
parameters:
- - name: filter
</file context>
| if (SPRING_HTTP_INTERFACE.equals(library)) { | ||
| nbleApi.fileContains( | ||
| ") String mandatory", | ||
| ") @Nullable String optional"); |
There was a problem hiding this comment.
P3: In testJspecify the nbleApi.fileContains(") String mandatory", ") @nullable String optional") block is now byte-for-byte identical in the SPRING_HTTP_INTERFACE branch and the else branch, so the if/else reads as if it varies the NbleApi assertions when only the fooApiAssert color value differs. Hoist the identical nbleApi assertion above the if/else and keep only the differing color assertion inside each branch.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java, line 7239:
<comment>In testJspecify the nbleApi.fileContains(") String mandatory", ") @Nullable String optional") block is now byte-for-byte identical in the SPRING_HTTP_INTERFACE branch and the else branch, so the if/else reads as if it varies the NbleApi assertions when only the fooApiAssert color value differs. Hoist the identical nbleApi assertion above the if/else and keep only the differing color assertion inside each branch.</comment>
<file context>
@@ -7231,16 +7231,18 @@ public void testJspecify(String library, int springBootVersion) throws IOExcepti
- " @Nullable String filter");
+ nbleApi.fileContains(
+ ") String mandatory",
+ ") @Nullable String optional");
fooApiAssert.fileContains(
"@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color");
</file context>
fix #24737
For spring-http-interface only: the api arguments with default value are annotated with
@Nullable.Spring will inject the specified default value if null is provided.
Small refactoring of the spring templates:
nullableAnnotation.mustacheis kept for model onlynullableAnnotation_apiArgument.mustacheis introduced for all api arguments (queryParams, pathParams, headerParams)This make it easier to redefine nullableAnnotation_apiArgument for spring-http-interface to handle the default parameters.
PR checklist
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example
./bin/generate-samples.sh bin/configs/java*.IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
@cachescrubber (2022/02) @welshm (2022/02) @MelleD (2022/02) @atextor (2022/02) @manedev79 (2022/02) @javisst (2022/02) @borsch (2022/02) @banlevente (2022/02) @Zomzog (2022/09) @martin-mfg (2023/08) @KannaKim (2026/07)
Summary by cubic
Fixes
spring-http-interfaceJSpecify handling so API arguments with default values orrequired=falsenullable schemas are marked@Nullable, matching Spring's actual injection behavior.nullableAnnotation_apiArgument.mustacheplus aspring-http-interfaceoverride, and switches body, query, header, cookie, and path templates to use it;nullableAnnotation.mustachestays for models only.nullableAnnotation_jspecify.mustacheto remove the redundant guard, preventing duplicate@Nullable.Migration
org.jspecify:jspecifyis on the classpath.Written for commit 6432c0b. Summary will update on new commits.