ktor-swagger-ui
ktor-swagger-ui copied to clipboard
Issues with generated schemas
Hey,
Thanks for the nice library. It was an easy setup!
This is more of a nice-to-have, but would be immensely helpful in my case.
I was able to configure the library in a Ktor project and the documentation is being properly generated from the response body types I supply.
The problem: I want to use the resulting JSON to generate an API client in the frontend using @openapitools/openapi-generator-cli. This was possible while I was manually editing the spec file, but now in the generated spec I found 2 issues with the resulting schemas (check comments in this snippet):
{
"components": {
"schemas": {
// === Map usage
"Map(DayOfWeek,String)": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"ScheduleData": {
"schedule": {
"$ref": "#/components/schemas/Map(DayOfWeek,String)"
}
}
},
// === Compelete import paths
"com.project.util.RecordsPage<com.project.schedule.ScheduleData>": {
"type": "object",
"properties": {
"meta": {
"$ref": "#/components/schemas/PageMeta"
},
"records": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ScheduleData"
}
}
}
}
}
}
These issues prevent the generation of API client with the following errors:
Exception in thread "main" org.openapitools.codegen.SpecValidationException: There were issues with the specification. The option can be disabled via validateSpec (Maven/Gradle) or --skip-validate-spec (CLI).
| Error count: 3, Warning count: 0
Errors:
-components.schemas.Schema name Map(DayOfWeek,String) doesn't adhere to regular expression ^[a-zA-Z0-9\.\-_]+$
-components.schemas.Schema name sit.adcommon.util.RecordsPage<sit.adcommon.unpackingTime.UnpackingTimeData> doesn't adhere to regular expression ^[a-zA-Z0-9\.\-_]+$
Can these be solved somehow? Maybe by providing an alias for the response type instead of having function calls and long imports?
Thanks, Mahmoud
Hi, Thank you!
it adds the raw/complete name (including the package path) when the type has a generic type. I don't remember the reason for this behaviour, but it should be possible to change. If not, i think the alias is a good idea - maybe even in general without the long name problem.
I'll look into it.
I have the same problem. Do you have any alternative solution? Or wait for a new release? @SMILEY4
Hi, I am currently a bit confused about the api code generator.
Assuming you could give every schema in the components section its own name/alias, how would the generator handle that?
For example you would create an alias ScheduleMapping for Map(DayOfWeek,String), what class/type would the generator create from that? How would it know its a map? Same with RecordsPage and the generic type?
Currently, it looks like automatically creating clean names isnt possible and even attacking aliases to everything is not that easy (e.g. afaik, the Map(DayOfWeek,String) is a nested field and the name comes from the schema-generation-library), so i'am trying to understand the problem better and how to solve it best.
Thank you
@SMILEY4 I made some test APIs to demonstrate how the file is generated and what is presented when validating via Swagger. Disregard the names of classes and methods. Just to demonstrate and facilitate understanding.
data class CfcResponse<T>(
val data: T? = null,
val message: String? = null
)
fun Route.test() {
route("test") {
post({
response {
HttpStatusCode.OK to {
description = ""
body<Map<String, LoginRequest>>()
}
}
}) { }
post({
response {
HttpStatusCode.OK to {
description = ""
body<Map<String, String>>()
}
}
}) { }
post({
response {
HttpStatusCode.OK to {
description = ""
body<CfcResponse<LoginRequest>>()
}
}
}) { }
}
}
YAML/JSON
openapi: 3.0.1
info:
title: TEST API
version: '1.0'
externalDocs:
url: /
servers:
- url: http://localhost:8080
description: Development server
tags: []
paths:
/v1/login:
post:
tags: []
parameters: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: token
headers: {}
content:
application/json:
schema:
$ref: '#/components/schemas/br.com.infiniteloop.domain.model.response.CfcResponse<kotlin.String>'
deprecated: false
/v1/user:
put:
tags: []
parameters: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UserUpdateRequest'
responses:
'204':
description: no content
headers: {}
deprecated: false
/v1/test:
post:
tags: []
parameters: []
responses:
'200':
description: ''
headers: {}
content:
application/json:
schema:
$ref: '#/components/schemas/br.com.infiniteloop.domain.model.response.CfcResponse<br.com.infiniteloop.domain.model.request.LoginRequest>'
deprecated: false
components:
schemas:
LoginRequest:
type: object
properties:
codeAccess:
type: string
pass:
type: string
br.com.infiniteloop.domain.model.response.CfcResponse<kotlin.String>:
type: object
properties:
data:
type: string
message:
type: string
UserUpdateRequest:
type: object
properties:
name:
type: string
pass:
type: string
ssap:
type: string
kotlin.collections.Map<kotlin.String, br.com.infiniteloop.domain.model.request.LoginRequest>:
type: object
additionalProperties:
$ref: '#/components/schemas/LoginRequest'
kotlin.collections.Map<kotlin.String, kotlin.String>:
type: object
additionalProperties:
type: string
br.com.infiniteloop.domain.model.response.CfcResponse<br.com.infiniteloop.domain.model.request.LoginRequest>:
type: object
properties:
data:
$ref: '#/components/schemas/LoginRequest'
message:
type: string
examples: {}
Swagger Validate
@ma7moudat as a temporary workaround you could use skipValidateSpec.set(true) in your openApiGenerate configuration (CLI --skip-validate-spec)
Hi, I am currently a bit confused about the api code generator.
Assuming you could give every schema in the components section its own name/alias, how would the generator handle that? For example you would create an alias
ScheduleMappingforMap(DayOfWeek,String), what class/type would the generator create from that? How would it know its a map? Same withRecordsPageand the generic type?Currently, it looks like automatically creating clean names isnt possible and even attacking aliases to everything is not that easy (e.g. afaik, the Map(DayOfWeek,String) is a nested field and the name comes from the schema-generation-library), so i'am trying to understand the problem better and how to solve it best.
Thank you
Hi @SMILEY4,
True, you're absolutely right. It's more complex than it seems on the first look, especially with my lack of Kotlin/Ktor experience.
Some gaps are hard to bridge between Kotlin/Ktor and Typescript (or any 2 programming languages in general), but I was hoping to find a way to turn Map(DayOfWeek,String) from Kotlin into something like Map<String, String> in Typescript somehow, but that means your library and the Open API spec has to be aware of both the source and target languages, which kinda beats the purpose of having a language-agnostic API spec. That's why I suggested the alias idea, but with your insight I'm not sure it's feasible either.
It's an interesting problem to tackle!
@ma7moudat as a temporary workaround you could use
skipValidateSpec.set(true)in your openApiGenerate configuration (CLI --skip-validate-spec)
Hi @Sterta
Not sure how that would work!
The problem isn't with generating the spec from the Kotlin code, rather when transforming the spec back into an a Typescript API client. And since I need to generate valid code, skipping validation doesn't seem like a workaround.
@SMILEY4 maybe I just need to implement some post-processing to handle these issues myself, seems like an issue that would be a little different for every project/developer. The library shouldn't have to handle a million edge cases IMO. However, if you find the inspiration for a solution, that would rock!
@ma7moudat how did you fix this in typescript. I am also facing same issue.