feat: v3 locations (#292)
This commit is contained in:
@ -11,8 +11,10 @@ import io.bkbn.kompendium.core.metadata.PatchInfo
|
||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||
import io.bkbn.kompendium.core.metadata.PutInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
import io.bkbn.kompendium.core.util.Helpers.addToSpec
|
||||
import io.bkbn.kompendium.core.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.core.util.Helpers.getSimpleSlug
|
||||
import io.bkbn.kompendium.core.util.SpecConfig
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
@ -31,17 +33,17 @@ import kotlin.reflect.KType
|
||||
|
||||
object NotarizedRoute {
|
||||
|
||||
class Config {
|
||||
var tags: Set<String> = emptySet()
|
||||
var parameters: List<Parameter> = emptyList()
|
||||
var get: GetInfo? = null
|
||||
var post: PostInfo? = null
|
||||
var put: PutInfo? = null
|
||||
var delete: DeleteInfo? = null
|
||||
var patch: PatchInfo? = null
|
||||
var head: HeadInfo? = null
|
||||
var options: OptionsInfo? = null
|
||||
var security: Map<String, List<String>>? = null
|
||||
class Config : SpecConfig {
|
||||
override var tags: Set<String> = emptySet()
|
||||
override var parameters: List<Parameter> = emptyList()
|
||||
override var get: GetInfo? = null
|
||||
override var post: PostInfo? = null
|
||||
override var put: PutInfo? = null
|
||||
override var delete: DeleteInfo? = null
|
||||
override var patch: PatchInfo? = null
|
||||
override var head: HeadInfo? = null
|
||||
override var options: OptionsInfo? = null
|
||||
override var security: Map<String, List<String>>? = null
|
||||
internal var path: Path? = null
|
||||
}
|
||||
|
||||
@ -86,86 +88,5 @@ object NotarizedRoute {
|
||||
pluginConfig.path = path
|
||||
}
|
||||
|
||||
private fun MethodInfo.addToSpec(path: Path, spec: OpenApiSpec, config: Config) {
|
||||
SchemaGenerator.fromTypeOrUnit(this.response.responseType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[this.response.responseType.getSimpleSlug()] = schema
|
||||
}
|
||||
|
||||
errors.forEach { error ->
|
||||
SchemaGenerator.fromTypeOrUnit(error.responseType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[error.responseType.getSimpleSlug()] = schema
|
||||
}
|
||||
}
|
||||
|
||||
when (this) {
|
||||
is MethodInfoWithRequest -> {
|
||||
SchemaGenerator.fromTypeOrUnit(this.request.requestType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[this.request.requestType.getSimpleSlug()] = schema
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
val operations = this.toPathOperation(config)
|
||||
|
||||
when (this) {
|
||||
is DeleteInfo -> path.delete = operations
|
||||
is GetInfo -> path.get = operations
|
||||
is HeadInfo -> path.head = operations
|
||||
is PatchInfo -> path.patch = operations
|
||||
is PostInfo -> path.post = operations
|
||||
is PutInfo -> path.put = operations
|
||||
is OptionsInfo -> path.options = operations
|
||||
}
|
||||
}
|
||||
|
||||
private fun MethodInfo.toPathOperation(config: Config) = PathOperation(
|
||||
tags = config.tags.plus(this.tags),
|
||||
summary = this.summary,
|
||||
description = this.description,
|
||||
externalDocs = this.externalDocumentation,
|
||||
operationId = this.operationId,
|
||||
deprecated = this.deprecated,
|
||||
parameters = this.parameters,
|
||||
security = config.security
|
||||
?.map { (k, v) -> k to v }
|
||||
?.map { listOf(it).toMap() }
|
||||
?.toList(),
|
||||
requestBody = when (this) {
|
||||
is MethodInfoWithRequest -> Request(
|
||||
description = this.request.description,
|
||||
content = this.request.requestType.toReferenceContent(this.request.examples),
|
||||
required = true
|
||||
)
|
||||
|
||||
else -> null
|
||||
},
|
||||
responses = mapOf(
|
||||
this.response.responseCode.value to Response(
|
||||
description = this.response.description,
|
||||
content = this.response.responseType.toReferenceContent(this.response.examples)
|
||||
)
|
||||
).plus(this.errors.toResponseMap())
|
||||
)
|
||||
|
||||
private fun List<ResponseInfo>.toResponseMap(): Map<Int, Response> = associate { error ->
|
||||
error.responseCode.value to Response(
|
||||
description = error.description,
|
||||
content = error.responseType.toReferenceContent(error.examples)
|
||||
)
|
||||
}
|
||||
|
||||
private fun KType.toReferenceContent(examples: Map<String, MediaType.Example>?): Map<String, MediaType>? =
|
||||
when (this.classifier as KClass<*>) {
|
||||
Unit::class -> null
|
||||
else -> mapOf(
|
||||
"application/json" to MediaType(
|
||||
schema = ReferenceDefinition(this.getReferenceSlug()),
|
||||
examples = examples
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun Route.calculateRoutePath() = toString().replace(Regex("/\\(.+\\)"), "")
|
||||
}
|
||||
|
@ -1,13 +1,26 @@
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import io.bkbn.kompendium.core.metadata.DeleteInfo
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.HeadInfo
|
||||
import io.bkbn.kompendium.core.metadata.MethodInfo
|
||||
import io.bkbn.kompendium.core.metadata.MethodInfoWithRequest
|
||||
import io.bkbn.kompendium.core.metadata.OptionsInfo
|
||||
import io.bkbn.kompendium.core.metadata.PatchInfo
|
||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||
import io.bkbn.kompendium.core.metadata.PutInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
import io.bkbn.kompendium.core.plugin.NotarizedRoute
|
||||
import io.bkbn.kompendium.json.schema.SchemaGenerator
|
||||
import io.bkbn.kompendium.json.schema.definition.ReferenceDefinition
|
||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
import io.bkbn.kompendium.oas.path.Path
|
||||
import io.bkbn.kompendium.oas.path.PathOperation
|
||||
import io.bkbn.kompendium.oas.payload.MediaType
|
||||
import io.bkbn.kompendium.oas.payload.Request
|
||||
import io.bkbn.kompendium.oas.payload.Response
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.util.Locale
|
||||
|
||||
object Helpers {
|
||||
|
||||
@ -32,4 +45,85 @@ object Helpers {
|
||||
.map { it.simpleName }
|
||||
return classNames.joinToString(separator = "-", prefix = "${clazz.simpleName}-")
|
||||
}
|
||||
|
||||
fun MethodInfo.addToSpec(path: Path, spec: OpenApiSpec, config: SpecConfig) {
|
||||
SchemaGenerator.fromTypeOrUnit(this.response.responseType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[this.response.responseType.getSimpleSlug()] = schema
|
||||
}
|
||||
|
||||
errors.forEach { error ->
|
||||
SchemaGenerator.fromTypeOrUnit(error.responseType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[error.responseType.getSimpleSlug()] = schema
|
||||
}
|
||||
}
|
||||
|
||||
when (this) {
|
||||
is MethodInfoWithRequest -> {
|
||||
SchemaGenerator.fromTypeOrUnit(this.request.requestType, spec.components.schemas)?.let { schema ->
|
||||
spec.components.schemas[this.request.requestType.getSimpleSlug()] = schema
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
val operations = this.toPathOperation(config)
|
||||
|
||||
when (this) {
|
||||
is DeleteInfo -> path.delete = operations
|
||||
is GetInfo -> path.get = operations
|
||||
is HeadInfo -> path.head = operations
|
||||
is PatchInfo -> path.patch = operations
|
||||
is PostInfo -> path.post = operations
|
||||
is PutInfo -> path.put = operations
|
||||
is OptionsInfo -> path.options = operations
|
||||
}
|
||||
}
|
||||
|
||||
private fun MethodInfo.toPathOperation(config: SpecConfig) = PathOperation(
|
||||
tags = config.tags.plus(this.tags),
|
||||
summary = this.summary,
|
||||
description = this.description,
|
||||
externalDocs = this.externalDocumentation,
|
||||
operationId = this.operationId,
|
||||
deprecated = this.deprecated,
|
||||
parameters = this.parameters,
|
||||
security = config.security
|
||||
?.map { (k, v) -> k to v }
|
||||
?.map { listOf(it).toMap() }
|
||||
?.toList(),
|
||||
requestBody = when (this) {
|
||||
is MethodInfoWithRequest -> Request(
|
||||
description = this.request.description,
|
||||
content = this.request.requestType.toReferenceContent(this.request.examples),
|
||||
required = true
|
||||
)
|
||||
|
||||
else -> null
|
||||
},
|
||||
responses = mapOf(
|
||||
this.response.responseCode.value to Response(
|
||||
description = this.response.description,
|
||||
content = this.response.responseType.toReferenceContent(this.response.examples)
|
||||
)
|
||||
).plus(this.errors.toResponseMap())
|
||||
)
|
||||
|
||||
private fun List<ResponseInfo>.toResponseMap(): Map<Int, Response> = associate { error ->
|
||||
error.responseCode.value to Response(
|
||||
description = error.description,
|
||||
content = error.responseType.toReferenceContent(error.examples)
|
||||
)
|
||||
}
|
||||
|
||||
private fun KType.toReferenceContent(examples: Map<String, MediaType.Example>?): Map<String, MediaType>? =
|
||||
when (this.classifier as KClass<*>) {
|
||||
Unit::class -> null
|
||||
else -> mapOf(
|
||||
"application/json" to MediaType(
|
||||
schema = ReferenceDefinition(this.getReferenceSlug()),
|
||||
examples = examples
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import io.bkbn.kompendium.core.metadata.DeleteInfo
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.HeadInfo
|
||||
import io.bkbn.kompendium.core.metadata.OptionsInfo
|
||||
import io.bkbn.kompendium.core.metadata.PatchInfo
|
||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||
import io.bkbn.kompendium.core.metadata.PutInfo
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
|
||||
interface SpecConfig {
|
||||
var tags: Set<String>
|
||||
var parameters: List<Parameter>
|
||||
var get: GetInfo?
|
||||
var post: PostInfo?
|
||||
var put: PutInfo?
|
||||
var delete: DeleteInfo?
|
||||
var patch: PatchInfo?
|
||||
var head: HeadInfo?
|
||||
var options: OptionsInfo?
|
||||
var security: Map<String, List<String>>?
|
||||
}
|
@ -23,6 +23,7 @@ import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.serialization.gson.gson
|
||||
import io.ktor.serialization.jackson.jackson
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.routing.Routing
|
||||
import io.ktor.server.testing.ApplicationTestBuilder
|
||||
@ -63,17 +64,19 @@ object TestHelpers {
|
||||
fun openApiTestAllSerializers(
|
||||
snapshotName: String,
|
||||
customTypes: Map<KType, JsonSchema> = emptyMap(),
|
||||
applicationSetup: Application.() -> Unit = { },
|
||||
routeUnderTest: Routing.() -> Unit
|
||||
) {
|
||||
openApiTest(snapshotName, SupportedSerializer.KOTLINX, routeUnderTest, customTypes)
|
||||
openApiTest(snapshotName, SupportedSerializer.JACKSON, routeUnderTest, customTypes)
|
||||
openApiTest(snapshotName, SupportedSerializer.GSON, routeUnderTest, customTypes)
|
||||
openApiTest(snapshotName, SupportedSerializer.KOTLINX, routeUnderTest, applicationSetup, customTypes)
|
||||
openApiTest(snapshotName, SupportedSerializer.JACKSON, routeUnderTest, applicationSetup, customTypes)
|
||||
openApiTest(snapshotName, SupportedSerializer.GSON, routeUnderTest, applicationSetup, customTypes)
|
||||
}
|
||||
|
||||
private fun openApiTest(
|
||||
snapshotName: String,
|
||||
serializer: SupportedSerializer,
|
||||
routeUnderTest: Routing.() -> Unit,
|
||||
applicationSetup: Application.() -> Unit,
|
||||
typeOverrides: Map<KType, JsonSchema> = emptyMap()
|
||||
) = testApplication {
|
||||
install(NotarizedApplication()) {
|
||||
@ -95,6 +98,7 @@ object TestHelpers {
|
||||
}
|
||||
}
|
||||
}
|
||||
application(applicationSetup)
|
||||
routing {
|
||||
redoc()
|
||||
routeUnderTest()
|
||||
|
@ -1,84 +0,0 @@
|
||||
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?.takeIf { it.hasAnnotation<Location>() }
|
||||
// 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,110 +0,0 @@
|
||||
package io.bkbn.kompendium.locations
|
||||
|
||||
//import io.bkbn.kompendium.core.KompendiumPreFlight.methodNotarizationPreFlight
|
||||
//import io.bkbn.kompendium.core.metadata.method.DeleteInfo
|
||||
//import io.bkbn.kompendium.core.metadata.method.GetInfo
|
||||
//import io.bkbn.kompendium.core.metadata.method.PostInfo
|
||||
//import io.bkbn.kompendium.core.metadata.method.PutInfo
|
||||
//import io.bkbn.kompendium.oas.path.PathOperation
|
||||
//import io.ktor.application.ApplicationCall
|
||||
//import io.ktor.http.HttpMethod
|
||||
//import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
//import io.ktor.locations.handle
|
||||
//import io.ktor.locations.location
|
||||
//import io.ktor.routing.Route
|
||||
//import io.ktor.routing.method
|
||||
//import io.ktor.util.pipeline.PipelineContext
|
||||
//
|
||||
///**
|
||||
// * This version of notarized routes leverages the Ktor [io.ktor.locations.Locations] plugin to provide type safe access
|
||||
// * to all path and query parameters.
|
||||
// */
|
||||
//@KtorExperimentalLocationsAPI
|
||||
//object NotarizedLocation {
|
||||
//
|
||||
// /**
|
||||
// * Notarization for an HTTP GET request leveraging the Ktor [io.ktor.locations.Locations] plugin
|
||||
// * @param TParam The class containing all parameter fields.
|
||||
// * Each field must be annotated with @[io.bkbn.kompendium.annotations.Param].
|
||||
// * Additionally, the class must be annotated with @[io.ktor.locations.Location].
|
||||
// * @param TResp Class detailing the expected API response
|
||||
// * @param info Route metadata
|
||||
// * @param postProcess Adds an optional callback hook to perform manual overrides on the generated [PathOperation]
|
||||
// */
|
||||
// inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedGet(
|
||||
// info: GetInfo<TParam, TResp>,
|
||||
// postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||
// noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
||||
// ): Route = methodNotarizationPreFlight<TParam, Unit, TResp>() { paramType, requestType, responseType ->
|
||||
// val lbi = LocationMethodParser.processBaseInfo<TParam>(paramType, requestType, responseType, info, this)
|
||||
// lbi.feature.config.spec.paths[lbi.path]?.get = postProcess(lbi.op)
|
||||
// return location(TParam::class) {
|
||||
// method(HttpMethod.Get) { handle(body) }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Notarization for an HTTP POST request leveraging the Ktor [io.ktor.locations.Locations] plugin
|
||||
// * @param TParam The class containing all parameter fields.
|
||||
// * Each field must be annotated with @[io.bkbn.kompendium.annotations.Param]
|
||||
// * Additionally, the class must be annotated with @[io.ktor.locations.Location].
|
||||
// * @param TReq Class detailing the expected API request body
|
||||
// * @param TResp Class detailing the expected API response
|
||||
// * @param info Route metadata
|
||||
// * @param postProcess Adds an optional callback hook to perform manual overrides on the generated [PathOperation]
|
||||
// */
|
||||
// inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPost(
|
||||
// info: PostInfo<TParam, TReq, TResp>,
|
||||
// postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||
// noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
||||
// ): Route = methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||
// val lbi = LocationMethodParser.processBaseInfo<TParam>(paramType, requestType, responseType, info, this)
|
||||
// lbi.feature.config.spec.paths[lbi.path]?.post = postProcess(lbi.op)
|
||||
// return location(TParam::class) {
|
||||
// method(HttpMethod.Post) { handle(body) }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Notarization for an HTTP Delete request leveraging the Ktor [io.ktor.locations.Locations] plugin
|
||||
// * @param TParam The class containing all parameter fields.
|
||||
// * Each field must be annotated with @[io.bkbn.kompendium.annotations.Param]
|
||||
// * Additionally, the class must be annotated with @[io.ktor.locations.Location].
|
||||
// * @param TReq Class detailing the expected API request body
|
||||
// * @param TResp Class detailing the expected API response
|
||||
// * @param info Route metadata
|
||||
// * @param postProcess Adds an optional callback hook to perform manual overrides on the generated [PathOperation]
|
||||
// */
|
||||
// inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPut(
|
||||
// info: PutInfo<TParam, TReq, TResp>,
|
||||
// postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||
// noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
||||
// ): Route = methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||
// val lbi = LocationMethodParser.processBaseInfo<TParam>(paramType, requestType, responseType, info, this)
|
||||
// lbi.feature.config.spec.paths[lbi.path]?.put = postProcess(lbi.op)
|
||||
// return location(TParam::class) {
|
||||
// method(HttpMethod.Put) { handle(body) }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Notarization for an HTTP POST request leveraging the Ktor [io.ktor.locations.Locations] plugin
|
||||
// * @param TParam The class containing all parameter fields.
|
||||
// * Each field must be annotated with @[io.bkbn.kompendium.annotations.Param]
|
||||
// * Additionally, the class must be annotated with @[io.ktor.locations.Location].
|
||||
// * @param TResp Class detailing the expected API response
|
||||
// * @param info Route metadata
|
||||
// * @param postProcess Adds an optional callback hook to perform manual overrides on the generated [PathOperation]
|
||||
// */
|
||||
// inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedDelete(
|
||||
// info: DeleteInfo<TParam, TResp>,
|
||||
// postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||
// noinline body: suspend PipelineContext<Unit, ApplicationCall>.(TParam) -> Unit
|
||||
// ): Route = methodNotarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
||||
// val lbi = LocationMethodParser.processBaseInfo<TParam>(paramType, requestType, responseType, info, this)
|
||||
// lbi.feature.config.spec.paths[lbi.path]?.delete = postProcess(lbi.op)
|
||||
// return location(TParam::class) {
|
||||
// method(HttpMethod.Delete) { handle(body) }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -0,0 +1,79 @@
|
||||
package io.bkbn.kompendium.locations
|
||||
|
||||
import io.bkbn.kompendium.core.attribute.KompendiumAttributes
|
||||
import io.bkbn.kompendium.core.metadata.DeleteInfo
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.HeadInfo
|
||||
import io.bkbn.kompendium.core.metadata.OptionsInfo
|
||||
import io.bkbn.kompendium.core.metadata.PatchInfo
|
||||
import io.bkbn.kompendium.core.metadata.PostInfo
|
||||
import io.bkbn.kompendium.core.metadata.PutInfo
|
||||
import io.bkbn.kompendium.core.util.Helpers.addToSpec
|
||||
import io.bkbn.kompendium.core.util.SpecConfig
|
||||
import io.bkbn.kompendium.oas.path.Path
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.ktor.server.application.createApplicationPlugin
|
||||
import io.ktor.server.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.server.locations.Location
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.hasAnnotation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
object NotarizedLocations {
|
||||
|
||||
data class LocationMetadata(
|
||||
override var tags: Set<String> = emptySet(),
|
||||
override var parameters: List<Parameter> = emptyList(),
|
||||
override var get: GetInfo? = null,
|
||||
override var post: PostInfo? = null,
|
||||
override var put: PutInfo? = null,
|
||||
override var delete: DeleteInfo? = null,
|
||||
override var patch: PatchInfo? = null,
|
||||
override var head: HeadInfo? = null,
|
||||
override var options: OptionsInfo? = null,
|
||||
override var security: Map<String, List<String>>? = null,
|
||||
) : SpecConfig
|
||||
|
||||
class Config {
|
||||
lateinit var locations: Map<KClass<*>, LocationMetadata>
|
||||
}
|
||||
|
||||
operator fun invoke() = createApplicationPlugin(
|
||||
name = "NotarizedLocations",
|
||||
createConfiguration = ::Config
|
||||
) {
|
||||
val spec = application.attributes[KompendiumAttributes.openApiSpec]
|
||||
pluginConfig.locations.forEach { (k, v) ->
|
||||
val path = Path()
|
||||
path.parameters = v.parameters
|
||||
v.get?.addToSpec(path, spec, v)
|
||||
v.delete?.addToSpec(path, spec, v)
|
||||
v.head?.addToSpec(path, spec, v)
|
||||
v.options?.addToSpec(path, spec, v)
|
||||
v.post?.addToSpec(path, spec, v)
|
||||
v.put?.addToSpec(path, spec, v)
|
||||
v.patch?.addToSpec(path, spec, v)
|
||||
|
||||
val location = k.getLocationFromClass()
|
||||
spec.paths[location] = path
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(KtorExperimentalLocationsAPI::class)
|
||||
private fun KClass<*>.getLocationFromClass(): String {
|
||||
// todo if parent
|
||||
|
||||
val location = findAnnotation<Location>()
|
||||
?: error("Cannot notarize a location without annotating with @Location")
|
||||
|
||||
val path = location.path
|
||||
val parent = memberProperties.map { it.returnType.classifier as KClass<*> }.find { it.hasAnnotation<Location>() }
|
||||
|
||||
return if (parent == null) {
|
||||
path
|
||||
} else {
|
||||
parent.getLocationFromClass() + path
|
||||
}
|
||||
}
|
||||
}
|
@ -1,82 +1,119 @@
|
||||
package io.bkbn.kompendium.locations
|
||||
|
||||
//import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||
//import io.bkbn.kompendium.locations.util.locationsConfig
|
||||
//import io.bkbn.kompendium.locations.util.notarizedDeleteNestedLocation
|
||||
//import io.bkbn.kompendium.locations.util.notarizedDeleteSimpleLocation
|
||||
//import io.bkbn.kompendium.locations.util.notarizedGetNestedLocation
|
||||
//import io.bkbn.kompendium.locations.util.notarizedGetNestedLocationFromNonLocationClass
|
||||
//import io.bkbn.kompendium.locations.util.notarizedGetSimpleLocation
|
||||
//import io.bkbn.kompendium.locations.util.notarizedPostNestedLocation
|
||||
//import io.bkbn.kompendium.locations.util.notarizedPostSimpleLocation
|
||||
//import io.bkbn.kompendium.locations.util.notarizedPutNestedLocation
|
||||
//import io.bkbn.kompendium.locations.util.notarizedPutSimpleLocation
|
||||
//import io.kotest.core.spec.style.DescribeSpec
|
||||
//
|
||||
//class KompendiumLocationsTest : DescribeSpec({
|
||||
// describe("Locations") {
|
||||
// it("Can notarize a get request with a simple location") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_get_simple_location.json") {
|
||||
// locationsConfig()
|
||||
// notarizedGetSimpleLocation()
|
||||
// }
|
||||
// }
|
||||
// it("Can notarize a get request with a nested location") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_get_nested_location.json") {
|
||||
// locationsConfig()
|
||||
// notarizedGetNestedLocation()
|
||||
// }
|
||||
// }
|
||||
// it("Can notarize a post with a simple location") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_post_simple_location.json") {
|
||||
// locationsConfig()
|
||||
// notarizedPostSimpleLocation()
|
||||
// }
|
||||
// }
|
||||
// it("Can notarize a post with a nested location") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_post_nested_location.json") {
|
||||
// locationsConfig()
|
||||
// notarizedPostNestedLocation()
|
||||
// }
|
||||
// }
|
||||
// it("Can notarize a put with a simple location") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_put_simple_location.json") {
|
||||
// locationsConfig()
|
||||
// notarizedPutSimpleLocation()
|
||||
// }
|
||||
// }
|
||||
// it("Can notarize a put with a nested location") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_put_nested_location.json") {
|
||||
// locationsConfig()
|
||||
// notarizedPutNestedLocation()
|
||||
// }
|
||||
// }
|
||||
// it("Can notarize a delete with a simple location") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_delete_simple_location.json") {
|
||||
// locationsConfig()
|
||||
// notarizedDeleteSimpleLocation()
|
||||
// }
|
||||
// }
|
||||
// it("Can notarize a delete with a nested location") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_delete_nested_location.json") {
|
||||
// locationsConfig()
|
||||
// notarizedDeleteNestedLocation()
|
||||
// }
|
||||
// }
|
||||
// it("Can notarize a get with a nested location nested in a non-location class") {
|
||||
// // act
|
||||
// openApiTestAllSerializers("notarized_get_nested_location_from_non_location_class.json") {
|
||||
// locationsConfig()
|
||||
// notarizedGetNestedLocationFromNonLocationClass()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//})
|
||||
import Listing
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.locations.Locations
|
||||
import io.ktor.server.locations.get
|
||||
import io.ktor.server.response.respondText
|
||||
|
||||
class KompendiumLocationsTest : DescribeSpec({
|
||||
describe("Location Tests") {
|
||||
it("Can notarize a simple location") {
|
||||
openApiTestAllSerializers(
|
||||
snapshotName = "T0001__simple_location.json",
|
||||
applicationSetup = {
|
||||
install(Locations)
|
||||
install(NotarizedLocations()) {
|
||||
locations = mapOf(
|
||||
Listing::class to NotarizedLocations.LocationMetadata(
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
name = "name",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING
|
||||
),
|
||||
Parameter(
|
||||
name = "page",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.INT
|
||||
)
|
||||
),
|
||||
get = GetInfo.builder {
|
||||
summary("Location")
|
||||
description("example location")
|
||||
response {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
description("does great things")
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
get<Listing> { listing ->
|
||||
call.respondText("Listing ${listing.name}, page ${listing.page}")
|
||||
}
|
||||
}
|
||||
}
|
||||
it("Can notarize nested locations") {
|
||||
openApiTestAllSerializers(
|
||||
snapshotName = "T0002__nested_locations.json",
|
||||
applicationSetup = {
|
||||
install(Locations)
|
||||
install(NotarizedLocations()) {
|
||||
locations = mapOf(
|
||||
Type.Edit::class to NotarizedLocations.LocationMetadata(
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
name = "name",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING
|
||||
)
|
||||
),
|
||||
get = GetInfo.builder {
|
||||
summary("Edit")
|
||||
description("example location")
|
||||
response {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
description("does great things")
|
||||
}
|
||||
}
|
||||
),
|
||||
Type.Other::class to NotarizedLocations.LocationMetadata(
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
name = "name",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING
|
||||
),
|
||||
Parameter(
|
||||
name = "page",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.INT
|
||||
)
|
||||
),
|
||||
get = GetInfo.builder {
|
||||
summary("Other")
|
||||
description("example location")
|
||||
response {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<TestResponse>()
|
||||
description("does great things")
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
get<Type.Edit> { edit ->
|
||||
call.respondText("Listing ${edit.parent.name}")
|
||||
}
|
||||
get<Type.Other> { other ->
|
||||
call.respondText("Listing ${other.parent.name}, page ${other.page}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1,22 +1,12 @@
|
||||
package io.bkbn.kompendium.locations.util
|
||||
import io.ktor.server.locations.Location
|
||||
|
||||
//import io.bkbn.kompendium.annotations.Param
|
||||
//import io.bkbn.kompendium.annotations.ParamType
|
||||
//import io.ktor.locations.Location
|
||||
//
|
||||
//@Location("/test/{name}")
|
||||
//data class SimpleLoc(@Param(ParamType.PATH) val name: String) {
|
||||
// @Location("/nesty")
|
||||
// data class NestedLoc(@Param(ParamType.QUERY) val isCool: Boolean, val parent: SimpleLoc)
|
||||
//}
|
||||
//
|
||||
//object NonLocationObject {
|
||||
// @Location("/test/{name}")
|
||||
// data class SimpleLoc(@Param(ParamType.PATH) val name: String) {
|
||||
// @Location("/nesty")
|
||||
// data class NestedLoc(@Param(ParamType.QUERY) val isCool: Boolean, val parent: SimpleLoc)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//data class SimpleResponse(val result: Boolean)
|
||||
//data class SimpleRequest(val input: String)
|
||||
@Location("/list/{name}/page/{page}")
|
||||
data class Listing(val name: String, val page: Int)
|
||||
|
||||
@Location("/type/{name}")
|
||||
data class Type(val name: String) {
|
||||
@Location("/edit")
|
||||
data class Edit(val parent: Type)
|
||||
@Location("/other/{page}")
|
||||
data class Other(val parent: Type, val page: Int)
|
||||
}
|
||||
|
@ -1,107 +0,0 @@
|
||||
package io.bkbn.kompendium.locations.util
|
||||
|
||||
//import io.bkbn.kompendium.locations.NotarizedLocation.notarizedDelete
|
||||
//import io.bkbn.kompendium.locations.NotarizedLocation.notarizedGet
|
||||
//import io.bkbn.kompendium.locations.NotarizedLocation.notarizedPost
|
||||
//import io.bkbn.kompendium.locations.NotarizedLocation.notarizedPut
|
||||
//import io.ktor.application.Application
|
||||
//import io.ktor.application.call
|
||||
//import io.ktor.application.install
|
||||
//import io.ktor.locations.Locations
|
||||
//import io.ktor.response.respondText
|
||||
//import io.ktor.routing.route
|
||||
//import io.ktor.routing.routing
|
||||
//
|
||||
//fun Application.locationsConfig() {
|
||||
// install(Locations)
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedGetSimpleLocation() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedGet(TestResponseInfo.testGetSimpleLocation) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedGetNestedLocation() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedGet(TestResponseInfo.testGetNestedLocation) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedPostSimpleLocation() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedPost(TestResponseInfo.testPostSimpleLocation) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedPostNestedLocation() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedPost(TestResponseInfo.testPostNestedLocation) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedPutSimpleLocation() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedPut(TestResponseInfo.testPutSimpleLocation) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedPutNestedLocation() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedPut(TestResponseInfo.testPutNestedLocation) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedDeleteSimpleLocation() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedDelete(TestResponseInfo.testDeleteSimpleLocation) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedDeleteNestedLocation() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedDelete(TestResponseInfo.testDeleteNestedLocation) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//fun Application.notarizedGetNestedLocationFromNonLocationClass() {
|
||||
// routing {
|
||||
// route("/test") {
|
||||
// notarizedGet(TestResponseInfo.testGetNestedLocationFromNonLocationClass) {
|
||||
// call.respondText { "hey dude ‼️ congratz on the get request" }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -1,97 +0,0 @@
|
||||
package io.bkbn.kompendium.locations.util
|
||||
|
||||
//import io.bkbn.kompendium.core.legacy.metadata.RequestInfo
|
||||
//import io.bkbn.kompendium.core.legacy.metadata.ResponseInfo
|
||||
//import io.bkbn.kompendium.core.legacy.metadata.method.DeleteInfo
|
||||
//import io.bkbn.kompendium.core.legacy.metadata.method.GetInfo
|
||||
//import io.bkbn.kompendium.core.legacy.metadata.method.PostInfo
|
||||
//import io.bkbn.kompendium.core.legacy.metadata.method.PutInfo
|
||||
//import io.ktor.http.HttpStatusCode
|
||||
//
|
||||
//object TestResponseInfo {
|
||||
// val testGetSimpleLocation = GetInfo<SimpleLoc, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
// val testPostSimpleLocation = PostInfo<SimpleLoc, SimpleRequest, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// requestInfo = RequestInfo(
|
||||
// description = "Cool stuff"
|
||||
// ),
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
// val testPutSimpleLocation = PutInfo<SimpleLoc, SimpleRequest, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// requestInfo = RequestInfo(
|
||||
// description = "Cool stuff"
|
||||
// ),
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
// val testDeleteSimpleLocation = DeleteInfo<SimpleLoc, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
// val testGetNestedLocation = GetInfo<SimpleLoc.NestedLoc, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
// val testPostNestedLocation = PostInfo<SimpleLoc.NestedLoc, SimpleRequest, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// requestInfo = RequestInfo(
|
||||
// description = "Cool stuff"
|
||||
// ),
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
// val testPutNestedLocation = PutInfo<SimpleLoc.NestedLoc, SimpleRequest, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// requestInfo = RequestInfo(
|
||||
// description = "Cool stuff"
|
||||
// ),
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
// val testDeleteNestedLocation = DeleteInfo<SimpleLoc.NestedLoc, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
//
|
||||
// val testGetNestedLocationFromNonLocationClass = GetInfo<NonLocationObject.SimpleLoc.NestedLoc, SimpleResponse>(
|
||||
// summary = "Location Test",
|
||||
// description = "A cool test",
|
||||
// responseInfo = ResponseInfo(
|
||||
// status = HttpStatusCode.OK,
|
||||
// description = "A successful endeavor"
|
||||
// )
|
||||
// )
|
||||
//}
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
@ -26,59 +27,62 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}/nesty": {
|
||||
"/list/{name}/page/{page}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "isCool",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"summary": "Location",
|
||||
"description": "example location",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"description": "does great things",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleResponse": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
"c"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
124
locations/src/test/resources/T0002__nested_locations.json
Normal file
124
locations/src/test/resources/T0002__nested_locations.json
Normal file
@ -0,0 +1,124 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/type/{name}/edit": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Edit",
|
||||
"description": "example location",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "does great things",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"/type/{name}/other/{page}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Other",
|
||||
"description": "example location",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "does great things",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "page",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number",
|
||||
"format": "int32"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"webhooks": {},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"TestResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
]
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}/nesty": {
|
||||
"delete": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "isCool",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}": {
|
||||
"delete": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}/nesty": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "isCool",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}/nesty": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "isCool",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"description": "Cool stuff",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleRequest": {
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"input"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SimpleResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"description": "Cool stuff",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleRequest": {
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"input"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SimpleResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}/nesty": {
|
||||
"put": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "isCool",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"description": "Cool stuff",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleRequest": {
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"input"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SimpleResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/test/{name}": {
|
||||
"put": {
|
||||
"tags": [],
|
||||
"summary": "Location Test",
|
||||
"description": "A cool test",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"description": "Cool stuff",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/SimpleResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"SimpleRequest": {
|
||||
"properties": {
|
||||
"input": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"input"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SimpleResponse": {
|
||||
"properties": {
|
||||
"result": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"result"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -71,7 +71,7 @@ private fun Application.mainModule() {
|
||||
redoc(pageTitle = "Simple API Docs")
|
||||
authenticate("basic") {
|
||||
route("/{id}") {
|
||||
idDocumentation()
|
||||
locationDocumentation()
|
||||
get {
|
||||
call.respond(HttpStatusCode.OK, ExampleResponse(true))
|
||||
}
|
||||
@ -80,7 +80,7 @@ private fun Application.mainModule() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Route.idDocumentation() {
|
||||
private fun Route.locationDocumentation() {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
|
@ -47,7 +47,7 @@ private fun Application.mainModule() {
|
||||
redoc(pageTitle = "Simple API Docs")
|
||||
|
||||
route("/{id}") {
|
||||
idDocumentation()
|
||||
locationDocumentation()
|
||||
get {
|
||||
call.respond(HttpStatusCode.OK, ExampleResponse(true))
|
||||
}
|
||||
@ -55,7 +55,7 @@ private fun Application.mainModule() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Route.idDocumentation() {
|
||||
private fun Route.locationDocumentation() {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
|
@ -54,7 +54,7 @@ private fun Application.mainModule() {
|
||||
redoc(pageTitle = "Simple API Docs")
|
||||
|
||||
route("/{id}") {
|
||||
idDocumentation()
|
||||
locationDocumentation()
|
||||
get {
|
||||
call.respond(
|
||||
HttpStatusCode.OK,
|
||||
@ -68,7 +68,7 @@ private fun Application.mainModule() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Route.idDocumentation() {
|
||||
private fun Route.locationDocumentation() {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
|
@ -13,7 +13,6 @@ import io.bkbn.kompendium.playground.util.Util.baseSpec
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.netty.Netty
|
||||
@ -55,7 +54,7 @@ private fun Application.mainModule() {
|
||||
redoc(pageTitle = "Simple API Docs")
|
||||
|
||||
route("/{id}") {
|
||||
idDocumentation()
|
||||
locationDocumentation()
|
||||
get {
|
||||
throw RuntimeException("This wasn't your fault I promise <3")
|
||||
}
|
||||
@ -63,7 +62,7 @@ private fun Application.mainModule() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Route.idDocumentation() {
|
||||
private fun Route.locationDocumentation() {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
|
@ -43,7 +43,7 @@ private fun Application.mainModule() {
|
||||
redoc(pageTitle = "Simple API Docs")
|
||||
|
||||
route("/{id}") {
|
||||
idDocumentation()
|
||||
locationDocumentation()
|
||||
get {
|
||||
call.respond(HttpStatusCode.OK, ExampleResponse(true))
|
||||
}
|
||||
@ -51,7 +51,7 @@ private fun Application.mainModule() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Route.idDocumentation() {
|
||||
private fun Route.locationDocumentation() {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
|
@ -82,7 +82,7 @@ private fun Application.mainModule() {
|
||||
authenticate("basic") {
|
||||
redoc(pageTitle = "Simple API Docs")
|
||||
route("/{id}") {
|
||||
idDocumentation()
|
||||
locationDocumentation()
|
||||
get {
|
||||
call.respond(HttpStatusCode.OK, ExampleResponse(true))
|
||||
}
|
||||
@ -91,7 +91,7 @@ private fun Application.mainModule() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Route.idDocumentation() {
|
||||
private fun Route.locationDocumentation() {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
|
@ -46,7 +46,7 @@ private fun Application.mainModule() {
|
||||
redoc(pageTitle = "Simple API Docs")
|
||||
|
||||
route("/{id}") {
|
||||
idDocumentation()
|
||||
locationDocumentation()
|
||||
get {
|
||||
call.respond(HttpStatusCode.OK, ExampleResponse(true))
|
||||
}
|
||||
@ -54,7 +54,7 @@ private fun Application.mainModule() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Route.idDocumentation() {
|
||||
private fun Route.locationDocumentation() {
|
||||
install(NotarizedRoute()) {
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
|
@ -0,0 +1,80 @@
|
||||
package io.bkbn.kompendium.playground
|
||||
|
||||
import io.bkbn.kompendium.core.metadata.GetInfo
|
||||
import io.bkbn.kompendium.core.plugin.NotarizedApplication
|
||||
import io.bkbn.kompendium.core.routes.redoc
|
||||
import io.bkbn.kompendium.json.schema.definition.TypeDefinition
|
||||
import io.bkbn.kompendium.locations.NotarizedLocations
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||
import io.bkbn.kompendium.playground.util.ExampleResponse
|
||||
import io.bkbn.kompendium.playground.util.Listing
|
||||
import io.bkbn.kompendium.playground.util.Util.baseSpec
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import io.ktor.server.application.Application
|
||||
import io.ktor.server.application.call
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.locations.Locations
|
||||
import io.ktor.server.locations.get
|
||||
import io.ktor.server.netty.Netty
|
||||
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.routing.routing
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
fun main() {
|
||||
embeddedServer(
|
||||
Netty,
|
||||
port = 8081,
|
||||
module = Application::mainModule
|
||||
).start(wait = true)
|
||||
}
|
||||
|
||||
private fun Application.mainModule() {
|
||||
install(Locations)
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
serializersModule = KompendiumSerializersModule.module
|
||||
encodeDefaults = true
|
||||
explicitNulls = false
|
||||
})
|
||||
}
|
||||
install(NotarizedApplication()) {
|
||||
spec = baseSpec
|
||||
}
|
||||
install(NotarizedLocations()) {
|
||||
locations = mapOf(
|
||||
Listing::class to NotarizedLocations.LocationMetadata(
|
||||
parameters = listOf(
|
||||
Parameter(
|
||||
name = "name",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.STRING
|
||||
),
|
||||
Parameter(
|
||||
name = "page",
|
||||
`in` = Parameter.Location.path,
|
||||
schema = TypeDefinition.INT
|
||||
)
|
||||
),
|
||||
get = GetInfo.builder {
|
||||
summary("Get user by id")
|
||||
description("A very neat endpoint!")
|
||||
response {
|
||||
responseCode(HttpStatusCode.OK)
|
||||
responseType<ExampleResponse>()
|
||||
description("Will return whether or not the user is real 😱")
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
routing {
|
||||
redoc(pageTitle = "Simple API Docs")
|
||||
get<Listing> { listing ->
|
||||
call.respondText("Listing ${listing.name}, page ${listing.page}")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package io.bkbn.kompendium.playground.util
|
||||
|
||||
import io.ktor.server.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.server.locations.Location
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@ -14,3 +16,12 @@ data class CustomTypeResponse(
|
||||
|
||||
@Serializable
|
||||
data class ExceptionResponse(val message: String)
|
||||
|
||||
@Location("/list/{name}/page/{page}")
|
||||
data class Listing(val name: String, val page: Int)
|
||||
|
||||
@Location("/type/{name}") data class Type(val name: String) {
|
||||
// In these classes we have to include the `name` property matching the parent.
|
||||
@Location("/edit") data class Edit(val parent: Type)
|
||||
@Location("/other/{page}") data class Other(val parent: Type, val page: Int)
|
||||
}
|
||||
|
Reference in New Issue
Block a user