papsign / Ktor-OpenAPI-Generator

Ktor OpenAPI/Swagger 3 Generator
Apache License 2.0
241 stars 42 forks source link

Sets not handled properly #40

Closed uuf6429 closed 4 years ago

uuf6429 commented 4 years ago

If I have a response class with a List<SomeEnum> field, the generated entity looks like:

TheEntity:
  type: object
  properties:
    test:
      type: array
      items:
        $ref: "#/components/schemas/TheEnum"

TheEnum:
  enum: ["A","B","C"]
  nullable: false
  type: string

That looks pretty good, but if instead I had Set<SomeEnum>, I get:

TheEntity:
  type: object
  properties:
    test:
      $ref: "#/components/schemas/TheEnum"

Set_TheEnum_:
  type: object
  properties:
    size:
      type: integer
      format: int32
      nullable: false
      minimum: -2147483648
      maximum: 2147483647
  required: ["size"]
  nullable: false

I think the expected schema should have looked like:

TheEntity:
  type: object
  properties:
    test:
      type: array
      items:
        $ref: "#/components/schemas/TheEnum"
      uniqueItems: true               # <----- very important for sets

TheEnum:
  enum: ["A","B","C"]
  nullable: false
  type: string

In the source code, I saw a few places with clazz.isSubclassOf(List::class), not sure but I'm thinking the fix involves changing those places.

Wicpar commented 4 years ago

I reworked the Type Schema builder system for exactly these cases, so in theory you could have simply added a module defined like this:

object DefaultSetSchemaProvider: SchemaBuilderProviderModule, OpenAPIGenModuleExtension, DefaultOpenAPIModule {

    private val builders = listOf(
        Builder(getKType<Set<*>?>()) { type: KType ->
            type.arguments[0].type ?: error("bad type $type: star projected types are not supported")
        }
    )

    override fun provide(apiGen: OpenAPIGen, provider: ModuleProvider<*>): List<SchemaBuilder> {
        return builders
    }

    private data class Builder(override val superType: KType, private val getter: (KType) -> KType) :
        SchemaBuilder {
        override fun build(type: KType, builder: FinalSchemaBuilder, finalize: (SchemaModel<*>)->SchemaModel<*>): SchemaModel<*> {
            checkType(type)
            return finalize(SchemaModel.SchemaModelArr<Any?>(builder.build(getter(type)), type.isMarkedNullable, uniqueItems = true))
        }
    }
}

But the array model type didn't have the uniqueItems property so it would have been futile. I made the appropriate changes. I also merged the experimental branch, you can get the changes in the last release.

uuf6429 commented 4 years ago

Awesome, that was very fast! Many thanks!