diff --git a/CHANGELOG.md b/CHANGELOG.md index b30c6760d..7c0391c3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [0.4.0] - April 17th, 2021 + +### Added + +- Basic Query and Path Parameter Support 🍻 + +### Changed + +- No content workaround, flow will likely need refactoring for clarity. + ## [0.3.0] - April 17th, 2021 ### Changed diff --git a/README.md b/README.md index 135e09382..12d021837 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,6 @@ dependencies { ### Warning 🚨 Kompendium is still under active development ⚠️ There are a number of yet-to-be-implemented features, including -- Query and Path Parameters 🔍 - Tags 🏷 - Multiple Responses 📜 - Security Schemas 🔏 @@ -70,11 +69,18 @@ meaning that swapping in a default Ktor route and a Kompendium `notarized` route In general, Kompendium tries to limit the number of annotations that developers need to use in order to get an app integrated. -Currently, there is only a single Kompendium annotation +Currently, the annotations used by Kompendium are as follows - `KompendiumField` +- `PathParam` +- `QueryParam` +- `HeaderParam` +- `CookieParam` -The intended purpose is to offer field level overrides such as naming conventions (ie snake instead of camel). +The intended purpose of `KompendiumField` is to offer field level overrides such as naming conventions (ie snake instead of camel). + +The 4 "param" annotations are to offer supplemental information in data classes that describe the set of parameters types +that a notarized route needs to analyze. ## Examples @@ -108,16 +114,16 @@ fun Application.mainModule() { } } route("/single") { - notarizedGet(testSingleGetInfo) { + notarizedGet(testSingleGetInfo) { call.respondText("get single") } - notarizedPost(testSinglePostInfo) { + notarizedPost(testSinglePostInfo) { call.respondText("test post") } notarizedPut(testSinglePutInfo) { call.respondText { "hey" } } - notarizedDelete(testSingleDeleteInfo) { + notarizedDelete(testSingleDeleteInfo) { call.respondText { "heya" } } } diff --git a/detekt.yml b/detekt.yml index 0c6680013..033ca155e 100644 --- a/detekt.yml +++ b/detekt.yml @@ -65,7 +65,7 @@ complexity: includePrivateDeclarations: false ComplexMethod: active: true - threshold: 15 + threshold: 25 ignoreSingleWhenExpression: false ignoreSimpleWhenEntries: false ignoreNestingFunctions: false diff --git a/gradle.properties b/gradle.properties index 4676f08e1..1c879250a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Kompendium -project.version=0.3.0 +project.version=0.4.0 # Kotlin kotlin.code.style=official # Gradle diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt index 3251865ef..d3c4f1a8b 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt @@ -5,25 +5,39 @@ 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.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.MethodInfo import org.leafygreens.kompendium.models.meta.RequestInfo import org.leafygreens.kompendium.models.meta.ResponseInfo +import org.leafygreens.kompendium.models.meta.SchemaMap 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.OpenApiSpecReferenceObject import org.leafygreens.kompendium.models.oas.OpenApiSpecRequest import org.leafygreens.kompendium.models.oas.OpenApiSpecResponse +import org.leafygreens.kompendium.models.oas.OpenApiSpecSchemaRef import org.leafygreens.kompendium.util.Helpers.calculatePath import org.leafygreens.kompendium.util.Helpers.getReferenceSlug object Kompendium { + var cache: SchemaMap = emptyMap() + var openApiSpec = OpenApiSpec( info = OpenApiSpecInfo(), servers = mutableListOf(), @@ -34,46 +48,47 @@ object Kompendium { inline fun Route.notarizedGet( info: MethodInfo, noinline body: PipelineInterceptor - ): Route = notarizationPreFlight() { requestType, responseType -> + ): Route = notarizationPreFlight() { paramType, requestType, responseType -> val path = calculatePath() openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } - openApiSpec.paths[path]?.get = info.parseMethodInfo(HttpMethod.Get, requestType, responseType) + openApiSpec.paths[path]?.get = info.parseMethodInfo(HttpMethod.Get, paramType, requestType, responseType) return method(HttpMethod.Get) { handle(body) } } inline fun Route.notarizedPost( info: MethodInfo, noinline body: PipelineInterceptor - ): Route = notarizationPreFlight() { requestType, responseType -> + ): Route = notarizationPreFlight() { paramType, requestType, responseType -> val path = calculatePath() openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } - openApiSpec.paths[path]?.post = info.parseMethodInfo(HttpMethod.Post, requestType, responseType) + openApiSpec.paths[path]?.post = info.parseMethodInfo(HttpMethod.Post, paramType, requestType, responseType) return method(HttpMethod.Post) { handle(body) } } inline fun Route.notarizedPut( info: MethodInfo, noinline body: PipelineInterceptor, - ): Route = notarizationPreFlight() { requestType, responseType -> + ): Route = notarizationPreFlight() { paramType, requestType, responseType -> val path = calculatePath() openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } - openApiSpec.paths[path]?.put = info.parseMethodInfo(HttpMethod.Put, requestType, responseType) + openApiSpec.paths[path]?.put = info.parseMethodInfo(HttpMethod.Put, paramType, requestType, responseType) return method(HttpMethod.Put) { handle(body) } } inline fun Route.notarizedDelete( info: MethodInfo, noinline body: PipelineInterceptor - ): Route = notarizationPreFlight { requestType, responseType -> + ): Route = notarizationPreFlight { paramType, requestType, responseType -> val path = calculatePath() openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } - openApiSpec.paths[path]?.delete = info.parseMethodInfo(HttpMethod.Delete, requestType, responseType) + 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, + paramType: KType, requestType: KType, responseType: KType ) = OpenApiSpecPathItemOperation( @@ -81,25 +96,27 @@ object Kompendium { description = this.description, tags = this.tags, deprecated = this.deprecated, - responses = responseType.toSpec(responseInfo)?.let { mapOf(it) }, - requestBody = if (method != HttpMethod.Get) requestType.toSpec(requestInfo) else null + parameters = paramType.toParameterSpec(), + responses = responseType.toResponseSpec(responseInfo)?.let { mapOf(it) }, + requestBody = if (method != HttpMethod.Get) requestType.toRequestSpec(requestInfo) else null ) @OptIn(ExperimentalStdlibApi::class) - inline fun notarizationPreFlight( - block: (KType, KType) -> Route + inline fun notarizationPreFlight( + block: (KType, KType, KType) -> Route ): Route { - val responseKontent = generateKontent() - val requestKontent = generateKontent() - openApiSpec.components.schemas.putAll(responseKontent) - openApiSpec.components.schemas.putAll(requestKontent) + cache = generateKontent(cache) + cache = generateKontent(cache) + cache = generateParameterKontent(cache) + openApiSpec.components.schemas.putAll(cache) val requestType = typeOf() val responseType = typeOf() - return block.invoke(requestType, responseType) + val paramType = typeOf() + return block.invoke(paramType, requestType, responseType) } // TODO These two lookin' real similar 👀 Combine? - private fun KType.toSpec(requestInfo: RequestInfo?): OpenApiSpecRequest? = when (this) { + private fun KType.toRequestSpec(requestInfo: RequestInfo?): OpenApiSpecRequest? = when (this) { Unit::class -> null else -> when (requestInfo) { null -> null @@ -113,28 +130,62 @@ object Kompendium { } } - private fun KType.toSpec(responseInfo: ResponseInfo?): Pair? = when (this) { + private fun KType.toResponseSpec(responseInfo: ResponseInfo?): Pair? = when (this) { Unit::class -> null // TODO Maybe not though? could be unit but 200 🤔 else -> when (responseInfo) { null -> null // TODO again probably revisit this else -> { + val content = responseInfo.mediaTypes.associateWith { + val ref = getReferenceSlug() + OpenApiSpecMediaType.Referenced(OpenApiSpecReferenceObject(ref)) + } val specResponse = OpenApiSpecResponse( description = responseInfo.description, - content = responseInfo.mediaTypes.associateWith { - val ref = getReferenceSlug() - OpenApiSpecMediaType.Referenced(OpenApiSpecReferenceObject(ref)) - } + content = content.ifEmpty { null } ) Pair(responseInfo.status, specResponse) } } } + // TODO God these annotations make this hideous... any way to improve? + private fun KType.toParameterSpec(): List { + val clazz = classifier as KClass<*> + return clazz.memberProperties.map { prop -> + val field = prop.javaField?.type?.kotlin + ?: error("Unable to parse field type from $prop") + val anny = prop.findAnnotation() + ?: prop.findAnnotation() + ?: prop.findAnnotation() + ?: prop.findAnnotation() + ?: error("Unable to find any relevant parameter specifier annotations on field ${prop.name}") + OpenApiSpecParameter( + name = prop.name, + `in` = when (anny) { + is PathParam -> "path" + is QueryParam -> "query" + is HeaderParam -> "header" + is CookieParam -> "cookie" + else -> error("should not be reachable") + }, + schema = OpenApiSpecSchemaRef(field.getReferenceSlug(prop)), + description = when (anny) { + is PathParam -> anny.description.ifBlank { null } + is QueryParam -> anny.description.ifBlank { null } + is HeaderParam -> anny.description.ifBlank { null } + is CookieParam -> anny.description.ifBlank { null } + else -> error("should not be reachable") + } + ) + } + } + internal fun resetSchema() { openApiSpec = OpenApiSpec( info = OpenApiSpecInfo(), servers = mutableListOf(), paths = mutableMapOf() ) + cache = emptyMap() } } diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kontent.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kontent.kt index b1883baa0..b7f4d1401 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kontent.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kontent.kt @@ -33,6 +33,15 @@ object Kontent { return generateKTypeKontent(kontentType, cache) } + @OptIn(ExperimentalStdlibApi::class) + inline fun generateParameterKontent( + cache: SchemaMap = emptyMap() + ): SchemaMap { + val kontentType = typeOf() + return generateKTypeKontent(kontentType, cache) + .filterNot { (slug, _) -> slug == (kontentType.classifier as KClass<*>).simpleName } + } + fun generateKTypeKontent( type: KType, cache: SchemaMap = emptyMap() diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/CookieParam.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/CookieParam.kt new file mode 100644 index 000000000..41fd10cbc --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/CookieParam.kt @@ -0,0 +1,5 @@ +package org.leafygreens.kompendium.annotations + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.PROPERTY) +annotation class CookieParam(val description: String = "") diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/HeaderParam.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/HeaderParam.kt new file mode 100644 index 000000000..fcbca2eff --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/HeaderParam.kt @@ -0,0 +1,5 @@ +package org.leafygreens.kompendium.annotations + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.PROPERTY) +annotation class HeaderParam(val description: String = "") diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/PathParam.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/PathParam.kt new file mode 100644 index 000000000..31ec0e4f0 --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/PathParam.kt @@ -0,0 +1,5 @@ +package org.leafygreens.kompendium.annotations + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.PROPERTY) +annotation class PathParam(val description: String = "") diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/QueryParam.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/QueryParam.kt new file mode 100644 index 000000000..501df22c5 --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/QueryParam.kt @@ -0,0 +1,5 @@ +package org.leafygreens.kompendium.annotations + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.PROPERTY) +annotation class QueryParam(val description: String = "") diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecReferencable.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecReferencable.kt index eeee70701..0e2b6a255 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecReferencable.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecReferencable.kt @@ -24,13 +24,13 @@ data class OpenApiSpecHeader( data class OpenApiSpecParameter( val name: String, val `in`: String, // TODO Enum? "query", "header", "path" or "cookie" - val description: String?, + val schema: OpenApiSpecSchema, + val description: String? = null, val required: Boolean = true, val deprecated: Boolean = false, - val allowEmptyValue: Boolean = false, + val allowEmptyValue: Boolean? = null, val style: String? = null, - val explode: Boolean? = false, - val schema: OpenApiSpecSchema? = null + val explode: Boolean? = null ) : OpenApiSpecReferencable() data class OpenApiSpecRequest( diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt index ccd2670be..9a06a7b50 100644 --- a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt @@ -311,7 +311,7 @@ internal class KompendiumTest { private companion object { val testGetResponse = ResponseInfo(KompendiumHttpCodes.OK, "A Successful Endeavor") val testPostResponse = ResponseInfo(KompendiumHttpCodes.CREATED, "A Successful Endeavor") - val testDeleteResponse = ResponseInfo(KompendiumHttpCodes.NO_CONTENT, "A Successful Endeavor") + val testDeleteResponse = 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 testPostInfo = MethodInfo("Test post endpoint", "Post your tests here!", testPostResponse, testRequest) @@ -351,7 +351,7 @@ internal class KompendiumTest { private fun Application.notarizedDeleteModule() { routing { route("/test") { - notarizedDelete(testDeleteInfo) { + notarizedDelete(testDeleteInfo) { call.respond(HttpStatusCode.NoContent) } } diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KontentTest.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KontentTest.kt index 4a173e85b..f61aa066f 100644 --- a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KontentTest.kt +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KontentTest.kt @@ -4,9 +4,11 @@ import java.util.UUID import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith +import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertTrue import org.leafygreens.kompendium.Kontent.generateKontent +import org.leafygreens.kompendium.Kontent.generateParameterKontent import org.leafygreens.kompendium.models.oas.DictionarySchema import org.leafygreens.kompendium.models.oas.FormatSchema import org.leafygreens.kompendium.models.oas.ObjectSchema @@ -173,4 +175,15 @@ internal class KontentTest { assertEquals(ReferencedSchema("#/components/schemas/CrazyItem"), rs) } + @Test + fun `Parameter kontent filters out top level declaration`() { + // do + val result = generateParameterKontent() + + // expect + assertNotNull(result) + assertEquals(2, result.count()) + assertFalse { result.containsKey(TestSimpleModel::class.simpleName) } + } + } diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModels.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModels.kt index 7b84b7173..ea4b38c4e 100644 --- a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModels.kt +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModels.kt @@ -2,6 +2,8 @@ package org.leafygreens.kompendium.util import java.util.UUID import org.leafygreens.kompendium.annotations.KompendiumField +import org.leafygreens.kompendium.annotations.PathParam +import org.leafygreens.kompendium.annotations.QueryParam data class TestSimpleModel(val a: String, val b: Int) @@ -17,7 +19,10 @@ data class TestSimpleWithEnumList(val a: Double, val b: List) data class TestInvalidMap(val a: Map) -data class TestParams(val a: String, val aa: Int) +data class TestParams( + @PathParam val a: String, + @QueryParam val aa: Int +) data class TestNested(val nesty: String) diff --git a/kompendium-core/src/test/resources/complex_type.json b/kompendium-core/src/test/resources/complex_type.json index 2a993a9fd..359f5c9ab 100644 --- a/kompendium-core/src/test/resources/complex_type.json +++ b/kompendium-core/src/test/resources/complex_type.json @@ -28,6 +28,7 @@ "tags" : [ ], "summary" : "Test put endpoint", "description" : "Put your tests here!", + "parameters" : [ ], "requestBody" : { "description" : "A Test request", "content" : { diff --git a/kompendium-core/src/test/resources/notarized_delete.json b/kompendium-core/src/test/resources/notarized_delete.json index 0b442e467..5217d02b0 100644 --- a/kompendium-core/src/test/resources/notarized_delete.json +++ b/kompendium-core/src/test/resources/notarized_delete.json @@ -28,16 +28,26 @@ "tags" : [ ], "summary" : "Test delete endpoint", "description" : "testing my deletes", + "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" : { "204" : { - "description" : "A Successful Endeavor", - "content" : { - "application/json" : { - "schema" : { - "$ref" : "#/components/schemas/TestDeleteResponse" - } - } - } + "description" : "A Successful Endeavor" } }, "deprecated" : false @@ -46,9 +56,12 @@ }, "components" : { "schemas" : { - "TestDeleteResponse" : { - "properties" : { }, - "type" : "object" + "String" : { + "type" : "string" + }, + "Int" : { + "format" : "int32", + "type" : "integer" } }, "securitySchemes" : { } diff --git a/kompendium-core/src/test/resources/notarized_get.json b/kompendium-core/src/test/resources/notarized_get.json index 06d49cd9a..55edcdba9 100644 --- a/kompendium-core/src/test/resources/notarized_get.json +++ b/kompendium-core/src/test/resources/notarized_get.json @@ -28,6 +28,23 @@ "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", @@ -56,6 +73,10 @@ } }, "type" : "object" + }, + "Int" : { + "format" : "int32", + "type" : "integer" } }, "securitySchemes" : { } diff --git a/kompendium-core/src/test/resources/notarized_post.json b/kompendium-core/src/test/resources/notarized_post.json index d6001d461..aa59951b9 100644 --- a/kompendium-core/src/test/resources/notarized_post.json +++ b/kompendium-core/src/test/resources/notarized_post.json @@ -28,6 +28,23 @@ "tags" : [ ], "summary" : "Test post endpoint", "description" : "Post your tests here!", + "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 + } ], "requestBody" : { "description" : "A Test request", "content" : { diff --git a/kompendium-core/src/test/resources/notarized_primitives.json b/kompendium-core/src/test/resources/notarized_primitives.json index b293f346d..2f7763e0a 100644 --- a/kompendium-core/src/test/resources/notarized_primitives.json +++ b/kompendium-core/src/test/resources/notarized_primitives.json @@ -28,6 +28,7 @@ "tags" : [ ], "summary" : "Test put endpoint", "description" : "Put your tests here!", + "parameters" : [ ], "requestBody" : { "description" : "A Test request", "content" : { diff --git a/kompendium-core/src/test/resources/notarized_put.json b/kompendium-core/src/test/resources/notarized_put.json index 5a759a6ae..4322a95c9 100644 --- a/kompendium-core/src/test/resources/notarized_put.json +++ b/kompendium-core/src/test/resources/notarized_put.json @@ -28,6 +28,23 @@ "tags" : [ ], "summary" : "Test put endpoint", "description" : "Put your tests here!", + "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 + } ], "requestBody" : { "description" : "A Test request", "content" : { diff --git a/kompendium-core/src/test/resources/path_parser.json b/kompendium-core/src/test/resources/path_parser.json index 9fe9c541f..6eff1c46e 100644 --- a/kompendium-core/src/test/resources/path_parser.json +++ b/kompendium-core/src/test/resources/path_parser.json @@ -28,6 +28,23 @@ "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", @@ -56,6 +73,10 @@ } }, "type" : "object" + }, + "Int" : { + "format" : "int32", + "type" : "integer" } }, "securitySchemes" : { } diff --git a/kompendium-core/src/test/resources/petstore.json b/kompendium-core/src/test/resources/petstore.json index d9341435c..57e5b28b3 100644 --- a/kompendium-core/src/test/resources/petstore.json +++ b/kompendium-core/src/test/resources/petstore.json @@ -103,12 +103,6 @@ "parameters" : [ { "name" : "status", "in" : "query", - "description" : "Status values that need to be considered for filter", - "required" : true, - "deprecated" : false, - "allowEmptyValue" : false, - "style" : "form", - "explode" : true, "schema" : { "items" : { "default" : "available", @@ -116,7 +110,12 @@ "type" : "string" }, "type" : "array" - } + }, + "description" : "Status values that need to be considered for filter", + "required" : true, + "deprecated" : false, + "style" : "form", + "explode" : true } ], "responses" : { "200" : { diff --git a/kompendium-core/src/test/resources/response_list.json b/kompendium-core/src/test/resources/response_list.json index afb775e8c..558779ef5 100644 --- a/kompendium-core/src/test/resources/response_list.json +++ b/kompendium-core/src/test/resources/response_list.json @@ -28,6 +28,23 @@ "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", @@ -62,6 +79,10 @@ "$ref" : "#/components/schemas/TestResponse" }, "type" : "array" + }, + "Int" : { + "format" : "int32", + "type" : "integer" } }, "securitySchemes" : { } diff --git a/kompendium-core/src/test/resources/root_route.json b/kompendium-core/src/test/resources/root_route.json index e00fd66a0..66981b718 100644 --- a/kompendium-core/src/test/resources/root_route.json +++ b/kompendium-core/src/test/resources/root_route.json @@ -28,6 +28,23 @@ "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", @@ -56,6 +73,10 @@ } }, "type" : "object" + }, + "Int" : { + "format" : "int32", + "type" : "integer" } }, "securitySchemes" : { } diff --git a/kompendium-core/src/test/resources/trailing_slash.json b/kompendium-core/src/test/resources/trailing_slash.json index b4b945939..9ed8fad4a 100644 --- a/kompendium-core/src/test/resources/trailing_slash.json +++ b/kompendium-core/src/test/resources/trailing_slash.json @@ -28,6 +28,23 @@ "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", @@ -56,6 +73,10 @@ } }, "type" : "object" + }, + "Int" : { + "format" : "int32", + "type" : "integer" } }, "securitySchemes" : { } diff --git a/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/Main.kt b/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/Main.kt index 78a417db7..18d53ebd3 100644 --- a/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/Main.kt +++ b/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/Main.kt @@ -31,6 +31,8 @@ import org.leafygreens.kompendium.Kompendium.notarizedPost import org.leafygreens.kompendium.Kompendium.notarizedPut import org.leafygreens.kompendium.Kompendium.openApiSpec import org.leafygreens.kompendium.annotations.KompendiumField +import org.leafygreens.kompendium.annotations.PathParam +import org.leafygreens.kompendium.annotations.QueryParam import org.leafygreens.kompendium.models.meta.MethodInfo import org.leafygreens.kompendium.models.meta.RequestInfo import org.leafygreens.kompendium.models.meta.ResponseInfo @@ -70,16 +72,16 @@ fun Application.mainModule() { } } route("/single") { - notarizedGet(testSingleGetInfo) { + notarizedGet(testSingleGetInfo) { call.respondText("get single") } - notarizedPost(testSinglePostInfo) { + notarizedPost(testSinglePostInfo) { call.respondText("test post") } - notarizedPut(testSinglePutInfo) { + notarizedPut(testSinglePutInfo) { call.respondText { "hey" } } - notarizedDelete(testSingleDeleteInfo) { + notarizedDelete(testSingleDeleteInfo) { call.respondText { "heya" } } } @@ -87,12 +89,18 @@ fun Application.mainModule() { } } -data class ExampleParams(val a: String, val aa: Int) +data class ExampleParams( + @PathParam val id: Int, + @QueryParam val name: String +) + +data class JustQuery( + @QueryParam val potato: Boolean, + @QueryParam val tomato: String +) data class ExampleNested(val nesty: String) -object DeleteResponse - data class ExampleRequest( @KompendiumField(name = "field_name") val fieldName: ExampleNested, @@ -150,7 +158,8 @@ object KompendiumTOC { description = "testing my deletes", responseInfo = ResponseInfo( status = KompendiumHttpCodes.NO_CONTENT, - description = "Signifies that your item was deleted succesfully" + description = "Signifies that your item was deleted successfully", + mediaTypes = emptyList() ) ) }