From a41028691fff46b5cf188d08899100e852b975e6 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Wed, 19 Aug 2026 19:11:22 +0200 Subject: [PATCH 01/10] Fix spring-http-interface @Nullable if default --- .../nullableAnnotation.mustache | 1 + .../nullableAnnotation_apiArgument.mustache | 1 + .../java/spring/SpringCodegenTest.java | 35 +++++++------------ .../java/org/openapitools/api/FakeApi.java | 4 +-- .../java/org/openapitools/api/FakeApi.java | 4 +-- .../java/org/openapitools/api/FakeApi.java | 4 +-- .../java/org/openapitools/api/FakeApi.java | 4 +-- .../java/org/openapitools/api/FakeApi.java | 4 +-- .../java/org/openapitools/api/FakeApi.java | 4 +-- .../java/org/openapitools/api/FakeApi.java | 4 +-- .../java/org/openapitools/api/FakeApi.java | 4 +-- 11 files changed, 30 insertions(+), 39 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation.mustache create mode 100644 modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation.mustache new file mode 100644 index 000000000000..16209a40fb6c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation.mustache @@ -0,0 +1 @@ +{{#paramName}}{{>nullableAnnotation_apiArgument}}{{/paramName}}{{^paramName}}{{>nullableAnnotation_jspecify}}{{^useJspecify}}{{!backward compatibility}}{{>nullableAnnotation_default}}{{/useJspecify}}{{/paramName}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache new file mode 100644 index 000000000000..a1a0ed33254b --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache @@ -0,0 +1 @@ +{{#lambda.jSpecifyNullable}}{{#defaultValue}}@Nullable {{/defaultValue}}{{^required}}{{^defaultValue}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/defaultValue}}{{#defaultValue}}{{^openApiNullable}}{{#isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{/defaultValue}}{{/required}}{{/lambda.jSpecifyNullable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index d959fdcb607a..13225f180de1 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -7180,34 +7180,23 @@ public void testJspecify(String library, int springBootVersion) throws IOExcepti "Foo.Builder dt(java.time.@Nullable Instant dt)", "Foo.Builder requiredDt(java.time.Instant requiredDt)", "Foo.Builder nullableNumber(@Nullable BigDecimal nullableNumber)" - ).fileDoesNotContain( - "javax.annotation.Nullable", - "jakarta.annotation.Nullable") - .assertMethod("getRequiredDt").assertMethodAnnotations().containsWithName("NotNull").containsWithName("Valid"); - JavaFileAssert.assertThat(files.get("FooApi.java")) + ); + JavaFileAssert fooApi = JavaFileAssert.assertThat(files.get("FooApi.java")); + fooApi .assertTypeAnnotations().doesImportAnnotation("org.jspecify.annotations.Nullable").toType() .fileContains( "java.time.@Nullable Instant dtParam", "java.time.@Nullable Instant dtQuery", - "java.time.@Nullable Instant dtCookie", - " @RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color" + "java.time.@Nullable Instant dtCookie" ); - JavaFileAssert.assertThat(files.get("RequiredAndNullable.java")) - .fileContains( - "private @Nullable String str = null;", - "private @Nullable List _list;", - "RequiredAndNullable(@Nullable String str, org.springframework.core.io.@Nullable Resource file, @Nullable String color, String onlyRequired, @Nullable List _list)", - "@Nullable String getStr()", - "void setStr(@Nullable String str)", - "RequiredAndNullable str(@Nullable String str)", - "RequiredAndNullable.Builder str(@Nullable String str)" - ) - .assertMethod("getStr").assertMethodAnnotations().doesNotContainWithName("NotNull"); - if (!library.equals(SPRING_HTTP_INTERFACE)) { - // SPRING_HTTP_INTERFACE does not support @Schema generation (yet) - JavaFileAssert.assertThat(files.get("RequiredAndNullable.java")) - .fileContains( - "@Schema(name = \"str\", requiredMode = Schema.RequiredMode.REQUIRED, nullable = true)"); + if (SPRING_HTTP_INTERFACE.equals(library)) { + fooApi.fileContains( + "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color" + ); + } else { + fooApi.fileContains( + "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color" + ); } JavaFileAssert.assertThat(files.get("FileContent.java")) .fileContains("VirusScanEnum getVirusScan()"); diff --git a/samples/client/petstore/spring-http-interface-bean-validation/src/main/java/org/openapitools/api/FakeApi.java b/samples/client/petstore/spring-http-interface-bean-validation/src/main/java/org/openapitools/api/FakeApi.java index 7358c6c39e53..3a5e73e2421b 100644 --- a/samples/client/petstore/spring-http-interface-bean-validation/src/main/java/org/openapitools/api/FakeApi.java +++ b/samples/client/petstore/spring-http-interface-bean-validation/src/main/java/org/openapitools/api/FakeApi.java @@ -248,9 +248,9 @@ ResponseEntity testEndpointParameters( ) ResponseEntity testEnumParameters( @RequestHeader(value = "enum_header_string_array", required = false) @Nullable List enumHeaderStringArray, - @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") String enumHeaderString, + @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") @Nullable String enumHeaderString, @Valid @RequestParam(value = "enum_query_string_array", required = false) @Nullable List enumQueryStringArray, - @Valid @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") String enumQueryString, + @Valid @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") @Nullable String enumQueryString, @Valid @RequestParam(value = "enum_query_integer", required = false) @Nullable Integer enumQueryInteger, @Valid @RequestParam(value = "enum_query_double", required = false) @Nullable Double enumQueryDouble, @Valid @RequestPart(value = "enum_form_string_array", required = false) List enumFormStringArray, diff --git a/samples/client/petstore/spring-http-interface-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java b/samples/client/petstore/spring-http-interface-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java index 540a382c4ada..d4f956e27b69 100644 --- a/samples/client/petstore/spring-http-interface-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java +++ b/samples/client/petstore/spring-http-interface-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java @@ -255,9 +255,9 @@ void testEndpointParameters( ) void testEnumParameters( @RequestHeader(value = "enum_header_string_array", required = false) @Nullable List enumHeaderStringArray, - @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") String enumHeaderString, + @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") @Nullable String enumHeaderString, @RequestParam(value = "enum_query_string_array", required = false) @Nullable List enumQueryStringArray, - @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") String enumQueryString, + @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") @Nullable String enumQueryString, @RequestParam(value = "enum_query_integer", required = false) @Nullable Integer enumQueryInteger, @RequestParam(value = "enum_query_double", required = false) @Nullable Double enumQueryDouble, @RequestPart(value = "enum_form_string_array", required = false) List enumFormStringArray, diff --git a/samples/client/petstore/spring-http-interface-reactive-bean-validation/src/main/java/org/openapitools/api/FakeApi.java b/samples/client/petstore/spring-http-interface-reactive-bean-validation/src/main/java/org/openapitools/api/FakeApi.java index 749cc042e246..be9694a1acc2 100644 --- a/samples/client/petstore/spring-http-interface-reactive-bean-validation/src/main/java/org/openapitools/api/FakeApi.java +++ b/samples/client/petstore/spring-http-interface-reactive-bean-validation/src/main/java/org/openapitools/api/FakeApi.java @@ -252,9 +252,9 @@ Mono> testEndpointParameters( ) Mono> testEnumParameters( @RequestHeader(value = "enum_header_string_array", required = false) @Nullable List enumHeaderStringArray, - @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") String enumHeaderString, + @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") @Nullable String enumHeaderString, @Valid @RequestParam(value = "enum_query_string_array", required = false) @Nullable List enumQueryStringArray, - @Valid @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") String enumQueryString, + @Valid @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") @Nullable String enumQueryString, @Valid @RequestParam(value = "enum_query_integer", required = false) @Nullable Integer enumQueryInteger, @Valid @RequestParam(value = "enum_query_double", required = false) @Nullable Double enumQueryDouble, @Valid @RequestPart(value = "enum_form_string_array", required = false) List enumFormStringArray, diff --git a/samples/client/petstore/spring-http-interface-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java b/samples/client/petstore/spring-http-interface-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java index 5ae358897b23..1cd88b7569af 100644 --- a/samples/client/petstore/spring-http-interface-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java +++ b/samples/client/petstore/spring-http-interface-reactive-noResponseEntity/src/main/java/org/openapitools/api/FakeApi.java @@ -259,9 +259,9 @@ Mono testEndpointParameters( ) Mono testEnumParameters( @RequestHeader(value = "enum_header_string_array", required = false) @Nullable List enumHeaderStringArray, - @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") String enumHeaderString, + @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") @Nullable String enumHeaderString, @RequestParam(value = "enum_query_string_array", required = false) @Nullable List enumQueryStringArray, - @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") String enumQueryString, + @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") @Nullable String enumQueryString, @RequestParam(value = "enum_query_integer", required = false) @Nullable Integer enumQueryInteger, @RequestParam(value = "enum_query_double", required = false) @Nullable Double enumQueryDouble, @RequestPart(value = "enum_form_string_array", required = false) List enumFormStringArray, diff --git a/samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/api/FakeApi.java b/samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/api/FakeApi.java index 7e98783fcfb6..2d65801a2210 100644 --- a/samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/api/FakeApi.java +++ b/samples/client/petstore/spring-http-interface-reactive/src/main/java/org/openapitools/api/FakeApi.java @@ -249,9 +249,9 @@ Mono> testEndpointParameters( ) Mono> testEnumParameters( @RequestHeader(value = "enum_header_string_array", required = false) @Nullable List enumHeaderStringArray, - @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") String enumHeaderString, + @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") @Nullable String enumHeaderString, @RequestParam(value = "enum_query_string_array", required = false) @Nullable List enumQueryStringArray, - @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") String enumQueryString, + @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") @Nullable String enumQueryString, @RequestParam(value = "enum_query_integer", required = false) @Nullable Integer enumQueryInteger, @RequestParam(value = "enum_query_double", required = false) @Nullable Double enumQueryDouble, @RequestPart(value = "enum_form_string_array", required = false) List enumFormStringArray, diff --git a/samples/client/petstore/spring-http-interface-springboot-4/src/main/java/org/openapitools/api/FakeApi.java b/samples/client/petstore/spring-http-interface-springboot-4/src/main/java/org/openapitools/api/FakeApi.java index 7358c6c39e53..3a5e73e2421b 100644 --- a/samples/client/petstore/spring-http-interface-springboot-4/src/main/java/org/openapitools/api/FakeApi.java +++ b/samples/client/petstore/spring-http-interface-springboot-4/src/main/java/org/openapitools/api/FakeApi.java @@ -248,9 +248,9 @@ ResponseEntity testEndpointParameters( ) ResponseEntity testEnumParameters( @RequestHeader(value = "enum_header_string_array", required = false) @Nullable List enumHeaderStringArray, - @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") String enumHeaderString, + @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") @Nullable String enumHeaderString, @Valid @RequestParam(value = "enum_query_string_array", required = false) @Nullable List enumQueryStringArray, - @Valid @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") String enumQueryString, + @Valid @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") @Nullable String enumQueryString, @Valid @RequestParam(value = "enum_query_integer", required = false) @Nullable Integer enumQueryInteger, @Valid @RequestParam(value = "enum_query_double", required = false) @Nullable Double enumQueryDouble, @Valid @RequestPart(value = "enum_form_string_array", required = false) List enumFormStringArray, diff --git a/samples/client/petstore/spring-http-interface-useHttpServiceProxyFactoryInterfacesConfigurator/src/main/java/org/openapitools/api/FakeApi.java b/samples/client/petstore/spring-http-interface-useHttpServiceProxyFactoryInterfacesConfigurator/src/main/java/org/openapitools/api/FakeApi.java index 49e2652ea1aa..2701a428f9c2 100644 --- a/samples/client/petstore/spring-http-interface-useHttpServiceProxyFactoryInterfacesConfigurator/src/main/java/org/openapitools/api/FakeApi.java +++ b/samples/client/petstore/spring-http-interface-useHttpServiceProxyFactoryInterfacesConfigurator/src/main/java/org/openapitools/api/FakeApi.java @@ -248,9 +248,9 @@ ResponseEntity testEndpointParameters( ) ResponseEntity testEnumParameters( @RequestHeader(value = "enum_header_string_array", required = false) @Nullable List enumHeaderStringArray, - @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") String enumHeaderString, + @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") @Nullable String enumHeaderString, @Valid @RequestParam(value = "enum_query_string_array", required = false) @Nullable List enumQueryStringArray, - @Valid @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") String enumQueryString, + @Valid @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") @Nullable String enumQueryString, @Valid @RequestParam(value = "enum_query_integer", required = false) @Nullable Integer enumQueryInteger, @Valid @RequestParam(value = "enum_query_double", required = false) @Nullable Double enumQueryDouble, @Valid @RequestPart(value = "enum_form_string_array", required = false) List enumFormStringArray, diff --git a/samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/api/FakeApi.java b/samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/api/FakeApi.java index d4dd396c38fe..c0d879d448c5 100644 --- a/samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/api/FakeApi.java +++ b/samples/client/petstore/spring-http-interface/src/main/java/org/openapitools/api/FakeApi.java @@ -245,9 +245,9 @@ ResponseEntity testEndpointParameters( ) ResponseEntity testEnumParameters( @RequestHeader(value = "enum_header_string_array", required = false) @Nullable List enumHeaderStringArray, - @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") String enumHeaderString, + @RequestHeader(value = "enum_header_string", required = false, defaultValue = "-efg") @Nullable String enumHeaderString, @RequestParam(value = "enum_query_string_array", required = false) @Nullable List enumQueryStringArray, - @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") String enumQueryString, + @RequestParam(value = "enum_query_string", required = false, defaultValue = "-efg") @Nullable String enumQueryString, @RequestParam(value = "enum_query_integer", required = false) @Nullable Integer enumQueryInteger, @RequestParam(value = "enum_query_double", required = false) @Nullable Double enumQueryDouble, @RequestPart(value = "enum_form_string_array", required = false) List enumFormStringArray, From 16f02cc6f170f6783a4e7a1f7a8ef9a12f5214eb Mon Sep 17 00:00:00 2001 From: jpfinne Date: Thu, 20 Aug 2026 11:14:31 +0200 Subject: [PATCH 02/10] Force build --- .../codegen/java/spring/SpringCodegenTest.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 13225f180de1..fd91c6436465 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -7190,13 +7190,9 @@ public void testJspecify(String library, int springBootVersion) throws IOExcepti "java.time.@Nullable Instant dtCookie" ); if (SPRING_HTTP_INTERFACE.equals(library)) { - fooApi.fileContains( - "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color" - ); + fooApi.fileContains("@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color"); } else { - fooApi.fileContains( - "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color" - ); + fooApi.fileContains("@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color"); } JavaFileAssert.assertThat(files.get("FileContent.java")) .fileContains("VirusScanEnum getVirusScan()"); From 61eb6bf8da6bd0cbcad3a9a4ab661062590e8728 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Thu, 20 Aug 2026 18:48:57 +0200 Subject: [PATCH 03/10] Force build --- .../codegen/java/spring/SpringCodegenTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index fd91c6436465..13225f180de1 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -7190,9 +7190,13 @@ public void testJspecify(String library, int springBootVersion) throws IOExcepti "java.time.@Nullable Instant dtCookie" ); if (SPRING_HTTP_INTERFACE.equals(library)) { - fooApi.fileContains("@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color"); + fooApi.fileContains( + "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color" + ); } else { - fooApi.fileContains("@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color"); + fooApi.fileContains( + "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color" + ); } JavaFileAssert.assertThat(files.get("FileContent.java")) .fileContains("VirusScanEnum getVirusScan()"); From 455affefa21deb8829fea833b3a3778a150f9be7 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Thu, 20 Aug 2026 19:15:44 +0200 Subject: [PATCH 04/10] Refactoring of nullableAnnotation -> nullable_apiArgument for api parameters --- .../src/main/resources/JavaSpring/cookieParams.mustache | 2 +- .../src/main/resources/JavaSpring/headerParams.mustache | 2 +- .../libraries/spring-http-interface/nullableAnnotation.mustache | 1 - .../src/main/resources/JavaSpring/nullableAnnotation.mustache | 2 +- .../JavaSpring/nullableAnnotation_apiArgument.mustache | 1 + .../resources/JavaSpring/nullableAnnotation_jspecify.mustache | 2 +- .../src/main/resources/JavaSpring/pathParams.mustache | 2 +- .../src/main/resources/JavaSpring/queryParams.mustache | 2 +- 8 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation.mustache create mode 100644 modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation_apiArgument.mustache diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/cookieParams.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/cookieParams.mustache index a255b5c7daf2..32db956c4813 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/cookieParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/cookieParams.mustache @@ -1 +1 @@ -{{#isCookieParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}} @CookieValue(name = "{{baseName}}"{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{>dateTimeParam}} {{>nullableAnnotation}}{{>optionalDataType}} {{paramName}}{{/isCookieParam}} \ No newline at end of file +{{#isCookieParam}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}} @CookieValue(name = "{{baseName}}"{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{>dateTimeParam}} {{>nullableAnnotation_apiArgument}}{{>optionalDataType}} {{paramName}}{{/isCookieParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/headerParams.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/headerParams.mustache index 80b1d0a82341..61c5b3e6257c 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/headerParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/headerParams.mustache @@ -1 +1 @@ -{{#isHeaderParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}} @RequestHeader(value = "{{baseName}}", required = {{#required}}true{{/required}}{{^required}}false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{>dateTimeParam}} {{>nullableAnnotation}}{{>optionalDataType}} {{paramName}}{{/isHeaderParam}} \ No newline at end of file +{{#isHeaderParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}} @RequestHeader(value = "{{baseName}}", required = {{#required}}true{{/required}}{{^required}}false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{>dateTimeParam}} {{>nullableAnnotation_apiArgument}}{{>optionalDataType}} {{paramName}}{{/isHeaderParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation.mustache deleted file mode 100644 index 16209a40fb6c..000000000000 --- a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#paramName}}{{>nullableAnnotation_apiArgument}}{{/paramName}}{{^paramName}}{{>nullableAnnotation_jspecify}}{{^useJspecify}}{{!backward compatibility}}{{>nullableAnnotation_default}}{{/useJspecify}}{{/paramName}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation.mustache index 65c0e39b2ead..f389ca095c8c 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation.mustache @@ -1 +1 @@ -{{#paramName}}{{>nullableAnnotation_default}}{{/paramName}}{{^paramName}}{{>nullableAnnotation_jspecify}}{{^useJspecify}}{{!backward compatibility}}{{>nullableAnnotation_default}}{{/useJspecify}}{{/paramName}} \ No newline at end of file +{{#useJspecify}}{{>nullableAnnotation_jspecify}}{{/useJspecify}}{{^useJspecify}}{{!backward compatibility}}{{>nullableAnnotation_default}}{{/useJspecify}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation_apiArgument.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation_apiArgument.mustache new file mode 100644 index 000000000000..a3ff1bad10e2 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation_apiArgument.mustache @@ -0,0 +1 @@ +{{#lambda.jSpecifyNullable}}{{^required}}{{^defaultValue}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/defaultValue}}{{#defaultValue}}{{^openApiNullable}}{{#isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{/defaultValue}}{{/required}}{{/lambda.jSpecifyNullable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation_jspecify.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation_jspecify.mustache index c7624e180091..a121f6bc6625 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation_jspecify.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/nullableAnnotation_jspecify.mustache @@ -1 +1 @@ -{{#useJspecify}}{{#lambda.jSpecifyNullable}}{{#required}}{{#isNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/isNullable}}{{/required}}{{^required}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/required}}{{/lambda.jSpecifyNullable}}{{/useJspecify}} \ No newline at end of file +{{#lambda.jSpecifyNullable}}{{#required}}{{#isNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/isNullable}}{{/required}}{{^required}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/required}}{{/lambda.jSpecifyNullable}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/pathParams.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/pathParams.mustache index 5a351829131d..881becf0f0de 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/pathParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/pathParams.mustache @@ -1 +1 @@ -{{#isPathParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}{{>paramDoc}} @PathVariable("{{baseName}}"){{>dateTimeParam}}{{#isDeprecated}} @Deprecated{{/isDeprecated}} {{>nullableAnnotation}}{{>optionalDataType}} {{paramName}}{{/isPathParam}} \ No newline at end of file +{{#isPathParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationPathParams}}{{/useBeanValidation}}{{>paramDoc}} @PathVariable("{{baseName}}"){{>dateTimeParam}}{{#isDeprecated}} @Deprecated{{/isDeprecated}} {{>nullableAnnotation_apiArgument}}{{>optionalDataType}} {{paramName}}{{/isPathParam}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache index 56f7527eb92a..786be39bba78 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/queryParams.mustache @@ -1 +1 @@ -{{#isQueryParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}}{{#useBeanValidation}} @Valid{{/useBeanValidation}}{{^isModel}} @RequestParam(value = {{#isMap}}""{{/isMap}}{{^isMap}}"{{baseName}}"{{/isMap}}{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/isModel}}{{>dateTimeParam}}{{#isDeprecated}} @Deprecated{{/isDeprecated}} {{>nullableAnnotation}}{{>optionalDataType}} {{paramName}}{{/isQueryParam}} \ No newline at end of file +{{#isQueryParam}}{{#vendorExtensions.x-field-extra-annotation}}{{{.}}} {{/vendorExtensions.x-field-extra-annotation}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{>paramDoc}}{{#useBeanValidation}} @Valid{{/useBeanValidation}}{{^isModel}} @RequestParam(value = {{#isMap}}""{{/isMap}}{{^isMap}}"{{baseName}}"{{/isMap}}{{#required}}, required = true{{/required}}{{^required}}, required = false{{/required}}{{#defaultValue}}, defaultValue = "{{{.}}}"{{/defaultValue}}){{/isModel}}{{>dateTimeParam}}{{#isDeprecated}} @Deprecated{{/isDeprecated}} {{>nullableAnnotation_apiArgument}}{{>optionalDataType}} {{paramName}}{{/isQueryParam}} \ No newline at end of file From 9d1fc7d313e2094a04a04b296ccd1cd2e2a18292 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Thu, 20 Aug 2026 21:24:06 +0200 Subject: [PATCH 05/10] Refactoring of nullableAnnotation -> nullable_apiArgument for api parameters --- .../src/main/resources/JavaSpring/bodyParams.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/bodyParams.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/bodyParams.mustache index 26bea8c9b706..fb36a1407072 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/bodyParams.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/bodyParams.mustache @@ -1 +1 @@ -{{#isBodyParam}}{{>paramDoc}}{{#useBeanValidation}} {{>beanValidationBodyParams}}@Valid{{/useBeanValidation}} @RequestBody{{^required}}(required = false){{/required}} {{^reactive}}{{>nullableAnnotation}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}Mono<{{{dataType}}}>{{/isArray}}{{#isArray}}Flux<{{{baseType}}}>{{/isArray}}{{/reactive}} {{paramName}}{{/isBodyParam}} \ No newline at end of file +{{#isBodyParam}}{{>paramDoc}}{{#useBeanValidation}} {{>beanValidationBodyParams}}@Valid{{/useBeanValidation}} @RequestBody{{^required}}(required = false){{/required}} {{^reactive}}{{>nullableAnnotation_apiArgument}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}Mono<{{{dataType}}}>{{/isArray}}{{#isArray}}Flux<{{{baseType}}}>{{/isArray}}{{/reactive}} {{paramName}}{{/isBodyParam}} \ No newline at end of file From 5d162d114bc99373f976aa121c95e04c6f47dd22 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Thu, 20 Aug 2026 21:36:24 +0200 Subject: [PATCH 06/10] fix double @Nullable --- .../nullableAnnotation_apiArgument.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache index a1a0ed33254b..dfb00beab5af 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache @@ -1 +1 @@ -{{#lambda.jSpecifyNullable}}{{#defaultValue}}@Nullable {{/defaultValue}}{{^required}}{{^defaultValue}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/defaultValue}}{{#defaultValue}}{{^openApiNullable}}{{#isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{/defaultValue}}{{/required}}{{/lambda.jSpecifyNullable}} \ No newline at end of file +{{#lambda.jSpecifyNullable}}{{#defaultValue}}@Nullable {{/defaultValue}}{{^required}}{{^defaultValue}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/defaultValue}}{{/required}}{{/lambda.jSpecifyNullable}} \ No newline at end of file From fb38a0c45f8e52673faed05ad15e7f0d44ab5990 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Thu, 20 Aug 2026 21:43:02 +0200 Subject: [PATCH 07/10] Revert missing test --- .../java/spring/SpringCodegenTest.java | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 13225f180de1..91998a5d7dc1 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -7180,23 +7180,39 @@ public void testJspecify(String library, int springBootVersion) throws IOExcepti "Foo.Builder dt(java.time.@Nullable Instant dt)", "Foo.Builder requiredDt(java.time.Instant requiredDt)", "Foo.Builder nullableNumber(@Nullable BigDecimal nullableNumber)" - ); - JavaFileAssert fooApi = JavaFileAssert.assertThat(files.get("FooApi.java")); - fooApi + ).fileDoesNotContain( + "javax.annotation.Nullable", + "jakarta.annotation.Nullable") + .assertMethod("getRequiredDt").assertMethodAnnotations().containsWithName("NotNull").containsWithName("Valid"); + JavaFileAssert fooApiAsset = JavaFileAssert.assertThat(files.get("FooApi.java")) .assertTypeAnnotations().doesImportAnnotation("org.jspecify.annotations.Nullable").toType() .fileContains( "java.time.@Nullable Instant dtParam", "java.time.@Nullable Instant dtQuery", - "java.time.@Nullable Instant dtCookie" - ); + "java.time.@Nullable Instant dtCookie"); if (SPRING_HTTP_INTERFACE.equals(library)) { - fooApi.fileContains( - "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color" - ); + fooApiAsset.fileContains( + "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color"); } else { - fooApi.fileContains( - "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color" - ); + fooApiAsset.fileContains( + "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color"); + } + JavaFileAssert.assertThat(files.get("RequiredAndNullable.java")) + .fileContains( + "private @Nullable String str = null;", + "private @Nullable List _list;", + "RequiredAndNullable(@Nullable String str, org.springframework.core.io.@Nullable Resource file, @Nullable String color, String onlyRequired, @Nullable List _list)", + "@Nullable String getStr()", + "void setStr(@Nullable String str)", + "RequiredAndNullable str(@Nullable String str)", + "RequiredAndNullable.Builder str(@Nullable String str)" + ) + .assertMethod("getStr").assertMethodAnnotations().doesNotContainWithName("NotNull"); + if (!library.equals(SPRING_HTTP_INTERFACE)) { + // SPRING_HTTP_INTERFACE does not support @Schema generation (yet) + JavaFileAssert.assertThat(files.get("RequiredAndNullable.java")) + .fileContains( + "@Schema(name = \"str\", requiredMode = Schema.RequiredMode.REQUIRED, nullable = true)"); } JavaFileAssert.assertThat(files.get("FileContent.java")) .fileContains("VirusScanEnum getVirusScan()"); From c2003fac899bab712ed7ebe70d03614b692aee3e Mon Sep 17 00:00:00 2001 From: jpfinne Date: Fri, 21 Aug 2026 09:12:58 +0200 Subject: [PATCH 08/10] Better default vs !default --- .../nullableAnnotation_apiArgument.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache index dfb00beab5af..ee377a07c167 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/nullableAnnotation_apiArgument.mustache @@ -1 +1 @@ -{{#lambda.jSpecifyNullable}}{{#defaultValue}}@Nullable {{/defaultValue}}{{^required}}{{^defaultValue}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/defaultValue}}{{/required}}{{/lambda.jSpecifyNullable}} \ No newline at end of file +{{#lambda.jSpecifyNullable}}{{#defaultValue}}@Nullable {{/defaultValue}}{{^defaultValue}}{{^required}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/required}}{{/defaultValue}}{{/lambda.jSpecifyNullable}} \ No newline at end of file From 0923570f0998519e618d2ad091594928d9b48986 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Fri, 21 Aug 2026 09:13:20 +0200 Subject: [PATCH 09/10] Fix typo --- .../openapitools/codegen/java/spring/SpringCodegenTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 91998a5d7dc1..a1524b7080da 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -7184,17 +7184,17 @@ public void testJspecify(String library, int springBootVersion) throws IOExcepti "javax.annotation.Nullable", "jakarta.annotation.Nullable") .assertMethod("getRequiredDt").assertMethodAnnotations().containsWithName("NotNull").containsWithName("Valid"); - JavaFileAssert fooApiAsset = JavaFileAssert.assertThat(files.get("FooApi.java")) + JavaFileAssert fooApiAssert = JavaFileAssert.assertThat(files.get("FooApi.java")) .assertTypeAnnotations().doesImportAnnotation("org.jspecify.annotations.Nullable").toType() .fileContains( "java.time.@Nullable Instant dtParam", "java.time.@Nullable Instant dtQuery", "java.time.@Nullable Instant dtCookie"); if (SPRING_HTTP_INTERFACE.equals(library)) { - fooApiAsset.fileContains( + fooApiAssert.fileContains( "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color"); } else { - fooApiAsset.fileContains( + fooApiAssert.fileContains( "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color"); } JavaFileAssert.assertThat(files.get("RequiredAndNullable.java")) From 4e821b1ff6a6dd72bebec0d777dd55fd4a5385b3 Mon Sep 17 00:00:00 2001 From: jpfinne Date: Fri, 21 Aug 2026 10:38:04 +0200 Subject: [PATCH 10/10] Force build --- .../openapitools/codegen/java/spring/SpringCodegenTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index a1524b7080da..0ae03030fb3c 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -7190,6 +7190,8 @@ public void testJspecify(String library, int springBootVersion) throws IOExcepti "java.time.@Nullable Instant dtParam", "java.time.@Nullable Instant dtQuery", "java.time.@Nullable Instant dtCookie"); + + // SPRING_HTTP_INTERFACE has other requirements for default in api arguments if (SPRING_HTTP_INTERFACE.equals(library)) { fooApiAssert.fileContains( "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") @Nullable String color"); @@ -7197,6 +7199,7 @@ public void testJspecify(String library, int springBootVersion) throws IOExcepti fooApiAssert.fileContains( "@RequestParam(value = \"color\", required = false, defaultValue = \"red\") String color"); } + JavaFileAssert.assertThat(files.get("RequiredAndNullable.java")) .fileContains( "private @Nullable String str = null;",