kompendium icon indicating copy to clipboard operation
kompendium copied to clipboard

Add Http Methode typed NotarizedResource

Open ezienecker opened this issue 9 months ago • 0 comments

Is your feature request related to a problem? Please describe.

Ktor offers several ways to organize the typed safe routes. We favor the approach where you have a separate method for each route. For us it offers the most flexibility to group these by paths:

routing {
  route("/api") {
    listUserRoute()

    getUserRoute()
    getUserRolesRoute()
    assignUserRolesRoute()
  }

  route("/system") {
    createUserRoute()
    createUserRoleRoute()
  }
}

This has the consequence that we also implement the documentation per route method. If we now implement the documentation for the List User endpoint and the Create User endpoint:

fun Route.createUserRoute() {
  createUserDocumentation()
  ...
}

private fun Route.createUserDocumentation() {
  install(NotarizedResource<User>()) {
  }
}

fun Route.listUserRoute() {
  listUserDocumentation()
  ...
}

private fun Route.listUserDocumentation() {
  install(NotarizedResource<User>()) {
  }
}

we will get the following error:

io.ktor.server.application.DuplicatePluginException: Please make sure that you use unique name for the plugin and don't install it twice. Plugin `io.bkbn.kompendium.resources.NotarizedResource@aafcffa<io.bkbn.kompendium.playground.User>` is already installed to the pipeline /

This makes sense because both use the User resource. One route with the Get method and the other with the Post method.

Describe the solution you'd like

An analysis has shown that the problem comes from the way the plugin is installed (NotarizedResource.kt):

  inline operator fun <reified T> invoke() = createRouteScopedPlugin(
    name = "NotarizedResource<${T::class.qualifiedName}>",
    createConfiguration = NotarizedRoute::Config
  ) {
  ...
}

One way to solve this problem would be if the Http methods could be found in io.bkbn.kompendium.resources.NotarizedResource. This means that in future there will be the following NotarizedResource:

  • NotarizedResource (for backward compatibility reasons)
  • NotarizedGetResource
  • NotarizedPostResource
  • NotarizedPutResource
  • NotarizedDeleteResource
  • NotarizedHeadResource
  • NotarizedPatchResource
  • NotarizedOptionsResource

The current class NotarizedResource is renamed to NotarizedMethodResource and becomes an abstract class. All the NotarizedResource mentioned above inherit from this class and are the new objects. The invoke method is also adapted accordingly:

  inline operator fun <reified T> invoke() = createRouteScopedPlugin(
    name = "$this<${T::class.qualifiedName}>",
    createConfiguration = NotarizedRoute::Config
  ) {
  ...
}

This means that the documentation will be even more strongly typified in future because the Http method is used as a plug-in name in addition to the resource.

The new implementation will look like this:

fun Route.createUserRoute() {
  createUserDocumentation()
  ...
}

private fun Route.createUserDocumentation() {
  install(NotarizedPostResource<User>()) {
  }
}

fun Route.listUserRoute() {
  listUserDocumentation()
  ...
}

private fun Route.listUserDocumentation() {
  install(NotarizedGetResource<User>()) {
  }
}

Describe alternatives you've considered I have not found an alternative to the above approach.

Additional context If desired, I can open a pull request.

ezienecker avatar Mar 15 '25 18:03 ezienecker