OpenAPITools / OpenAPITools/openapi-generator
[BUG] [Kotlin] [Kotlin-server] [KTOR] Kotlin server does not generate a REST api
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator (example)?
- Have you tested with the latest master to confirm the issue still exists?
- Have you searched for related issues/PRs?
- What's the actual output vs expected output?
- [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description
Paths.kt
When the kotlin-server generator generates ktor code, it creates Resources as described in the ktor documentation, under "Type Safe Routing", seen here in the following format:
/**
* Updated user
* This can only be done by the logged in user.
* @param username name that need to be deleted
* @param body Updated user object
*/
@Serializable @Resource("/user/{username}") class updateUser(val username: kotlin.String, val body: User)
One could easily assume that the body parameter here represents the expected body of the request, however all parameters inside a @Resource class are url parameters. It is therefore impossible to trigger this resource, and the generated code makes no sense, even though it compiles and runs properly.
val body: User should not be a part of the params for a @Resource. Only url parameters should be.
In the following example, a part of the url is reflected in the url parameters:
/**
* Delete purchase order by ID
* For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
* @param orderId ID of the order that needs to be deleted
*/
@Serializable @Resource("/store/order/{orderId}") class deleteOrder(val orderId: kotlin.String)
This is also incorrect, according to the doc, as orderId is not a url param. It should be fetched inside the path itself, like this:
delete<Paths.deleteOrder> {
val orderId = call.parameters['orderId'] // orderId from url segment
call.respond(HttpStatusCode.NotImplemented)
}
UserApi.kt
get<Paths.getUserByName> {
val exampleContentType = "application/json"
val exampleContentString = """{
"firstName" : "firstName",
"lastName" : "lastName",
"password" : "password",
"userStatus" : 6,
"phone" : "phone",
"id" : 0,
"email" : "email",
"username" : "username"
}"""
when (exampleContentType) {
"application/json" -> call.respond(gson.fromJson(exampleContentString, empty::class.java))
"application/xml" -> call.respondText(exampleContentString, ContentType.Text.Xml)
else -> call.respondText(exampleContentString)
}
}
This code makes no sense to me. There is a string representing a json. in the first case, we deserialize this string into a generic Gson json, and respond with that, which is then, presumably, immediately serialized again. In the second case, we respond with the same raw string, which is still a json, but we set the content type to xml. I have to assume this is left over from an older variant, but in my opinion it could be removed. I think the generated code should look like this:
get<Paths.getUserByName> {
val username = call.parameters["username"]
val exampleContent = User(
firstName = "firstName",
lastName = "lastName",
password = "password",
userStatus = 6,
phone = "phone",
id = 0,
email = "email",
username = "username"
)
call.respond(exampleContent)
}
For completeness, you should get the body from an incoming request like this:
post<Paths.createUser> {
val user = call.receive<User>()
call.respond(HttpStatusCode.NotImplemented)
}
Note also that @Resources do not need to be serialized, and don't need the @Serializable decorator. They are only used inside the ktor app and don't need to be serialized and sent anywhere.
classpath
The classpath provided in the generated gradle file is the wrong classpath and the jar file does not run.
openapi-generator version
7.1.0
OpenAPI declaration file content or url
This goes for all generation files that use any kind of REST, as far as I can tell, and probably others
Generation Details
$ ./bin/generate-samples.sh bin/configs/kotlin-server-ktor.yaml
Steps to reproduce
Generate and look at the generated files.
Alternatively, to see the classname error, generate and run:
$ java -jar build/libs/kotlin-server.jar
Suggest a fix
See description for what I believe the generated code should look like.
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
Regenerate the sample with ./bin/generate-samples.sh bin/configs/kotlin-server-ktor.yaml, then inspect Paths.kt, UserApi.kt, and the generated Gradle file. Compare the generated routing, request bodies, responses, and classpath with the Ktor examples in the issue. Done means the generated REST server runs and its routes and request/response handling match the documented behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- kotlin
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100