Error Responses, PR Template, Code Refactor, and Test plugin update (#42)
This commit is contained in:
@ -1,22 +1,17 @@
|
||||
package org.leafygreens.kompendium
|
||||
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.method
|
||||
import io.ktor.util.pipeline.PipelineInterceptor
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import kotlin.reflect.typeOf
|
||||
import org.leafygreens.kompendium.Kontent.generateKontent
|
||||
import org.leafygreens.kompendium.Kontent.generateParameterKontent
|
||||
import org.leafygreens.kompendium.annotations.CookieParam
|
||||
import org.leafygreens.kompendium.annotations.HeaderParam
|
||||
import org.leafygreens.kompendium.annotations.PathParam
|
||||
import org.leafygreens.kompendium.annotations.QueryParam
|
||||
import org.leafygreens.kompendium.models.meta.ErrorMap
|
||||
import org.leafygreens.kompendium.models.meta.MethodInfo
|
||||
import org.leafygreens.kompendium.models.meta.RequestInfo
|
||||
import org.leafygreens.kompendium.models.meta.ResponseInfo
|
||||
@ -25,8 +20,8 @@ import org.leafygreens.kompendium.models.oas.OpenApiSpec
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecMediaType
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecParameter
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecPathItem
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecPathItemOperation
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecReferencable
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecReferenceObject
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecRequest
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecResponse
|
||||
@ -38,6 +33,7 @@ import org.leafygreens.kompendium.util.Helpers.getReferenceSlug
|
||||
|
||||
object Kompendium {
|
||||
|
||||
var errorMap: ErrorMap = emptyMap()
|
||||
var cache: SchemaMap = emptyMap()
|
||||
|
||||
var openApiSpec = OpenApiSpec(
|
||||
@ -48,47 +44,6 @@ object Kompendium {
|
||||
|
||||
var pathCalculator: PathCalculator = CorePathCalculator()
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedGet(
|
||||
info: MethodInfo,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route = notarizationPreFlight<TParam, Unit, TResp>() { paramType, requestType, responseType ->
|
||||
val path = pathCalculator.calculate(this)
|
||||
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
openApiSpec.paths[path]?.get = info.parseMethodInfo(HttpMethod.Get, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Get) { handle(body) }
|
||||
}
|
||||
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPost(
|
||||
info: MethodInfo,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route = notarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||
val path = pathCalculator.calculate(this)
|
||||
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
openApiSpec.paths[path]?.post = info.parseMethodInfo(HttpMethod.Post, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Post) { handle(body) }
|
||||
}
|
||||
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPut(
|
||||
info: MethodInfo,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>,
|
||||
): Route = notarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||
val path = pathCalculator.calculate(this)
|
||||
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
openApiSpec.paths[path]?.put = info.parseMethodInfo(HttpMethod.Put, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Put) { handle(body) }
|
||||
}
|
||||
|
||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedDelete(
|
||||
info: MethodInfo,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route = notarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
||||
val path = pathCalculator.calculate(this)
|
||||
openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
openApiSpec.paths[path]?.delete = info.parseMethodInfo(HttpMethod.Delete, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Delete) { handle(body) }
|
||||
}
|
||||
|
||||
// TODO here down is a mess, needs refactor once core functionality is in place
|
||||
fun MethodInfo.parseMethodInfo(
|
||||
method: HttpMethod,
|
||||
@ -101,7 +56,18 @@ object Kompendium {
|
||||
tags = this.tags,
|
||||
deprecated = this.deprecated,
|
||||
parameters = paramType.toParameterSpec(),
|
||||
responses = responseType.toResponseSpec(responseInfo)?.let { mapOf(it) },
|
||||
responses = responseType.toResponseSpec(responseInfo)?.let { mapOf(it) }.let {
|
||||
when (it) {
|
||||
null -> {
|
||||
val throwables = parseThrowables(canThrow)
|
||||
when (throwables.isEmpty()) {
|
||||
true -> null
|
||||
false -> throwables
|
||||
}
|
||||
}
|
||||
else -> it.plus(parseThrowables(canThrow))
|
||||
}
|
||||
},
|
||||
requestBody = if (method != HttpMethod.Get) requestType.toRequestSpec(requestInfo) else null,
|
||||
security = if (this.securitySchemes.isNotEmpty()) listOf(
|
||||
// TODO support scopes
|
||||
@ -109,18 +75,15 @@ object Kompendium {
|
||||
) else null
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> notarizationPreFlight(
|
||||
block: (KType, KType, KType) -> Route
|
||||
): Route {
|
||||
cache = generateKontent<TResp>(cache)
|
||||
cache = generateKontent<TReq>(cache)
|
||||
cache = generateParameterKontent<TParam>(cache)
|
||||
openApiSpec.components.schemas.putAll(cache)
|
||||
val requestType = typeOf<TReq>()
|
||||
val responseType = typeOf<TResp>()
|
||||
val paramType = typeOf<TParam>()
|
||||
return block.invoke(paramType, requestType, responseType)
|
||||
private fun parseThrowables(throwables: Set<KClass<*>>): Map<Int, OpenApiSpecReferencable> = throwables.mapNotNull {
|
||||
errorMap[it.createType()]
|
||||
}.toMap()
|
||||
|
||||
fun ResponseInfo.parseErrorInfo(
|
||||
errorType: KType,
|
||||
responseType: KType
|
||||
) {
|
||||
errorMap = errorMap.plus(errorType to responseType.toResponseSpec(this))
|
||||
}
|
||||
|
||||
// TODO These two lookin' real similar 👀 Combine?
|
||||
|
@ -0,0 +1,33 @@
|
||||
package org.leafygreens.kompendium
|
||||
|
||||
import io.ktor.routing.Route
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
object KompendiumPreFlight {
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> methodNotarizationPreFlight(
|
||||
block: (KType, KType, KType) -> Route
|
||||
): Route {
|
||||
Kompendium.cache = Kontent.generateKontent<TResp>(Kompendium.cache)
|
||||
Kompendium.cache = Kontent.generateKontent<TReq>(Kompendium.cache)
|
||||
Kompendium.cache = Kontent.generateParameterKontent<TParam>(Kompendium.cache)
|
||||
Kompendium.openApiSpec.components.schemas.putAll(Kompendium.cache)
|
||||
val requestType = typeOf<TReq>()
|
||||
val responseType = typeOf<TResp>()
|
||||
val paramType = typeOf<TParam>()
|
||||
return block.invoke(paramType, requestType, responseType)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified TErr: Throwable, reified TResp : Any> errorNotarizationPreFlight(
|
||||
block: (KType, KType) -> Unit
|
||||
) {
|
||||
Kompendium.cache = Kontent.generateKontent<TResp>(Kompendium.cache)
|
||||
Kompendium.openApiSpec.components.schemas.putAll(Kompendium.cache)
|
||||
val errorType = typeOf<TErr>()
|
||||
val responseType = typeOf<TResp>()
|
||||
return block.invoke(errorType, responseType)
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.leafygreens.kompendium
|
||||
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.features.StatusPages
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.method
|
||||
import io.ktor.util.pipeline.PipelineContext
|
||||
import io.ktor.util.pipeline.PipelineInterceptor
|
||||
import org.leafygreens.kompendium.Kompendium.parseErrorInfo
|
||||
import org.leafygreens.kompendium.Kompendium.parseMethodInfo
|
||||
import org.leafygreens.kompendium.KompendiumPreFlight.errorNotarizationPreFlight
|
||||
import org.leafygreens.kompendium.models.meta.MethodInfo
|
||||
import org.leafygreens.kompendium.models.meta.ResponseInfo
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecPathItem
|
||||
|
||||
object Notarized {
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedGet(
|
||||
info: MethodInfo,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route =
|
||||
KompendiumPreFlight.methodNotarizationPreFlight<TParam, Unit, TResp>() { paramType, requestType, responseType ->
|
||||
val path = Kompendium.pathCalculator.calculate(this)
|
||||
Kompendium.openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
Kompendium.openApiSpec.paths[path]?.get =
|
||||
info.parseMethodInfo(HttpMethod.Get, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Get) { handle(body) }
|
||||
}
|
||||
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPost(
|
||||
info: MethodInfo,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route =
|
||||
KompendiumPreFlight.methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||
val path = Kompendium.pathCalculator.calculate(this)
|
||||
Kompendium.openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
Kompendium.openApiSpec.paths[path]?.post =
|
||||
info.parseMethodInfo(HttpMethod.Post, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Post) { handle(body) }
|
||||
}
|
||||
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPut(
|
||||
info: MethodInfo,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>,
|
||||
): Route =
|
||||
KompendiumPreFlight.methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||
val path = Kompendium.pathCalculator.calculate(this)
|
||||
Kompendium.openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
Kompendium.openApiSpec.paths[path]?.put =
|
||||
info.parseMethodInfo(HttpMethod.Put, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Put) { handle(body) }
|
||||
}
|
||||
|
||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedDelete(
|
||||
info: MethodInfo,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route =
|
||||
KompendiumPreFlight.methodNotarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
||||
val path = Kompendium.pathCalculator.calculate(this)
|
||||
Kompendium.openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
Kompendium.openApiSpec.paths[path]?.delete =
|
||||
info.parseMethodInfo(HttpMethod.Delete, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Delete) { handle(body) }
|
||||
}
|
||||
|
||||
inline fun <reified TErr : Throwable, reified TResp : Any> StatusPages.Configuration.notarizedException(
|
||||
info: ResponseInfo,
|
||||
noinline handler: suspend PipelineContext<Unit, ApplicationCall>.(TErr) -> Unit
|
||||
) = errorNotarizationPreFlight<TErr, TResp>() { errorType, responseType ->
|
||||
info.parseErrorInfo(errorType, responseType)
|
||||
exception(handler)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package org.leafygreens.kompendium.models.meta
|
||||
|
||||
import kotlin.reflect.KType
|
||||
import org.leafygreens.kompendium.models.oas.OpenApiSpecResponse
|
||||
|
||||
typealias ErrorMap = Map<KType, Pair<Int, OpenApiSpecResponse>?>
|
@ -1,5 +1,7 @@
|
||||
package org.leafygreens.kompendium.models.meta
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
// TODO Seal and extend by method type?
|
||||
data class MethodInfo(
|
||||
val summary: String,
|
||||
@ -8,5 +10,6 @@ data class MethodInfo(
|
||||
val requestInfo: RequestInfo? = null,
|
||||
val tags: Set<String> = emptySet(),
|
||||
val deprecated: Boolean = false,
|
||||
val securitySchemes: Set<String> = emptySet()
|
||||
val securitySchemes: Set<String> = emptySet(),
|
||||
val canThrow: Set<KClass<*>> = emptySet()
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.leafygreens.kompendium.models.meta
|
||||
|
||||
data class ResponseInfo(
|
||||
val status: Int, // TODO How to handle error codes?
|
||||
val status: Int,
|
||||
val description: String,
|
||||
val mediaTypes: List<String> = listOf("application/json")
|
||||
)
|
||||
|
@ -6,6 +6,7 @@ import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.application.install
|
||||
import io.ktor.features.ContentNegotiation
|
||||
import io.ktor.features.StatusPages
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.jackson.jackson
|
||||
@ -19,10 +20,11 @@ import java.net.URI
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import org.leafygreens.kompendium.Kompendium.notarizedDelete
|
||||
import org.leafygreens.kompendium.Kompendium.notarizedGet
|
||||
import org.leafygreens.kompendium.Kompendium.notarizedPost
|
||||
import org.leafygreens.kompendium.Kompendium.notarizedPut
|
||||
import org.leafygreens.kompendium.Notarized.notarizedDelete
|
||||
import org.leafygreens.kompendium.Notarized.notarizedException
|
||||
import org.leafygreens.kompendium.Notarized.notarizedGet
|
||||
import org.leafygreens.kompendium.Notarized.notarizedPost
|
||||
import org.leafygreens.kompendium.Notarized.notarizedPut
|
||||
import org.leafygreens.kompendium.annotations.QueryParam
|
||||
import org.leafygreens.kompendium.models.meta.MethodInfo
|
||||
import org.leafygreens.kompendium.models.meta.RequestInfo
|
||||
@ -34,6 +36,7 @@ import org.leafygreens.kompendium.models.oas.OpenApiSpecServer
|
||||
import org.leafygreens.kompendium.routes.openApi
|
||||
import org.leafygreens.kompendium.routes.redoc
|
||||
import org.leafygreens.kompendium.util.ComplexRequest
|
||||
import org.leafygreens.kompendium.util.ExceptionResponse
|
||||
import org.leafygreens.kompendium.util.KompendiumHttpCodes
|
||||
import org.leafygreens.kompendium.util.TestCreatedResponse
|
||||
import org.leafygreens.kompendium.util.TestData
|
||||
@ -374,6 +377,42 @@ internal class KompendiumTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Generates additional responses when passed a throwable`() {
|
||||
withTestApplication({
|
||||
statusPageModule()
|
||||
configModule()
|
||||
docs()
|
||||
notarizedGetWithNotarizedException()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = TestData.getFileSnapshot("notarized_get_with_exception_response.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
fun `Generates additional responses when passed multiple throwables`() {
|
||||
withTestApplication({
|
||||
statusPageMultiExceptions()
|
||||
configModule()
|
||||
docs()
|
||||
notarizedGetWithMultipleThrowables()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = TestData.getFileSnapshot("notarized_get_with_multiple_exception_responses.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val testGetResponse = ResponseInfo(KompendiumHttpCodes.OK, "A Successful Endeavor")
|
||||
val testPostResponse = ResponseInfo(KompendiumHttpCodes.CREATED, "A Successful Endeavor")
|
||||
@ -381,6 +420,12 @@ internal class KompendiumTest {
|
||||
ResponseInfo(KompendiumHttpCodes.NO_CONTENT, "A Successful Endeavor", mediaTypes = emptyList())
|
||||
val testRequest = RequestInfo("A Test request")
|
||||
val testGetInfo = MethodInfo("Another get test", "testing more", testGetResponse)
|
||||
val testGetWithException = testGetInfo.copy(
|
||||
canThrow = setOf(Exception::class)
|
||||
)
|
||||
val testGetWithMultipleExceptions = testGetInfo.copy(
|
||||
canThrow = setOf(AccessDeniedException::class, Exception::class)
|
||||
)
|
||||
val testPostInfo = MethodInfo("Test post endpoint", "Post your tests here!", testPostResponse, testRequest)
|
||||
val testPutInfo = MethodInfo("Test put endpoint", "Put your tests here!", testPostResponse, testRequest)
|
||||
val testDeleteInfo = MethodInfo("Test delete endpoint", "testing my deletes", testDeleteResponse)
|
||||
@ -396,6 +441,45 @@ internal class KompendiumTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Application.statusPageModule() {
|
||||
install(StatusPages) {
|
||||
notarizedException<Exception, ExceptionResponse>(info = ResponseInfo(400, "Bad Things Happened")) {
|
||||
call.respond(HttpStatusCode.BadRequest, ExceptionResponse("Why you do dis?"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Application.statusPageMultiExceptions() {
|
||||
install(StatusPages) {
|
||||
notarizedException<AccessDeniedException, Unit>(info = ResponseInfo(403, "New API who dis?")) {
|
||||
call.respond(HttpStatusCode.Forbidden)
|
||||
}
|
||||
notarizedException<Exception, ExceptionResponse>(info = ResponseInfo(400, "Bad Things Happened")) {
|
||||
call.respond(HttpStatusCode.BadRequest, ExceptionResponse("Why you do dis?"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Application.notarizedGetWithNotarizedException() {
|
||||
routing {
|
||||
route("/test") {
|
||||
notarizedGet<TestParams, TestResponse>(testGetWithException) {
|
||||
error("something terrible has happened!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Application.notarizedGetWithMultipleThrowables() {
|
||||
routing {
|
||||
route("/test") {
|
||||
notarizedGet<TestParams, TestResponse>(testGetWithMultipleExceptions) {
|
||||
error("something terrible has happened!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Application.notarizedGetModule() {
|
||||
routing {
|
||||
route("/test") {
|
||||
|
@ -71,3 +71,5 @@ sealed class TestSealedClass(open val a: String)
|
||||
data class SimpleTSC(val b: Int) : TestSealedClass("hey")
|
||||
open class MediumTSC(override val a: String, val b: Int) : TestSealedClass(a)
|
||||
data class WildTSC(val c: Boolean, val d: String, val e: Int) : MediumTSC(d, e)
|
||||
|
||||
data class ExceptionResponse(val message: String)
|
||||
|
@ -0,0 +1,104 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish \uD83D\uDE09 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/lg-backbone/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" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400" : {
|
||||
"description" : "Bad Things Happened",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/ExceptionResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"ExceptionResponse" : {
|
||||
"properties" : {
|
||||
"message" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
},
|
||||
"type" : "object"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
},
|
||||
"type" : "object"
|
||||
},
|
||||
"Int" : {
|
||||
"format" : "int32",
|
||||
"type" : "integer"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish \uD83D\uDE09 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/lg-backbone/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" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403" : {
|
||||
"description" : "New API who dis?"
|
||||
},
|
||||
"400" : {
|
||||
"description" : "Bad Things Happened",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/ExceptionResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"ExceptionResponse" : {
|
||||
"properties" : {
|
||||
"message" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
},
|
||||
"type" : "object"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
},
|
||||
"type" : "object"
|
||||
},
|
||||
"Int" : {
|
||||
"format" : "int32",
|
||||
"type" : "integer"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
}
|
Reference in New Issue
Block a user