fix: locations inheritance (#135)
This commit is contained in:
@ -8,6 +8,7 @@
|
|||||||
### Changed
|
### Changed
|
||||||
- Kompendium now leverages the chosen API serializer. Supports Jackson, Gson and Kotlinx Serialization
|
- Kompendium now leverages the chosen API serializer. Supports Jackson, Gson and Kotlinx Serialization
|
||||||
- Fixed bug where overridden field names were not reflected in serialized object and required array
|
- Fixed bug where overridden field names were not reflected in serialized object and required array
|
||||||
|
- Fixed bug where Ktor Location parents were not being scanned for parameters
|
||||||
|
|
||||||
### Remove
|
### Remove
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package io.bkbn.kompendium.core
|
||||||
|
|
||||||
|
import io.bkbn.kompendium.core.parser.IMethodParser
|
||||||
|
|
||||||
|
object DefaultMethodParser : IMethodParser
|
@ -1,8 +1,9 @@
|
|||||||
package io.bkbn.kompendium.core
|
package io.bkbn.kompendium.core
|
||||||
|
|
||||||
import io.bkbn.kompendium.annotations.Param
|
import io.bkbn.kompendium.annotations.Param
|
||||||
|
import io.bkbn.kompendium.core.DefaultMethodParser.calculateRoutePath
|
||||||
import io.bkbn.kompendium.core.KompendiumPreFlight.methodNotarizationPreFlight
|
import io.bkbn.kompendium.core.KompendiumPreFlight.methodNotarizationPreFlight
|
||||||
import io.bkbn.kompendium.core.MethodParser.parseMethodInfo
|
import io.bkbn.kompendium.core.DefaultMethodParser.parseMethodInfo
|
||||||
import io.bkbn.kompendium.core.metadata.method.DeleteInfo
|
import io.bkbn.kompendium.core.metadata.method.DeleteInfo
|
||||||
import io.bkbn.kompendium.core.metadata.method.GetInfo
|
import io.bkbn.kompendium.core.metadata.method.GetInfo
|
||||||
import io.bkbn.kompendium.core.metadata.method.HeadInfo
|
import io.bkbn.kompendium.core.metadata.method.HeadInfo
|
||||||
@ -168,10 +169,4 @@ object Notarized {
|
|||||||
feature.config.spec.paths[path]?.options = postProcess(baseInfo)
|
feature.config.spec.paths[path]?.options = postProcess(baseInfo)
|
||||||
return method(HttpMethod.Options) { handle(body) }
|
return method(HttpMethod.Options) { handle(body) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the built-in Ktor route path [Route.toString] but cuts out any meta route such as authentication... anything
|
|
||||||
* that matches the RegEx pattern `/\\(.+\\)`
|
|
||||||
*/
|
|
||||||
fun Route.calculateRoutePath() = toString().replace(Regex("/\\(.+\\)"), "")
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package io.bkbn.kompendium.core
|
package io.bkbn.kompendium.core.parser
|
||||||
|
|
||||||
import io.bkbn.kompendium.annotations.Param
|
import io.bkbn.kompendium.annotations.Param
|
||||||
import io.bkbn.kompendium.core.Kontent.generateKontent
|
import io.bkbn.kompendium.core.Kompendium
|
||||||
|
import io.bkbn.kompendium.core.Kontent
|
||||||
import io.bkbn.kompendium.core.metadata.ExceptionInfo
|
import io.bkbn.kompendium.core.metadata.ExceptionInfo
|
||||||
import io.bkbn.kompendium.core.metadata.ParameterExample
|
import io.bkbn.kompendium.core.metadata.ParameterExample
|
||||||
import io.bkbn.kompendium.core.metadata.RequestInfo
|
import io.bkbn.kompendium.core.metadata.RequestInfo
|
||||||
@ -19,22 +20,20 @@ import io.bkbn.kompendium.oas.payload.Request
|
|||||||
import io.bkbn.kompendium.oas.payload.Response
|
import io.bkbn.kompendium.oas.payload.Response
|
||||||
import io.bkbn.kompendium.oas.schema.AnyOfSchema
|
import io.bkbn.kompendium.oas.schema.AnyOfSchema
|
||||||
import io.bkbn.kompendium.oas.schema.ObjectSchema
|
import io.bkbn.kompendium.oas.schema.ObjectSchema
|
||||||
|
import io.ktor.routing.Route
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KParameter
|
import kotlin.reflect.KParameter
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.full.createType
|
import kotlin.reflect.full.createType
|
||||||
import kotlin.reflect.full.findAnnotation
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
import kotlin.reflect.full.hasAnnotation
|
||||||
import kotlin.reflect.full.memberProperties
|
import kotlin.reflect.full.memberProperties
|
||||||
import kotlin.reflect.full.primaryConstructor
|
import kotlin.reflect.full.primaryConstructor
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
/**
|
interface IMethodParser {
|
||||||
* The MethodParser is responsible for converting route metadata and types into an OpenAPI compatible data class.
|
|
||||||
*/
|
|
||||||
object MethodParser {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the OpenAPI Path spec from provided metadata
|
* Generates the OpenAPI Path spec from provided metadata
|
||||||
* @param info implementation of the [MethodInfo] sealed class
|
* @param info implementation of the [MethodInfo] sealed class
|
||||||
@ -68,17 +67,17 @@ object MethodParser {
|
|||||||
) else null
|
) else null
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun parseResponse(
|
fun parseResponse(
|
||||||
responseType: KType,
|
responseType: KType,
|
||||||
responseInfo: ResponseInfo<*>?,
|
responseInfo: ResponseInfo<*>?,
|
||||||
feature: Kompendium
|
feature: Kompendium
|
||||||
): Map<Int, Response> = responseType.toResponseSpec(responseInfo, feature)?.let { mapOf(it) }.orEmpty()
|
): Map<Int, Response> = responseType.toResponseSpec(responseInfo, feature)?.let { mapOf(it) }.orEmpty()
|
||||||
|
|
||||||
private fun parseExceptions(
|
fun parseExceptions(
|
||||||
exceptionInfo: Set<ExceptionInfo<*>>,
|
exceptionInfo: Set<ExceptionInfo<*>>,
|
||||||
feature: Kompendium,
|
feature: Kompendium,
|
||||||
): Map<Int, Response> = exceptionInfo.associate { info ->
|
): Map<Int, Response> = exceptionInfo.associate { info ->
|
||||||
feature.config.cache = generateKontent(info.responseType, feature.config.cache)
|
feature.config.cache = Kontent.generateKontent(info.responseType, feature.config.cache)
|
||||||
val response = Response(
|
val response = Response(
|
||||||
description = info.description,
|
description = info.description,
|
||||||
content = feature.resolveContent(info.responseType, info.mediaTypes, info.examples)
|
content = feature.resolveContent(info.responseType, info.mediaTypes, info.examples)
|
||||||
@ -92,7 +91,7 @@ object MethodParser {
|
|||||||
* @param requestInfo request metadata
|
* @param requestInfo request metadata
|
||||||
* @return Will return a generated [Request] if requestInfo is not null
|
* @return Will return a generated [Request] if requestInfo is not null
|
||||||
*/
|
*/
|
||||||
private fun KType.toRequestSpec(requestInfo: RequestInfo<*>?, feature: Kompendium): Request? =
|
fun KType.toRequestSpec(requestInfo: RequestInfo<*>?, feature: Kompendium): Request? =
|
||||||
when (requestInfo) {
|
when (requestInfo) {
|
||||||
null -> null
|
null -> null
|
||||||
else -> {
|
else -> {
|
||||||
@ -111,7 +110,7 @@ object MethodParser {
|
|||||||
* @param responseInfo response metadata
|
* @param responseInfo response metadata
|
||||||
* @return Will return a generated [Pair] if responseInfo is not null
|
* @return Will return a generated [Pair] if responseInfo is not null
|
||||||
*/
|
*/
|
||||||
private fun KType.toResponseSpec(responseInfo: ResponseInfo<*>?, feature: Kompendium): Pair<Int, Response>? =
|
fun KType.toResponseSpec(responseInfo: ResponseInfo<*>?, feature: Kompendium): Pair<Int, Response>? =
|
||||||
when (responseInfo) {
|
when (responseInfo) {
|
||||||
null -> null
|
null -> null
|
||||||
else -> {
|
else -> {
|
||||||
@ -130,7 +129,7 @@ object MethodParser {
|
|||||||
* @param examples Mapping of named examples of valid bodies.
|
* @param examples Mapping of named examples of valid bodies.
|
||||||
* @return Named mapping of media types.
|
* @return Named mapping of media types.
|
||||||
*/
|
*/
|
||||||
private fun Kompendium.resolveContent(
|
fun Kompendium.resolveContent(
|
||||||
type: KType,
|
type: KType,
|
||||||
mediaTypes: List<String>,
|
mediaTypes: List<String>,
|
||||||
examples: Map<String, Any>
|
examples: Map<String, Any>
|
||||||
@ -163,29 +162,36 @@ object MethodParser {
|
|||||||
* @return list of valid parameter specs as detailed by the [KType] members
|
* @return list of valid parameter specs as detailed by the [KType] members
|
||||||
* @throws [IllegalStateException] if the class could not be parsed properly
|
* @throws [IllegalStateException] if the class could not be parsed properly
|
||||||
*/
|
*/
|
||||||
private fun KType.toParameterSpec(info: MethodInfo<*, *>, feature: Kompendium): List<Parameter> {
|
fun KType.toParameterSpec(info: MethodInfo<*, *>, feature: Kompendium): List<Parameter> {
|
||||||
val clazz = classifier as KClass<*>
|
val clazz = classifier as KClass<*>
|
||||||
return clazz.memberProperties.filter { prop ->
|
return clazz.memberProperties
|
||||||
prop.findAnnotation<Param>() != null
|
.filter { prop -> prop.hasAnnotation<Param>() }
|
||||||
}.map { prop ->
|
.map { prop -> prop.toParameter(info, this, clazz, feature) }
|
||||||
val wrapperSchema = feature.config.cache[this.getSimpleSlug()]!! as ObjectSchema
|
|
||||||
val anny = prop.findAnnotation<Param>()
|
|
||||||
?: error("Field ${prop.name} is not annotated with KompendiumParam")
|
|
||||||
val schema = wrapperSchema.properties[prop.name]
|
|
||||||
?: error("Could not find component type for $prop")
|
|
||||||
val defaultValue = getDefaultParameterValue(clazz, prop)
|
|
||||||
Parameter(
|
|
||||||
name = prop.name,
|
|
||||||
`in` = anny.type.name.lowercase(Locale.getDefault()),
|
|
||||||
schema = schema.addDefault(defaultValue),
|
|
||||||
description = schema.description,
|
|
||||||
required = !prop.returnType.isMarkedNullable && defaultValue == null,
|
|
||||||
examples = info.parameterExamples.mapToSpec(prop.name)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Set<ParameterExample>.mapToSpec(parameterName: String): Map<String, Parameter.Example>? {
|
fun KProperty<*>.toParameter(
|
||||||
|
info: MethodInfo<*, *>,
|
||||||
|
parentType: KType,
|
||||||
|
parentClazz: KClass<*>,
|
||||||
|
feature: Kompendium
|
||||||
|
): Parameter {
|
||||||
|
val wrapperSchema = feature.config.cache[parentType.getSimpleSlug()]!! as ObjectSchema
|
||||||
|
val anny = this.findAnnotation<Param>()
|
||||||
|
?: error("Field $name is not annotated with KompendiumParam")
|
||||||
|
val schema = wrapperSchema.properties[name]
|
||||||
|
?: error("Could not find component type for $this")
|
||||||
|
val defaultValue = getDefaultParameterValue(parentClazz, this)
|
||||||
|
return Parameter(
|
||||||
|
name = name,
|
||||||
|
`in` = anny.type.name.lowercase(Locale.getDefault()),
|
||||||
|
schema = schema.addDefault(defaultValue),
|
||||||
|
description = schema.description,
|
||||||
|
required = !returnType.isMarkedNullable && defaultValue == null,
|
||||||
|
examples = info.parameterExamples.mapToSpec(name)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Set<ParameterExample>.mapToSpec(parameterName: String): Map<String, Parameter.Example>? {
|
||||||
val filtered = filter { it.parameterName == parameterName }
|
val filtered = filter { it.parameterName == parameterName }
|
||||||
return if (filtered.isEmpty()) {
|
return if (filtered.isEmpty()) {
|
||||||
null
|
null
|
||||||
@ -200,7 +206,7 @@ object MethodParser {
|
|||||||
* @param prop the property in question
|
* @param prop the property in question
|
||||||
* @return The default value if found
|
* @return The default value if found
|
||||||
*/
|
*/
|
||||||
private fun getDefaultParameterValue(clazz: KClass<*>, prop: KProperty<*>): Any? {
|
fun getDefaultParameterValue(clazz: KClass<*>, prop: KProperty<*>): Any? {
|
||||||
val constructor = clazz.primaryConstructor
|
val constructor = clazz.primaryConstructor
|
||||||
val parameterInQuestion = constructor
|
val parameterInQuestion = constructor
|
||||||
?.parameters
|
?.parameters
|
||||||
@ -227,7 +233,7 @@ object MethodParser {
|
|||||||
* @return value of the proper type to match param
|
* @return value of the proper type to match param
|
||||||
* @throws [IllegalStateException] if parameter type is not one of the basic types supported below.
|
* @throws [IllegalStateException] if parameter type is not one of the basic types supported below.
|
||||||
*/
|
*/
|
||||||
private fun defaultValueInjector(param: KParameter): Any = when (param.type.classifier) {
|
fun defaultValueInjector(param: KParameter): Any = when (param.type.classifier) {
|
||||||
String::class -> "test"
|
String::class -> "test"
|
||||||
Boolean::class -> false
|
Boolean::class -> false
|
||||||
Int::class -> 1
|
Int::class -> 1
|
||||||
@ -237,4 +243,10 @@ object MethodParser {
|
|||||||
UUID::class -> UUID.randomUUID()
|
UUID::class -> UUID.randomUUID()
|
||||||
else -> error("Unsupported Type")
|
else -> error("Unsupported Type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the built-in Ktor route path [Route.toString] but cuts out any meta route such as authentication... anything
|
||||||
|
* that matches the RegEx pattern `/\\(.+\\)`
|
||||||
|
*/
|
||||||
|
fun Route.calculateRoutePath() = toString().replace(Regex("/\\(.+\\)"), "")
|
||||||
}
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package io.bkbn.kompendium.locations
|
||||||
|
|
||||||
|
import io.bkbn.kompendium.annotations.Param
|
||||||
|
import io.bkbn.kompendium.core.Kompendium
|
||||||
|
import io.bkbn.kompendium.core.metadata.method.MethodInfo
|
||||||
|
import io.bkbn.kompendium.core.parser.IMethodParser
|
||||||
|
import io.bkbn.kompendium.oas.path.Path
|
||||||
|
import io.bkbn.kompendium.oas.path.PathOperation
|
||||||
|
import io.bkbn.kompendium.oas.payload.Parameter
|
||||||
|
import io.ktor.application.feature
|
||||||
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
|
import io.ktor.locations.Location
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.application
|
||||||
|
import kotlin.reflect.KAnnotatedElement
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KClassifier
|
||||||
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.full.createType
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
import kotlin.reflect.full.hasAnnotation
|
||||||
|
import kotlin.reflect.full.memberProperties
|
||||||
|
|
||||||
|
@OptIn(KtorExperimentalLocationsAPI::class)
|
||||||
|
object LocationMethodParser : IMethodParser {
|
||||||
|
override fun KType.toParameterSpec(info: MethodInfo<*, *>, feature: Kompendium): List<Parameter> {
|
||||||
|
val clazzList = determineLocationParents(classifier!!)
|
||||||
|
return clazzList.associateWith { it.memberProperties }
|
||||||
|
.flatMap { (clazz, memberProperties) -> memberProperties.associateWith { clazz }.toList() }
|
||||||
|
.filter { (prop, _) -> prop.hasAnnotation<Param>() }
|
||||||
|
.map { (prop, clazz) -> prop.toParameter(info, clazz.createType(), clazz, feature) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun determineLocationParents(classifier: KClassifier): List<KClass<*>> {
|
||||||
|
var clazz: KClass<*>? = classifier as KClass<*>
|
||||||
|
val clazzList = mutableListOf<KClass<*>>()
|
||||||
|
while (clazz != null) {
|
||||||
|
clazzList.add(clazz)
|
||||||
|
clazz = getLocationParent(clazz)
|
||||||
|
}
|
||||||
|
return clazzList
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLocationParent(clazz: KClass<*>): KClass<*>? {
|
||||||
|
val parent = clazz.memberProperties
|
||||||
|
.find { (it.returnType.classifier as KAnnotatedElement).hasAnnotation<Location>() }
|
||||||
|
return parent?.returnType?.classifier as? KClass<*>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KClass<*>.calculateLocationPath(suffix: String = ""): String {
|
||||||
|
val locationAnnotation = this.findAnnotation<Location>()
|
||||||
|
require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" }
|
||||||
|
val parent = this.java.declaringClass?.kotlin
|
||||||
|
val newSuffix = locationAnnotation.path.plus(suffix)
|
||||||
|
return when (parent) {
|
||||||
|
null -> newSuffix
|
||||||
|
else -> parent.calculateLocationPath(newSuffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified TParam : Any> processBaseInfo(
|
||||||
|
paramType: KType,
|
||||||
|
requestType: KType,
|
||||||
|
responseType: KType,
|
||||||
|
info: MethodInfo<*, *>,
|
||||||
|
route: Route
|
||||||
|
): LocationBaseInfo {
|
||||||
|
val locationAnnotation = TParam::class.findAnnotation<Location>()
|
||||||
|
require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" }
|
||||||
|
val path = route.calculateRoutePath()
|
||||||
|
val locationPath = TParam::class.calculateLocationPath()
|
||||||
|
val pathWithLocation = path.plus(locationPath)
|
||||||
|
val feature = route.application.feature(Kompendium)
|
||||||
|
feature.config.spec.paths.getOrPut(pathWithLocation) { Path() }
|
||||||
|
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
||||||
|
return LocationBaseInfo(baseInfo, feature, pathWithLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class LocationBaseInfo(
|
||||||
|
val op: PathOperation,
|
||||||
|
val feature: Kompendium,
|
||||||
|
val path: String
|
||||||
|
)
|
||||||
|
}
|
@ -1,28 +1,19 @@
|
|||||||
package io.bkbn.kompendium.locations
|
package io.bkbn.kompendium.locations
|
||||||
|
|
||||||
import io.bkbn.kompendium.core.Kompendium
|
|
||||||
import io.bkbn.kompendium.core.KompendiumPreFlight.methodNotarizationPreFlight
|
import io.bkbn.kompendium.core.KompendiumPreFlight.methodNotarizationPreFlight
|
||||||
import io.bkbn.kompendium.core.MethodParser.parseMethodInfo
|
|
||||||
import io.bkbn.kompendium.core.Notarized.calculateRoutePath
|
|
||||||
import io.bkbn.kompendium.core.metadata.method.DeleteInfo
|
import io.bkbn.kompendium.core.metadata.method.DeleteInfo
|
||||||
import io.bkbn.kompendium.core.metadata.method.GetInfo
|
import io.bkbn.kompendium.core.metadata.method.GetInfo
|
||||||
import io.bkbn.kompendium.core.metadata.method.PostInfo
|
import io.bkbn.kompendium.core.metadata.method.PostInfo
|
||||||
import io.bkbn.kompendium.core.metadata.method.PutInfo
|
import io.bkbn.kompendium.core.metadata.method.PutInfo
|
||||||
import io.bkbn.kompendium.oas.path.Path
|
|
||||||
import io.bkbn.kompendium.oas.path.PathOperation
|
import io.bkbn.kompendium.oas.path.PathOperation
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.application.feature
|
|
||||||
import io.ktor.http.HttpMethod
|
import io.ktor.http.HttpMethod
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
import io.ktor.locations.Location
|
|
||||||
import io.ktor.locations.handle
|
import io.ktor.locations.handle
|
||||||
import io.ktor.locations.location
|
import io.ktor.locations.location
|
||||||
import io.ktor.routing.Route
|
import io.ktor.routing.Route
|
||||||
import io.ktor.routing.application
|
|
||||||
import io.ktor.routing.method
|
import io.ktor.routing.method
|
||||||
import io.ktor.util.pipeline.PipelineContext
|
import io.ktor.util.pipeline.PipelineContext
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.reflect.full.findAnnotation
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This version of notarized routes leverages the Ktor [io.ktor.locations.Locations] plugin to provide type safe access
|
* This version of notarized routes leverages the Ktor [io.ktor.locations.Locations] plugin to provide type safe access
|
||||||
@ -45,15 +36,8 @@ object NotarizedLocation {
|
|||||||
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||||
noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
||||||
): Route = methodNotarizationPreFlight<TParam, Unit, TResp>() { paramType, requestType, responseType ->
|
): Route = methodNotarizationPreFlight<TParam, Unit, TResp>() { paramType, requestType, responseType ->
|
||||||
val locationAnnotation = TParam::class.findAnnotation<Location>()
|
val lbi = LocationMethodParser.processBaseInfo<TParam>(paramType, requestType, responseType, info, this)
|
||||||
require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" }
|
lbi.feature.config.spec.paths[lbi.path]?.get = postProcess(lbi.op)
|
||||||
val feature = application.feature(Kompendium)
|
|
||||||
val path = calculateRoutePath()
|
|
||||||
val locationPath = TParam::class.calculateLocationPath()
|
|
||||||
val pathWithLocation = path.plus(locationPath)
|
|
||||||
feature.config.spec.paths.getOrPut(pathWithLocation) { Path() }
|
|
||||||
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
|
||||||
feature.config.spec.paths[pathWithLocation]?.get = postProcess(baseInfo)
|
|
||||||
return location(TParam::class) {
|
return location(TParam::class) {
|
||||||
method(HttpMethod.Get) { handle(body) }
|
method(HttpMethod.Get) { handle(body) }
|
||||||
}
|
}
|
||||||
@ -74,15 +58,8 @@ object NotarizedLocation {
|
|||||||
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||||
noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
||||||
): Route = methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
): Route = methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||||
val locationAnnotation = TParam::class.findAnnotation<Location>()
|
val lbi = LocationMethodParser.processBaseInfo<TParam>(paramType, requestType, responseType, info, this)
|
||||||
require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" }
|
lbi.feature.config.spec.paths[lbi.path]?.post = postProcess(lbi.op)
|
||||||
val feature = application.feature(Kompendium)
|
|
||||||
val path = calculateRoutePath()
|
|
||||||
val locationPath = TParam::class.calculateLocationPath()
|
|
||||||
val pathWithLocation = path.plus(locationPath)
|
|
||||||
feature.config.spec.paths.getOrPut(pathWithLocation) { Path() }
|
|
||||||
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
|
||||||
feature.config.spec.paths[pathWithLocation]?.post = postProcess(baseInfo)
|
|
||||||
return location(TParam::class) {
|
return location(TParam::class) {
|
||||||
method(HttpMethod.Post) { handle(body) }
|
method(HttpMethod.Post) { handle(body) }
|
||||||
}
|
}
|
||||||
@ -103,15 +80,8 @@ object NotarizedLocation {
|
|||||||
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||||
noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
||||||
): Route = methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
): Route = methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||||
val locationAnnotation = TParam::class.findAnnotation<Location>()
|
val lbi = LocationMethodParser.processBaseInfo<TParam>(paramType, requestType, responseType, info, this)
|
||||||
require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" }
|
lbi.feature.config.spec.paths[lbi.path]?.put = postProcess(lbi.op)
|
||||||
val feature = application.feature(Kompendium)
|
|
||||||
val path = calculateRoutePath()
|
|
||||||
val locationPath = TParam::class.calculateLocationPath()
|
|
||||||
val pathWithLocation = path.plus(locationPath)
|
|
||||||
feature.config.spec.paths.getOrPut(pathWithLocation) { Path() }
|
|
||||||
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
|
||||||
feature.config.spec.paths[pathWithLocation]?.put = postProcess(baseInfo)
|
|
||||||
return location(TParam::class) {
|
return location(TParam::class) {
|
||||||
method(HttpMethod.Put) { handle(body) }
|
method(HttpMethod.Put) { handle(body) }
|
||||||
}
|
}
|
||||||
@ -131,28 +101,10 @@ object NotarizedLocation {
|
|||||||
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||||
noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
||||||
): Route = methodNotarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
): Route = methodNotarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
||||||
val locationAnnotation = TParam::class.findAnnotation<Location>()
|
val lbi = LocationMethodParser.processBaseInfo<TParam>(paramType, requestType, responseType, info, this)
|
||||||
require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" }
|
lbi.feature.config.spec.paths[lbi.path]?.delete = postProcess(lbi.op)
|
||||||
val feature = application.feature(Kompendium)
|
|
||||||
val path = calculateRoutePath()
|
|
||||||
val locationPath = TParam::class.calculateLocationPath()
|
|
||||||
val pathWithLocation = path.plus(locationPath)
|
|
||||||
feature.config.spec.paths.getOrPut(pathWithLocation) { Path() }
|
|
||||||
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
|
||||||
feature.config.spec.paths[pathWithLocation]?.delete = postProcess(baseInfo)
|
|
||||||
return location(TParam::class) {
|
return location(TParam::class) {
|
||||||
method(HttpMethod.Delete) { handle(body) }
|
method(HttpMethod.Delete) { handle(body) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KClass<*>.calculateLocationPath(suffix: String = ""): String {
|
|
||||||
val locationAnnotation = this.findAnnotation<Location>()
|
|
||||||
require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" }
|
|
||||||
val parent = this.java.declaringClass?.kotlin
|
|
||||||
val newSuffix = locationAnnotation.path.plus(suffix)
|
|
||||||
return when (parent) {
|
|
||||||
null -> newSuffix
|
|
||||||
else -> parent.calculateLocationPath(newSuffix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,15 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"deprecated": false
|
"deprecated": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
@ -40,6 +40,15 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"deprecated": false
|
"deprecated": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
@ -40,6 +40,15 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"deprecated": false
|
"deprecated": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
|
@ -40,6 +40,15 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"deprecated": false
|
"deprecated": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"in": "path",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"required": true,
|
||||||
|
"deprecated": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
|
@ -20,5 +20,4 @@ object NumberSerializer : KSerializer<Number> {
|
|||||||
override fun serialize(encoder: Encoder, value: Number) {
|
override fun serialize(encoder: Encoder, value: Number) {
|
||||||
encoder.encodeString(value.toString())
|
encoder.encodeString(value.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user