From f413c910955324af322a1c1280d3a947cdd3edf1 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 12 Apr 2021 15:58:09 -0400 Subject: [PATCH 1/4] added notarization for get and post --- kompendium-core/build.gradle.kts | 1 + .../org/leafygreens/kompendium/Kompendium.kt | 35 +++++- .../kompendium/models/OpenApiSpec.kt | 10 +- .../kompendium/models/OpenApiSpecPathItem.kt | 24 ++-- kompendium-playground/build.gradle.kts | 2 - .../leafygreens/kompendium/playground/Main.kt | 50 ++++---- kompendium-processor/build.gradle.kts | 33 ------ .../processor/KompendiumProcessor.kt | 112 ------------------ settings.gradle.kts | 1 - 9 files changed, 79 insertions(+), 189 deletions(-) delete mode 100644 kompendium-processor/build.gradle.kts delete mode 100644 kompendium-processor/src/main/kotlin/org/leafygreens/kompendium/processor/KompendiumProcessor.kt diff --git a/kompendium-core/build.gradle.kts b/kompendium-core/build.gradle.kts index 665d3ea55..50c08531f 100644 --- a/kompendium-core/build.gradle.kts +++ b/kompendium-core/build.gradle.kts @@ -6,6 +6,7 @@ plugins { dependencies { implementation(platform("org.jetbrains.kotlin:kotlin-bom")) implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + implementation(libs.bundles.ktor) testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit") testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.0") 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 a5459bd6a..ad221073f 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt @@ -1,12 +1,45 @@ package org.leafygreens.kompendium +import io.ktor.application.ApplicationCall +import io.ktor.http.HttpMethod +import io.ktor.routing.PathSegmentConstantRouteSelector +import io.ktor.routing.PathSegmentParameterRouteSelector +import io.ktor.routing.RootRouteSelector +import io.ktor.routing.Route +import io.ktor.routing.method +import io.ktor.util.InternalAPI +import io.ktor.util.pipeline.PipelineInterceptor import org.leafygreens.kompendium.models.OpenApiSpec import org.leafygreens.kompendium.models.OpenApiSpecInfo +import org.leafygreens.kompendium.models.OpenApiSpecPathItem +import org.leafygreens.kompendium.models.OpenApiSpecPathItemOperation -class Kompendium { +object Kompendium { val spec = OpenApiSpec( info = OpenApiSpecInfo(), servers = mutableListOf(), paths = mutableMapOf() ) + + fun Route.notarizedGet(body: PipelineInterceptor): Route { + val path = calculatePath() + spec.paths.getOrPut(path) { OpenApiSpecPathItem() } + spec.paths[path]?.get = OpenApiSpecPathItemOperation(tags = setOf("test")) + return method(HttpMethod.Get) { handle(body) } + } + + fun Route.notarizedPost(body: PipelineInterceptor): Route { + val path = calculatePath() + spec.paths.getOrPut(path) { OpenApiSpecPathItem() } + spec.paths[path]?.post = OpenApiSpecPathItemOperation(tags = setOf("test")) + return method(HttpMethod.Post) { handle(body) } + } + + @OptIn(InternalAPI::class) + private fun Route.calculatePath(tail: String = ""): String = when (selector) { + is RootRouteSelector -> tail + is PathSegmentParameterRouteSelector -> parent?.calculatePath("/$selector$tail") ?: "/{$selector}$tail" + is PathSegmentConstantRouteSelector -> parent?.calculatePath("/$selector$tail") ?: "/$selector$tail" + else -> error("unknown selector type $selector") + } } diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpec.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpec.kt index 7872cea12..49bc56763 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpec.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpec.kt @@ -2,13 +2,13 @@ package org.leafygreens.kompendium.models data class OpenApiSpec( val openapi: String = "3.0.3", - val info: OpenApiSpecInfo? = null, + val info: OpenApiSpecInfo, // TODO Needs to default to server object with url of `/` - val servers: MutableList? = null, - val paths: MutableMap? = null, + val servers: MutableList = mutableListOf(), + val paths: MutableMap = mutableMapOf(), val components: OpenApiSpecComponents? = null, // todo needs to reference objects in the components -> security scheme 🤔 - val security: List>>? = null, - val tags: List? = null, + val security: MutableList>> = mutableListOf(), + val tags: MutableList = mutableListOf(), val externalDocs: OpenApiSpecExternalDocumentation? = null ) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt index 94f7b85e9..3d10b6d79 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt @@ -1,16 +1,16 @@ package org.leafygreens.kompendium.models data class OpenApiSpecPathItem( - val summary: String? = null, - val description: String? = null, - val get: OpenApiSpecPathItemOperation? = null, - val put: OpenApiSpecPathItemOperation? = null, - val post: OpenApiSpecPathItemOperation? = null, - val delete: OpenApiSpecPathItemOperation? = null, - val options: OpenApiSpecPathItemOperation? = null, - val head: OpenApiSpecPathItemOperation? = null, - val patch: OpenApiSpecPathItemOperation? = null, - val trace: OpenApiSpecPathItemOperation? = null, - val servers: List? = null, - val parameters: List? = null + var summary: String? = null, + var description: String? = null, + var get: OpenApiSpecPathItemOperation? = null, + var put: OpenApiSpecPathItemOperation? = null, + var post: OpenApiSpecPathItemOperation? = null, + var delete: OpenApiSpecPathItemOperation? = null, + var options: OpenApiSpecPathItemOperation? = null, + var head: OpenApiSpecPathItemOperation? = null, + var patch: OpenApiSpecPathItemOperation? = null, + var trace: OpenApiSpecPathItemOperation? = null, + var servers: List? = null, + var parameters: List? = null ) diff --git a/kompendium-playground/build.gradle.kts b/kompendium-playground/build.gradle.kts index ca6801f1f..b6d02a1db 100644 --- a/kompendium-playground/build.gradle.kts +++ b/kompendium-playground/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - kotlin("kapt") application } @@ -8,7 +7,6 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation(projects.kompendiumCore) - kapt(projects.kompendiumProcessor) implementation(libs.bundles.ktor) implementation(libs.bundles.logging) 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 c0b45afbf..b25d3f24d 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 @@ -2,33 +2,20 @@ package org.leafygreens.kompendium.playground import io.ktor.application.Application import io.ktor.application.call +import io.ktor.application.install +import io.ktor.features.ContentNegotiation +import io.ktor.jackson.jackson +import io.ktor.response.respond import io.ktor.response.respondText import io.ktor.routing.get import io.ktor.routing.route import io.ktor.routing.routing import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty -import org.leafygreens.kompendium.annotations.KompendiumContact -import org.leafygreens.kompendium.annotations.KompendiumInfo -import org.leafygreens.kompendium.annotations.KompendiumLicense -import org.leafygreens.kompendium.annotations.KompendiumModule -import org.leafygreens.kompendium.annotations.KompendiumServers +import org.leafygreens.kompendium.Kompendium +import org.leafygreens.kompendium.Kompendium.notarizedGet +import org.leafygreens.kompendium.Kompendium.notarizedPost -@KompendiumInfo( - title = "Test API", - version = "0.0.1", - description = "An API for testing" -) -@KompendiumContact( - name = "Homer Simpson", - url = "https://en.wikipedia.org/wiki/The_Simpsons", - email = "chunkylover53@aol.com" -) -@KompendiumLicense( - name = "DOH", - url = "https://opensource.org/licenses/DOH" -) -@KompendiumServers(urls = [ "https://thesimpsonsquoteapi.glitch.me/quotes" ]) fun main() { embeddedServer( Netty, @@ -37,12 +24,29 @@ fun main() { ).start(wait = true) } -@KompendiumModule fun Application.mainModule() { + install(ContentNegotiation) { + jackson() + } routing { - route("/") { + route("/test") { + route("/{id}") { + notarizedGet { + call.respondText("get by id") + } + } + route("/single") { + notarizedGet { + call.respondText("get single") + } + notarizedPost { + call.respondText("test post") + } + } + } + route("/openapi.json") { get { - call.respondText("hi") + call.respond(Kompendium.spec) } } } diff --git a/kompendium-processor/build.gradle.kts b/kompendium-processor/build.gradle.kts deleted file mode 100644 index bb3cd3747..000000000 --- a/kompendium-processor/build.gradle.kts +++ /dev/null @@ -1,33 +0,0 @@ -plugins { - kotlin("kapt") - `java-library` - `maven-publish` -} - -dependencies { - implementation(platform("org.jetbrains.kotlin:kotlin-bom")) - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") - implementation(projects.kompendiumCore) - implementation("com.google.auto.service:auto-service:1.0") - kapt("com.google.auto.service:auto-service:1.0") - testImplementation("org.jetbrains.kotlin:kotlin-test") - testImplementation("org.jetbrains.kotlin:kotlin-test-junit") -} -// -//publishing { -// repositories { -// maven { -// name = "GithubPackages" -// url = uri("https://maven.pkg.github.com/lg-backbone/kompendium") -// credentials { -// username = System.getenv("GITHUB_ACTOR") -// password = System.getenv("GITHUB_TOKEN") -// } -// } -// } -// publications { -// create("kompendium") { -// from(components["kotlin"]) -// } -// } -//} diff --git a/kompendium-processor/src/main/kotlin/org/leafygreens/kompendium/processor/KompendiumProcessor.kt b/kompendium-processor/src/main/kotlin/org/leafygreens/kompendium/processor/KompendiumProcessor.kt deleted file mode 100644 index 48fbc5885..000000000 --- a/kompendium-processor/src/main/kotlin/org/leafygreens/kompendium/processor/KompendiumProcessor.kt +++ /dev/null @@ -1,112 +0,0 @@ -package org.leafygreens.kompendium.processor - -import com.google.auto.service.AutoService -import java.net.URI -import javax.annotation.processing.AbstractProcessor -import javax.annotation.processing.Processor -import javax.annotation.processing.RoundEnvironment -import javax.annotation.processing.SupportedSourceVersion -import javax.lang.model.SourceVersion -import javax.lang.model.element.TypeElement -import org.leafygreens.kompendium.Kompendium -import org.leafygreens.kompendium.annotations.KompendiumContact -import org.leafygreens.kompendium.annotations.KompendiumInfo -import org.leafygreens.kompendium.annotations.KompendiumLicense -import org.leafygreens.kompendium.annotations.KompendiumModule -import org.leafygreens.kompendium.annotations.KompendiumServers -import org.leafygreens.kompendium.models.OpenApiSpecInfoContact -import org.leafygreens.kompendium.models.OpenApiSpecInfoLicense -import org.leafygreens.kompendium.models.OpenApiSpecServer - -@AutoService(Processor::class) -@SupportedSourceVersion(SourceVersion.RELEASE_8) -class KompendiumProcessor : AbstractProcessor() { - - private val kompendium = Kompendium() - - companion object { - const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated" - } - - override fun getSupportedAnnotationTypes(): MutableSet { - return mutableSetOf( - KompendiumInfo::class.java.canonicalName, - KompendiumContact::class.java.canonicalName, - KompendiumLicense::class.java.canonicalName, - KompendiumServers::class.java.canonicalName, - KompendiumModule::class.java.canonicalName - ) - } - - // TODO Throw error if more than 1 info, contact, etc.? - override fun process(annotations: MutableSet?, roundEnv: RoundEnvironment?): Boolean { - roundEnv?.getElementsAnnotatedWith(KompendiumInfo::class.java)?.forEach { - val info = it.getAnnotation(KompendiumInfo::class.java) - processKompendiumInfo(info) - } - roundEnv?.getElementsAnnotatedWith(KompendiumContact::class.java)?.forEach { - val contact = it.getAnnotation(KompendiumContact::class.java) - processKompendiumContact(contact) - } - roundEnv?.getElementsAnnotatedWith(KompendiumLicense::class.java)?.forEach { - val license = it.getAnnotation(KompendiumLicense::class.java) - processKompendiumLicense(license) - } - roundEnv?.getElementsAnnotatedWith(KompendiumServers::class.java)?.forEach { - val servers = it.getAnnotation(KompendiumServers::class.java) - processKompendiumServers(servers) - } - return true - } - - private fun processKompendiumInfo(info: KompendiumInfo) { - kompendium.spec.info?.apply { - this.title = info.title - this.version = info.version - info.description.blankToNull()?.let { desc -> - this.description = desc - } - info.termsOfService.blankToNull()?.let { tos -> - this.termsOfService = URI(tos) - } - } - } - - private fun processKompendiumContact(contact: KompendiumContact) { - kompendium.spec.info?.apply { - this.contact = OpenApiSpecInfoContact( - name = contact.name - ).apply { - contact.url.blankToNull()?.let { url -> - this.url = URI(url) - } - contact.email.blankToNull()?.let { email -> - this.email = email - } - } - } - } - - private fun processKompendiumLicense(license: KompendiumLicense) { - kompendium.spec.info?.apply { - this.license = OpenApiSpecInfoLicense( - name = license.name - ).apply { - license.url.blankToNull()?.let { url -> - this.url = URI(url) - } - } - } - } - - private fun processKompendiumServers(servers: KompendiumServers) { - servers.urls.forEach { url -> - kompendium.spec.servers?.add(OpenApiSpecServer(URI(url))) - } - } - - private fun String.blankToNull(): String? = ifBlank { - null - } - -} diff --git a/settings.gradle.kts b/settings.gradle.kts index bbf181450..936601000 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,5 @@ rootProject.name = "kompendium" include("kompendium-core") -include("kompendium-processor") include("kompendium-playground") // Feature Previews -- 2.49.0 From be6c84b77e9c3948864ba136d07150970f228c4b Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 12 Apr 2021 19:08:04 -0400 Subject: [PATCH 2/4] generating object schemas --- .../org/leafygreens/kompendium/Kompendium.kt | 59 +++++++++++++------ .../kompendium/annotations/KompendiumField.kt | 5 ++ .../models/OpenApiSpecComponentSchema.kt | 14 +++++ .../models/OpenApiSpecComponents.kt | 1 + .../models/OpenApiSpecPathItemOperation.kt | 26 ++++---- .../leafygreens/kompendium/util/Helpers.kt | 45 ++++++++++++++ .../leafygreens/kompendium/KompendiumTest.kt | 3 +- .../leafygreens/kompendium/util/TestData.kt | 2 +- .../leafygreens/kompendium/playground/Main.kt | 42 +++++++++++-- 9 files changed, 158 insertions(+), 39 deletions(-) create mode 100644 kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumField.kt create mode 100644 kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt create mode 100644 kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt 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 ad221073f..33ac15714 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt @@ -2,44 +2,69 @@ package org.leafygreens.kompendium import io.ktor.application.ApplicationCall import io.ktor.http.HttpMethod -import io.ktor.routing.PathSegmentConstantRouteSelector -import io.ktor.routing.PathSegmentParameterRouteSelector -import io.ktor.routing.RootRouteSelector import io.ktor.routing.Route +import io.ktor.routing.createRouteFromPath import io.ktor.routing.method -import io.ktor.util.InternalAPI import io.ktor.util.pipeline.PipelineInterceptor import org.leafygreens.kompendium.models.OpenApiSpec import org.leafygreens.kompendium.models.OpenApiSpecInfo import org.leafygreens.kompendium.models.OpenApiSpecPathItem import org.leafygreens.kompendium.models.OpenApiSpecPathItemOperation +import org.leafygreens.kompendium.util.Helpers.calculatePath +import org.leafygreens.kompendium.util.Helpers.objectSchema + +data class RouteInfo(val summary: String, val description: String? = null) +data class MethodInfo(val summary: String, val description: String? = null, val tags: Set = emptySet()) object Kompendium { - val spec = OpenApiSpec( + val openApiSpec = OpenApiSpec( info = OpenApiSpecInfo(), servers = mutableListOf(), paths = mutableMapOf() ) - fun Route.notarizedGet(body: PipelineInterceptor): Route { + fun Route.notarizedRoute(path: String, info: RouteInfo, build: Route.() -> Unit): Route { + val fullPath = calculatePath().plus(path) + openApiSpec.paths.getOrPut(fullPath) { OpenApiSpecPathItem(summary = info.summary, description = info.description) } + return createRouteFromPath(path).apply(build) + } + + fun Route.notarizedGet(info: MethodInfo, body: PipelineInterceptor): Route { val path = calculatePath() - spec.paths.getOrPut(path) { OpenApiSpecPathItem() } - spec.paths[path]?.get = OpenApiSpecPathItemOperation(tags = setOf("test")) + openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } + openApiSpec.paths[path]?.get = OpenApiSpecPathItemOperation( + summary = info.summary, + description = info.description, + tags = info.tags + ) return method(HttpMethod.Get) { handle(body) } } - fun Route.notarizedPost(body: PipelineInterceptor): Route { + fun Route.notarizedPost(info: MethodInfo, body: PipelineInterceptor): Route { val path = calculatePath() - spec.paths.getOrPut(path) { OpenApiSpecPathItem() } - spec.paths[path]?.post = OpenApiSpecPathItemOperation(tags = setOf("test")) + openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } + openApiSpec.paths[path]?.post = OpenApiSpecPathItemOperation( + summary = info.summary, + description = info.description, + tags = info.tags + ) return method(HttpMethod.Post) { handle(body) } } - @OptIn(InternalAPI::class) - private fun Route.calculatePath(tail: String = ""): String = when (selector) { - is RootRouteSelector -> tail - is PathSegmentParameterRouteSelector -> parent?.calculatePath("/$selector$tail") ?: "/{$selector}$tail" - is PathSegmentConstantRouteSelector -> parent?.calculatePath("/$selector$tail") ?: "/$selector$tail" - else -> error("unknown selector type $selector") + inline fun Route.notarizedPut( + info: MethodInfo, + noinline body: PipelineInterceptor + ): Route { + println(objectSchema(TQ::class)) + println(objectSchema(TP::class)) + println(objectSchema(TR::class)) + val path = calculatePath() + openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } + openApiSpec.paths[path]?.put = OpenApiSpecPathItemOperation( + summary = info.summary, + description = info.description, + tags = info.tags + ) + return method(HttpMethod.Put) { handle(body) } } } diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumField.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumField.kt new file mode 100644 index 000000000..754c073db --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumField.kt @@ -0,0 +1,5 @@ +package org.leafygreens.kompendium.annotations + +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.PROPERTY) +annotation class KompendiumField(val name: String, val description: String) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt new file mode 100644 index 000000000..92a3d2c30 --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt @@ -0,0 +1,14 @@ +package org.leafygreens.kompendium.models + +// TODO Enum for type? +sealed class OpenApiSpecComponentSchema(open val type: String) + +data class ObjectSchema( + val properties: Map +) : OpenApiSpecComponentSchema("object") + +data class SimpleSchema(override val type: String) : OpenApiSpecComponentSchema(type) + +data class FormatSchema(val format: String, override val type: String) : OpenApiSpecComponentSchema(type) + +data class ArraySchema(val arrayType: String) : OpenApiSpecComponentSchema("array") diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponents.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponents.kt index dea3d65d3..21e4be94a 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponents.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponents.kt @@ -2,5 +2,6 @@ package org.leafygreens.kompendium.models // TODO I *think* the only thing I need here is the security https://swagger.io/specification/#components-object data class OpenApiSpecComponents( + val components: MutableMap, val securitySchemes: Map ) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt index dfb04aad5..c5981d827 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt @@ -1,19 +1,19 @@ package org.leafygreens.kompendium.models data class OpenApiSpecPathItemOperation( - val tags: Set = emptySet(), - val summary: String? = null, - val description: String? = null, - val externalDocs: OpenApiSpecExternalDocumentation? = null, - val operationId: String? = null, - val parameters: List? = null, - val requestBody: OpenApiSpecReferencable? = null, + var tags: Set = emptySet(), + var summary: String? = null, + var description: String? = null, + var externalDocs: OpenApiSpecExternalDocumentation? = null, + var operationId: String? = null, + var parameters: List? = null, + var requestBody: OpenApiSpecReferencable? = null, // TODO How to enforce `default` requirement 🧐 - val responses: Map? = null, - val callbacks: Map? = null, - val deprecated: Boolean = false, + var responses: Map? = null, + var callbacks: Map? = null, + var deprecated: Boolean = false, // todo big yikes... also needs to reference objects in the security scheme 🤔 - val security: List>>? = null, - val servers: List? = null, - val `x-codegen-request-body-name`: String? = null + var security: List>>? = null, + var servers: List? = null, + var `x-codegen-request-body-name`: String? = null ) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt new file mode 100644 index 000000000..22609c628 --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt @@ -0,0 +1,45 @@ +package org.leafygreens.kompendium.util + +import io.ktor.routing.PathSegmentConstantRouteSelector +import io.ktor.routing.PathSegmentParameterRouteSelector +import io.ktor.routing.RootRouteSelector +import io.ktor.routing.Route +import io.ktor.util.InternalAPI +import kotlin.reflect.KClass +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.javaField +import org.leafygreens.kompendium.annotations.KompendiumField +import org.leafygreens.kompendium.models.FormatSchema +import org.leafygreens.kompendium.models.ObjectSchema +import org.leafygreens.kompendium.models.SimpleSchema + +object Helpers { + + @OptIn(InternalAPI::class) + fun Route.calculatePath(tail: String = ""): String = when (selector) { + is RootRouteSelector -> tail + is PathSegmentParameterRouteSelector -> parent?.calculatePath("/$selector$tail") ?: "/{$selector}$tail" + is PathSegmentConstantRouteSelector -> parent?.calculatePath("/$selector$tail") ?: "/$selector$tail" + else -> error("unknown selector type $selector") + } + + fun objectSchema(clazz: KClass<*>): ObjectSchema = ObjectSchema( + properties = + clazz.memberProperties.associate { prop -> + val field = prop.javaField?.type?.kotlin + val anny = prop.findAnnotation() + // TODO How to handle arrays? + val schema = when (field) { + Int::class -> FormatSchema("int32", "integer") + Long::class -> FormatSchema("int64", "integer") + Double::class -> FormatSchema("double", "number") + Float::class -> FormatSchema("float", "number") + String::class -> SimpleSchema("string") + Boolean::class -> SimpleSchema("boolean") + else -> Helpers.objectSchema(field as KClass<*>) + } + Pair(prop.name, schema) + }) + +} 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 b130eb281..e0feed44d 100644 --- a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt @@ -7,8 +7,7 @@ internal class KompendiumTest { @Test fun `Kompendium can be instantiated with no details`() { - val kompendium = Kompendium() - assertEquals(kompendium.spec.openapi, "3.0.3", "Kompendium has a default spec version of 3.0.3") + assertEquals(Kompendium.openApiSpec.openapi, "3.0.3", "Kompendium has a default spec version of 3.0.3") } } diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt index 289e424e7..0ee72902a 100644 --- a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt @@ -61,7 +61,7 @@ object TestData { url = URI("http://petstore.swagger.io/v2") ) ), - tags = listOf( + tags = mutableListOf( OpenApiSpecTag( name = "pet", description = "Everything about your Pets", 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 b25d3f24d..b3c7390c7 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 @@ -12,9 +12,19 @@ import io.ktor.routing.route import io.ktor.routing.routing import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty -import org.leafygreens.kompendium.Kompendium import org.leafygreens.kompendium.Kompendium.notarizedGet import org.leafygreens.kompendium.Kompendium.notarizedPost +import org.leafygreens.kompendium.Kompendium.notarizedPut +import org.leafygreens.kompendium.Kompendium.notarizedRoute +import org.leafygreens.kompendium.Kompendium.openApiSpec +import org.leafygreens.kompendium.MethodInfo +import org.leafygreens.kompendium.RouteInfo +import org.leafygreens.kompendium.annotations.KompendiumField +import org.leafygreens.kompendium.playground.KompendiumTOC.testIdGetInfo +import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleGetInfo +import org.leafygreens.kompendium.playground.KompendiumTOC.testSinglePostInfo +import org.leafygreens.kompendium.playground.KompendiumTOC.testSinglePutInfo +import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleRouteInfo fun main() { embeddedServer( @@ -24,6 +34,23 @@ fun main() { ).start(wait = true) } +data class A(val a: String, val aa: Int) +data class B( + @KompendiumField(name = "AYY", description = "a field") + val a: A, + val b: Double, +) + +data class C(val c: String) + +object KompendiumTOC { + val testIdGetInfo = MethodInfo("Get Test", "Test for getting", tags = setOf("test", "example", "get")) + val testSingleRouteInfo = RouteInfo("Test", "Route for test") + val testSingleGetInfo = MethodInfo("Another get test", "testing more") + val testSinglePostInfo = MethodInfo("Test post endpoint", "Post your tests here!") + val testSinglePutInfo = MethodInfo("Test put endpoint", "Put your tests here!") +} + fun Application.mainModule() { install(ContentNegotiation) { jackson() @@ -31,22 +58,25 @@ fun Application.mainModule() { routing { route("/test") { route("/{id}") { - notarizedGet { + notarizedGet(testIdGetInfo) { call.respondText("get by id") } } - route("/single") { - notarizedGet { + notarizedRoute(path = "/single", info = testSingleRouteInfo) { + notarizedGet(testSingleGetInfo) { call.respondText("get single") } - notarizedPost { + notarizedPost(testSinglePostInfo) { call.respondText("test post") } + notarizedPut(testSinglePutInfo) { + call.respondText { "hey" } + } } } route("/openapi.json") { get { - call.respond(Kompendium.spec) + call.respond(openApiSpec) } } } -- 2.49.0 From 3cf7bf834d71faf1e492d2da21e478899d774dcb Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 12 Apr 2021 19:59:47 -0400 Subject: [PATCH 3/4] ingesting lists --- .tool-versions | 1 + gradle/wrapper/gradle-wrapper.properties | 2 +- .../models/OpenApiSpecComponentSchema.kt | 2 +- .../leafygreens/kompendium/util/Helpers.kt | 32 +++++++++++++++---- .../leafygreens/kompendium/playground/Main.kt | 4 +-- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/.tool-versions b/.tool-versions index a54bc706e..b76921ca0 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,3 @@ kotlin 1.5.0-M2 java openjdk-14.0.1 +gradle 7.0 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 352ddde00..f371643ee 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-7.1-20210328220041+0000-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt index 92a3d2c30..c0ed29bc9 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt @@ -11,4 +11,4 @@ data class SimpleSchema(override val type: String) : OpenApiSpecComponentSchema( data class FormatSchema(val format: String, override val type: String) : OpenApiSpecComponentSchema(type) -data class ArraySchema(val arrayType: String) : OpenApiSpecComponentSchema("array") +data class ArraySchema(val items: OpenApiSpecComponentSchema) : OpenApiSpecComponentSchema("array") diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt index 22609c628..5d5f2c787 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt @@ -5,13 +5,17 @@ import io.ktor.routing.PathSegmentParameterRouteSelector import io.ktor.routing.RootRouteSelector import io.ktor.routing.Route import io.ktor.util.InternalAPI +import java.lang.reflect.ParameterizedType import kotlin.reflect.KClass +import kotlin.reflect.KProperty import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.javaField import org.leafygreens.kompendium.annotations.KompendiumField +import org.leafygreens.kompendium.models.ArraySchema import org.leafygreens.kompendium.models.FormatSchema import org.leafygreens.kompendium.models.ObjectSchema +import org.leafygreens.kompendium.models.OpenApiSpecComponentSchema import org.leafygreens.kompendium.models.SimpleSchema object Helpers { @@ -30,16 +34,30 @@ object Helpers { val field = prop.javaField?.type?.kotlin val anny = prop.findAnnotation() // TODO How to handle arrays? + val schema = when (field) { - Int::class -> FormatSchema("int32", "integer") - Long::class -> FormatSchema("int64", "integer") - Double::class -> FormatSchema("double", "number") - Float::class -> FormatSchema("float", "number") - String::class -> SimpleSchema("string") - Boolean::class -> SimpleSchema("boolean") - else -> Helpers.objectSchema(field as KClass<*>) + List::class -> listFieldSchema(prop, field) + else -> fieldToSchema(field as KClass<*>) } + Pair(prop.name, schema) }) + private fun listFieldSchema(prop: KProperty<*>, field: KClass<*>): ArraySchema { + val listType = ((prop.javaField?.genericType + as ParameterizedType).actualTypeArguments.first() + as Class<*>).kotlin + return ArraySchema(fieldToSchema(listType)) + } + + private fun fieldToSchema(field: KClass<*>): OpenApiSpecComponentSchema = when (field) { + Int::class -> FormatSchema("int32", "integer") + Long::class -> FormatSchema("int64", "integer") + Double::class -> FormatSchema("double", "number") + Float::class -> FormatSchema("float", "number") + String::class -> SimpleSchema("string") + Boolean::class -> SimpleSchema("boolean") + else -> Helpers.objectSchema(field) + } + } 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 b3c7390c7..b07d50abe 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 @@ -29,12 +29,12 @@ import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleRouteInfo fun main() { embeddedServer( Netty, - port = 8080, + port = 8081, module = Application::mainModule ).start(wait = true) } -data class A(val a: String, val aa: Int) +data class A(val a: String, val aa: Int, val aaa: List) data class B( @KompendiumField(name = "AYY", description = "a field") val a: A, -- 2.49.0 From 6e0c1170dfc0256567ca2aebaac41eba4a63d26c Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 12 Apr 2021 21:13:30 -0400 Subject: [PATCH 4/4] the fuck?? --- CHANGELOG.md | 7 ++ README.md | 52 +++++++- .../org/leafygreens/kompendium/Kompendium.kt | 116 +++++++++++++----- .../kompendium/annotations/KompendiumField.kt | 2 +- .../annotations/KompendiumInternal.kt | 18 +++ .../models/OpenApiSpecComponents.kt | 7 -- .../kompendium/models/meta/MethodInfo.kt | 3 + .../models/{ => oas}/OpenApiSpec.kt | 4 +- .../{ => oas}/OpenApiSpecComponentSchema.kt | 2 +- .../models/oas/OpenApiSpecComponents.kt | 7 ++ .../OpenApiSpecExternalDocumentation.kt | 2 +- .../models/{ => oas}/OpenApiSpecInfo.kt | 2 +- .../{ => oas}/OpenApiSpecInfoContact.kt | 2 +- .../{ => oas}/OpenApiSpecInfoLicense.kt | 2 +- .../models/{ => oas}/OpenApiSpecLink.kt | 2 +- .../models/{ => oas}/OpenApiSpecMediaType.kt | 2 +- .../models/{ => oas}/OpenApiSpecOAuthFlow.kt | 2 +- .../models/{ => oas}/OpenApiSpecOAuthFlows.kt | 2 +- .../models/{ => oas}/OpenApiSpecPathItem.kt | 4 +- .../{ => oas}/OpenApiSpecPathItemOperation.kt | 2 +- .../{ => oas}/OpenApiSpecReferencable.kt | 16 +-- .../models/{ => oas}/OpenApiSpecSchema.kt | 16 +-- .../models/{ => oas}/OpenApiSpecServer.kt | 2 +- .../{ => oas}/OpenApiSpecServerVariable.kt | 2 +- .../models/{ => oas}/OpenApiSpecTag.kt | 2 +- .../leafygreens/kompendium/util/Helpers.kt | 44 +------ .../leafygreens/kompendium/util/TestData.kt | 45 +++---- .../src/test/resources/petstore.json | 2 + .../leafygreens/kompendium/playground/Main.kt | 26 ++-- 29 files changed, 245 insertions(+), 150 deletions(-) create mode 100644 kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumInternal.kt delete mode 100644 kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponents.kt create mode 100644 kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/meta/MethodInfo.kt rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpec.kt (83%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecComponentSchema.kt (91%) create mode 100644 kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecComponents.kt rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecExternalDocumentation.kt (70%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecInfo.kt (85%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecInfoContact.kt (77%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecInfoLicense.kt (68%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecLink.kt (87%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecMediaType.kt (91%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecOAuthFlow.kt (80%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecOAuthFlows.kt (62%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecPathItem.kt (83%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecPathItemOperation.kt (94%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecReferencable.kt (71%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecSchema.kt (62%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecServer.kt (78%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecServerVariable.kt (75%) rename kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/{ => oas}/OpenApiSpecTag.kt (76%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16bbfd887..554f58abc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.0.2] - April 12th, 2021 + +### Added + +- Beginning of an implementation. Currently, able to generate a rough outline of the API at runtime, along with generating +full data classes represented by JSON Schema. + ## [0.0.1] - April 11th, 2021 ### Added diff --git a/README.md b/README.md index abf8d2e0d..f68ca2027 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,56 @@ ## What is Kompendium -Kompendium is intended to be a non-intrusive OpenApi Specification generator for [Ktor](https://ktor.io). -Non-invasive meaning that users will use only Ktor native functions when implementing their API, and will supplement -with Kompendium code in order to generate the appropriate spec. +Kompendium is intended to be a minimally invasive +OpenApi Specification generator for +[Ktor](https://ktor.io). +Minimally invasive meaning that users will use only +Ktor native functions when implementing their API, +and will supplement with Kompendium code in order +to generate the appropriate spec. ## Modules TODO + +## Examples + +```kotlin +// Minimal API Example +fun Application.mainModule() { + install(ContentNegotiation) { + jackson() + } + routing { + route("/test") { + route("/{id}") { + notarizedGet(testIdGetInfo) { + call.respondText("get by id") + } + } + route("/single") { + notarizedGet(testSingleGetInfo) { + call.respondText("get single") + } + notarizedPost(testSinglePostInfo) { + call.respondText("test post") + } + notarizedPut(testSinglePutInfo) { + call.respondText { "hey" } + } + } + } + route("/openapi.json") { + get { + call.respond(openApiSpec.copy( + info = OpenApiSpecInfo( + title = "Test API", + version = "1.3.3.7", + description = "An amazing, fully-ish 😉 generated API spec" + ) + )) + } + } + } +} +``` 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 33ac15714..43e813eb3 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt @@ -6,15 +6,26 @@ import io.ktor.routing.Route import io.ktor.routing.createRouteFromPath import io.ktor.routing.method import io.ktor.util.pipeline.PipelineInterceptor -import org.leafygreens.kompendium.models.OpenApiSpec -import org.leafygreens.kompendium.models.OpenApiSpecInfo -import org.leafygreens.kompendium.models.OpenApiSpecPathItem -import org.leafygreens.kompendium.models.OpenApiSpecPathItemOperation +import java.lang.reflect.ParameterizedType +import kotlin.reflect.KClass +import kotlin.reflect.KProperty +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.javaField +import org.leafygreens.kompendium.annotations.KompendiumField +import org.leafygreens.kompendium.annotations.KompendiumInternal +import org.leafygreens.kompendium.models.oas.ArraySchema +import org.leafygreens.kompendium.models.oas.FormatSchema +import org.leafygreens.kompendium.models.oas.ObjectSchema +import org.leafygreens.kompendium.models.oas.OpenApiSpec +import org.leafygreens.kompendium.models.oas.OpenApiSpecComponentSchema +import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo +import org.leafygreens.kompendium.models.oas.OpenApiSpecPathItem +import org.leafygreens.kompendium.models.oas.OpenApiSpecPathItemOperation +import org.leafygreens.kompendium.models.oas.SimpleSchema +import org.leafygreens.kompendium.models.meta.MethodInfo import org.leafygreens.kompendium.util.Helpers.calculatePath -import org.leafygreens.kompendium.util.Helpers.objectSchema - -data class RouteInfo(val summary: String, val description: String? = null) -data class MethodInfo(val summary: String, val description: String? = null, val tags: Set = emptySet()) +import org.leafygreens.kompendium.util.Helpers.putPairIfAbsent object Kompendium { val openApiSpec = OpenApiSpec( @@ -23,12 +34,6 @@ object Kompendium { paths = mutableMapOf() ) - fun Route.notarizedRoute(path: String, info: RouteInfo, build: Route.() -> Unit): Route { - val fullPath = calculatePath().plus(path) - openApiSpec.paths.getOrPut(fullPath) { OpenApiSpecPathItem(summary = info.summary, description = info.description) } - return createRouteFromPath(path).apply(build) - } - fun Route.notarizedGet(info: MethodInfo, body: PipelineInterceptor): Route { val path = calculatePath() openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } @@ -40,31 +45,84 @@ object Kompendium { return method(HttpMethod.Get) { handle(body) } } - fun Route.notarizedPost(info: MethodInfo, body: PipelineInterceptor): Route { + inline fun Route.notarizedPost( + info: MethodInfo, + noinline body: PipelineInterceptor + ): Route = generateComponentSchemas(info, body) { i, b -> val path = calculatePath() openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } openApiSpec.paths[path]?.post = OpenApiSpecPathItemOperation( - summary = info.summary, - description = info.description, - tags = info.tags + summary = i.summary, + description = i.description, + tags = i.tags ) - return method(HttpMethod.Post) { handle(body) } + return method(HttpMethod.Post) { handle(b) } } - inline fun Route.notarizedPut( + inline fun Route.notarizedPut( info: MethodInfo, - noinline body: PipelineInterceptor - ): Route { - println(objectSchema(TQ::class)) - println(objectSchema(TP::class)) - println(objectSchema(TR::class)) + noinline body: PipelineInterceptor, + ): Route = generateComponentSchemas(info, body) { i, b -> val path = calculatePath() openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() } openApiSpec.paths[path]?.put = OpenApiSpecPathItemOperation( - summary = info.summary, - description = info.description, - tags = info.tags + summary = i.summary, + description = i.description, + tags = i.tags ) - return method(HttpMethod.Put) { handle(body) } + return method(HttpMethod.Put) { handle(b) } + } + + @OptIn(KompendiumInternal::class) + inline fun generateComponentSchemas( + info: MethodInfo, + noinline body: PipelineInterceptor, + block: (MethodInfo, PipelineInterceptor) -> Route + ): Route { + openApiSpec.components.schemas.putPairIfAbsent(objectSchemaPair(TQ::class)) + openApiSpec.components.schemas.putPairIfAbsent(objectSchemaPair(TR::class)) + openApiSpec.components.schemas.putPairIfAbsent(objectSchemaPair(TP::class)) + return block.invoke(info, body) + } + + @KompendiumInternal + // TODO Investigate a caching mechanism to reduce overhead... then just reference once created + fun objectSchemaPair(clazz: KClass<*>): Pair { + val o = objectSchema(clazz) + return Pair(clazz.qualifiedName!!, o) + } + + private fun objectSchema(clazz: KClass<*>): ObjectSchema = + ObjectSchema(properties = clazz.memberProperties.associate { prop -> + val field = prop.javaField?.type?.kotlin + val anny = prop.findAnnotation() + val schema = when (field) { + List::class -> listFieldSchema(prop) + else -> fieldToSchema(field as KClass<*>) + } + + val name = anny?.let { + anny.name + } ?: prop.name + + Pair(name, schema) + }) + + private fun listFieldSchema(prop: KProperty<*>): ArraySchema { + val listType = ((prop.javaField?.genericType + as ParameterizedType).actualTypeArguments.first() + as Class<*>).kotlin + return ArraySchema(fieldToSchema(listType)) + } + + @OptIn(KompendiumInternal::class) + private fun fieldToSchema(field: KClass<*>): OpenApiSpecComponentSchema = when (field) { + Int::class -> FormatSchema("int32", "integer") + Long::class -> FormatSchema("int64", "integer") + Double::class -> FormatSchema("double", "number") + Float::class -> FormatSchema("float", "number") + String::class -> SimpleSchema("string") + Boolean::class -> SimpleSchema("boolean") + else -> objectSchema(field) } } diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumField.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumField.kt index 754c073db..cc64f8da0 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumField.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumField.kt @@ -2,4 +2,4 @@ package org.leafygreens.kompendium.annotations @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.PROPERTY) -annotation class KompendiumField(val name: String, val description: String) +annotation class KompendiumField(val name: String) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumInternal.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumInternal.kt new file mode 100644 index 000000000..2146e2350 --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/annotations/KompendiumInternal.kt @@ -0,0 +1,18 @@ +package org.leafygreens.kompendium.annotations + +@Suppress("DEPRECATION") +@RequiresOptIn( + level = RequiresOptIn.Level.WARNING, + message = "This API internal to Kompendium and should not be used. It could be removed or changed without notice." +) +@Experimental(level = Experimental.Level.WARNING) +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.TYPEALIAS, + AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY, + AnnotationTarget.FIELD, + AnnotationTarget.CONSTRUCTOR, + AnnotationTarget.PROPERTY_SETTER +) +annotation class KompendiumInternal diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponents.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponents.kt deleted file mode 100644 index 21e4be94a..000000000 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponents.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.leafygreens.kompendium.models - -// TODO I *think* the only thing I need here is the security https://swagger.io/specification/#components-object -data class OpenApiSpecComponents( - val components: MutableMap, - val securitySchemes: Map -) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/meta/MethodInfo.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/meta/MethodInfo.kt new file mode 100644 index 000000000..1d4588229 --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/meta/MethodInfo.kt @@ -0,0 +1,3 @@ +package org.leafygreens.kompendium.models.meta + +data class MethodInfo(val summary: String, val description: String? = null, val tags: Set = emptySet()) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpec.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpec.kt similarity index 83% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpec.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpec.kt index 49bc56763..a5205a84d 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpec.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpec.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas data class OpenApiSpec( val openapi: String = "3.0.3", @@ -6,7 +6,7 @@ data class OpenApiSpec( // TODO Needs to default to server object with url of `/` val servers: MutableList = mutableListOf(), val paths: MutableMap = mutableMapOf(), - val components: OpenApiSpecComponents? = null, + val components: OpenApiSpecComponents = OpenApiSpecComponents(), // todo needs to reference objects in the components -> security scheme 🤔 val security: MutableList>> = mutableListOf(), val tags: MutableList = mutableListOf(), diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecComponentSchema.kt similarity index 91% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecComponentSchema.kt index c0ed29bc9..eae3b6d36 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecComponentSchema.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecComponentSchema.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas // TODO Enum for type? sealed class OpenApiSpecComponentSchema(open val type: String) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecComponents.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecComponents.kt new file mode 100644 index 000000000..69c0c0c1c --- /dev/null +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecComponents.kt @@ -0,0 +1,7 @@ +package org.leafygreens.kompendium.models.oas + +// TODO I *think* the only thing I need here is the security https://swagger.io/specification/#components-object +data class OpenApiSpecComponents( + val schemas: MutableMap = mutableMapOf(), + val securitySchemes: MutableMap = mutableMapOf() +) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecExternalDocumentation.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecExternalDocumentation.kt similarity index 70% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecExternalDocumentation.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecExternalDocumentation.kt index baaf1b8b8..42398d333 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecExternalDocumentation.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecExternalDocumentation.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas import java.net.URI diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfo.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfo.kt similarity index 85% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfo.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfo.kt index 86cf5dedd..e37e32217 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfo.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfo.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas import java.net.URI diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoContact.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfoContact.kt similarity index 77% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoContact.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfoContact.kt index 1a2042b7f..e70e54589 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoContact.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfoContact.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas import java.net.URI diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoLicense.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfoLicense.kt similarity index 68% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoLicense.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfoLicense.kt index 293a11ab7..a8f6af80b 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoLicense.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecInfoLicense.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas import java.net.URI diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecLink.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecLink.kt similarity index 87% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecLink.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecLink.kt index 95f1ee16e..5f345ec88 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecLink.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecLink.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas data class OpenApiSpecLink( val operationRef: String?, // todo mutually exclusive with operationId diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecMediaType.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecMediaType.kt similarity index 91% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecMediaType.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecMediaType.kt index a413eebc1..b792586fd 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecMediaType.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecMediaType.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas // TODO Oof -> https://swagger.io/specification/#media-type-object data class OpenApiSpecMediaType( diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecOAuthFlow.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecOAuthFlow.kt similarity index 80% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecOAuthFlow.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecOAuthFlow.kt index 4e7820f32..8a63b8216 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecOAuthFlow.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecOAuthFlow.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas import java.net.URI diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecOAuthFlows.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecOAuthFlows.kt similarity index 62% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecOAuthFlows.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecOAuthFlows.kt index 9e380a20a..d9612cfe8 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecOAuthFlows.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecOAuthFlows.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas data class OpenApiSpecOAuthFlows( val implicit: OpenApiSpecOAuthFlow?, diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecPathItem.kt similarity index 83% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecPathItem.kt index 3d10b6d79..cf41f5b8b 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecPathItem.kt @@ -1,8 +1,6 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas data class OpenApiSpecPathItem( - var summary: String? = null, - var description: String? = null, var get: OpenApiSpecPathItemOperation? = null, var put: OpenApiSpecPathItemOperation? = null, var post: OpenApiSpecPathItemOperation? = null, diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecPathItemOperation.kt similarity index 94% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecPathItemOperation.kt index c5981d827..e894f2786 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecPathItemOperation.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas data class OpenApiSpecPathItemOperation( var tags: Set = emptySet(), diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecReferencable.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecReferencable.kt similarity index 71% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecReferencable.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecReferencable.kt index 07a1385e8..eeee70701 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecReferencable.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecReferencable.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas sealed class OpenApiSpecReferencable @@ -9,10 +9,10 @@ data class OpenApiSpecCallback( ) : OpenApiSpecReferencable() data class OpenApiSpecResponse( - val description: String? = null, - val headers: Map? = null, - val content: Map? = null, - val links: Map? = null + val description: String? = null, + val headers: Map? = null, + val content: Map? = null, + val links: Map? = null ) : OpenApiSpecReferencable() data class OpenApiSpecHeader( @@ -34,7 +34,7 @@ data class OpenApiSpecParameter( ) : OpenApiSpecReferencable() data class OpenApiSpecRequest( - val description: String?, - val content: Map, - val required: Boolean = false + val description: String?, + val content: Map, + val required: Boolean = false ) : OpenApiSpecReferencable() diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecSchema.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecSchema.kt similarity index 62% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecSchema.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecSchema.kt index e660f154a..c999deeea 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecSchema.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecSchema.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas sealed class OpenApiSpecSchema @@ -21,11 +21,11 @@ data class OpenApiSpecSchemaRef( ) : OpenApiSpecSchema() data class OpenApiSpecSchemaSecurity( - val type: String? = null, // TODO Enum? "apiKey", "http", "oauth2", "openIdConnect" - val name: String? = null, - val `in`: String? = null, - val scheme: String? = null, - val flows: OpenApiSpecOAuthFlows? = null, - val bearerFormat: String? = null, - val description: String? = null, + val type: String? = null, // TODO Enum? "apiKey", "http", "oauth2", "openIdConnect" + val name: String? = null, + val `in`: String? = null, + val scheme: String? = null, + val flows: OpenApiSpecOAuthFlows? = null, + val bearerFormat: String? = null, + val description: String? = null, ) : OpenApiSpecSchema() diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServer.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecServer.kt similarity index 78% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServer.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecServer.kt index 60fa659bb..bd58384b5 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServer.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecServer.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas import java.net.URI diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServerVariable.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecServerVariable.kt similarity index 75% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServerVariable.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecServerVariable.kt index b50d219e5..ce6504c8d 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServerVariable.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecServerVariable.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas data class OpenApiSpecServerVariable( val `enum`: Set, // todo enforce not empty diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTag.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecTag.kt similarity index 76% rename from kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTag.kt rename to kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecTag.kt index 21c8c11e5..276800b94 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTag.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/oas/OpenApiSpecTag.kt @@ -1,4 +1,4 @@ -package org.leafygreens.kompendium.models +package org.leafygreens.kompendium.models.oas data class OpenApiSpecTag( val name: String, diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt index 5d5f2c787..c0c1f722d 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/Helpers.kt @@ -5,18 +5,6 @@ import io.ktor.routing.PathSegmentParameterRouteSelector import io.ktor.routing.RootRouteSelector import io.ktor.routing.Route import io.ktor.util.InternalAPI -import java.lang.reflect.ParameterizedType -import kotlin.reflect.KClass -import kotlin.reflect.KProperty -import kotlin.reflect.full.findAnnotation -import kotlin.reflect.full.memberProperties -import kotlin.reflect.jvm.javaField -import org.leafygreens.kompendium.annotations.KompendiumField -import org.leafygreens.kompendium.models.ArraySchema -import org.leafygreens.kompendium.models.FormatSchema -import org.leafygreens.kompendium.models.ObjectSchema -import org.leafygreens.kompendium.models.OpenApiSpecComponentSchema -import org.leafygreens.kompendium.models.SimpleSchema object Helpers { @@ -28,36 +16,6 @@ object Helpers { else -> error("unknown selector type $selector") } - fun objectSchema(clazz: KClass<*>): ObjectSchema = ObjectSchema( - properties = - clazz.memberProperties.associate { prop -> - val field = prop.javaField?.type?.kotlin - val anny = prop.findAnnotation() - // TODO How to handle arrays? - - val schema = when (field) { - List::class -> listFieldSchema(prop, field) - else -> fieldToSchema(field as KClass<*>) - } - - Pair(prop.name, schema) - }) - - private fun listFieldSchema(prop: KProperty<*>, field: KClass<*>): ArraySchema { - val listType = ((prop.javaField?.genericType - as ParameterizedType).actualTypeArguments.first() - as Class<*>).kotlin - return ArraySchema(fieldToSchema(listType)) - } - - private fun fieldToSchema(field: KClass<*>): OpenApiSpecComponentSchema = when (field) { - Int::class -> FormatSchema("int32", "integer") - Long::class -> FormatSchema("int64", "integer") - Double::class -> FormatSchema("double", "number") - Float::class -> FormatSchema("float", "number") - String::class -> SimpleSchema("string") - Boolean::class -> SimpleSchema("boolean") - else -> Helpers.objectSchema(field) - } + fun MutableMap.putPairIfAbsent(pair: Pair) = putIfAbsent(pair.first, pair.second) } diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt index 0ee72902a..9c422b65d 100644 --- a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt @@ -2,26 +2,26 @@ package org.leafygreens.kompendium.util import java.io.File import java.net.URI -import org.leafygreens.kompendium.models.OpenApiSpec -import org.leafygreens.kompendium.models.OpenApiSpecComponents -import org.leafygreens.kompendium.models.OpenApiSpecExternalDocumentation -import org.leafygreens.kompendium.models.OpenApiSpecInfo -import org.leafygreens.kompendium.models.OpenApiSpecInfoContact -import org.leafygreens.kompendium.models.OpenApiSpecInfoLicense -import org.leafygreens.kompendium.models.OpenApiSpecMediaType -import org.leafygreens.kompendium.models.OpenApiSpecOAuthFlow -import org.leafygreens.kompendium.models.OpenApiSpecOAuthFlows -import org.leafygreens.kompendium.models.OpenApiSpecParameter -import org.leafygreens.kompendium.models.OpenApiSpecPathItem -import org.leafygreens.kompendium.models.OpenApiSpecPathItemOperation -import org.leafygreens.kompendium.models.OpenApiSpecRequest -import org.leafygreens.kompendium.models.OpenApiSpecResponse -import org.leafygreens.kompendium.models.OpenApiSpecSchemaArray -import org.leafygreens.kompendium.models.OpenApiSpecSchemaRef -import org.leafygreens.kompendium.models.OpenApiSpecSchemaSecurity -import org.leafygreens.kompendium.models.OpenApiSpecSchemaString -import org.leafygreens.kompendium.models.OpenApiSpecServer -import org.leafygreens.kompendium.models.OpenApiSpecTag +import org.leafygreens.kompendium.models.oas.OpenApiSpec +import org.leafygreens.kompendium.models.oas.OpenApiSpecComponents +import org.leafygreens.kompendium.models.oas.OpenApiSpecExternalDocumentation +import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo +import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoContact +import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoLicense +import org.leafygreens.kompendium.models.oas.OpenApiSpecMediaType +import org.leafygreens.kompendium.models.oas.OpenApiSpecOAuthFlow +import org.leafygreens.kompendium.models.oas.OpenApiSpecOAuthFlows +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.OpenApiSpecRequest +import org.leafygreens.kompendium.models.oas.OpenApiSpecResponse +import org.leafygreens.kompendium.models.oas.OpenApiSpecSchemaArray +import org.leafygreens.kompendium.models.oas.OpenApiSpecSchemaRef +import org.leafygreens.kompendium.models.oas.OpenApiSpecSchemaSecurity +import org.leafygreens.kompendium.models.oas.OpenApiSpecSchemaString +import org.leafygreens.kompendium.models.oas.OpenApiSpecServer +import org.leafygreens.kompendium.models.oas.OpenApiSpecTag object TestData { fun getFileSnapshot(fileName: String): String { @@ -201,7 +201,7 @@ object TestData { ) ), components = OpenApiSpecComponents( - securitySchemes = mapOf( + securitySchemes = mutableMapOf( "petstore_auth" to OpenApiSpecSchemaSecurity( type = "oauth2", flows = OpenApiSpecOAuthFlows( @@ -219,7 +219,8 @@ object TestData { name = "api_key", `in` = "header" ) - ) + ), + schemas = mutableMapOf() ) ) } diff --git a/kompendium-core/src/test/resources/petstore.json b/kompendium-core/src/test/resources/petstore.json index 520560bcf..d9341435c 100644 --- a/kompendium-core/src/test/resources/petstore.json +++ b/kompendium-core/src/test/resources/petstore.json @@ -153,6 +153,7 @@ } }, "components" : { + "schemas" : { }, "securitySchemes" : { "petstore_auth" : { "type" : "oauth2", @@ -173,6 +174,7 @@ } } }, + "security" : [ ], "tags" : [ { "name" : "pet", "description" : "Everything about your Pets", 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 b07d50abe..56b70f2c2 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 @@ -15,16 +15,14 @@ import io.ktor.server.netty.Netty import org.leafygreens.kompendium.Kompendium.notarizedGet import org.leafygreens.kompendium.Kompendium.notarizedPost import org.leafygreens.kompendium.Kompendium.notarizedPut -import org.leafygreens.kompendium.Kompendium.notarizedRoute import org.leafygreens.kompendium.Kompendium.openApiSpec -import org.leafygreens.kompendium.MethodInfo -import org.leafygreens.kompendium.RouteInfo import org.leafygreens.kompendium.annotations.KompendiumField +import org.leafygreens.kompendium.models.meta.MethodInfo +import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo import org.leafygreens.kompendium.playground.KompendiumTOC.testIdGetInfo import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleGetInfo import org.leafygreens.kompendium.playground.KompendiumTOC.testSinglePostInfo import org.leafygreens.kompendium.playground.KompendiumTOC.testSinglePutInfo -import org.leafygreens.kompendium.playground.KompendiumTOC.testSingleRouteInfo fun main() { embeddedServer( @@ -36,16 +34,16 @@ fun main() { data class A(val a: String, val aa: Int, val aaa: List) data class B( - @KompendiumField(name = "AYY", description = "a field") + @KompendiumField(name = "AYY") val a: A, val b: Double, ) - data class C(val c: String) +data class D(val a: A, val b: B, val c: C) + object KompendiumTOC { val testIdGetInfo = MethodInfo("Get Test", "Test for getting", tags = setOf("test", "example", "get")) - val testSingleRouteInfo = RouteInfo("Test", "Route for test") val testSingleGetInfo = MethodInfo("Another get test", "testing more") val testSinglePostInfo = MethodInfo("Test post endpoint", "Post your tests here!") val testSinglePutInfo = MethodInfo("Test put endpoint", "Put your tests here!") @@ -62,21 +60,27 @@ fun Application.mainModule() { call.respondText("get by id") } } - notarizedRoute(path = "/single", info = testSingleRouteInfo) { + route("/single") { notarizedGet(testSingleGetInfo) { call.respondText("get single") } - notarizedPost(testSinglePostInfo) { + notarizedPost(testSinglePostInfo) { call.respondText("test post") } - notarizedPut(testSinglePutInfo) { + notarizedPut(testSinglePutInfo) { call.respondText { "hey" } } } } route("/openapi.json") { get { - call.respond(openApiSpec) + call.respond(openApiSpec.copy( + info = OpenApiSpecInfo( + title = "Test API", + version = "1.3.3.7", + description = "An amazing, fully-ish 😉 generated API spec" + ) + )) } } } -- 2.49.0