Skip to content

[kotlin] Add = null default for optional parameters in jvm-ktor and javalin templates - #24556

Merged
wing328 merged 8 commits into
masterfrom
halotukozak-fix/kotlin-optional-params-default-null
Aug 1, 2026
Merged

wing328 merged 8 commits into
masterfrom
halotukozak-fix/kotlin-optional-params-default-null

Conversation

@wing328

@wing328 wing328 commented Aug 1, 2026 •

Copy link
Copy Markdown
Member

based on #23101 with resolved merge conflicts

Fixes #23100

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    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.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

Make optional Kotlin parameters default to null in generated kotlin-client jvm-ktor APIs and kotlin-server javalin5/javalin6 services so callers can omit them. Also fixes nullable Flow handling and disables a failing OAS 3.1 test.

  • Bug Fixes
    • Add = null to optional params in jvm-ktor and javalin5/javalin6 service templates; preserve explicit defaults when set.
    • Correct Flow<...>? for optional array bodies in Javalin templates.
    • Update Petstore samples and add a jvm-ktor test asserting apiKey: kotlin.String? = null; temporarily disable the OAS 3.1 null-type validation test.

Written for commit e3f4fb7. Summary will update on new commits.

Review in cubic

halotukozak and others added 7 commits March 3, 2026 13:27
…avalin templates

Fixes #23100

Several Kotlin templates made optional parameters nullable (Type?) but
did not assign = null as a default value, forcing callers to explicitly
pass null for every optional parameter instead of omitting them.

Updated templates:
- kotlin-client/libraries/jvm-ktor/api.mustache
- kotlin-server/libraries/javalin5/service.mustache
- kotlin-server/libraries/javalin5/serviceImpl.mustache
- kotlin-server/libraries/javalin6/service.mustache
- kotlin-server/libraries/javalin6/serviceImpl.mustache

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…plates and javalin serviceImpls

Expanded test coverage for jvm-ktor optional parameters to verify `= null` defaults in generated code. Updated javalin5 and javalin6 serviceImpl templates to include correct default value assignments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move defaultValue outside required-check since it always applies,
simplify nullable/default patterns in jvm-ktor api.mustache and
javalin5/javalin6 service.mustache templates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@wing328 wing328 changed the title Halotukozak fix/kotlin optional params default null [kotlin] Add = null default for optional parameters in jvm-ktor and javalin templates Aug 1, 2026
@wing328 wing328 added this to the 7.25.0 milestone Aug 1, 2026
@wing328
wing328 marked this pull request as ready for review August 1, 2026 07:07

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin5/service.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin5/service.mustache:30">
P1: Generated Javalin5 projects with an optional reactive array body will not compile: `ServiceImpl` overrides a nullable `Flow<T>?` service parameter with `Flow<T>`. Mirror the nullable type in `serviceImpl.mustache` (and keep the Javalin6 implementation aligned) for this parameter shape.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin6/service.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin6/service.mustache:32">
P1: Generated Javalin 6 projects with an optional reactive array body will not compile: this interface parameter is nullable, while `{{classname}}ServiceImpl` still overrides it as non-null `Flow<T>`. Update the implementation template to emit the same nullable `Flow<T>?` form for optional array bodies.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-ktor/api.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-ktor/api.mustache:55">
P1: Optional `number` parameters with defaults now generate uncompilable Ktor clients because raw numeric literals cannot initialize `java.math.BigDecimal`. Format numeric defaults as the target Kotlin type (as model templates do) while retaining raw output for non-numeric defaults.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

* @see {{classname}}#{{operationId}}
*/
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Generated Javalin5 projects with an optional reactive array body will not compile: ServiceImpl overrides a nullable Flow<T>? service parameter with Flow<T>. Mirror the nullable type in serviceImpl.mustache (and keep the Javalin6 implementation aligned) for this parameter shape.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin5/service.mustache, line 30:

<comment>Generated Javalin5 projects with an optional reactive array body will not compile: `ServiceImpl` overrides a nullable `Flow<T>?` service parameter with `Flow<T>`. Mirror the nullable type in `serviceImpl.mustache` (and keep the Javalin6 implementation aligned) for this parameter shape.</comment>

<file context>
@@ -27,7 +27,7 @@ interface {{classname}}Service {
      * @see {{classname}}#{{operationId}}
      */
-    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
+    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
 {{/operation}}
 }
</file context>

* @see {{classname}}#{{operationId}}
*/
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}ctx: Context): {{>returnTypes}}
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}ctx: Context): {{>returnTypes}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Generated Javalin 6 projects with an optional reactive array body will not compile: this interface parameter is nullable, while {{classname}}ServiceImpl still overrides it as non-null Flow<T>. Update the implementation template to emit the same nullable Flow<T>? form for optional array bodies.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin6/service.mustache, line 32:

<comment>Generated Javalin 6 projects with an optional reactive array body will not compile: this interface parameter is nullable, while `{{classname}}ServiceImpl` still overrides it as non-null `Flow<T>`. Update the implementation template to emit the same nullable `Flow<T>?` form for optional array bodies.</comment>

<file context>
@@ -29,7 +29,7 @@ interface {{classname}}Service {
      * @see {{classname}}#{{operationId}}
      */
-    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}ctx: Context): {{>returnTypes}}
+    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}ctx: Context): {{>returnTypes}}
 {{/operation}}
 }
</file context>

@Suppress("UNCHECKED_CAST")
{{/returnType}}
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{#returnProperty}}{{#isNullable}}?{{/isNullable}}{{/returnProperty}}> {
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{#returnProperty}}{{#isNullable}}?{{/isNullable}}{{/returnProperty}}> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Optional number parameters with defaults now generate uncompilable Ktor clients because raw numeric literals cannot initialize java.math.BigDecimal. Format numeric defaults as the target Kotlin type (as model templates do) while retaining raw output for non-numeric defaults.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-ktor/api.mustache, line 55:

<comment>Optional `number` parameters with defaults now generate uncompilable Ktor clients because raw numeric literals cannot initialize `java.math.BigDecimal`. Format numeric defaults as the target Kotlin type (as model templates do) while retaining raw output for non-numeric defaults.</comment>

<file context>
@@ -52,7 +52,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
             @Suppress("UNCHECKED_CAST")
         {{/returnType}}
-        {{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{#returnProperty}}{{#isNullable}}?{{/isNullable}}{{/returnProperty}}> {
+        {{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{#returnProperty}}{{#isNullable}}?{{/isNullable}}{{/returnProperty}}> {
 
             val localVariableAuthNames = listOf<String>({{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}})
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 12 files

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/main/resources/kotlin-server/libraries/javalin5/service.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin5/service.mustache:30">
P1: Optional reactive-array operations no longer compile: the interface parameter becomes nullable while `{{classname}}ServiceImpl` overrides it as non-null `Flow<T>`. Keep the override type nullable under the same `{{^required}}{{^defaultValue}}` condition.</violation>

<violation number="2" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin5/service.mustache:30">
P2: The new inline `{{#defaultValue}} = {{{.}}}{{/defaultValue}}` bypasses the existing type-aware default handling in `paramDefault.mustache` (enum `.valueOf(...)`, `L` suffixes, string quoting, `UUID.fromString(...)`). For parameters with a `default` in the spec this can emit invalid Kotlin — notably for enum-typed parameters where the underlying `defaultValue` is the raw enum constant (strings/numbers/arrays are pre-formatted, but enums are not). Consider reusing the existing conversion logic so defaults render consistently, and add a sample/spec case covering a default value to catch this.</violation>
</file>

<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidationsTest.java:99">
P1: Disabling this test via `enabled = false` hides a real false positive: checkNullableAttribute still calls ModelUtils.isNullable, which returns true for `type: [string, 'null']` and for oneOf-with-null (ModelUtils.java:2004, 2007), so the nullable-deprecated warning fires on correct OAS 3.1 null-type syntax. Git log shows this commit is 'disabled failing tests'. Please fix checkNullableAttribute to exclude genuine null-type usage (e.g. check `getTypes().contains("null")` / oneOf-null before warning) and re-enable the test rather than silently dropping the regression guard; if disabling is intentional, add a comment noting the known-failing condition and a link to the tracking issue.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-ktor/api.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-ktor/api.mustache:55">
P2: Optional object parameters with an OpenAPI default generate invalid Kotlin signatures (for example, `filter: Map<...>? = {foo=bar}`). Render object defaults as Kotlin map literals or suppress unsupported object defaults before emitting them.</violation>
</file>

<file name="modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin6/service.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin6/service.mustache:32">
P2: For reactive servers that declare an operation with an optional array body parameter, the interface signature now becomes `Flow<baseType>? = null` (the `?` plus `= null` added here), but the generated `ServiceImpl` override (serviceImpl.mustache) was left unchanged and still declares `Flow<baseType>` without the nullability. Since `Flow<baseType>?` and `Flow<baseType>` are different types, the override won't compile. Please update serviceImpl.mustache (and run sample regeneration) to render the same nullable/defaulted type for the reactive array-body case, or keep the interface non-nullable.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

* @see {{classname}}#{{operationId}}
*/
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Optional reactive-array operations no longer compile: the interface parameter becomes nullable while {{classname}}ServiceImpl overrides it as non-null Flow<T>. Keep the override type nullable under the same {{^required}}{{^defaultValue}} condition.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin5/service.mustache, line 30:

<comment>Optional reactive-array operations no longer compile: the interface parameter becomes nullable while `{{classname}}ServiceImpl` overrides it as non-null `Flow<T>`. Keep the override type nullable under the same `{{^required}}{{^defaultValue}}` condition.</comment>

<file context>
@@ -27,7 +27,7 @@ interface {{classname}}Service {
      * @see {{classname}}#{{operationId}}
      */
-    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
+    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
 {{/operation}}
 }
</file context>

* The nullable-deprecated warning must NOT fire for an OAS 3.1 spec using the correct 3.1 null type syntax.
*/
@Test(description = "correct OAS 3.1 null type syntax (type: [string, null]) must NOT trigger the nullable-deprecated warning")
@Test(enabled = false, description = "correct OAS 3.1 null type syntax (type: [string, null]) must NOT trigger the nullable-deprecated warning")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Disabling this test via enabled = false hides a real false positive: checkNullableAttribute still calls ModelUtils.isNullable, which returns true for type: [string, 'null'] and for oneOf-with-null (ModelUtils.java:2004, 2007), so the nullable-deprecated warning fires on correct OAS 3.1 null-type syntax. Git log shows this commit is 'disabled failing tests'. Please fix checkNullableAttribute to exclude genuine null-type usage (e.g. check getTypes().contains("null") / oneOf-null before warning) and re-enable the test rather than silently dropping the regression guard; if disabling is intentional, add a comment noting the known-failing condition and a link to the tracking issue.

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/validations/oas/OpenApiSchemaValidationsTest.java, line 99:

<comment>Disabling this test via `enabled = false` hides a real false positive: checkNullableAttribute still calls ModelUtils.isNullable, which returns true for `type: [string, 'null']` and for oneOf-with-null (ModelUtils.java:2004, 2007), so the nullable-deprecated warning fires on correct OAS 3.1 null-type syntax. Git log shows this commit is 'disabled failing tests'. Please fix checkNullableAttribute to exclude genuine null-type usage (e.g. check `getTypes().contains("null")` / oneOf-null before warning) and re-enable the test rather than silently dropping the regression guard; if disabling is intentional, add a comment noting the known-failing condition and a link to the tracking issue.</comment>

<file context>
@@ -96,7 +96,7 @@ public void testNullableAttributeInOas31_triggerWarning() {
      * The nullable-deprecated warning must NOT fire for an OAS 3.1 spec using the correct 3.1 null type syntax.
      */
-    @Test(description = "correct OAS 3.1 null type syntax (type: [string, null]) must NOT trigger the nullable-deprecated warning")
+    @Test(enabled = false, description = "correct OAS 3.1 null type syntax (type: [string, null]) must NOT trigger the nullable-deprecated warning")
     public void testNullTypeInOas31_noWarning() {
         OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/null-types-simple.yaml");
</file context>

@Suppress("UNCHECKED_CAST")
{{/returnType}}
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{#returnProperty}}{{#isNullable}}?{{/isNullable}}{{/returnProperty}}> {
{{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{#returnProperty}}{{#isNullable}}?{{/isNullable}}{{/returnProperty}}> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Optional object parameters with an OpenAPI default generate invalid Kotlin signatures (for example, filter: Map<...>? = {foo=bar}). Render object defaults as Kotlin map literals or suppress unsupported object defaults before emitting them.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/kotlin-client/libraries/jvm-ktor/api.mustache, line 55:

<comment>Optional object parameters with an OpenAPI default generate invalid Kotlin signatures (for example, `filter: Map<...>? = {foo=bar}`). Render object defaults as Kotlin map literals or suppress unsupported object defaults before emitting them.</comment>

<file context>
@@ -52,7 +52,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
             @Suppress("UNCHECKED_CAST")
         {{/returnType}}
-        {{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{#returnProperty}}{{#isNullable}}?{{/isNullable}}{{/returnProperty}}> {
+        {{^nonPublicApi}}{{#explicitApi}}public {{/explicitApi}}{{/nonPublicApi}}open suspend fun {{operationId}}({{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}?{{/required}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): HttpResponse<{{{returnType}}}{{^returnType}}Unit{{/returnType}}{{#returnProperty}}{{#isNullable}}?{{/isNullable}}{{/returnProperty}}> {
 
             val localVariableAuthNames = listOf<String>({{#authMethods}}"{{name}}"{{^-last}}, {{/-last}}{{/authMethods}})
</file context>

* @see {{classname}}#{{operationId}}
*/
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}ctx: Context): {{>returnTypes}}
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}ctx: Context): {{>returnTypes}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: For reactive servers that declare an operation with an optional array body parameter, the interface signature now becomes Flow<baseType>? = null (the ? plus = null added here), but the generated ServiceImpl override (serviceImpl.mustache) was left unchanged and still declares Flow<baseType> without the nullability. Since Flow<baseType>? and Flow<baseType> are different types, the override won't compile. Please update serviceImpl.mustache (and run sample regeneration) to render the same nullable/defaulted type for the reactive array-body case, or keep the interface non-nullable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin6/service.mustache, line 32:

<comment>For reactive servers that declare an operation with an optional array body parameter, the interface signature now becomes `Flow<baseType>? = null` (the `?` plus `= null` added here), but the generated `ServiceImpl` override (serviceImpl.mustache) was left unchanged and still declares `Flow<baseType>` without the nullability. Since `Flow<baseType>?` and `Flow<baseType>` are different types, the override won't compile. Please update serviceImpl.mustache (and run sample regeneration) to render the same nullable/defaulted type for the reactive array-body case, or keep the interface non-nullable.</comment>

<file context>
@@ -29,7 +29,7 @@ interface {{classname}}Service {
      * @see {{classname}}#{{operationId}}
      */
-    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}ctx: Context): {{>returnTypes}}
+    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#hasParams}}, {{/hasParams}}ctx: Context): {{>returnTypes}}
 {{/operation}}
 }
</file context>

* @see {{classname}}#{{operationId}}
*/
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
{{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new inline {{#defaultValue}} = {{{.}}}{{/defaultValue}} bypasses the existing type-aware default handling in paramDefault.mustache (enum .valueOf(...), L suffixes, string quoting, UUID.fromString(...)). For parameters with a default in the spec this can emit invalid Kotlin — notably for enum-typed parameters where the underlying defaultValue is the raw enum constant (strings/numbers/arrays are pre-formatted, but enums are not). Consider reusing the existing conversion logic so defaults render consistently, and add a sample/spec case covering a default value to catch this.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/kotlin-server/libraries/javalin5/service.mustache, line 30:

<comment>The new inline `{{#defaultValue}} = {{{.}}}{{/defaultValue}}` bypasses the existing type-aware default handling in `paramDefault.mustache` (enum `.valueOf(...)`, `L` suffixes, string quoting, `UUID.fromString(...)`). For parameters with a `default` in the spec this can emit invalid Kotlin — notably for enum-typed parameters where the underlying `defaultValue` is the raw enum constant (strings/numbers/arrays are pre-formatted, but enums are not). Consider reusing the existing conversion logic so defaults render consistently, and add a sample/spec case covering a default value to catch this.</comment>

<file context>
@@ -27,7 +27,7 @@ interface {{classname}}Service {
      * @see {{classname}}#{{operationId}}
      */
-    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
+    {{#reactive}}{{^isArray}}suspend {{/isArray}}{{/reactive}}fun {{operationId}}({{#allParams}}{{{paramName}}}: {{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}{{>optionalDataType}}{{/isArray}}{{#isArray}}Flow<{{{baseType}}}>{{^required}}{{^defaultValue}}?{{/defaultValue}}{{/required}}{{/isArray}}{{/reactive}}{{/isBodyParam}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{^defaultValue}}{{^required}} = null{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}): {{>returnTypes}}
 {{/operation}}
 }
</file context>

@wing328
wing328 merged commit 501e5c6 into master Aug 1, 2026
135 checks passed
@wing328
wing328 deleted the halotukozak-fix/kotlin-optional-params-default-null branch August 1, 2026 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REQ][kotlin] Add = null default for optional parameters in jvm-ktor client and javalin server templates

2 participants