bkbnio / kompendium

Ktor OpenAPI Spec Generator
https://bkbn.gitbook.io/kompendium
MIT License
148 stars 22 forks source link

Top level unstructured data is not supported #217

Closed MarcelBochtler closed 2 years ago

MarcelBochtler commented 2 years ago

Describe the bug When trying to document an Endpoint that responds a plain JsonElement, I'm no longer to start my application. It crashes with this exception:

[main] INFO ktor.application - Autoreload is disabled because the development mode is off.
Exception in thread "main" java.lang.IllegalStateException: Unable to parse field type from val kotlinx.serialization.json.JsonPrimitive.content: kotlin.String
    at io.bkbn.kompendium.core.handler.ObjectHandler.handleDefault(ObjectHandler.kt:88)
    at io.bkbn.kompendium.core.handler.ObjectHandler.generateFieldMap(ObjectHandler.kt:70)
    at io.bkbn.kompendium.core.handler.ObjectHandler.handle(ObjectHandler.kt:50)
    at io.bkbn.kompendium.core.Kontent$generateKTypeKontent$2.invoke(Kontent.kt:96)
    at io.bkbn.kompendium.core.util.Helpers.logged(Helpers.kt:26)
    at io.bkbn.kompendium.core.Kontent.generateKTypeKontent(Kontent.kt:78)
    at io.bkbn.kompendium.core.Kontent.generateKontent(Kontent.kt:55)
    at io.bkbn.kompendium.core.KompendiumPreFlight.addToCache(KompendiumPreFlight.kt:38)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt$mainModule$3$5.invoke(BasicPlayground.kt:225)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt$mainModule$3$5.invoke(BasicPlayground.kt:106)
    at io.ktor.routing.RoutingBuilderKt.route(RoutingBuilder.kt:18)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt$mainModule$3.invoke(BasicPlayground.kt:106)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt$mainModule$3.invoke(BasicPlayground.kt:76)
    at io.ktor.routing.RoutingKt.routing(Routing.kt:129)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt.mainModule(BasicPlayground.kt:76)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt.access$mainModule(BasicPlayground.kt:1)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt$main$1.invoke(BasicPlayground.kt:57)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt$main$1.invoke(BasicPlayground.kt:57)
    at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$instantiateAndConfigureApplication$1.invoke(ApplicationEngineEnvironmentReloading.kt:321)
    at io.ktor.server.engine.ApplicationEngineEnvironmentReloading$instantiateAndConfigureApplication$1.invoke(ApplicationEngineEnvironmentReloading.kt:310)
    at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.avoidingDoubleStartup(ApplicationEngineEnvironmentReloading.kt:338)
    at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.instantiateAndConfigureApplication(ApplicationEngineEnvironmentReloading.kt:310)
    at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.createApplication(ApplicationEngineEnvironmentReloading.kt:143)
    at io.ktor.server.engine.ApplicationEngineEnvironmentReloading.start(ApplicationEngineEnvironmentReloading.kt:277)
    at io.ktor.server.netty.NettyApplicationEngine.start(NettyApplicationEngine.kt:174)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt.main(BasicPlayground.kt:58)
    at io.bkbn.kompendium.playground.BasicPlaygroundKt.main(BasicPlayground.kt)

To Reproduce See: https://github.com/MarcelBochtler/kompendium/commit/045e40af73425d599ec37b4d50a20f6cb1ab8c67

Expected behavior The application is able to start and displays the serialized JsonElement in the OpenApi docs.

brizzbuzz commented 2 years ago

Would the intent to be to take in unstructured data?

brizzbuzz commented 2 years ago

Assuming that is the case, you should be able to annotate the object with @FreeFormObject like so

data class FreeFormData(
  @FreeFormObject
  val data: JsonElement
)
MarcelBochtler commented 2 years ago

Assuming that is the case, you should be able to annotate the object with @FreeFormObject like so

data class FreeFormData(
  @FreeFormObject
  val data: JsonElement
)

Using that application is able to start up, and to create a valid openapi.json. But the resulting example Response is wrong:

{
  "data": {
    ...
  }
}

While the endpoint just returns the plain JsonObject:

{
  ...
}
brizzbuzz commented 2 years ago

oh i see, so you want the complete payload to be totally unstructured? I don't believe that is supported. Likely, this will require allowing the FreeFormObject annotation to be applied to classes along with properties, as well as performing a top level check when doing the core reflection to look for that annotation and short circuit if found.

So, you would be able to declare something like

@FreeFormObject
object AnythingGoesMan
MarcelBochtler commented 1 year ago

@unredundant was this functionality removed / changed with version 3? Unfortunately, I'm no longer able to generate the OpenAPI spec with an object using JsonElement as a value. @FreeFormObject no longer exists and I cannot find a substitute for it.

brizzbuzz commented 1 year ago

Oh yea it was... all annotations were removed in V3. I wasn't a fan of having to have to use annotations in order to get Kompendium to work (though I may still need to add them back to handle things like property constraints)

I think the best way to go here is to define a custom type https://bkbn.gitbook.io/kompendium/index-2/notarized_application#custom-types to represent the object you are trying to pass. You'll need to hand roll the JSON schema using the json schema interface, but that shouldn't be too bad for a totally free form object.

Could you give that a go and if it's too arduous, feel free to open a new issue and we will think through a way to make the experience nicer :)