[kotlin][jvm-spring-restclient] honour nullableReturnType in body access#23613
Open
takkiraz wants to merge 3 commits intoOpenAPITools:masterfrom
Open
[kotlin][jvm-spring-restclient] honour nullableReturnType in body access#23613takkiraz wants to merge 3 commits intoOpenAPITools:masterfrom
takkiraz wants to merge 3 commits intoOpenAPITools:masterfrom
Conversation
When `nullableReturnType=true` is set, the generated API function signature correctly returns a nullable type (e.g. `String?`), but the implementation force-unwraps the response body with `!!`. This throws a NullPointerException at runtime whenever the server returns an empty body, even though the signature promises that null is a valid return value. This change: * Drops the `!!` in `api.mustache` when `nullableReturnType` is enabled. * Propagates nullability into the generic type parameter of the `WithHttpInfo` variant for consistency. * Relaxes the type parameter in `ApiClient.kt.mustache` from `T: Any` to `T: Any?` so that `ResponseEntity<T?>` compiles. * Adds a new sample `kotlin-jvm-spring-3-restclient-nullable-return` that exercises `nullableReturnType=true`, including a regression test that reproduces the original NPE when the fix is reverted.
2141bc4 to
3504c25
Compare
Contributor
There was a problem hiding this comment.
2 issues found across 31 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=".github/workflows/samples-kotlin-client.yaml">
<violation number="1" location=".github/workflows/samples-kotlin-client.yaml:69">
P2: New Kotlin sample was added to the CI matrix without adding a matching workflow trigger path, so changes in that sample directory can bypass this CI workflow.</violation>
</file>
<file name="samples/client/others/kotlin-jvm-spring-3-restclient-nullable-return/docs/DefaultApi.md">
<violation number="1" location="samples/client/others/kotlin-jvm-spring-3-restclient-nullable-return/docs/DefaultApi.md:53">
P2: `returnNullableString` is documented as non-null in signature/return type, but nullable in the example, creating an API contract mismatch.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…in api_doc template - Add workflow trigger path for kotlin-jvm-spring-3-restclient-nullable-return sample so changes in that directory actually run CI. - Propagate nullableReturnType into the signature and return-type sections of api_doc.mustache (previously only the example code showed the '?'). - Regenerate DefaultApi.md for the new sample to reflect the fix.
Member
|
please follow step 3 to update the samples so as to fix https://github.com/OpenAPITools/openapi-generator/actions/runs/24888307974/job/72874225141?pr=23613 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #23612
Problem
When using the
kotlingenerator withlibrary=jvm-spring-restclientandnullableReturnType=true, the generated API function signature correctly declares a nullable return type (e.g.String?), but the implementation force-unwraps the response body with!!. This causes aNullPointerExceptionat runtime whenever the server returns an empty body, even though the signature promises thatnullis a valid return value.Minimal reproducer:
Generated with
--additional-properties=nullableReturnType=true,useSpringBoot3=true,serializationLibrary=jackson:The contract says "can return null", the implementation says "will crash on null". These are inconsistent.
Fix
Three template changes that together honour
nullableReturnType:api.mustache— drop the!!when the return type is nullable:api.mustache— propagate nullability into the generic type of theWithHttpInfovariant for consistency:infrastructure/ApiClient.kt.mustache— relax the generic bound fromT: AnytoT: Any?so thatResponseEntity<T?>compiles:(Plus the corresponding adjustments on
prepare()andnullableBody()and a cast on the body value to keepRestClient.body()happy.)When
nullableReturnType=false(the default), the generated code is unchanged.Test coverage
A new sample
kotlin-jvm-spring-3-restclient-nullable-returnhas been added that exercisesnullableReturnType=true. It includes a regression test (NullableReturnTypeTest) that uses Spring'sMockServerRestClientCustomizerto assert:null(the case that previously threw NPE),Reverting either template change before running the sample's tests reproduces the original NPE, confirming the fix is exercised.
bin/utils/test_file_list.yamland.github/workflows/samples-kotlin-client.yamlhave been updated so the new sample builds and runs in CI.Compatibility
Generated API consumers
nullableReturnType=false(default) the generated API classes are identical; no source- or binary-level change visible.nullableReturnType=truethe generated API classes now correctly handle null response bodies instead of throwing NPE. Any code that relied on the broken!!behaviour was already crashing in the empty-body edge case.ApiClientgeneric bound relaxationThe
ApiClient.request<I, T>()function isprotectedandinline. Its generic bound was relaxed fromT : AnytoT : Any?. This is a strictly widening type change:T : Any(String,Pet,Unit, …) continues to satisfyT : Any?— all existing call-sites in generated code and samples compile unchanged.ApiClientdirectly) and overriderequest()with an explicitT : Anybound. This is an exceptionally uncommon pattern since the function is an internal dispatch helper, not a public API boundary.The same approach (relaxing generic bounds to allow
T : Any?) is already used elsewhere in the Kotlin client templates where nullability matters, so this keeps thejvm-spring-restclientlibrary consistent with the ecosystem.cc @karismann @Zomzog @andrewemery @4brunu @yutaka0m @stefankoppier @e5l @dennisameling (Kotlin technical committee)
PR checklist
./mvnw clean packageon the relevant modules and./bin/generate-samples.shfor the affected configs. All touched samples compile; the new sample's tests pass.master(fix is additive with fallback vianullableReturnTypebeing opt-in).Summary by cubic
Fixes null handling for
nullableReturnType=truein thekotlinjvm-spring-restclientgenerator by returning nullable bodies instead of force-unwrapping, preventing NPEs on empty responses. Also updates generated API docs and CI to reflect nullable return types.Bug Fixes
api.mustache, returnresult.bodywhennullableReturnType=true; keep!!otherwise.api.mustacheWithHttpInfo, propagate?toResponseEntitygeneric (e.g.,ResponseEntity<String?>).infrastructure/ApiClient.kt.mustache, relax generics toI: Any?andT: Any?, updateprepare/nullableBody, and cast body toAnyforRestClient.body().api_doc.mustache, add?in the method signature header and “Return type” whennullableReturnType=true.kotlin-jvm-spring-3-restclient-nullable-returnwith a regression test; added CI trigger paths so changes to this sample run in CI.Migration
Written for commit ec8c734. Summary will update on new commits.