swagger-api / swagger-api/swagger-codegen
[Kotlin] Empty POST body causes KotlinNullPointerException
Nobody has claimed this yet.
- Dominant language
- Mustache
- Stars
- 17.8k
- Forks
- 6k
- PR merge metrics
- No merged PRs in 30d
Description
Description
Swagger-codegen version
2.2.3
Swagger declaration file content or url
https://gist.github.com/NikitoZZZZZ/7d3340b6668a269ad8e8dfe2189abc8e
Command line used for generation
https://gist.github.com/NikitoZZZZZ/6303480bffdfa670db124878bc9e1633
Steps to reproduce
Generated method from my swagger api doc looks like this:
@Suppress("UNCHECKED_CAST")
fun cancelNotificationUsingPOST(id: kotlin.String) : NotificationResponse {
val localVariableBody: kotlin.Any? = null
val localVariableQuery: MultiValueMap = mapOf()
val localVariableHeaders: kotlin.collections.Map<kotlin.String,kotlin.String> = mapOf()
val localVariableConfig = RequestConfig(
RequestMethod.POST,
"/api/v1/notification/{id}/cancel".replace("{"+"id"+"}", "$id"),
query = localVariableQuery,
headers = localVariableHeaders
)
val response = request<NotificationResponse>(
localVariableConfig,
localVariableBody
)
return when (response.responseType) {
ResponseType.Success -> (response as Success<*>).data as NotificationResponse
ResponseType.Informational -> TODO()
ResponseType.Redirection -> TODO()
ResponseType.ClientError -> throw ClientException((response as ClientError<*>).body as? String ?: "Client error")
ResponseType.ServerError -> throw ServerException((response as ServerError<*>).message ?: "Server error")
else -> throw kotlin.IllegalStateException("Undefined ResponseType.")
}
}
Generated ApiClient class has method request that looks like this:
inline protected fun <reified T: Any?> request(requestConfig: RequestConfig, body : Any? = null): ApiInfrastructureResponse<T?> {
val httpUrl = HttpUrl.parse(baseUrl) ?: throw IllegalStateException("baseUrl is invalid.")
var urlBuilder = httpUrl.newBuilder()
.addPathSegments(requestConfig.path.trimStart('/'))
requestConfig.query.forEach { k, v ->
v.forEach { queryValue ->
urlBuilder = urlBuilder.addQueryParameter(k,queryValue)
}
}
val url = urlBuilder.build()
val headers = requestConfig.headers + defaultHeaders
if(headers[ContentType] ?: "" == "") {
throw kotlin.IllegalStateException("Missing Content-Type header. This is required.")
}
if(headers[Accept] ?: "" == "") {
throw kotlin.IllegalStateException("Missing Accept header. This is required.")
}
// TODO: support multiple contentType,accept options here.
val contentType = (headers[ContentType] as String).substringBefore(";").toLowerCase()
val accept = (headers[Accept] as String).substringBefore(";").toLowerCase()
var request : Request.Builder = when (requestConfig.method) {
RequestMethod.DELETE -> Request.Builder().url(url).delete()
RequestMethod.GET -> Request.Builder().url(url)
RequestMethod.HEAD -> Request.Builder().url(url).head()
RequestMethod.PATCH -> Request.Builder().url(url).patch(requestBody(body!!, contentType))
RequestMethod.PUT -> Request.Builder().url(url).put(requestBody(body!!, contentType))
RequestMethod.POST -> Request.Builder().url(url).post(requestBody(body!!, contentType))
RequestMethod.OPTIONS -> Request.Builder().url(url).method("OPTIONS", null)
}
headers.forEach { header -> request = request.addHeader(header.key, header.value) }
val realRequest = request.build()
val response = client.newCall(realRequest).execute()
// TODO: handle specific mapping types. e.g. Map<int, Class<?>>
when {
response.isRedirect -> return Redirection(
response.code(),
response.headers().toMultimap()
)
response.isInformational -> return Informational(
response.message(),
response.code(),
response.headers().toMultimap()
)
response.isSuccessful -> return Success(
responseBody(response.body(), accept),
response.code(),
response.headers().toMultimap()
)
response.isClientError -> return ClientError(
response.body()?.string(),
response.code(),
response.headers().toMultimap()
)
else -> return ServerError(
null,
response.body()?.string(),
response.code(),
response.headers().toMultimap()
)
}
}
As you can see variable localVariableBody is set to null in method cancelNotificationUsingPOST because POST method doesn't have a body. So I get KotlinNullPointerException in method request in line RequestMethod.POST -> Request.Builder().url(url).post(requestBody(body!!, contentType)) since parameter body!! is null.
Suggest a fix/enhancement
Change POST body generation from null to empty string(""). WA for now is to set it to "" manually.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the generated cancelNotificationUsingPOST call from the linked command, then inspect the generated ApiClient.request entry point and its POST handling. Done means a POST operation without a declared body no longer throws KotlinNullPointerException and still produces a valid request.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- kotlin
- Domain
- api
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 38/100