added explicit path calculator interface (#31)
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.5.2] - April 19th, 2021
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Removed `Route.calculatePath`
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added an explicit `PathCalculator` interface to allow for easier handling of routes external to the core set of Ktor route selectors.
|
||||||
|
|
||||||
## [0.5.1] - April 19th, 2021
|
## [0.5.1] - April 19th, 2021
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Kompendium
|
# Kompendium
|
||||||
project.version=0.5.1
|
project.version=0.5.2
|
||||||
# Kotlin
|
# Kotlin
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
# Gradle
|
# Gradle
|
||||||
|
@ -31,7 +31,8 @@ import org.leafygreens.kompendium.models.oas.OpenApiSpecReferenceObject
|
|||||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecRequest
|
import org.leafygreens.kompendium.models.oas.OpenApiSpecRequest
|
||||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecResponse
|
import org.leafygreens.kompendium.models.oas.OpenApiSpecResponse
|
||||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecSchemaRef
|
import org.leafygreens.kompendium.models.oas.OpenApiSpecSchemaRef
|
||||||
import org.leafygreens.kompendium.util.Helpers.calculatePath
|
import org.leafygreens.kompendium.path.CorePathCalculator
|
||||||
|
import org.leafygreens.kompendium.path.PathCalculator
|
||||||
import org.leafygreens.kompendium.util.Helpers.getReferenceSlug
|
import org.leafygreens.kompendium.util.Helpers.getReferenceSlug
|
||||||
|
|
||||||
object Kompendium {
|
object Kompendium {
|
||||||
@ -44,12 +45,14 @@ object Kompendium {
|
|||||||
paths = mutableMapOf()
|
paths = mutableMapOf()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var pathCalculator: PathCalculator = CorePathCalculator()
|
||||||
|
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedGet(
|
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedGet(
|
||||||
info: MethodInfo,
|
info: MethodInfo,
|
||||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||||
): Route = notarizationPreFlight<TParam, Unit, TResp>() { paramType, requestType, responseType ->
|
): Route = notarizationPreFlight<TParam, Unit, TResp>() { paramType, requestType, responseType ->
|
||||||
val path = calculatePath()
|
val path = pathCalculator.calculate(this)
|
||||||
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||||
openApiSpec.paths[path]?.get = info.parseMethodInfo(HttpMethod.Get, paramType, requestType, responseType)
|
openApiSpec.paths[path]?.get = info.parseMethodInfo(HttpMethod.Get, paramType, requestType, responseType)
|
||||||
return method(HttpMethod.Get) { handle(body) }
|
return method(HttpMethod.Get) { handle(body) }
|
||||||
@ -59,7 +62,7 @@ object Kompendium {
|
|||||||
info: MethodInfo,
|
info: MethodInfo,
|
||||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||||
): Route = notarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
): Route = notarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||||
val path = calculatePath()
|
val path = pathCalculator.calculate(this)
|
||||||
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||||
openApiSpec.paths[path]?.post = info.parseMethodInfo(HttpMethod.Post, paramType, requestType, responseType)
|
openApiSpec.paths[path]?.post = info.parseMethodInfo(HttpMethod.Post, paramType, requestType, responseType)
|
||||||
return method(HttpMethod.Post) { handle(body) }
|
return method(HttpMethod.Post) { handle(body) }
|
||||||
@ -69,7 +72,7 @@ object Kompendium {
|
|||||||
info: MethodInfo,
|
info: MethodInfo,
|
||||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>,
|
noinline body: PipelineInterceptor<Unit, ApplicationCall>,
|
||||||
): Route = notarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
): Route = notarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||||
val path = calculatePath()
|
val path = pathCalculator.calculate(this)
|
||||||
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||||
openApiSpec.paths[path]?.put = info.parseMethodInfo(HttpMethod.Put, paramType, requestType, responseType)
|
openApiSpec.paths[path]?.put = info.parseMethodInfo(HttpMethod.Put, paramType, requestType, responseType)
|
||||||
return method(HttpMethod.Put) { handle(body) }
|
return method(HttpMethod.Put) { handle(body) }
|
||||||
@ -79,7 +82,7 @@ object Kompendium {
|
|||||||
info: MethodInfo,
|
info: MethodInfo,
|
||||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||||
): Route = notarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
): Route = notarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
||||||
val path = calculatePath()
|
val path = pathCalculator.calculate(this)
|
||||||
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||||
openApiSpec.paths[path]?.delete = info.parseMethodInfo(HttpMethod.Delete, paramType, requestType, responseType)
|
openApiSpec.paths[path]?.delete = info.parseMethodInfo(HttpMethod.Delete, paramType, requestType, responseType)
|
||||||
return method(HttpMethod.Delete) { handle(body) }
|
return method(HttpMethod.Delete) { handle(body) }
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package org.leafygreens.kompendium.path
|
||||||
|
|
||||||
|
import io.ktor.routing.PathSegmentConstantRouteSelector
|
||||||
|
import io.ktor.routing.PathSegmentParameterRouteSelector
|
||||||
|
import io.ktor.routing.RootRouteSelector
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.util.InternalAPI
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
open class CorePathCalculator : PathCalculator {
|
||||||
|
|
||||||
|
private val logger = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
|
@OptIn(InternalAPI::class)
|
||||||
|
override fun calculate(
|
||||||
|
route: Route?,
|
||||||
|
tail: String
|
||||||
|
): String = when (route) {
|
||||||
|
null -> tail
|
||||||
|
else -> when (route.selector) {
|
||||||
|
is RootRouteSelector -> {
|
||||||
|
logger.debug("Root route detected, returning path: $tail")
|
||||||
|
tail
|
||||||
|
}
|
||||||
|
is PathSegmentParameterRouteSelector -> {
|
||||||
|
logger.debug("Found segment parameter ${route.selector}, continuing to parent")
|
||||||
|
val newTail = "/${route.selector}$tail"
|
||||||
|
calculate(route.parent, newTail)
|
||||||
|
}
|
||||||
|
is PathSegmentConstantRouteSelector -> {
|
||||||
|
logger.debug("Found segment constant ${route.selector}, continuing to parent")
|
||||||
|
val newTail = "/${route.selector}$tail"
|
||||||
|
calculate(route.parent, newTail)
|
||||||
|
}
|
||||||
|
else -> when (route.selector.javaClass.simpleName) {
|
||||||
|
"TrailingSlashRouteSelector" -> {
|
||||||
|
logger.debug("Found trailing slash route selector")
|
||||||
|
val newTail = tail.ifBlank { "/" }
|
||||||
|
calculate(route.parent, newTail)
|
||||||
|
}
|
||||||
|
else -> handleCustomSelectors(route, tail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleCustomSelectors(route: Route, tail: String): String = error("Unknown selector ${route.selector}")
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package org.leafygreens.kompendium.path
|
||||||
|
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
|
||||||
|
interface PathCalculator {
|
||||||
|
|
||||||
|
fun calculate(route: Route?, tail: String = ""): String
|
||||||
|
|
||||||
|
fun handleCustomSelectors(route: Route, tail: String): String
|
||||||
|
|
||||||
|
}
|
@ -18,47 +18,6 @@ object Helpers {
|
|||||||
|
|
||||||
const val COMPONENT_SLUG = "#/components/schemas"
|
const val COMPONENT_SLUG = "#/components/schemas"
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO Explain this
|
|
||||||
*/
|
|
||||||
@OptIn(InternalAPI::class)
|
|
||||||
fun Route.calculatePath(tail: String = ""): String {
|
|
||||||
logger.info("Building path for ${selector::class}")
|
|
||||||
return when (selector) {
|
|
||||||
is RootRouteSelector -> {
|
|
||||||
logger.info("Root route detected, returning path: $tail")
|
|
||||||
tail
|
|
||||||
}
|
|
||||||
is PathSegmentParameterRouteSelector -> {
|
|
||||||
logger.info("Found segment parameter $selector, continuing to parent")
|
|
||||||
val newTail = "/$selector$tail"
|
|
||||||
parent?.calculatePath(newTail) ?: run {
|
|
||||||
logger.info("No parent found, returning current path")
|
|
||||||
newTail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is PathSegmentConstantRouteSelector -> {
|
|
||||||
logger.info("Found segment constant $selector, continuing to parent")
|
|
||||||
val newTail = "/$selector$tail"
|
|
||||||
parent?.calculatePath(newTail) ?: run {
|
|
||||||
logger.info("No parent found, returning current path")
|
|
||||||
newTail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> when (selector.javaClass.simpleName) {
|
|
||||||
// dumb ass workaround to this object being internal to ktor
|
|
||||||
"TrailingSlashRouteSelector" -> {
|
|
||||||
logger.info("Found trailing slash route selector")
|
|
||||||
val newTail = tail.ifBlank { "/" }
|
|
||||||
parent?.calculatePath(newTail) ?: run {
|
|
||||||
logger.info("No parent found, returning current path")
|
|
||||||
newTail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("Unhandled selector type ${selector::class}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple extension function that will take a [Pair] and place it (if absent) into a [MutableMap].
|
* Simple extension function that will take a [Pair] and place it (if absent) into a [MutableMap].
|
||||||
|
Reference in New Issue
Block a user