From 77d74e7b46a7faa2d260c69af2d2c58d834708b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mu=C3=B1oz?= Date: Thu, 29 Oct 2026 12:06:47 +0100 Subject: [PATCH 1/3] Fix null defaults for Spring query parameters --- .../codegen/languages/SpringCodegen.java | 11 ++++ .../java/spring/SpringCodegenTest.java | 44 ++++++++++++++++ .../spring/optional-query-null-default.yaml | 52 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index cb32ed5a0266..2febb3917ddb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -17,6 +17,7 @@ package org.openapitools.codegen.languages; +import com.fasterxml.jackson.databind.JsonNode; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.MethodDeclaration; @@ -93,6 +94,16 @@ public class SpringCodegen extends AbstractJavaCodegen public static final String SKIP_DEFAULT_INTERFACE = "skipDefaultInterface"; public static final String GENERATE_CONSTRUCTOR_WITH_REQUIRED_ARGS = "generatedConstructorWithRequiredArgs"; + @Override + public String toDefaultParameterValue(Schema schema) { + Schema effectiveSchema = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema) : schema; + Object defaultValue = effectiveSchema.getDefault(); + if (defaultValue instanceof JsonNode && ((JsonNode) defaultValue).isNull()) { + return null; + } + return super.toDefaultParameterValue(schema); + } + public static final String RESOURCE_FOLDER = "resourceFolder"; public static final String RESOURCE_FOLDER_DESC = "resource folder for generated resources"; 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 144bb6927b96..00ed30b4b99f 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 @@ -3029,6 +3029,50 @@ public void shouldIgnorePageableForSpringHttpInterface_issue24720() throws IOExc .doesNotHaveParameter("pageable"); } + @Test + public void nullSchemaDefaultIsNotRenderedAsSpringRequestParamDefault() throws IOException { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(INTERFACE_ONLY, true); + additionalProperties.put(SKIP_DEFAULT_INTERFACE, true); + additionalProperties.put(USE_SPRING_BOOT4, true); + additionalProperties.put("useJackson3", true); + additionalProperties.put(USE_JSPECIFY, true); + additionalProperties.put(USE_DEDUCTION_FOR_ONE_OF_INTERFACES, true); + additionalProperties.put(AbstractJavaCodegen.CONTAINER_DEFAULT_TO_NULL, true); + additionalProperties.put(GENERATE_JSON_INCLUDE_ANNOTATIONS, true); + additionalProperties.put(PERFORM_BEANVALIDATION, true); + additionalProperties.put(AbstractJavaCodegen.OPENAPI_NULLABLE, false); + + Map files = generateFromContract( + "src/test/resources/3_1/spring/optional-query-null-default.yaml", SPRING_HTTP_INTERFACE, additionalProperties); + + JavaFileAssert api = JavaFileAssert.assertThat(files.get("PlacesApi.java")); + api.assertMethod("getPlaces") + .assertParameter("ids").hasType("List") + .assertParameterAnnotations() + .containsWithNameAndDoesNotContainAttributes("RequestParam", List.of("defaultValue")); + api.assertMethod("getPlaces") + .assertParameter("search") + .assertParameterAnnotations() + .containsWithNameAndDoesNotContainAttributes("RequestParam", List.of("defaultValue")); + api.assertMethod("getPlaces") + .assertParameter("limit") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"10\"")); + api.assertMethod("getPlaces") + .assertParameter("requiredIds") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("required", "true")); + api.assertMethod("getPlaces") + .assertParameter("defaultIds") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"a,b\"")); + api.assertMethod("getPlaces") + .assertParameter("literalNull") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"null\"")); + } + @Test public void explicitXSpringPaginatedFalseKeepsQueryParamsForSpringCloud_issue24720() throws IOException { // Regression #24720: x-spring-paginated: false on a spring-cloud operation must NOT be diff --git a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml new file mode 100644 index 000000000000..6637bf8b7a88 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml @@ -0,0 +1,52 @@ +openapi: 3.1.0 +info: + title: Optional query null default + version: 1.0.0 +paths: + /places: + get: + operationId: getPlaces + parameters: + - name: ids + in: query + required: false + schema: + type: array + items: + type: string + default: null + - name: search + in: query + required: false + schema: + type: string + - name: limit + in: query + required: false + schema: + type: integer + default: 10 + - name: requiredIds + in: query + required: true + schema: + type: array + items: + type: string + - name: defaultIds + in: query + required: false + schema: + type: array + items: + type: string + default: [a, b] + - name: literalNull + in: query + required: false + schema: + type: string + default: "null" + responses: + "200": + description: OK From 0702c2abe12701030c1f1cd3e1bae82c36d57902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mu=C3=B1oz?= Date: Thu, 29 Oct 2026 12:44:11 +0100 Subject: [PATCH 2/3] Preserve parameter defaults on schema references --- .../openapitools/codegen/languages/SpringCodegen.java | 2 +- .../codegen/java/spring/SpringCodegenTest.java | 4 ++++ .../3_1/spring/optional-query-null-default.yaml | 11 +++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index 2febb3917ddb..4621879cc917 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -97,7 +97,7 @@ public class SpringCodegen extends AbstractJavaCodegen @Override public String toDefaultParameterValue(Schema schema) { Schema effectiveSchema = schema.get$ref() != null ? ModelUtils.getReferencedSchema(openAPI, schema) : schema; - Object defaultValue = effectiveSchema.getDefault(); + Object defaultValue = schema.getDefault() != null ? schema.getDefault() : effectiveSchema.getDefault(); if (defaultValue instanceof JsonNode && ((JsonNode) defaultValue).isNull()) { return null; } 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 00ed30b4b99f..e2dcc196eaa6 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 @@ -3071,6 +3071,10 @@ public void nullSchemaDefaultIsNotRenderedAsSpringRequestParamDefault() throws I .assertParameter("literalNull") .assertParameterAnnotations() .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"null\"")); + api.assertMethod("getPlaces") + .assertParameter("referencedLiteralNull") + .assertParameterAnnotations() + .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"null\"")); } @Test diff --git a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml index 6637bf8b7a88..f773d1705401 100644 --- a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml +++ b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml @@ -47,6 +47,17 @@ paths: schema: type: string default: "null" + - name: referencedLiteralNull + in: query + required: false + schema: + $ref: '#/components/schemas/stringWithNullDefault' + default: "null" responses: "200": description: OK +components: + schemas: + stringWithNullDefault: + type: string + default: null From f9c19a1055f483978a2aad1700d6fd3f22e69652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mu=C3=B1oz?= Date: Thu, 29 Oct 2026 13:03:21 +0100 Subject: [PATCH 3/3] Cover referenced null defaults without overrides --- .../openapitools/codegen/java/spring/SpringCodegenTest.java | 4 ++++ .../resources/3_1/spring/optional-query-null-default.yaml | 5 +++++ 2 files changed, 9 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 e2dcc196eaa6..398850ed9b5b 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 @@ -3075,6 +3075,10 @@ public void nullSchemaDefaultIsNotRenderedAsSpringRequestParamDefault() throws I .assertParameter("referencedLiteralNull") .assertParameterAnnotations() .containsWithNameAndAttributes("RequestParam", ImmutableMap.of("defaultValue", "\"null\"")); + api.assertMethod("getPlaces") + .assertParameter("referencedNullDefault") + .assertParameterAnnotations() + .containsWithNameAndDoesNotContainAttributes("RequestParam", List.of("defaultValue")); } @Test diff --git a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml index f773d1705401..7fd268572cf2 100644 --- a/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml +++ b/modules/openapi-generator/src/test/resources/3_1/spring/optional-query-null-default.yaml @@ -53,6 +53,11 @@ paths: schema: $ref: '#/components/schemas/stringWithNullDefault' default: "null" + - name: referencedNullDefault + in: query + required: false + schema: + $ref: '#/components/schemas/stringWithNullDefault' responses: "200": description: OK